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(&register_mutex);
9562306a36Sopenharmony_ci			device = snd_pcm_next(card, device);
9662306a36Sopenharmony_ci			mutex_unlock(&register_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(&register_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(&register_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(&register_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(&register_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(&register_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(&register_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(&register_mutex);
116162306a36Sopenharmony_ci	if (nfree) {
116262306a36Sopenharmony_ci		list_del(&notify->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(&notify->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(&register_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(&register_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(&register_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