162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Digital Audio (PCM) abstract layer 462306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/time.h> 1162306a36Sopenharmony_ci#include <linux/mutex.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/nospec.h> 1462306a36Sopenharmony_ci#include <sound/core.h> 1562306a36Sopenharmony_ci#include <sound/minors.h> 1662306a36Sopenharmony_ci#include <sound/pcm.h> 1762306a36Sopenharmony_ci#include <sound/timer.h> 1862306a36Sopenharmony_ci#include <sound/control.h> 1962306a36Sopenharmony_ci#include <sound/info.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "pcm_local.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>"); 2462306a36Sopenharmony_ciMODULE_DESCRIPTION("Midlevel PCM code for ALSA."); 2562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic LIST_HEAD(snd_pcm_devices); 2862306a36Sopenharmony_cistatic DEFINE_MUTEX(register_mutex); 2962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS) 3062306a36Sopenharmony_cistatic LIST_HEAD(snd_pcm_notify_list); 3162306a36Sopenharmony_ci#endif 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int snd_pcm_free(struct snd_pcm *pcm); 3462306a36Sopenharmony_cistatic int snd_pcm_dev_free(struct snd_device *device); 3562306a36Sopenharmony_cistatic int snd_pcm_dev_register(struct snd_device *device); 3662306a36Sopenharmony_cistatic int snd_pcm_dev_disconnect(struct snd_device *device); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic struct snd_pcm *snd_pcm_get(struct snd_card *card, int device) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct snd_pcm *pcm; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci list_for_each_entry(pcm, &snd_pcm_devices, list) { 4362306a36Sopenharmony_ci if (pcm->card == card && pcm->device == device) 4462306a36Sopenharmony_ci return pcm; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci return NULL; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int snd_pcm_next(struct snd_card *card, int device) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct snd_pcm *pcm; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci list_for_each_entry(pcm, &snd_pcm_devices, list) { 5462306a36Sopenharmony_ci if (pcm->card == card && pcm->device > device) 5562306a36Sopenharmony_ci return pcm->device; 5662306a36Sopenharmony_ci else if (pcm->card->number > card->number) 5762306a36Sopenharmony_ci return -1; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci return -1; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic int snd_pcm_add(struct snd_pcm *newpcm) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct snd_pcm *pcm; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (newpcm->internal) 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci list_for_each_entry(pcm, &snd_pcm_devices, list) { 7062306a36Sopenharmony_ci if (pcm->card == newpcm->card && pcm->device == newpcm->device) 7162306a36Sopenharmony_ci return -EBUSY; 7262306a36Sopenharmony_ci if (pcm->card->number > newpcm->card->number || 7362306a36Sopenharmony_ci (pcm->card == newpcm->card && 7462306a36Sopenharmony_ci pcm->device > newpcm->device)) { 7562306a36Sopenharmony_ci list_add(&newpcm->list, pcm->list.prev); 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci list_add_tail(&newpcm->list, &snd_pcm_devices); 8062306a36Sopenharmony_ci return 0; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int snd_pcm_control_ioctl(struct snd_card *card, 8462306a36Sopenharmony_ci struct snd_ctl_file *control, 8562306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci switch (cmd) { 8862306a36Sopenharmony_ci case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE: 8962306a36Sopenharmony_ci { 9062306a36Sopenharmony_ci int device; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (get_user(device, (int __user *)arg)) 9362306a36Sopenharmony_ci return -EFAULT; 9462306a36Sopenharmony_ci mutex_lock(®ister_mutex); 9562306a36Sopenharmony_ci device = snd_pcm_next(card, device); 9662306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 9762306a36Sopenharmony_ci if (put_user(device, (int __user *)arg)) 9862306a36Sopenharmony_ci return -EFAULT; 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci case SNDRV_CTL_IOCTL_PCM_INFO: 10262306a36Sopenharmony_ci { 10362306a36Sopenharmony_ci struct snd_pcm_info __user *info; 10462306a36Sopenharmony_ci unsigned int device, subdevice; 10562306a36Sopenharmony_ci int stream; 10662306a36Sopenharmony_ci struct snd_pcm *pcm; 10762306a36Sopenharmony_ci struct snd_pcm_str *pstr; 10862306a36Sopenharmony_ci struct snd_pcm_substream *substream; 10962306a36Sopenharmony_ci int err; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci info = (struct snd_pcm_info __user *)arg; 11262306a36Sopenharmony_ci if (get_user(device, &info->device)) 11362306a36Sopenharmony_ci return -EFAULT; 11462306a36Sopenharmony_ci if (get_user(stream, &info->stream)) 11562306a36Sopenharmony_ci return -EFAULT; 11662306a36Sopenharmony_ci if (stream < 0 || stream > 1) 11762306a36Sopenharmony_ci return -EINVAL; 11862306a36Sopenharmony_ci stream = array_index_nospec(stream, 2); 11962306a36Sopenharmony_ci if (get_user(subdevice, &info->subdevice)) 12062306a36Sopenharmony_ci return -EFAULT; 12162306a36Sopenharmony_ci mutex_lock(®ister_mutex); 12262306a36Sopenharmony_ci pcm = snd_pcm_get(card, device); 12362306a36Sopenharmony_ci if (pcm == NULL) { 12462306a36Sopenharmony_ci err = -ENXIO; 12562306a36Sopenharmony_ci goto _error; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci pstr = &pcm->streams[stream]; 12862306a36Sopenharmony_ci if (pstr->substream_count == 0) { 12962306a36Sopenharmony_ci err = -ENOENT; 13062306a36Sopenharmony_ci goto _error; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci if (subdevice >= pstr->substream_count) { 13362306a36Sopenharmony_ci err = -ENXIO; 13462306a36Sopenharmony_ci goto _error; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci for (substream = pstr->substream; substream; 13762306a36Sopenharmony_ci substream = substream->next) 13862306a36Sopenharmony_ci if (substream->number == (int)subdevice) 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci if (substream == NULL) { 14162306a36Sopenharmony_ci err = -ENXIO; 14262306a36Sopenharmony_ci goto _error; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci mutex_lock(&pcm->open_mutex); 14562306a36Sopenharmony_ci err = snd_pcm_info_user(substream, info); 14662306a36Sopenharmony_ci mutex_unlock(&pcm->open_mutex); 14762306a36Sopenharmony_ci _error: 14862306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 14962306a36Sopenharmony_ci return err; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE: 15262306a36Sopenharmony_ci { 15362306a36Sopenharmony_ci int val; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (get_user(val, (int __user *)arg)) 15662306a36Sopenharmony_ci return -EFAULT; 15762306a36Sopenharmony_ci control->preferred_subdevice[SND_CTL_SUBDEV_PCM] = val; 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci return -ENOIOCTLCMD; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic const char * const snd_pcm_format_names[] = { 16762306a36Sopenharmony_ci FORMAT(S8), 16862306a36Sopenharmony_ci FORMAT(U8), 16962306a36Sopenharmony_ci FORMAT(S16_LE), 17062306a36Sopenharmony_ci FORMAT(S16_BE), 17162306a36Sopenharmony_ci FORMAT(U16_LE), 17262306a36Sopenharmony_ci FORMAT(U16_BE), 17362306a36Sopenharmony_ci FORMAT(S24_LE), 17462306a36Sopenharmony_ci FORMAT(S24_BE), 17562306a36Sopenharmony_ci FORMAT(U24_LE), 17662306a36Sopenharmony_ci FORMAT(U24_BE), 17762306a36Sopenharmony_ci FORMAT(S32_LE), 17862306a36Sopenharmony_ci FORMAT(S32_BE), 17962306a36Sopenharmony_ci FORMAT(U32_LE), 18062306a36Sopenharmony_ci FORMAT(U32_BE), 18162306a36Sopenharmony_ci FORMAT(FLOAT_LE), 18262306a36Sopenharmony_ci FORMAT(FLOAT_BE), 18362306a36Sopenharmony_ci FORMAT(FLOAT64_LE), 18462306a36Sopenharmony_ci FORMAT(FLOAT64_BE), 18562306a36Sopenharmony_ci FORMAT(IEC958_SUBFRAME_LE), 18662306a36Sopenharmony_ci FORMAT(IEC958_SUBFRAME_BE), 18762306a36Sopenharmony_ci FORMAT(MU_LAW), 18862306a36Sopenharmony_ci FORMAT(A_LAW), 18962306a36Sopenharmony_ci FORMAT(IMA_ADPCM), 19062306a36Sopenharmony_ci FORMAT(MPEG), 19162306a36Sopenharmony_ci FORMAT(GSM), 19262306a36Sopenharmony_ci FORMAT(SPECIAL), 19362306a36Sopenharmony_ci FORMAT(S24_3LE), 19462306a36Sopenharmony_ci FORMAT(S24_3BE), 19562306a36Sopenharmony_ci FORMAT(U24_3LE), 19662306a36Sopenharmony_ci FORMAT(U24_3BE), 19762306a36Sopenharmony_ci FORMAT(S20_3LE), 19862306a36Sopenharmony_ci FORMAT(S20_3BE), 19962306a36Sopenharmony_ci FORMAT(U20_3LE), 20062306a36Sopenharmony_ci FORMAT(U20_3BE), 20162306a36Sopenharmony_ci FORMAT(S18_3LE), 20262306a36Sopenharmony_ci FORMAT(S18_3BE), 20362306a36Sopenharmony_ci FORMAT(U18_3LE), 20462306a36Sopenharmony_ci FORMAT(U18_3BE), 20562306a36Sopenharmony_ci FORMAT(G723_24), 20662306a36Sopenharmony_ci FORMAT(G723_24_1B), 20762306a36Sopenharmony_ci FORMAT(G723_40), 20862306a36Sopenharmony_ci FORMAT(G723_40_1B), 20962306a36Sopenharmony_ci FORMAT(DSD_U8), 21062306a36Sopenharmony_ci FORMAT(DSD_U16_LE), 21162306a36Sopenharmony_ci FORMAT(DSD_U32_LE), 21262306a36Sopenharmony_ci FORMAT(DSD_U16_BE), 21362306a36Sopenharmony_ci FORMAT(DSD_U32_BE), 21462306a36Sopenharmony_ci}; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/** 21762306a36Sopenharmony_ci * snd_pcm_format_name - Return a name string for the given PCM format 21862306a36Sopenharmony_ci * @format: PCM format 21962306a36Sopenharmony_ci * 22062306a36Sopenharmony_ci * Return: the format name string 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ciconst char *snd_pcm_format_name(snd_pcm_format_t format) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names)) 22562306a36Sopenharmony_ci return "Unknown"; 22662306a36Sopenharmony_ci return snd_pcm_format_names[(__force unsigned int)format]; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_pcm_format_name); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci#ifdef CONFIG_SND_VERBOSE_PROCFS 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci#define STATE(v) [SNDRV_PCM_STATE_##v] = #v 23362306a36Sopenharmony_ci#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v 23462306a36Sopenharmony_ci#define READY(v) [SNDRV_PCM_READY_##v] = #v 23562306a36Sopenharmony_ci#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v 23662306a36Sopenharmony_ci#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v 23762306a36Sopenharmony_ci#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v 23862306a36Sopenharmony_ci#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v 23962306a36Sopenharmony_ci#define START(v) [SNDRV_PCM_START_##v] = #v 24062306a36Sopenharmony_ci#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic const char * const snd_pcm_stream_names[] = { 24362306a36Sopenharmony_ci STREAM(PLAYBACK), 24462306a36Sopenharmony_ci STREAM(CAPTURE), 24562306a36Sopenharmony_ci}; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic const char * const snd_pcm_state_names[] = { 24862306a36Sopenharmony_ci STATE(OPEN), 24962306a36Sopenharmony_ci STATE(SETUP), 25062306a36Sopenharmony_ci STATE(PREPARED), 25162306a36Sopenharmony_ci STATE(RUNNING), 25262306a36Sopenharmony_ci STATE(XRUN), 25362306a36Sopenharmony_ci STATE(DRAINING), 25462306a36Sopenharmony_ci STATE(PAUSED), 25562306a36Sopenharmony_ci STATE(SUSPENDED), 25662306a36Sopenharmony_ci STATE(DISCONNECTED), 25762306a36Sopenharmony_ci}; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic const char * const snd_pcm_access_names[] = { 26062306a36Sopenharmony_ci ACCESS(MMAP_INTERLEAVED), 26162306a36Sopenharmony_ci ACCESS(MMAP_NONINTERLEAVED), 26262306a36Sopenharmony_ci ACCESS(MMAP_COMPLEX), 26362306a36Sopenharmony_ci ACCESS(RW_INTERLEAVED), 26462306a36Sopenharmony_ci ACCESS(RW_NONINTERLEAVED), 26562306a36Sopenharmony_ci}; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic const char * const snd_pcm_subformat_names[] = { 26862306a36Sopenharmony_ci SUBFORMAT(STD), 26962306a36Sopenharmony_ci}; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic const char * const snd_pcm_tstamp_mode_names[] = { 27262306a36Sopenharmony_ci TSTAMP(NONE), 27362306a36Sopenharmony_ci TSTAMP(ENABLE), 27462306a36Sopenharmony_ci}; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic const char *snd_pcm_stream_name(int stream) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci return snd_pcm_stream_names[stream]; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic const char *snd_pcm_access_name(snd_pcm_access_t access) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci return snd_pcm_access_names[(__force int)access]; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci return snd_pcm_subformat_names[(__force int)subformat]; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic const char *snd_pcm_tstamp_mode_name(int mode) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci return snd_pcm_tstamp_mode_names[mode]; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic const char *snd_pcm_state_name(snd_pcm_state_t state) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci return snd_pcm_state_names[(__force int)state]; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS) 30262306a36Sopenharmony_ci#include <linux/soundcard.h> 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic const char *snd_pcm_oss_format_name(int format) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci switch (format) { 30762306a36Sopenharmony_ci case AFMT_MU_LAW: 30862306a36Sopenharmony_ci return "MU_LAW"; 30962306a36Sopenharmony_ci case AFMT_A_LAW: 31062306a36Sopenharmony_ci return "A_LAW"; 31162306a36Sopenharmony_ci case AFMT_IMA_ADPCM: 31262306a36Sopenharmony_ci return "IMA_ADPCM"; 31362306a36Sopenharmony_ci case AFMT_U8: 31462306a36Sopenharmony_ci return "U8"; 31562306a36Sopenharmony_ci case AFMT_S16_LE: 31662306a36Sopenharmony_ci return "S16_LE"; 31762306a36Sopenharmony_ci case AFMT_S16_BE: 31862306a36Sopenharmony_ci return "S16_BE"; 31962306a36Sopenharmony_ci case AFMT_S8: 32062306a36Sopenharmony_ci return "S8"; 32162306a36Sopenharmony_ci case AFMT_U16_LE: 32262306a36Sopenharmony_ci return "U16_LE"; 32362306a36Sopenharmony_ci case AFMT_U16_BE: 32462306a36Sopenharmony_ci return "U16_BE"; 32562306a36Sopenharmony_ci case AFMT_MPEG: 32662306a36Sopenharmony_ci return "MPEG"; 32762306a36Sopenharmony_ci default: 32862306a36Sopenharmony_ci return "unknown"; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci#endif 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, 33462306a36Sopenharmony_ci struct snd_info_buffer *buffer) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct snd_pcm_info *info; 33762306a36Sopenharmony_ci int err; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (! substream) 34062306a36Sopenharmony_ci return; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci info = kmalloc(sizeof(*info), GFP_KERNEL); 34362306a36Sopenharmony_ci if (!info) 34462306a36Sopenharmony_ci return; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci err = snd_pcm_info(substream, info); 34762306a36Sopenharmony_ci if (err < 0) { 34862306a36Sopenharmony_ci snd_iprintf(buffer, "error %d\n", err); 34962306a36Sopenharmony_ci kfree(info); 35062306a36Sopenharmony_ci return; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci snd_iprintf(buffer, "card: %d\n", info->card); 35362306a36Sopenharmony_ci snd_iprintf(buffer, "device: %d\n", info->device); 35462306a36Sopenharmony_ci snd_iprintf(buffer, "subdevice: %d\n", info->subdevice); 35562306a36Sopenharmony_ci snd_iprintf(buffer, "stream: %s\n", snd_pcm_stream_name(info->stream)); 35662306a36Sopenharmony_ci snd_iprintf(buffer, "id: %s\n", info->id); 35762306a36Sopenharmony_ci snd_iprintf(buffer, "name: %s\n", info->name); 35862306a36Sopenharmony_ci snd_iprintf(buffer, "subname: %s\n", info->subname); 35962306a36Sopenharmony_ci snd_iprintf(buffer, "class: %d\n", info->dev_class); 36062306a36Sopenharmony_ci snd_iprintf(buffer, "subclass: %d\n", info->dev_subclass); 36162306a36Sopenharmony_ci snd_iprintf(buffer, "subdevices_count: %d\n", info->subdevices_count); 36262306a36Sopenharmony_ci snd_iprintf(buffer, "subdevices_avail: %d\n", info->subdevices_avail); 36362306a36Sopenharmony_ci kfree(info); 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry, 36762306a36Sopenharmony_ci struct snd_info_buffer *buffer) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci snd_pcm_proc_info_read(((struct snd_pcm_str *)entry->private_data)->substream, 37062306a36Sopenharmony_ci buffer); 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic void snd_pcm_substream_proc_info_read(struct snd_info_entry *entry, 37462306a36Sopenharmony_ci struct snd_info_buffer *buffer) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci snd_pcm_proc_info_read(entry->private_data, buffer); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, 38062306a36Sopenharmony_ci struct snd_info_buffer *buffer) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct snd_pcm_substream *substream = entry->private_data; 38362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci mutex_lock(&substream->pcm->open_mutex); 38662306a36Sopenharmony_ci runtime = substream->runtime; 38762306a36Sopenharmony_ci if (!runtime) { 38862306a36Sopenharmony_ci snd_iprintf(buffer, "closed\n"); 38962306a36Sopenharmony_ci goto unlock; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci if (runtime->state == SNDRV_PCM_STATE_OPEN) { 39262306a36Sopenharmony_ci snd_iprintf(buffer, "no setup\n"); 39362306a36Sopenharmony_ci goto unlock; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access)); 39662306a36Sopenharmony_ci snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format)); 39762306a36Sopenharmony_ci snd_iprintf(buffer, "subformat: %s\n", snd_pcm_subformat_name(runtime->subformat)); 39862306a36Sopenharmony_ci snd_iprintf(buffer, "channels: %u\n", runtime->channels); 39962306a36Sopenharmony_ci snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den); 40062306a36Sopenharmony_ci snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size); 40162306a36Sopenharmony_ci snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size); 40262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS) 40362306a36Sopenharmony_ci if (substream->oss.oss) { 40462306a36Sopenharmony_ci snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format)); 40562306a36Sopenharmony_ci snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels); 40662306a36Sopenharmony_ci snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate); 40762306a36Sopenharmony_ci snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes); 40862306a36Sopenharmony_ci snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods); 40962306a36Sopenharmony_ci snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames); 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci#endif 41262306a36Sopenharmony_ci unlock: 41362306a36Sopenharmony_ci mutex_unlock(&substream->pcm->open_mutex); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, 41762306a36Sopenharmony_ci struct snd_info_buffer *buffer) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct snd_pcm_substream *substream = entry->private_data; 42062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci mutex_lock(&substream->pcm->open_mutex); 42362306a36Sopenharmony_ci runtime = substream->runtime; 42462306a36Sopenharmony_ci if (!runtime) { 42562306a36Sopenharmony_ci snd_iprintf(buffer, "closed\n"); 42662306a36Sopenharmony_ci goto unlock; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci if (runtime->state == SNDRV_PCM_STATE_OPEN) { 42962306a36Sopenharmony_ci snd_iprintf(buffer, "no setup\n"); 43062306a36Sopenharmony_ci goto unlock; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); 43362306a36Sopenharmony_ci snd_iprintf(buffer, "period_step: %u\n", runtime->period_step); 43462306a36Sopenharmony_ci snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min); 43562306a36Sopenharmony_ci snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold); 43662306a36Sopenharmony_ci snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold); 43762306a36Sopenharmony_ci snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold); 43862306a36Sopenharmony_ci snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size); 43962306a36Sopenharmony_ci snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary); 44062306a36Sopenharmony_ci unlock: 44162306a36Sopenharmony_ci mutex_unlock(&substream->pcm->open_mutex); 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, 44562306a36Sopenharmony_ci struct snd_info_buffer *buffer) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci struct snd_pcm_substream *substream = entry->private_data; 44862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime; 44962306a36Sopenharmony_ci struct snd_pcm_status64 status; 45062306a36Sopenharmony_ci int err; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci mutex_lock(&substream->pcm->open_mutex); 45362306a36Sopenharmony_ci runtime = substream->runtime; 45462306a36Sopenharmony_ci if (!runtime) { 45562306a36Sopenharmony_ci snd_iprintf(buffer, "closed\n"); 45662306a36Sopenharmony_ci goto unlock; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci memset(&status, 0, sizeof(status)); 45962306a36Sopenharmony_ci err = snd_pcm_status64(substream, &status); 46062306a36Sopenharmony_ci if (err < 0) { 46162306a36Sopenharmony_ci snd_iprintf(buffer, "error %d\n", err); 46262306a36Sopenharmony_ci goto unlock; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); 46562306a36Sopenharmony_ci snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid)); 46662306a36Sopenharmony_ci snd_iprintf(buffer, "trigger_time: %lld.%09lld\n", 46762306a36Sopenharmony_ci status.trigger_tstamp_sec, status.trigger_tstamp_nsec); 46862306a36Sopenharmony_ci snd_iprintf(buffer, "tstamp : %lld.%09lld\n", 46962306a36Sopenharmony_ci status.tstamp_sec, status.tstamp_nsec); 47062306a36Sopenharmony_ci snd_iprintf(buffer, "delay : %ld\n", status.delay); 47162306a36Sopenharmony_ci snd_iprintf(buffer, "avail : %ld\n", status.avail); 47262306a36Sopenharmony_ci snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max); 47362306a36Sopenharmony_ci snd_iprintf(buffer, "-----\n"); 47462306a36Sopenharmony_ci snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr); 47562306a36Sopenharmony_ci snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr); 47662306a36Sopenharmony_ci unlock: 47762306a36Sopenharmony_ci mutex_unlock(&substream->pcm->open_mutex); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci#ifdef CONFIG_SND_PCM_XRUN_DEBUG 48162306a36Sopenharmony_cistatic void snd_pcm_xrun_injection_write(struct snd_info_entry *entry, 48262306a36Sopenharmony_ci struct snd_info_buffer *buffer) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct snd_pcm_substream *substream = entry->private_data; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci snd_pcm_stop_xrun(substream); 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic void snd_pcm_xrun_debug_read(struct snd_info_entry *entry, 49062306a36Sopenharmony_ci struct snd_info_buffer *buffer) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct snd_pcm_str *pstr = entry->private_data; 49362306a36Sopenharmony_ci snd_iprintf(buffer, "%d\n", pstr->xrun_debug); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic void snd_pcm_xrun_debug_write(struct snd_info_entry *entry, 49762306a36Sopenharmony_ci struct snd_info_buffer *buffer) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct snd_pcm_str *pstr = entry->private_data; 50062306a36Sopenharmony_ci char line[64]; 50162306a36Sopenharmony_ci if (!snd_info_get_line(buffer, line, sizeof(line))) 50262306a36Sopenharmony_ci pstr->xrun_debug = simple_strtoul(line, NULL, 10); 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci#endif 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct snd_pcm *pcm = pstr->pcm; 50962306a36Sopenharmony_ci struct snd_info_entry *entry; 51062306a36Sopenharmony_ci char name[16]; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci sprintf(name, "pcm%i%c", pcm->device, 51362306a36Sopenharmony_ci pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); 51462306a36Sopenharmony_ci entry = snd_info_create_card_entry(pcm->card, name, 51562306a36Sopenharmony_ci pcm->card->proc_root); 51662306a36Sopenharmony_ci if (!entry) 51762306a36Sopenharmony_ci return -ENOMEM; 51862306a36Sopenharmony_ci entry->mode = S_IFDIR | 0555; 51962306a36Sopenharmony_ci pstr->proc_root = entry; 52062306a36Sopenharmony_ci entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root); 52162306a36Sopenharmony_ci if (entry) 52262306a36Sopenharmony_ci snd_info_set_text_ops(entry, pstr, snd_pcm_stream_proc_info_read); 52362306a36Sopenharmony_ci#ifdef CONFIG_SND_PCM_XRUN_DEBUG 52462306a36Sopenharmony_ci entry = snd_info_create_card_entry(pcm->card, "xrun_debug", 52562306a36Sopenharmony_ci pstr->proc_root); 52662306a36Sopenharmony_ci if (entry) { 52762306a36Sopenharmony_ci snd_info_set_text_ops(entry, pstr, snd_pcm_xrun_debug_read); 52862306a36Sopenharmony_ci entry->c.text.write = snd_pcm_xrun_debug_write; 52962306a36Sopenharmony_ci entry->mode |= 0200; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci#endif 53262306a36Sopenharmony_ci return 0; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci snd_info_free_entry(pstr->proc_root); 53862306a36Sopenharmony_ci pstr->proc_root = NULL; 53962306a36Sopenharmony_ci return 0; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic struct snd_info_entry * 54362306a36Sopenharmony_cicreate_substream_info_entry(struct snd_pcm_substream *substream, 54462306a36Sopenharmony_ci const char *name, 54562306a36Sopenharmony_ci void (*read)(struct snd_info_entry *, 54662306a36Sopenharmony_ci struct snd_info_buffer *)) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct snd_info_entry *entry; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci entry = snd_info_create_card_entry(substream->pcm->card, name, 55162306a36Sopenharmony_ci substream->proc_root); 55262306a36Sopenharmony_ci if (entry) 55362306a36Sopenharmony_ci snd_info_set_text_ops(entry, substream, read); 55462306a36Sopenharmony_ci return entry; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci struct snd_info_entry *entry; 56062306a36Sopenharmony_ci struct snd_card *card; 56162306a36Sopenharmony_ci char name[16]; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci card = substream->pcm->card; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci sprintf(name, "sub%i", substream->number); 56662306a36Sopenharmony_ci entry = snd_info_create_card_entry(card, name, 56762306a36Sopenharmony_ci substream->pstr->proc_root); 56862306a36Sopenharmony_ci if (!entry) 56962306a36Sopenharmony_ci return -ENOMEM; 57062306a36Sopenharmony_ci entry->mode = S_IFDIR | 0555; 57162306a36Sopenharmony_ci substream->proc_root = entry; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci create_substream_info_entry(substream, "info", 57462306a36Sopenharmony_ci snd_pcm_substream_proc_info_read); 57562306a36Sopenharmony_ci create_substream_info_entry(substream, "hw_params", 57662306a36Sopenharmony_ci snd_pcm_substream_proc_hw_params_read); 57762306a36Sopenharmony_ci create_substream_info_entry(substream, "sw_params", 57862306a36Sopenharmony_ci snd_pcm_substream_proc_sw_params_read); 57962306a36Sopenharmony_ci create_substream_info_entry(substream, "status", 58062306a36Sopenharmony_ci snd_pcm_substream_proc_status_read); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci#ifdef CONFIG_SND_PCM_XRUN_DEBUG 58362306a36Sopenharmony_ci entry = create_substream_info_entry(substream, "xrun_injection", NULL); 58462306a36Sopenharmony_ci if (entry) { 58562306a36Sopenharmony_ci entry->c.text.write = snd_pcm_xrun_injection_write; 58662306a36Sopenharmony_ci entry->mode = S_IFREG | 0200; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci#endif /* CONFIG_SND_PCM_XRUN_DEBUG */ 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci return 0; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci#else /* !CONFIG_SND_VERBOSE_PROCFS */ 59462306a36Sopenharmony_cistatic inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; } 59562306a36Sopenharmony_cistatic inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; } 59662306a36Sopenharmony_cistatic inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; } 59762306a36Sopenharmony_ci#endif /* CONFIG_SND_VERBOSE_PROCFS */ 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic const struct attribute_group *pcm_dev_attr_groups[]; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci/* 60262306a36Sopenharmony_ci * PM callbacks: we need to deal only with suspend here, as the resume is 60362306a36Sopenharmony_ci * triggered either from user-space or the driver's resume callback 60462306a36Sopenharmony_ci */ 60562306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 60662306a36Sopenharmony_cistatic int do_pcm_suspend(struct device *dev) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct snd_pcm_str *pstr = dev_get_drvdata(dev); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (!pstr->pcm->no_device_suspend) 61162306a36Sopenharmony_ci snd_pcm_suspend_all(pstr->pcm); 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci#endif 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic const struct dev_pm_ops pcm_dev_pm_ops = { 61762306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(do_pcm_suspend, NULL) 61862306a36Sopenharmony_ci}; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci/* device type for PCM -- basically only for passing PM callbacks */ 62162306a36Sopenharmony_cistatic const struct device_type pcm_dev_type = { 62262306a36Sopenharmony_ci .name = "pcm", 62362306a36Sopenharmony_ci .pm = &pcm_dev_pm_ops, 62462306a36Sopenharmony_ci}; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci/** 62762306a36Sopenharmony_ci * snd_pcm_new_stream - create a new PCM stream 62862306a36Sopenharmony_ci * @pcm: the pcm instance 62962306a36Sopenharmony_ci * @stream: the stream direction, SNDRV_PCM_STREAM_XXX 63062306a36Sopenharmony_ci * @substream_count: the number of substreams 63162306a36Sopenharmony_ci * 63262306a36Sopenharmony_ci * Creates a new stream for the pcm. 63362306a36Sopenharmony_ci * The corresponding stream on the pcm must have been empty before 63462306a36Sopenharmony_ci * calling this, i.e. zero must be given to the argument of 63562306a36Sopenharmony_ci * snd_pcm_new(). 63662306a36Sopenharmony_ci * 63762306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 63862306a36Sopenharmony_ci */ 63962306a36Sopenharmony_ciint snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci int idx, err; 64262306a36Sopenharmony_ci struct snd_pcm_str *pstr = &pcm->streams[stream]; 64362306a36Sopenharmony_ci struct snd_pcm_substream *substream, *prev; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS) 64662306a36Sopenharmony_ci mutex_init(&pstr->oss.setup_mutex); 64762306a36Sopenharmony_ci#endif 64862306a36Sopenharmony_ci pstr->stream = stream; 64962306a36Sopenharmony_ci pstr->pcm = pcm; 65062306a36Sopenharmony_ci pstr->substream_count = substream_count; 65162306a36Sopenharmony_ci if (!substream_count) 65262306a36Sopenharmony_ci return 0; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci err = snd_device_alloc(&pstr->dev, pcm->card); 65562306a36Sopenharmony_ci if (err < 0) 65662306a36Sopenharmony_ci return err; 65762306a36Sopenharmony_ci dev_set_name(pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device, 65862306a36Sopenharmony_ci stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); 65962306a36Sopenharmony_ci pstr->dev->groups = pcm_dev_attr_groups; 66062306a36Sopenharmony_ci pstr->dev->type = &pcm_dev_type; 66162306a36Sopenharmony_ci dev_set_drvdata(pstr->dev, pstr); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (!pcm->internal) { 66462306a36Sopenharmony_ci err = snd_pcm_stream_proc_init(pstr); 66562306a36Sopenharmony_ci if (err < 0) { 66662306a36Sopenharmony_ci pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n"); 66762306a36Sopenharmony_ci return err; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci prev = NULL; 67162306a36Sopenharmony_ci for (idx = 0, prev = NULL; idx < substream_count; idx++) { 67262306a36Sopenharmony_ci substream = kzalloc(sizeof(*substream), GFP_KERNEL); 67362306a36Sopenharmony_ci if (!substream) 67462306a36Sopenharmony_ci return -ENOMEM; 67562306a36Sopenharmony_ci substream->pcm = pcm; 67662306a36Sopenharmony_ci substream->pstr = pstr; 67762306a36Sopenharmony_ci substream->number = idx; 67862306a36Sopenharmony_ci substream->stream = stream; 67962306a36Sopenharmony_ci sprintf(substream->name, "subdevice #%i", idx); 68062306a36Sopenharmony_ci substream->buffer_bytes_max = UINT_MAX; 68162306a36Sopenharmony_ci if (prev == NULL) 68262306a36Sopenharmony_ci pstr->substream = substream; 68362306a36Sopenharmony_ci else 68462306a36Sopenharmony_ci prev->next = substream; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (!pcm->internal) { 68762306a36Sopenharmony_ci err = snd_pcm_substream_proc_init(substream); 68862306a36Sopenharmony_ci if (err < 0) { 68962306a36Sopenharmony_ci pcm_err(pcm, 69062306a36Sopenharmony_ci "Error in snd_pcm_stream_proc_init\n"); 69162306a36Sopenharmony_ci if (prev == NULL) 69262306a36Sopenharmony_ci pstr->substream = NULL; 69362306a36Sopenharmony_ci else 69462306a36Sopenharmony_ci prev->next = NULL; 69562306a36Sopenharmony_ci kfree(substream); 69662306a36Sopenharmony_ci return err; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci substream->group = &substream->self_group; 70062306a36Sopenharmony_ci snd_pcm_group_init(&substream->self_group); 70162306a36Sopenharmony_ci list_add_tail(&substream->link_list, &substream->self_group.substreams); 70262306a36Sopenharmony_ci atomic_set(&substream->mmap_count, 0); 70362306a36Sopenharmony_ci prev = substream; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci return 0; 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_pcm_new_stream); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic int _snd_pcm_new(struct snd_card *card, const char *id, int device, 71062306a36Sopenharmony_ci int playback_count, int capture_count, bool internal, 71162306a36Sopenharmony_ci struct snd_pcm **rpcm) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci struct snd_pcm *pcm; 71462306a36Sopenharmony_ci int err; 71562306a36Sopenharmony_ci static const struct snd_device_ops ops = { 71662306a36Sopenharmony_ci .dev_free = snd_pcm_dev_free, 71762306a36Sopenharmony_ci .dev_register = snd_pcm_dev_register, 71862306a36Sopenharmony_ci .dev_disconnect = snd_pcm_dev_disconnect, 71962306a36Sopenharmony_ci }; 72062306a36Sopenharmony_ci static const struct snd_device_ops internal_ops = { 72162306a36Sopenharmony_ci .dev_free = snd_pcm_dev_free, 72262306a36Sopenharmony_ci }; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (snd_BUG_ON(!card)) 72562306a36Sopenharmony_ci return -ENXIO; 72662306a36Sopenharmony_ci if (rpcm) 72762306a36Sopenharmony_ci *rpcm = NULL; 72862306a36Sopenharmony_ci pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); 72962306a36Sopenharmony_ci if (!pcm) 73062306a36Sopenharmony_ci return -ENOMEM; 73162306a36Sopenharmony_ci pcm->card = card; 73262306a36Sopenharmony_ci pcm->device = device; 73362306a36Sopenharmony_ci pcm->internal = internal; 73462306a36Sopenharmony_ci mutex_init(&pcm->open_mutex); 73562306a36Sopenharmony_ci init_waitqueue_head(&pcm->open_wait); 73662306a36Sopenharmony_ci INIT_LIST_HEAD(&pcm->list); 73762306a36Sopenharmony_ci if (id) 73862306a36Sopenharmony_ci strscpy(pcm->id, id, sizeof(pcm->id)); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, 74162306a36Sopenharmony_ci playback_count); 74262306a36Sopenharmony_ci if (err < 0) 74362306a36Sopenharmony_ci goto free_pcm; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count); 74662306a36Sopenharmony_ci if (err < 0) 74762306a36Sopenharmony_ci goto free_pcm; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_PCM, pcm, 75062306a36Sopenharmony_ci internal ? &internal_ops : &ops); 75162306a36Sopenharmony_ci if (err < 0) 75262306a36Sopenharmony_ci goto free_pcm; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (rpcm) 75562306a36Sopenharmony_ci *rpcm = pcm; 75662306a36Sopenharmony_ci return 0; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cifree_pcm: 75962306a36Sopenharmony_ci snd_pcm_free(pcm); 76062306a36Sopenharmony_ci return err; 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci/** 76462306a36Sopenharmony_ci * snd_pcm_new - create a new PCM instance 76562306a36Sopenharmony_ci * @card: the card instance 76662306a36Sopenharmony_ci * @id: the id string 76762306a36Sopenharmony_ci * @device: the device index (zero based) 76862306a36Sopenharmony_ci * @playback_count: the number of substreams for playback 76962306a36Sopenharmony_ci * @capture_count: the number of substreams for capture 77062306a36Sopenharmony_ci * @rpcm: the pointer to store the new pcm instance 77162306a36Sopenharmony_ci * 77262306a36Sopenharmony_ci * Creates a new PCM instance. 77362306a36Sopenharmony_ci * 77462306a36Sopenharmony_ci * The pcm operators have to be set afterwards to the new instance 77562306a36Sopenharmony_ci * via snd_pcm_set_ops(). 77662306a36Sopenharmony_ci * 77762306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_ciint snd_pcm_new(struct snd_card *card, const char *id, int device, 78062306a36Sopenharmony_ci int playback_count, int capture_count, struct snd_pcm **rpcm) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci return _snd_pcm_new(card, id, device, playback_count, capture_count, 78362306a36Sopenharmony_ci false, rpcm); 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ciEXPORT_SYMBOL(snd_pcm_new); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci/** 78862306a36Sopenharmony_ci * snd_pcm_new_internal - create a new internal PCM instance 78962306a36Sopenharmony_ci * @card: the card instance 79062306a36Sopenharmony_ci * @id: the id string 79162306a36Sopenharmony_ci * @device: the device index (zero based - shared with normal PCMs) 79262306a36Sopenharmony_ci * @playback_count: the number of substreams for playback 79362306a36Sopenharmony_ci * @capture_count: the number of substreams for capture 79462306a36Sopenharmony_ci * @rpcm: the pointer to store the new pcm instance 79562306a36Sopenharmony_ci * 79662306a36Sopenharmony_ci * Creates a new internal PCM instance with no userspace device or procfs 79762306a36Sopenharmony_ci * entries. This is used by ASoC Back End PCMs in order to create a PCM that 79862306a36Sopenharmony_ci * will only be used internally by kernel drivers. i.e. it cannot be opened 79962306a36Sopenharmony_ci * by userspace. It provides existing ASoC components drivers with a substream 80062306a36Sopenharmony_ci * and access to any private data. 80162306a36Sopenharmony_ci * 80262306a36Sopenharmony_ci * The pcm operators have to be set afterwards to the new instance 80362306a36Sopenharmony_ci * via snd_pcm_set_ops(). 80462306a36Sopenharmony_ci * 80562306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 80662306a36Sopenharmony_ci */ 80762306a36Sopenharmony_ciint snd_pcm_new_internal(struct snd_card *card, const char *id, int device, 80862306a36Sopenharmony_ci int playback_count, int capture_count, 80962306a36Sopenharmony_ci struct snd_pcm **rpcm) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci return _snd_pcm_new(card, id, device, playback_count, capture_count, 81262306a36Sopenharmony_ci true, rpcm); 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ciEXPORT_SYMBOL(snd_pcm_new_internal); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic void free_chmap(struct snd_pcm_str *pstr) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci if (pstr->chmap_kctl) { 81962306a36Sopenharmony_ci struct snd_card *card = pstr->pcm->card; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci snd_ctl_remove(card, pstr->chmap_kctl); 82262306a36Sopenharmony_ci pstr->chmap_kctl = NULL; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic void snd_pcm_free_stream(struct snd_pcm_str * pstr) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci struct snd_pcm_substream *substream, *substream_next; 82962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS) 83062306a36Sopenharmony_ci struct snd_pcm_oss_setup *setup, *setupn; 83162306a36Sopenharmony_ci#endif 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* free all proc files under the stream */ 83462306a36Sopenharmony_ci snd_pcm_stream_proc_done(pstr); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci substream = pstr->substream; 83762306a36Sopenharmony_ci while (substream) { 83862306a36Sopenharmony_ci substream_next = substream->next; 83962306a36Sopenharmony_ci snd_pcm_timer_done(substream); 84062306a36Sopenharmony_ci kfree(substream); 84162306a36Sopenharmony_ci substream = substream_next; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS) 84462306a36Sopenharmony_ci for (setup = pstr->oss.setup_list; setup; setup = setupn) { 84562306a36Sopenharmony_ci setupn = setup->next; 84662306a36Sopenharmony_ci kfree(setup->task_name); 84762306a36Sopenharmony_ci kfree(setup); 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci#endif 85062306a36Sopenharmony_ci free_chmap(pstr); 85162306a36Sopenharmony_ci if (pstr->substream_count) 85262306a36Sopenharmony_ci put_device(pstr->dev); 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS) 85662306a36Sopenharmony_ci#define pcm_call_notify(pcm, call) \ 85762306a36Sopenharmony_ci do { \ 85862306a36Sopenharmony_ci struct snd_pcm_notify *_notify; \ 85962306a36Sopenharmony_ci list_for_each_entry(_notify, &snd_pcm_notify_list, list) \ 86062306a36Sopenharmony_ci _notify->call(pcm); \ 86162306a36Sopenharmony_ci } while (0) 86262306a36Sopenharmony_ci#else 86362306a36Sopenharmony_ci#define pcm_call_notify(pcm, call) do {} while (0) 86462306a36Sopenharmony_ci#endif 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic int snd_pcm_free(struct snd_pcm *pcm) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci if (!pcm) 86962306a36Sopenharmony_ci return 0; 87062306a36Sopenharmony_ci if (!pcm->internal) 87162306a36Sopenharmony_ci pcm_call_notify(pcm, n_unregister); 87262306a36Sopenharmony_ci if (pcm->private_free) 87362306a36Sopenharmony_ci pcm->private_free(pcm); 87462306a36Sopenharmony_ci snd_pcm_lib_preallocate_free_for_all(pcm); 87562306a36Sopenharmony_ci snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); 87662306a36Sopenharmony_ci snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); 87762306a36Sopenharmony_ci kfree(pcm); 87862306a36Sopenharmony_ci return 0; 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic int snd_pcm_dev_free(struct snd_device *device) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci struct snd_pcm *pcm = device->device_data; 88462306a36Sopenharmony_ci return snd_pcm_free(pcm); 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ciint snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, 88862306a36Sopenharmony_ci struct file *file, 88962306a36Sopenharmony_ci struct snd_pcm_substream **rsubstream) 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci struct snd_pcm_str * pstr; 89262306a36Sopenharmony_ci struct snd_pcm_substream *substream; 89362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime; 89462306a36Sopenharmony_ci struct snd_card *card; 89562306a36Sopenharmony_ci int prefer_subdevice; 89662306a36Sopenharmony_ci size_t size; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (snd_BUG_ON(!pcm || !rsubstream)) 89962306a36Sopenharmony_ci return -ENXIO; 90062306a36Sopenharmony_ci if (snd_BUG_ON(stream != SNDRV_PCM_STREAM_PLAYBACK && 90162306a36Sopenharmony_ci stream != SNDRV_PCM_STREAM_CAPTURE)) 90262306a36Sopenharmony_ci return -EINVAL; 90362306a36Sopenharmony_ci *rsubstream = NULL; 90462306a36Sopenharmony_ci pstr = &pcm->streams[stream]; 90562306a36Sopenharmony_ci if (pstr->substream == NULL || pstr->substream_count == 0) 90662306a36Sopenharmony_ci return -ENODEV; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci card = pcm->card; 90962306a36Sopenharmony_ci prefer_subdevice = snd_ctl_get_preferred_subdevice(card, SND_CTL_SUBDEV_PCM); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { 91262306a36Sopenharmony_ci int opposite = !stream; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci for (substream = pcm->streams[opposite].substream; substream; 91562306a36Sopenharmony_ci substream = substream->next) { 91662306a36Sopenharmony_ci if (SUBSTREAM_BUSY(substream)) 91762306a36Sopenharmony_ci return -EAGAIN; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (file->f_flags & O_APPEND) { 92262306a36Sopenharmony_ci if (prefer_subdevice < 0) { 92362306a36Sopenharmony_ci if (pstr->substream_count > 1) 92462306a36Sopenharmony_ci return -EINVAL; /* must be unique */ 92562306a36Sopenharmony_ci substream = pstr->substream; 92662306a36Sopenharmony_ci } else { 92762306a36Sopenharmony_ci for (substream = pstr->substream; substream; 92862306a36Sopenharmony_ci substream = substream->next) 92962306a36Sopenharmony_ci if (substream->number == prefer_subdevice) 93062306a36Sopenharmony_ci break; 93162306a36Sopenharmony_ci } 93262306a36Sopenharmony_ci if (! substream) 93362306a36Sopenharmony_ci return -ENODEV; 93462306a36Sopenharmony_ci if (! SUBSTREAM_BUSY(substream)) 93562306a36Sopenharmony_ci return -EBADFD; 93662306a36Sopenharmony_ci substream->ref_count++; 93762306a36Sopenharmony_ci *rsubstream = substream; 93862306a36Sopenharmony_ci return 0; 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci for (substream = pstr->substream; substream; substream = substream->next) { 94262306a36Sopenharmony_ci if (!SUBSTREAM_BUSY(substream) && 94362306a36Sopenharmony_ci (prefer_subdevice == -1 || 94462306a36Sopenharmony_ci substream->number == prefer_subdevice)) 94562306a36Sopenharmony_ci break; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci if (substream == NULL) 94862306a36Sopenharmony_ci return -EAGAIN; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 95162306a36Sopenharmony_ci if (runtime == NULL) 95262306a36Sopenharmony_ci return -ENOMEM; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)); 95562306a36Sopenharmony_ci runtime->status = alloc_pages_exact(size, GFP_KERNEL); 95662306a36Sopenharmony_ci if (runtime->status == NULL) { 95762306a36Sopenharmony_ci kfree(runtime); 95862306a36Sopenharmony_ci return -ENOMEM; 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci memset(runtime->status, 0, size); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)); 96362306a36Sopenharmony_ci runtime->control = alloc_pages_exact(size, GFP_KERNEL); 96462306a36Sopenharmony_ci if (runtime->control == NULL) { 96562306a36Sopenharmony_ci free_pages_exact(runtime->status, 96662306a36Sopenharmony_ci PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); 96762306a36Sopenharmony_ci kfree(runtime); 96862306a36Sopenharmony_ci return -ENOMEM; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci memset(runtime->control, 0, size); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci init_waitqueue_head(&runtime->sleep); 97362306a36Sopenharmony_ci init_waitqueue_head(&runtime->tsleep); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_OPEN); 97662306a36Sopenharmony_ci mutex_init(&runtime->buffer_mutex); 97762306a36Sopenharmony_ci atomic_set(&runtime->buffer_accessing, 0); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci substream->runtime = runtime; 98062306a36Sopenharmony_ci substream->private_data = pcm->private_data; 98162306a36Sopenharmony_ci substream->ref_count = 1; 98262306a36Sopenharmony_ci substream->f_flags = file->f_flags; 98362306a36Sopenharmony_ci substream->pid = get_pid(task_pid(current)); 98462306a36Sopenharmony_ci pstr->substream_opened++; 98562306a36Sopenharmony_ci *rsubstream = substream; 98662306a36Sopenharmony_ci return 0; 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_civoid snd_pcm_detach_substream(struct snd_pcm_substream *substream) 99062306a36Sopenharmony_ci{ 99162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if (PCM_RUNTIME_CHECK(substream)) 99462306a36Sopenharmony_ci return; 99562306a36Sopenharmony_ci runtime = substream->runtime; 99662306a36Sopenharmony_ci if (runtime->private_free != NULL) 99762306a36Sopenharmony_ci runtime->private_free(runtime); 99862306a36Sopenharmony_ci free_pages_exact(runtime->status, 99962306a36Sopenharmony_ci PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); 100062306a36Sopenharmony_ci free_pages_exact(runtime->control, 100162306a36Sopenharmony_ci PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); 100262306a36Sopenharmony_ci kfree(runtime->hw_constraints.rules); 100362306a36Sopenharmony_ci /* Avoid concurrent access to runtime via PCM timer interface */ 100462306a36Sopenharmony_ci if (substream->timer) { 100562306a36Sopenharmony_ci spin_lock_irq(&substream->timer->lock); 100662306a36Sopenharmony_ci substream->runtime = NULL; 100762306a36Sopenharmony_ci spin_unlock_irq(&substream->timer->lock); 100862306a36Sopenharmony_ci } else { 100962306a36Sopenharmony_ci substream->runtime = NULL; 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci mutex_destroy(&runtime->buffer_mutex); 101262306a36Sopenharmony_ci snd_fasync_free(runtime->fasync); 101362306a36Sopenharmony_ci kfree(runtime); 101462306a36Sopenharmony_ci put_pid(substream->pid); 101562306a36Sopenharmony_ci substream->pid = NULL; 101662306a36Sopenharmony_ci substream->pstr->substream_opened--; 101762306a36Sopenharmony_ci} 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_cistatic ssize_t pcm_class_show(struct device *dev, 102062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci struct snd_pcm_str *pstr = dev_get_drvdata(dev); 102362306a36Sopenharmony_ci struct snd_pcm *pcm = pstr->pcm; 102462306a36Sopenharmony_ci const char *str; 102562306a36Sopenharmony_ci static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = { 102662306a36Sopenharmony_ci [SNDRV_PCM_CLASS_GENERIC] = "generic", 102762306a36Sopenharmony_ci [SNDRV_PCM_CLASS_MULTI] = "multi", 102862306a36Sopenharmony_ci [SNDRV_PCM_CLASS_MODEM] = "modem", 102962306a36Sopenharmony_ci [SNDRV_PCM_CLASS_DIGITIZER] = "digitizer", 103062306a36Sopenharmony_ci }; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci if (pcm->dev_class > SNDRV_PCM_CLASS_LAST) 103362306a36Sopenharmony_ci str = "none"; 103462306a36Sopenharmony_ci else 103562306a36Sopenharmony_ci str = strs[pcm->dev_class]; 103662306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", str); 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(pcm_class); 104062306a36Sopenharmony_cistatic struct attribute *pcm_dev_attrs[] = { 104162306a36Sopenharmony_ci &dev_attr_pcm_class.attr, 104262306a36Sopenharmony_ci NULL 104362306a36Sopenharmony_ci}; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic const struct attribute_group pcm_dev_attr_group = { 104662306a36Sopenharmony_ci .attrs = pcm_dev_attrs, 104762306a36Sopenharmony_ci}; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_cistatic const struct attribute_group *pcm_dev_attr_groups[] = { 105062306a36Sopenharmony_ci &pcm_dev_attr_group, 105162306a36Sopenharmony_ci NULL 105262306a36Sopenharmony_ci}; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic int snd_pcm_dev_register(struct snd_device *device) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci int cidx, err; 105762306a36Sopenharmony_ci struct snd_pcm_substream *substream; 105862306a36Sopenharmony_ci struct snd_pcm *pcm; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci if (snd_BUG_ON(!device || !device->device_data)) 106162306a36Sopenharmony_ci return -ENXIO; 106262306a36Sopenharmony_ci pcm = device->device_data; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci mutex_lock(®ister_mutex); 106562306a36Sopenharmony_ci err = snd_pcm_add(pcm); 106662306a36Sopenharmony_ci if (err) 106762306a36Sopenharmony_ci goto unlock; 106862306a36Sopenharmony_ci for (cidx = 0; cidx < 2; cidx++) { 106962306a36Sopenharmony_ci int devtype = -1; 107062306a36Sopenharmony_ci if (pcm->streams[cidx].substream == NULL) 107162306a36Sopenharmony_ci continue; 107262306a36Sopenharmony_ci switch (cidx) { 107362306a36Sopenharmony_ci case SNDRV_PCM_STREAM_PLAYBACK: 107462306a36Sopenharmony_ci devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; 107562306a36Sopenharmony_ci break; 107662306a36Sopenharmony_ci case SNDRV_PCM_STREAM_CAPTURE: 107762306a36Sopenharmony_ci devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; 107862306a36Sopenharmony_ci break; 107962306a36Sopenharmony_ci } 108062306a36Sopenharmony_ci /* register pcm */ 108162306a36Sopenharmony_ci err = snd_register_device(devtype, pcm->card, pcm->device, 108262306a36Sopenharmony_ci &snd_pcm_f_ops[cidx], pcm, 108362306a36Sopenharmony_ci pcm->streams[cidx].dev); 108462306a36Sopenharmony_ci if (err < 0) { 108562306a36Sopenharmony_ci list_del_init(&pcm->list); 108662306a36Sopenharmony_ci goto unlock; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) 109062306a36Sopenharmony_ci snd_pcm_timer_init(substream); 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci pcm_call_notify(pcm, n_register); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci unlock: 109662306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 109762306a36Sopenharmony_ci return err; 109862306a36Sopenharmony_ci} 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_cistatic int snd_pcm_dev_disconnect(struct snd_device *device) 110162306a36Sopenharmony_ci{ 110262306a36Sopenharmony_ci struct snd_pcm *pcm = device->device_data; 110362306a36Sopenharmony_ci struct snd_pcm_substream *substream; 110462306a36Sopenharmony_ci int cidx; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci mutex_lock(®ister_mutex); 110762306a36Sopenharmony_ci mutex_lock(&pcm->open_mutex); 110862306a36Sopenharmony_ci wake_up(&pcm->open_wait); 110962306a36Sopenharmony_ci list_del_init(&pcm->list); 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci for_each_pcm_substream(pcm, cidx, substream) { 111262306a36Sopenharmony_ci snd_pcm_stream_lock_irq(substream); 111362306a36Sopenharmony_ci if (substream->runtime) { 111462306a36Sopenharmony_ci if (snd_pcm_running(substream)) 111562306a36Sopenharmony_ci snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); 111662306a36Sopenharmony_ci /* to be sure, set the state unconditionally */ 111762306a36Sopenharmony_ci __snd_pcm_set_state(substream->runtime, 111862306a36Sopenharmony_ci SNDRV_PCM_STATE_DISCONNECTED); 111962306a36Sopenharmony_ci wake_up(&substream->runtime->sleep); 112062306a36Sopenharmony_ci wake_up(&substream->runtime->tsleep); 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci snd_pcm_stream_unlock_irq(substream); 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci for_each_pcm_substream(pcm, cidx, substream) 112662306a36Sopenharmony_ci snd_pcm_sync_stop(substream, false); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci pcm_call_notify(pcm, n_disconnect); 112962306a36Sopenharmony_ci for (cidx = 0; cidx < 2; cidx++) { 113062306a36Sopenharmony_ci if (pcm->streams[cidx].dev) 113162306a36Sopenharmony_ci snd_unregister_device(pcm->streams[cidx].dev); 113262306a36Sopenharmony_ci free_chmap(&pcm->streams[cidx]); 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci mutex_unlock(&pcm->open_mutex); 113562306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 113662306a36Sopenharmony_ci return 0; 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS) 114062306a36Sopenharmony_ci/** 114162306a36Sopenharmony_ci * snd_pcm_notify - Add/remove the notify list 114262306a36Sopenharmony_ci * @notify: PCM notify list 114362306a36Sopenharmony_ci * @nfree: 0 = register, 1 = unregister 114462306a36Sopenharmony_ci * 114562306a36Sopenharmony_ci * This adds the given notifier to the global list so that the callback is 114662306a36Sopenharmony_ci * called for each registered PCM devices. This exists only for PCM OSS 114762306a36Sopenharmony_ci * emulation, so far. 114862306a36Sopenharmony_ci * 114962306a36Sopenharmony_ci * Return: zero if successful, or a negative error code 115062306a36Sopenharmony_ci */ 115162306a36Sopenharmony_ciint snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) 115262306a36Sopenharmony_ci{ 115362306a36Sopenharmony_ci struct snd_pcm *pcm; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci if (snd_BUG_ON(!notify || 115662306a36Sopenharmony_ci !notify->n_register || 115762306a36Sopenharmony_ci !notify->n_unregister || 115862306a36Sopenharmony_ci !notify->n_disconnect)) 115962306a36Sopenharmony_ci return -EINVAL; 116062306a36Sopenharmony_ci mutex_lock(®ister_mutex); 116162306a36Sopenharmony_ci if (nfree) { 116262306a36Sopenharmony_ci list_del(¬ify->list); 116362306a36Sopenharmony_ci list_for_each_entry(pcm, &snd_pcm_devices, list) 116462306a36Sopenharmony_ci notify->n_unregister(pcm); 116562306a36Sopenharmony_ci } else { 116662306a36Sopenharmony_ci list_add_tail(¬ify->list, &snd_pcm_notify_list); 116762306a36Sopenharmony_ci list_for_each_entry(pcm, &snd_pcm_devices, list) 116862306a36Sopenharmony_ci notify->n_register(pcm); 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 117162306a36Sopenharmony_ci return 0; 117262306a36Sopenharmony_ci} 117362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_pcm_notify); 117462306a36Sopenharmony_ci#endif /* CONFIG_SND_PCM_OSS */ 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci#ifdef CONFIG_SND_PROC_FS 117762306a36Sopenharmony_ci/* 117862306a36Sopenharmony_ci * Info interface 117962306a36Sopenharmony_ci */ 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_cistatic void snd_pcm_proc_read(struct snd_info_entry *entry, 118262306a36Sopenharmony_ci struct snd_info_buffer *buffer) 118362306a36Sopenharmony_ci{ 118462306a36Sopenharmony_ci struct snd_pcm *pcm; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci mutex_lock(®ister_mutex); 118762306a36Sopenharmony_ci list_for_each_entry(pcm, &snd_pcm_devices, list) { 118862306a36Sopenharmony_ci snd_iprintf(buffer, "%02i-%02i: %s : %s", 118962306a36Sopenharmony_ci pcm->card->number, pcm->device, pcm->id, pcm->name); 119062306a36Sopenharmony_ci if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) 119162306a36Sopenharmony_ci snd_iprintf(buffer, " : playback %i", 119262306a36Sopenharmony_ci pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count); 119362306a36Sopenharmony_ci if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) 119462306a36Sopenharmony_ci snd_iprintf(buffer, " : capture %i", 119562306a36Sopenharmony_ci pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count); 119662306a36Sopenharmony_ci snd_iprintf(buffer, "\n"); 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 119962306a36Sopenharmony_ci} 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_cistatic struct snd_info_entry *snd_pcm_proc_entry; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_cistatic void snd_pcm_proc_init(void) 120462306a36Sopenharmony_ci{ 120562306a36Sopenharmony_ci struct snd_info_entry *entry; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL); 120862306a36Sopenharmony_ci if (entry) { 120962306a36Sopenharmony_ci snd_info_set_text_ops(entry, NULL, snd_pcm_proc_read); 121062306a36Sopenharmony_ci if (snd_info_register(entry) < 0) { 121162306a36Sopenharmony_ci snd_info_free_entry(entry); 121262306a36Sopenharmony_ci entry = NULL; 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci snd_pcm_proc_entry = entry; 121662306a36Sopenharmony_ci} 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_cistatic void snd_pcm_proc_done(void) 121962306a36Sopenharmony_ci{ 122062306a36Sopenharmony_ci snd_info_free_entry(snd_pcm_proc_entry); 122162306a36Sopenharmony_ci} 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci#else /* !CONFIG_SND_PROC_FS */ 122462306a36Sopenharmony_ci#define snd_pcm_proc_init() 122562306a36Sopenharmony_ci#define snd_pcm_proc_done() 122662306a36Sopenharmony_ci#endif /* CONFIG_SND_PROC_FS */ 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci/* 123062306a36Sopenharmony_ci * ENTRY functions 123162306a36Sopenharmony_ci */ 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_cistatic int __init alsa_pcm_init(void) 123462306a36Sopenharmony_ci{ 123562306a36Sopenharmony_ci snd_ctl_register_ioctl(snd_pcm_control_ioctl); 123662306a36Sopenharmony_ci snd_ctl_register_ioctl_compat(snd_pcm_control_ioctl); 123762306a36Sopenharmony_ci snd_pcm_proc_init(); 123862306a36Sopenharmony_ci return 0; 123962306a36Sopenharmony_ci} 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_cistatic void __exit alsa_pcm_exit(void) 124262306a36Sopenharmony_ci{ 124362306a36Sopenharmony_ci snd_ctl_unregister_ioctl(snd_pcm_control_ioctl); 124462306a36Sopenharmony_ci snd_ctl_unregister_ioctl_compat(snd_pcm_control_ioctl); 124562306a36Sopenharmony_ci snd_pcm_proc_done(); 124662306a36Sopenharmony_ci} 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_cimodule_init(alsa_pcm_init) 124962306a36Sopenharmony_cimodule_exit(alsa_pcm_exit) 1250