18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Digital Audio (PCM) abstract layer
48c2ecf20Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/compat.h>
88c2ecf20Sopenharmony_ci#include <linux/mm.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/file.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
138c2ecf20Sopenharmony_ci#include <linux/time.h>
148c2ecf20Sopenharmony_ci#include <linux/pm_qos.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
178c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
188c2ecf20Sopenharmony_ci#include <sound/core.h>
198c2ecf20Sopenharmony_ci#include <sound/control.h>
208c2ecf20Sopenharmony_ci#include <sound/info.h>
218c2ecf20Sopenharmony_ci#include <sound/pcm.h>
228c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
238c2ecf20Sopenharmony_ci#include <sound/timer.h>
248c2ecf20Sopenharmony_ci#include <sound/minors.h>
258c2ecf20Sopenharmony_ci#include <linux/uio.h>
268c2ecf20Sopenharmony_ci#include <linux/delay.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "pcm_local.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
318c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS
328c2ecf20Sopenharmony_ci#include "pcm_param_trace.h"
338c2ecf20Sopenharmony_ci#else
348c2ecf20Sopenharmony_ci#define trace_hw_mask_param_enabled()		0
358c2ecf20Sopenharmony_ci#define trace_hw_interval_param_enabled()	0
368c2ecf20Sopenharmony_ci#define trace_hw_mask_param(substream, type, index, prev, curr)
378c2ecf20Sopenharmony_ci#define trace_hw_interval_param(substream, type, index, prev, curr)
388c2ecf20Sopenharmony_ci#endif
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/*
418c2ecf20Sopenharmony_ci *  Compatibility
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistruct snd_pcm_hw_params_old {
458c2ecf20Sopenharmony_ci	unsigned int flags;
468c2ecf20Sopenharmony_ci	unsigned int masks[SNDRV_PCM_HW_PARAM_SUBFORMAT -
478c2ecf20Sopenharmony_ci			   SNDRV_PCM_HW_PARAM_ACCESS + 1];
488c2ecf20Sopenharmony_ci	struct snd_interval intervals[SNDRV_PCM_HW_PARAM_TICK_TIME -
498c2ecf20Sopenharmony_ci					SNDRV_PCM_HW_PARAM_SAMPLE_BITS + 1];
508c2ecf20Sopenharmony_ci	unsigned int rmask;
518c2ecf20Sopenharmony_ci	unsigned int cmask;
528c2ecf20Sopenharmony_ci	unsigned int info;
538c2ecf20Sopenharmony_ci	unsigned int msbits;
548c2ecf20Sopenharmony_ci	unsigned int rate_num;
558c2ecf20Sopenharmony_ci	unsigned int rate_den;
568c2ecf20Sopenharmony_ci	snd_pcm_uframes_t fifo_size;
578c2ecf20Sopenharmony_ci	unsigned char reserved[64];
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_SUPPORT_OLD_API
618c2ecf20Sopenharmony_ci#define SNDRV_PCM_IOCTL_HW_REFINE_OLD _IOWR('A', 0x10, struct snd_pcm_hw_params_old)
628c2ecf20Sopenharmony_ci#define SNDRV_PCM_IOCTL_HW_PARAMS_OLD _IOWR('A', 0x11, struct snd_pcm_hw_params_old)
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream,
658c2ecf20Sopenharmony_ci				      struct snd_pcm_hw_params_old __user * _oparams);
668c2ecf20Sopenharmony_cistatic int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
678c2ecf20Sopenharmony_ci				      struct snd_pcm_hw_params_old __user * _oparams);
688c2ecf20Sopenharmony_ci#endif
698c2ecf20Sopenharmony_cistatic int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/*
728c2ecf20Sopenharmony_ci *
738c2ecf20Sopenharmony_ci */
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic DECLARE_RWSEM(snd_pcm_link_rwsem);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_civoid snd_pcm_group_init(struct snd_pcm_group *group)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	spin_lock_init(&group->lock);
808c2ecf20Sopenharmony_ci	mutex_init(&group->mutex);
818c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&group->substreams);
828c2ecf20Sopenharmony_ci	refcount_set(&group->refs, 1);
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/* define group lock helpers */
868c2ecf20Sopenharmony_ci#define DEFINE_PCM_GROUP_LOCK(action, mutex_action) \
878c2ecf20Sopenharmony_cistatic void snd_pcm_group_ ## action(struct snd_pcm_group *group, bool nonatomic) \
888c2ecf20Sopenharmony_ci{ \
898c2ecf20Sopenharmony_ci	if (nonatomic) \
908c2ecf20Sopenharmony_ci		mutex_ ## mutex_action(&group->mutex); \
918c2ecf20Sopenharmony_ci	else \
928c2ecf20Sopenharmony_ci		spin_ ## action(&group->lock); \
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ciDEFINE_PCM_GROUP_LOCK(lock, lock);
968c2ecf20Sopenharmony_ciDEFINE_PCM_GROUP_LOCK(unlock, unlock);
978c2ecf20Sopenharmony_ciDEFINE_PCM_GROUP_LOCK(lock_irq, lock);
988c2ecf20Sopenharmony_ciDEFINE_PCM_GROUP_LOCK(unlock_irq, unlock);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/**
1018c2ecf20Sopenharmony_ci * snd_pcm_stream_lock - Lock the PCM stream
1028c2ecf20Sopenharmony_ci * @substream: PCM substream
1038c2ecf20Sopenharmony_ci *
1048c2ecf20Sopenharmony_ci * This locks the PCM stream's spinlock or mutex depending on the nonatomic
1058c2ecf20Sopenharmony_ci * flag of the given substream.  This also takes the global link rw lock
1068c2ecf20Sopenharmony_ci * (or rw sem), too, for avoiding the race with linked streams.
1078c2ecf20Sopenharmony_ci */
1088c2ecf20Sopenharmony_civoid snd_pcm_stream_lock(struct snd_pcm_substream *substream)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	snd_pcm_group_lock(&substream->self_group, substream->pcm->nonatomic);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/**
1158c2ecf20Sopenharmony_ci * snd_pcm_stream_unlock - Unlock the PCM stream
1168c2ecf20Sopenharmony_ci * @substream: PCM substream
1178c2ecf20Sopenharmony_ci *
1188c2ecf20Sopenharmony_ci * This unlocks the PCM stream that has been locked via snd_pcm_stream_lock().
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_civoid snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	snd_pcm_group_unlock(&substream->self_group, substream->pcm->nonatomic);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/**
1278c2ecf20Sopenharmony_ci * snd_pcm_stream_lock_irq - Lock the PCM stream
1288c2ecf20Sopenharmony_ci * @substream: PCM substream
1298c2ecf20Sopenharmony_ci *
1308c2ecf20Sopenharmony_ci * This locks the PCM stream like snd_pcm_stream_lock() and disables the local
1318c2ecf20Sopenharmony_ci * IRQ (only when nonatomic is false).  In nonatomic case, this is identical
1328c2ecf20Sopenharmony_ci * as snd_pcm_stream_lock().
1338c2ecf20Sopenharmony_ci */
1348c2ecf20Sopenharmony_civoid snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	snd_pcm_group_lock_irq(&substream->self_group,
1378c2ecf20Sopenharmony_ci			       substream->pcm->nonatomic);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic void snd_pcm_stream_lock_nested(struct snd_pcm_substream *substream)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	struct snd_pcm_group *group = &substream->self_group;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (substream->pcm->nonatomic)
1468c2ecf20Sopenharmony_ci		mutex_lock_nested(&group->mutex, SINGLE_DEPTH_NESTING);
1478c2ecf20Sopenharmony_ci	else
1488c2ecf20Sopenharmony_ci		spin_lock_nested(&group->lock, SINGLE_DEPTH_NESTING);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/**
1528c2ecf20Sopenharmony_ci * snd_pcm_stream_unlock_irq - Unlock the PCM stream
1538c2ecf20Sopenharmony_ci * @substream: PCM substream
1548c2ecf20Sopenharmony_ci *
1558c2ecf20Sopenharmony_ci * This is a counter-part of snd_pcm_stream_lock_irq().
1568c2ecf20Sopenharmony_ci */
1578c2ecf20Sopenharmony_civoid snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	snd_pcm_group_unlock_irq(&substream->self_group,
1608c2ecf20Sopenharmony_ci				 substream->pcm->nonatomic);
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irq);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ciunsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	unsigned long flags = 0;
1678c2ecf20Sopenharmony_ci	if (substream->pcm->nonatomic)
1688c2ecf20Sopenharmony_ci		mutex_lock(&substream->self_group.mutex);
1698c2ecf20Sopenharmony_ci	else
1708c2ecf20Sopenharmony_ci		spin_lock_irqsave(&substream->self_group.lock, flags);
1718c2ecf20Sopenharmony_ci	return flags;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/**
1768c2ecf20Sopenharmony_ci * snd_pcm_stream_unlock_irqrestore - Unlock the PCM stream
1778c2ecf20Sopenharmony_ci * @substream: PCM substream
1788c2ecf20Sopenharmony_ci * @flags: irq flags
1798c2ecf20Sopenharmony_ci *
1808c2ecf20Sopenharmony_ci * This is a counter-part of snd_pcm_stream_lock_irqsave().
1818c2ecf20Sopenharmony_ci */
1828c2ecf20Sopenharmony_civoid snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
1838c2ecf20Sopenharmony_ci				      unsigned long flags)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	if (substream->pcm->nonatomic)
1868c2ecf20Sopenharmony_ci		mutex_unlock(&substream->self_group.mutex);
1878c2ecf20Sopenharmony_ci	else
1888c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&substream->self_group.lock, flags);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/* Run PCM ioctl ops */
1938c2ecf20Sopenharmony_cistatic int snd_pcm_ops_ioctl(struct snd_pcm_substream *substream,
1948c2ecf20Sopenharmony_ci			     unsigned cmd, void *arg)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	if (substream->ops->ioctl)
1978c2ecf20Sopenharmony_ci		return substream->ops->ioctl(substream, cmd, arg);
1988c2ecf20Sopenharmony_ci	else
1998c2ecf20Sopenharmony_ci		return snd_pcm_lib_ioctl(substream, cmd, arg);
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ciint snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct snd_pcm *pcm = substream->pcm;
2058c2ecf20Sopenharmony_ci	struct snd_pcm_str *pstr = substream->pstr;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	memset(info, 0, sizeof(*info));
2088c2ecf20Sopenharmony_ci	info->card = pcm->card->number;
2098c2ecf20Sopenharmony_ci	info->device = pcm->device;
2108c2ecf20Sopenharmony_ci	info->stream = substream->stream;
2118c2ecf20Sopenharmony_ci	info->subdevice = substream->number;
2128c2ecf20Sopenharmony_ci	strlcpy(info->id, pcm->id, sizeof(info->id));
2138c2ecf20Sopenharmony_ci	strlcpy(info->name, pcm->name, sizeof(info->name));
2148c2ecf20Sopenharmony_ci	info->dev_class = pcm->dev_class;
2158c2ecf20Sopenharmony_ci	info->dev_subclass = pcm->dev_subclass;
2168c2ecf20Sopenharmony_ci	info->subdevices_count = pstr->substream_count;
2178c2ecf20Sopenharmony_ci	info->subdevices_avail = pstr->substream_count - pstr->substream_opened;
2188c2ecf20Sopenharmony_ci	strlcpy(info->subname, substream->name, sizeof(info->subname));
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	return 0;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ciint snd_pcm_info_user(struct snd_pcm_substream *substream,
2248c2ecf20Sopenharmony_ci		      struct snd_pcm_info __user * _info)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct snd_pcm_info *info;
2278c2ecf20Sopenharmony_ci	int err;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	info = kmalloc(sizeof(*info), GFP_KERNEL);
2308c2ecf20Sopenharmony_ci	if (! info)
2318c2ecf20Sopenharmony_ci		return -ENOMEM;
2328c2ecf20Sopenharmony_ci	err = snd_pcm_info(substream, info);
2338c2ecf20Sopenharmony_ci	if (err >= 0) {
2348c2ecf20Sopenharmony_ci		if (copy_to_user(_info, info, sizeof(*info)))
2358c2ecf20Sopenharmony_ci			err = -EFAULT;
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci	kfree(info);
2388c2ecf20Sopenharmony_ci	return err;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci/* macro for simplified cast */
2428c2ecf20Sopenharmony_ci#define PARAM_MASK_BIT(b)	(1U << (__force int)(b))
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic bool hw_support_mmap(struct snd_pcm_substream *substream)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_MMAP))
2478c2ecf20Sopenharmony_ci		return false;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	if (substream->ops->mmap || substream->ops->page)
2508c2ecf20Sopenharmony_ci		return true;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	switch (substream->dma_buffer.dev.type) {
2538c2ecf20Sopenharmony_ci	case SNDRV_DMA_TYPE_UNKNOWN:
2548c2ecf20Sopenharmony_ci		/* we can't know the device, so just assume that the driver does
2558c2ecf20Sopenharmony_ci		 * everything right
2568c2ecf20Sopenharmony_ci		 */
2578c2ecf20Sopenharmony_ci		return true;
2588c2ecf20Sopenharmony_ci	case SNDRV_DMA_TYPE_CONTINUOUS:
2598c2ecf20Sopenharmony_ci	case SNDRV_DMA_TYPE_VMALLOC:
2608c2ecf20Sopenharmony_ci		return true;
2618c2ecf20Sopenharmony_ci	default:
2628c2ecf20Sopenharmony_ci		return dma_can_mmap(substream->dma_buffer.dev.dev);
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic int constrain_mask_params(struct snd_pcm_substream *substream,
2678c2ecf20Sopenharmony_ci				 struct snd_pcm_hw_params *params)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct snd_pcm_hw_constraints *constrs =
2708c2ecf20Sopenharmony_ci					&substream->runtime->hw_constraints;
2718c2ecf20Sopenharmony_ci	struct snd_mask *m;
2728c2ecf20Sopenharmony_ci	unsigned int k;
2738c2ecf20Sopenharmony_ci	struct snd_mask old_mask;
2748c2ecf20Sopenharmony_ci	int changed;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) {
2778c2ecf20Sopenharmony_ci		m = hw_param_mask(params, k);
2788c2ecf20Sopenharmony_ci		if (snd_mask_empty(m))
2798c2ecf20Sopenharmony_ci			return -EINVAL;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		/* This parameter is not requested to change by a caller. */
2828c2ecf20Sopenharmony_ci		if (!(params->rmask & PARAM_MASK_BIT(k)))
2838c2ecf20Sopenharmony_ci			continue;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci		if (trace_hw_mask_param_enabled())
2868c2ecf20Sopenharmony_ci			old_mask = *m;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		changed = snd_mask_refine(m, constrs_mask(constrs, k));
2898c2ecf20Sopenharmony_ci		if (changed < 0)
2908c2ecf20Sopenharmony_ci			return changed;
2918c2ecf20Sopenharmony_ci		if (changed == 0)
2928c2ecf20Sopenharmony_ci			continue;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		/* Set corresponding flag so that the caller gets it. */
2958c2ecf20Sopenharmony_ci		trace_hw_mask_param(substream, k, 0, &old_mask, m);
2968c2ecf20Sopenharmony_ci		params->cmask |= PARAM_MASK_BIT(k);
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	return 0;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic int constrain_interval_params(struct snd_pcm_substream *substream,
3038c2ecf20Sopenharmony_ci				     struct snd_pcm_hw_params *params)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	struct snd_pcm_hw_constraints *constrs =
3068c2ecf20Sopenharmony_ci					&substream->runtime->hw_constraints;
3078c2ecf20Sopenharmony_ci	struct snd_interval *i;
3088c2ecf20Sopenharmony_ci	unsigned int k;
3098c2ecf20Sopenharmony_ci	struct snd_interval old_interval;
3108c2ecf20Sopenharmony_ci	int changed;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {
3138c2ecf20Sopenharmony_ci		i = hw_param_interval(params, k);
3148c2ecf20Sopenharmony_ci		if (snd_interval_empty(i))
3158c2ecf20Sopenharmony_ci			return -EINVAL;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci		/* This parameter is not requested to change by a caller. */
3188c2ecf20Sopenharmony_ci		if (!(params->rmask & PARAM_MASK_BIT(k)))
3198c2ecf20Sopenharmony_ci			continue;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci		if (trace_hw_interval_param_enabled())
3228c2ecf20Sopenharmony_ci			old_interval = *i;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci		changed = snd_interval_refine(i, constrs_interval(constrs, k));
3258c2ecf20Sopenharmony_ci		if (changed < 0)
3268c2ecf20Sopenharmony_ci			return changed;
3278c2ecf20Sopenharmony_ci		if (changed == 0)
3288c2ecf20Sopenharmony_ci			continue;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci		/* Set corresponding flag so that the caller gets it. */
3318c2ecf20Sopenharmony_ci		trace_hw_interval_param(substream, k, 0, &old_interval, i);
3328c2ecf20Sopenharmony_ci		params->cmask |= PARAM_MASK_BIT(k);
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	return 0;
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic int constrain_params_by_rules(struct snd_pcm_substream *substream,
3398c2ecf20Sopenharmony_ci				     struct snd_pcm_hw_params *params)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	struct snd_pcm_hw_constraints *constrs =
3428c2ecf20Sopenharmony_ci					&substream->runtime->hw_constraints;
3438c2ecf20Sopenharmony_ci	unsigned int k;
3448c2ecf20Sopenharmony_ci	unsigned int *rstamps;
3458c2ecf20Sopenharmony_ci	unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1];
3468c2ecf20Sopenharmony_ci	unsigned int stamp;
3478c2ecf20Sopenharmony_ci	struct snd_pcm_hw_rule *r;
3488c2ecf20Sopenharmony_ci	unsigned int d;
3498c2ecf20Sopenharmony_ci	struct snd_mask old_mask;
3508c2ecf20Sopenharmony_ci	struct snd_interval old_interval;
3518c2ecf20Sopenharmony_ci	bool again;
3528c2ecf20Sopenharmony_ci	int changed, err = 0;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	/*
3558c2ecf20Sopenharmony_ci	 * Each application of rule has own sequence number.
3568c2ecf20Sopenharmony_ci	 *
3578c2ecf20Sopenharmony_ci	 * Each member of 'rstamps' array represents the sequence number of
3588c2ecf20Sopenharmony_ci	 * recent application of corresponding rule.
3598c2ecf20Sopenharmony_ci	 */
3608c2ecf20Sopenharmony_ci	rstamps = kcalloc(constrs->rules_num, sizeof(unsigned int), GFP_KERNEL);
3618c2ecf20Sopenharmony_ci	if (!rstamps)
3628c2ecf20Sopenharmony_ci		return -ENOMEM;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/*
3658c2ecf20Sopenharmony_ci	 * Each member of 'vstamps' array represents the sequence number of
3668c2ecf20Sopenharmony_ci	 * recent application of rule in which corresponding parameters were
3678c2ecf20Sopenharmony_ci	 * changed.
3688c2ecf20Sopenharmony_ci	 *
3698c2ecf20Sopenharmony_ci	 * In initial state, elements corresponding to parameters requested by
3708c2ecf20Sopenharmony_ci	 * a caller is 1. For unrequested parameters, corresponding members
3718c2ecf20Sopenharmony_ci	 * have 0 so that the parameters are never changed anymore.
3728c2ecf20Sopenharmony_ci	 */
3738c2ecf20Sopenharmony_ci	for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
3748c2ecf20Sopenharmony_ci		vstamps[k] = (params->rmask & PARAM_MASK_BIT(k)) ? 1 : 0;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* Due to the above design, actual sequence number starts at 2. */
3778c2ecf20Sopenharmony_ci	stamp = 2;
3788c2ecf20Sopenharmony_ciretry:
3798c2ecf20Sopenharmony_ci	/* Apply all rules in order. */
3808c2ecf20Sopenharmony_ci	again = false;
3818c2ecf20Sopenharmony_ci	for (k = 0; k < constrs->rules_num; k++) {
3828c2ecf20Sopenharmony_ci		r = &constrs->rules[k];
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		/*
3858c2ecf20Sopenharmony_ci		 * Check condition bits of this rule. When the rule has
3868c2ecf20Sopenharmony_ci		 * some condition bits, parameter without the bits is
3878c2ecf20Sopenharmony_ci		 * never processed. SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP
3888c2ecf20Sopenharmony_ci		 * is an example of the condition bits.
3898c2ecf20Sopenharmony_ci		 */
3908c2ecf20Sopenharmony_ci		if (r->cond && !(r->cond & params->flags))
3918c2ecf20Sopenharmony_ci			continue;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		/*
3948c2ecf20Sopenharmony_ci		 * The 'deps' array includes maximum three dependencies
3958c2ecf20Sopenharmony_ci		 * to SNDRV_PCM_HW_PARAM_XXXs for this rule. The fourth
3968c2ecf20Sopenharmony_ci		 * member of this array is a sentinel and should be
3978c2ecf20Sopenharmony_ci		 * negative value.
3988c2ecf20Sopenharmony_ci		 *
3998c2ecf20Sopenharmony_ci		 * This rule should be processed in this time when dependent
4008c2ecf20Sopenharmony_ci		 * parameters were changed at former applications of the other
4018c2ecf20Sopenharmony_ci		 * rules.
4028c2ecf20Sopenharmony_ci		 */
4038c2ecf20Sopenharmony_ci		for (d = 0; r->deps[d] >= 0; d++) {
4048c2ecf20Sopenharmony_ci			if (vstamps[r->deps[d]] > rstamps[k])
4058c2ecf20Sopenharmony_ci				break;
4068c2ecf20Sopenharmony_ci		}
4078c2ecf20Sopenharmony_ci		if (r->deps[d] < 0)
4088c2ecf20Sopenharmony_ci			continue;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		if (trace_hw_mask_param_enabled()) {
4118c2ecf20Sopenharmony_ci			if (hw_is_mask(r->var))
4128c2ecf20Sopenharmony_ci				old_mask = *hw_param_mask(params, r->var);
4138c2ecf20Sopenharmony_ci		}
4148c2ecf20Sopenharmony_ci		if (trace_hw_interval_param_enabled()) {
4158c2ecf20Sopenharmony_ci			if (hw_is_interval(r->var))
4168c2ecf20Sopenharmony_ci				old_interval = *hw_param_interval(params, r->var);
4178c2ecf20Sopenharmony_ci		}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci		changed = r->func(params, r);
4208c2ecf20Sopenharmony_ci		if (changed < 0) {
4218c2ecf20Sopenharmony_ci			err = changed;
4228c2ecf20Sopenharmony_ci			goto out;
4238c2ecf20Sopenharmony_ci		}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci		/*
4268c2ecf20Sopenharmony_ci		 * When the parameter is changed, notify it to the caller
4278c2ecf20Sopenharmony_ci		 * by corresponding returned bit, then preparing for next
4288c2ecf20Sopenharmony_ci		 * iteration.
4298c2ecf20Sopenharmony_ci		 */
4308c2ecf20Sopenharmony_ci		if (changed && r->var >= 0) {
4318c2ecf20Sopenharmony_ci			if (hw_is_mask(r->var)) {
4328c2ecf20Sopenharmony_ci				trace_hw_mask_param(substream, r->var,
4338c2ecf20Sopenharmony_ci					k + 1, &old_mask,
4348c2ecf20Sopenharmony_ci					hw_param_mask(params, r->var));
4358c2ecf20Sopenharmony_ci			}
4368c2ecf20Sopenharmony_ci			if (hw_is_interval(r->var)) {
4378c2ecf20Sopenharmony_ci				trace_hw_interval_param(substream, r->var,
4388c2ecf20Sopenharmony_ci					k + 1, &old_interval,
4398c2ecf20Sopenharmony_ci					hw_param_interval(params, r->var));
4408c2ecf20Sopenharmony_ci			}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci			params->cmask |= PARAM_MASK_BIT(r->var);
4438c2ecf20Sopenharmony_ci			vstamps[r->var] = stamp;
4448c2ecf20Sopenharmony_ci			again = true;
4458c2ecf20Sopenharmony_ci		}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci		rstamps[k] = stamp++;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	/* Iterate to evaluate all rules till no parameters are changed. */
4518c2ecf20Sopenharmony_ci	if (again)
4528c2ecf20Sopenharmony_ci		goto retry;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci out:
4558c2ecf20Sopenharmony_ci	kfree(rstamps);
4568c2ecf20Sopenharmony_ci	return err;
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic int fixup_unreferenced_params(struct snd_pcm_substream *substream,
4608c2ecf20Sopenharmony_ci				     struct snd_pcm_hw_params *params)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	const struct snd_interval *i;
4638c2ecf20Sopenharmony_ci	const struct snd_mask *m;
4648c2ecf20Sopenharmony_ci	int err;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	if (!params->msbits) {
4678c2ecf20Sopenharmony_ci		i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
4688c2ecf20Sopenharmony_ci		if (snd_interval_single(i))
4698c2ecf20Sopenharmony_ci			params->msbits = snd_interval_value(i);
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	if (!params->rate_den) {
4738c2ecf20Sopenharmony_ci		i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
4748c2ecf20Sopenharmony_ci		if (snd_interval_single(i)) {
4758c2ecf20Sopenharmony_ci			params->rate_num = snd_interval_value(i);
4768c2ecf20Sopenharmony_ci			params->rate_den = 1;
4778c2ecf20Sopenharmony_ci		}
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (!params->fifo_size) {
4818c2ecf20Sopenharmony_ci		m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
4828c2ecf20Sopenharmony_ci		i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
4838c2ecf20Sopenharmony_ci		if (snd_mask_single(m) && snd_interval_single(i)) {
4848c2ecf20Sopenharmony_ci			err = snd_pcm_ops_ioctl(substream,
4858c2ecf20Sopenharmony_ci						SNDRV_PCM_IOCTL1_FIFO_SIZE,
4868c2ecf20Sopenharmony_ci						params);
4878c2ecf20Sopenharmony_ci			if (err < 0)
4888c2ecf20Sopenharmony_ci				return err;
4898c2ecf20Sopenharmony_ci		}
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if (!params->info) {
4938c2ecf20Sopenharmony_ci		params->info = substream->runtime->hw.info;
4948c2ecf20Sopenharmony_ci		params->info &= ~(SNDRV_PCM_INFO_FIFO_IN_FRAMES |
4958c2ecf20Sopenharmony_ci				  SNDRV_PCM_INFO_DRAIN_TRIGGER);
4968c2ecf20Sopenharmony_ci		if (!hw_support_mmap(substream))
4978c2ecf20Sopenharmony_ci			params->info &= ~(SNDRV_PCM_INFO_MMAP |
4988c2ecf20Sopenharmony_ci					  SNDRV_PCM_INFO_MMAP_VALID);
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	return 0;
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ciint snd_pcm_hw_refine(struct snd_pcm_substream *substream,
5058c2ecf20Sopenharmony_ci		      struct snd_pcm_hw_params *params)
5068c2ecf20Sopenharmony_ci{
5078c2ecf20Sopenharmony_ci	int err;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	params->info = 0;
5108c2ecf20Sopenharmony_ci	params->fifo_size = 0;
5118c2ecf20Sopenharmony_ci	if (params->rmask & PARAM_MASK_BIT(SNDRV_PCM_HW_PARAM_SAMPLE_BITS))
5128c2ecf20Sopenharmony_ci		params->msbits = 0;
5138c2ecf20Sopenharmony_ci	if (params->rmask & PARAM_MASK_BIT(SNDRV_PCM_HW_PARAM_RATE)) {
5148c2ecf20Sopenharmony_ci		params->rate_num = 0;
5158c2ecf20Sopenharmony_ci		params->rate_den = 0;
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	err = constrain_mask_params(substream, params);
5198c2ecf20Sopenharmony_ci	if (err < 0)
5208c2ecf20Sopenharmony_ci		return err;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	err = constrain_interval_params(substream, params);
5238c2ecf20Sopenharmony_ci	if (err < 0)
5248c2ecf20Sopenharmony_ci		return err;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	err = constrain_params_by_rules(substream, params);
5278c2ecf20Sopenharmony_ci	if (err < 0)
5288c2ecf20Sopenharmony_ci		return err;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	params->rmask = 0;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	return 0;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_pcm_hw_refine);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream,
5378c2ecf20Sopenharmony_ci				  struct snd_pcm_hw_params __user * _params)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	struct snd_pcm_hw_params *params;
5408c2ecf20Sopenharmony_ci	int err;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	params = memdup_user(_params, sizeof(*params));
5438c2ecf20Sopenharmony_ci	if (IS_ERR(params))
5448c2ecf20Sopenharmony_ci		return PTR_ERR(params);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	err = snd_pcm_hw_refine(substream, params);
5478c2ecf20Sopenharmony_ci	if (err < 0)
5488c2ecf20Sopenharmony_ci		goto end;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	err = fixup_unreferenced_params(substream, params);
5518c2ecf20Sopenharmony_ci	if (err < 0)
5528c2ecf20Sopenharmony_ci		goto end;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	if (copy_to_user(_params, params, sizeof(*params)))
5558c2ecf20Sopenharmony_ci		err = -EFAULT;
5568c2ecf20Sopenharmony_ciend:
5578c2ecf20Sopenharmony_ci	kfree(params);
5588c2ecf20Sopenharmony_ci	return err;
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic int period_to_usecs(struct snd_pcm_runtime *runtime)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	int usecs;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	if (! runtime->rate)
5668c2ecf20Sopenharmony_ci		return -1; /* invalid */
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	/* take 75% of period time as the deadline */
5698c2ecf20Sopenharmony_ci	usecs = (750000 / runtime->rate) * runtime->period_size;
5708c2ecf20Sopenharmony_ci	usecs += ((750000 % runtime->rate) * runtime->period_size) /
5718c2ecf20Sopenharmony_ci		runtime->rate;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	return usecs;
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_cistatic void snd_pcm_set_state(struct snd_pcm_substream *substream,
5778c2ecf20Sopenharmony_ci			      snd_pcm_state_t state)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
5808c2ecf20Sopenharmony_ci	if (substream->runtime->status->state != SNDRV_PCM_STATE_DISCONNECTED)
5818c2ecf20Sopenharmony_ci		substream->runtime->status->state = state;
5828c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
5838c2ecf20Sopenharmony_ci}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_cistatic inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
5868c2ecf20Sopenharmony_ci					int event)
5878c2ecf20Sopenharmony_ci{
5888c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_PCM_TIMER
5898c2ecf20Sopenharmony_ci	if (substream->timer)
5908c2ecf20Sopenharmony_ci		snd_timer_notify(substream->timer, event,
5918c2ecf20Sopenharmony_ci					&substream->runtime->trigger_tstamp);
5928c2ecf20Sopenharmony_ci#endif
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_civoid snd_pcm_sync_stop(struct snd_pcm_substream *substream, bool sync_irq)
5968c2ecf20Sopenharmony_ci{
5978c2ecf20Sopenharmony_ci	if (substream->runtime && substream->runtime->stop_operating) {
5988c2ecf20Sopenharmony_ci		substream->runtime->stop_operating = false;
5998c2ecf20Sopenharmony_ci		if (substream->ops && substream->ops->sync_stop)
6008c2ecf20Sopenharmony_ci			substream->ops->sync_stop(substream);
6018c2ecf20Sopenharmony_ci		else if (sync_irq && substream->pcm->card->sync_irq > 0)
6028c2ecf20Sopenharmony_ci			synchronize_irq(substream->pcm->card->sync_irq);
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci/**
6078c2ecf20Sopenharmony_ci * snd_pcm_hw_params_choose - choose a configuration defined by @params
6088c2ecf20Sopenharmony_ci * @pcm: PCM instance
6098c2ecf20Sopenharmony_ci * @params: the hw_params instance
6108c2ecf20Sopenharmony_ci *
6118c2ecf20Sopenharmony_ci * Choose one configuration from configuration space defined by @params.
6128c2ecf20Sopenharmony_ci * The configuration chosen is that obtained fixing in this order:
6138c2ecf20Sopenharmony_ci * first access, first format, first subformat, min channels,
6148c2ecf20Sopenharmony_ci * min rate, min period time, max buffer size, min tick time
6158c2ecf20Sopenharmony_ci *
6168c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure.
6178c2ecf20Sopenharmony_ci */
6188c2ecf20Sopenharmony_cistatic int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
6198c2ecf20Sopenharmony_ci				    struct snd_pcm_hw_params *params)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	static const int vars[] = {
6228c2ecf20Sopenharmony_ci		SNDRV_PCM_HW_PARAM_ACCESS,
6238c2ecf20Sopenharmony_ci		SNDRV_PCM_HW_PARAM_FORMAT,
6248c2ecf20Sopenharmony_ci		SNDRV_PCM_HW_PARAM_SUBFORMAT,
6258c2ecf20Sopenharmony_ci		SNDRV_PCM_HW_PARAM_CHANNELS,
6268c2ecf20Sopenharmony_ci		SNDRV_PCM_HW_PARAM_RATE,
6278c2ecf20Sopenharmony_ci		SNDRV_PCM_HW_PARAM_PERIOD_TIME,
6288c2ecf20Sopenharmony_ci		SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
6298c2ecf20Sopenharmony_ci		SNDRV_PCM_HW_PARAM_TICK_TIME,
6308c2ecf20Sopenharmony_ci		-1
6318c2ecf20Sopenharmony_ci	};
6328c2ecf20Sopenharmony_ci	const int *v;
6338c2ecf20Sopenharmony_ci	struct snd_mask old_mask;
6348c2ecf20Sopenharmony_ci	struct snd_interval old_interval;
6358c2ecf20Sopenharmony_ci	int changed;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	for (v = vars; *v != -1; v++) {
6388c2ecf20Sopenharmony_ci		/* Keep old parameter to trace. */
6398c2ecf20Sopenharmony_ci		if (trace_hw_mask_param_enabled()) {
6408c2ecf20Sopenharmony_ci			if (hw_is_mask(*v))
6418c2ecf20Sopenharmony_ci				old_mask = *hw_param_mask(params, *v);
6428c2ecf20Sopenharmony_ci		}
6438c2ecf20Sopenharmony_ci		if (trace_hw_interval_param_enabled()) {
6448c2ecf20Sopenharmony_ci			if (hw_is_interval(*v))
6458c2ecf20Sopenharmony_ci				old_interval = *hw_param_interval(params, *v);
6468c2ecf20Sopenharmony_ci		}
6478c2ecf20Sopenharmony_ci		if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE)
6488c2ecf20Sopenharmony_ci			changed = snd_pcm_hw_param_first(pcm, params, *v, NULL);
6498c2ecf20Sopenharmony_ci		else
6508c2ecf20Sopenharmony_ci			changed = snd_pcm_hw_param_last(pcm, params, *v, NULL);
6518c2ecf20Sopenharmony_ci		if (changed < 0)
6528c2ecf20Sopenharmony_ci			return changed;
6538c2ecf20Sopenharmony_ci		if (changed == 0)
6548c2ecf20Sopenharmony_ci			continue;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci		/* Trace the changed parameter. */
6578c2ecf20Sopenharmony_ci		if (hw_is_mask(*v)) {
6588c2ecf20Sopenharmony_ci			trace_hw_mask_param(pcm, *v, 0, &old_mask,
6598c2ecf20Sopenharmony_ci					    hw_param_mask(params, *v));
6608c2ecf20Sopenharmony_ci		}
6618c2ecf20Sopenharmony_ci		if (hw_is_interval(*v)) {
6628c2ecf20Sopenharmony_ci			trace_hw_interval_param(pcm, *v, 0, &old_interval,
6638c2ecf20Sopenharmony_ci						hw_param_interval(params, *v));
6648c2ecf20Sopenharmony_ci		}
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	return 0;
6688c2ecf20Sopenharmony_ci}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci/* acquire buffer_mutex; if it's in r/w operation, return -EBUSY, otherwise
6718c2ecf20Sopenharmony_ci * block the further r/w operations
6728c2ecf20Sopenharmony_ci */
6738c2ecf20Sopenharmony_cistatic int snd_pcm_buffer_access_lock(struct snd_pcm_runtime *runtime)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci	if (!atomic_dec_unless_positive(&runtime->buffer_accessing))
6768c2ecf20Sopenharmony_ci		return -EBUSY;
6778c2ecf20Sopenharmony_ci	mutex_lock(&runtime->buffer_mutex);
6788c2ecf20Sopenharmony_ci	return 0; /* keep buffer_mutex, unlocked by below */
6798c2ecf20Sopenharmony_ci}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci/* release buffer_mutex and clear r/w access flag */
6828c2ecf20Sopenharmony_cistatic void snd_pcm_buffer_access_unlock(struct snd_pcm_runtime *runtime)
6838c2ecf20Sopenharmony_ci{
6848c2ecf20Sopenharmony_ci	mutex_unlock(&runtime->buffer_mutex);
6858c2ecf20Sopenharmony_ci	atomic_inc(&runtime->buffer_accessing);
6868c2ecf20Sopenharmony_ci}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS)
6898c2ecf20Sopenharmony_ci#define is_oss_stream(substream)	((substream)->oss.oss)
6908c2ecf20Sopenharmony_ci#else
6918c2ecf20Sopenharmony_ci#define is_oss_stream(substream)	false
6928c2ecf20Sopenharmony_ci#endif
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cistatic int snd_pcm_hw_params(struct snd_pcm_substream *substream,
6958c2ecf20Sopenharmony_ci			     struct snd_pcm_hw_params *params)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
6988c2ecf20Sopenharmony_ci	int err, usecs;
6998c2ecf20Sopenharmony_ci	unsigned int bits;
7008c2ecf20Sopenharmony_ci	snd_pcm_uframes_t frames;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	if (PCM_RUNTIME_CHECK(substream))
7038c2ecf20Sopenharmony_ci		return -ENXIO;
7048c2ecf20Sopenharmony_ci	runtime = substream->runtime;
7058c2ecf20Sopenharmony_ci	err = snd_pcm_buffer_access_lock(runtime);
7068c2ecf20Sopenharmony_ci	if (err < 0)
7078c2ecf20Sopenharmony_ci		return err;
7088c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
7098c2ecf20Sopenharmony_ci	switch (runtime->status->state) {
7108c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_OPEN:
7118c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_SETUP:
7128c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_PREPARED:
7138c2ecf20Sopenharmony_ci		if (!is_oss_stream(substream) &&
7148c2ecf20Sopenharmony_ci		    atomic_read(&substream->mmap_count))
7158c2ecf20Sopenharmony_ci			err = -EBADFD;
7168c2ecf20Sopenharmony_ci		break;
7178c2ecf20Sopenharmony_ci	default:
7188c2ecf20Sopenharmony_ci		err = -EBADFD;
7198c2ecf20Sopenharmony_ci		break;
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
7228c2ecf20Sopenharmony_ci	if (err)
7238c2ecf20Sopenharmony_ci		goto unlock;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	snd_pcm_sync_stop(substream, true);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	params->rmask = ~0U;
7288c2ecf20Sopenharmony_ci	err = snd_pcm_hw_refine(substream, params);
7298c2ecf20Sopenharmony_ci	if (err < 0)
7308c2ecf20Sopenharmony_ci		goto _error;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	err = snd_pcm_hw_params_choose(substream, params);
7338c2ecf20Sopenharmony_ci	if (err < 0)
7348c2ecf20Sopenharmony_ci		goto _error;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	err = fixup_unreferenced_params(substream, params);
7378c2ecf20Sopenharmony_ci	if (err < 0)
7388c2ecf20Sopenharmony_ci		goto _error;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	if (substream->managed_buffer_alloc) {
7418c2ecf20Sopenharmony_ci		err = snd_pcm_lib_malloc_pages(substream,
7428c2ecf20Sopenharmony_ci					       params_buffer_bytes(params));
7438c2ecf20Sopenharmony_ci		if (err < 0)
7448c2ecf20Sopenharmony_ci			goto _error;
7458c2ecf20Sopenharmony_ci		runtime->buffer_changed = err > 0;
7468c2ecf20Sopenharmony_ci	}
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	if (substream->ops->hw_params != NULL) {
7498c2ecf20Sopenharmony_ci		err = substream->ops->hw_params(substream, params);
7508c2ecf20Sopenharmony_ci		if (err < 0)
7518c2ecf20Sopenharmony_ci			goto _error;
7528c2ecf20Sopenharmony_ci	}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	runtime->access = params_access(params);
7558c2ecf20Sopenharmony_ci	runtime->format = params_format(params);
7568c2ecf20Sopenharmony_ci	runtime->subformat = params_subformat(params);
7578c2ecf20Sopenharmony_ci	runtime->channels = params_channels(params);
7588c2ecf20Sopenharmony_ci	runtime->rate = params_rate(params);
7598c2ecf20Sopenharmony_ci	runtime->period_size = params_period_size(params);
7608c2ecf20Sopenharmony_ci	runtime->periods = params_periods(params);
7618c2ecf20Sopenharmony_ci	runtime->buffer_size = params_buffer_size(params);
7628c2ecf20Sopenharmony_ci	runtime->info = params->info;
7638c2ecf20Sopenharmony_ci	runtime->rate_num = params->rate_num;
7648c2ecf20Sopenharmony_ci	runtime->rate_den = params->rate_den;
7658c2ecf20Sopenharmony_ci	runtime->no_period_wakeup =
7668c2ecf20Sopenharmony_ci			(params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
7678c2ecf20Sopenharmony_ci			(params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	bits = snd_pcm_format_physical_width(runtime->format);
7708c2ecf20Sopenharmony_ci	runtime->sample_bits = bits;
7718c2ecf20Sopenharmony_ci	bits *= runtime->channels;
7728c2ecf20Sopenharmony_ci	runtime->frame_bits = bits;
7738c2ecf20Sopenharmony_ci	frames = 1;
7748c2ecf20Sopenharmony_ci	while (bits % 8 != 0) {
7758c2ecf20Sopenharmony_ci		bits *= 2;
7768c2ecf20Sopenharmony_ci		frames *= 2;
7778c2ecf20Sopenharmony_ci	}
7788c2ecf20Sopenharmony_ci	runtime->byte_align = bits / 8;
7798c2ecf20Sopenharmony_ci	runtime->min_align = frames;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	/* Default sw params */
7828c2ecf20Sopenharmony_ci	runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
7838c2ecf20Sopenharmony_ci	runtime->period_step = 1;
7848c2ecf20Sopenharmony_ci	runtime->control->avail_min = runtime->period_size;
7858c2ecf20Sopenharmony_ci	runtime->start_threshold = 1;
7868c2ecf20Sopenharmony_ci	runtime->stop_threshold = runtime->buffer_size;
7878c2ecf20Sopenharmony_ci	runtime->silence_threshold = 0;
7888c2ecf20Sopenharmony_ci	runtime->silence_size = 0;
7898c2ecf20Sopenharmony_ci	runtime->boundary = runtime->buffer_size;
7908c2ecf20Sopenharmony_ci	while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size)
7918c2ecf20Sopenharmony_ci		runtime->boundary *= 2;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	/* clear the buffer for avoiding possible kernel info leaks */
7948c2ecf20Sopenharmony_ci	if (runtime->dma_area && !substream->ops->copy_user) {
7958c2ecf20Sopenharmony_ci		size_t size = runtime->dma_bytes;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci		if (runtime->info & SNDRV_PCM_INFO_MMAP)
7988c2ecf20Sopenharmony_ci			size = PAGE_ALIGN(size);
7998c2ecf20Sopenharmony_ci		memset(runtime->dma_area, 0, size);
8008c2ecf20Sopenharmony_ci	}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	snd_pcm_timer_resolution_change(substream);
8038c2ecf20Sopenharmony_ci	snd_pcm_set_state(substream, SNDRV_PCM_STATE_SETUP);
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	if (cpu_latency_qos_request_active(&substream->latency_pm_qos_req))
8068c2ecf20Sopenharmony_ci		cpu_latency_qos_remove_request(&substream->latency_pm_qos_req);
8078c2ecf20Sopenharmony_ci	if ((usecs = period_to_usecs(runtime)) >= 0)
8088c2ecf20Sopenharmony_ci		cpu_latency_qos_add_request(&substream->latency_pm_qos_req,
8098c2ecf20Sopenharmony_ci					    usecs);
8108c2ecf20Sopenharmony_ci	err = 0;
8118c2ecf20Sopenharmony_ci _error:
8128c2ecf20Sopenharmony_ci	if (err) {
8138c2ecf20Sopenharmony_ci		/* hardware might be unusable from this time,
8148c2ecf20Sopenharmony_ci		 * so we force application to retry to set
8158c2ecf20Sopenharmony_ci		 * the correct hardware parameter settings
8168c2ecf20Sopenharmony_ci		 */
8178c2ecf20Sopenharmony_ci		snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
8188c2ecf20Sopenharmony_ci		if (substream->ops->hw_free != NULL)
8198c2ecf20Sopenharmony_ci			substream->ops->hw_free(substream);
8208c2ecf20Sopenharmony_ci		if (substream->managed_buffer_alloc)
8218c2ecf20Sopenharmony_ci			snd_pcm_lib_free_pages(substream);
8228c2ecf20Sopenharmony_ci	}
8238c2ecf20Sopenharmony_ci unlock:
8248c2ecf20Sopenharmony_ci	snd_pcm_buffer_access_unlock(runtime);
8258c2ecf20Sopenharmony_ci	return err;
8268c2ecf20Sopenharmony_ci}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_cistatic int snd_pcm_hw_params_user(struct snd_pcm_substream *substream,
8298c2ecf20Sopenharmony_ci				  struct snd_pcm_hw_params __user * _params)
8308c2ecf20Sopenharmony_ci{
8318c2ecf20Sopenharmony_ci	struct snd_pcm_hw_params *params;
8328c2ecf20Sopenharmony_ci	int err;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	params = memdup_user(_params, sizeof(*params));
8358c2ecf20Sopenharmony_ci	if (IS_ERR(params))
8368c2ecf20Sopenharmony_ci		return PTR_ERR(params);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	err = snd_pcm_hw_params(substream, params);
8398c2ecf20Sopenharmony_ci	if (err < 0)
8408c2ecf20Sopenharmony_ci		goto end;
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	if (copy_to_user(_params, params, sizeof(*params)))
8438c2ecf20Sopenharmony_ci		err = -EFAULT;
8448c2ecf20Sopenharmony_ciend:
8458c2ecf20Sopenharmony_ci	kfree(params);
8468c2ecf20Sopenharmony_ci	return err;
8478c2ecf20Sopenharmony_ci}
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_cistatic int do_hw_free(struct snd_pcm_substream *substream)
8508c2ecf20Sopenharmony_ci{
8518c2ecf20Sopenharmony_ci	int result = 0;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	snd_pcm_sync_stop(substream, true);
8548c2ecf20Sopenharmony_ci	if (substream->ops->hw_free)
8558c2ecf20Sopenharmony_ci		result = substream->ops->hw_free(substream);
8568c2ecf20Sopenharmony_ci	if (substream->managed_buffer_alloc)
8578c2ecf20Sopenharmony_ci		snd_pcm_lib_free_pages(substream);
8588c2ecf20Sopenharmony_ci	return result;
8598c2ecf20Sopenharmony_ci}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_cistatic int snd_pcm_hw_free(struct snd_pcm_substream *substream)
8628c2ecf20Sopenharmony_ci{
8638c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
8648c2ecf20Sopenharmony_ci	int result = 0;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	if (PCM_RUNTIME_CHECK(substream))
8678c2ecf20Sopenharmony_ci		return -ENXIO;
8688c2ecf20Sopenharmony_ci	runtime = substream->runtime;
8698c2ecf20Sopenharmony_ci	result = snd_pcm_buffer_access_lock(runtime);
8708c2ecf20Sopenharmony_ci	if (result < 0)
8718c2ecf20Sopenharmony_ci		return result;
8728c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
8738c2ecf20Sopenharmony_ci	switch (runtime->status->state) {
8748c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_SETUP:
8758c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_PREPARED:
8768c2ecf20Sopenharmony_ci		if (atomic_read(&substream->mmap_count))
8778c2ecf20Sopenharmony_ci			result = -EBADFD;
8788c2ecf20Sopenharmony_ci		break;
8798c2ecf20Sopenharmony_ci	default:
8808c2ecf20Sopenharmony_ci		result = -EBADFD;
8818c2ecf20Sopenharmony_ci		break;
8828c2ecf20Sopenharmony_ci	}
8838c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
8848c2ecf20Sopenharmony_ci	if (result)
8858c2ecf20Sopenharmony_ci		goto unlock;
8868c2ecf20Sopenharmony_ci	result = do_hw_free(substream);
8878c2ecf20Sopenharmony_ci	snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
8888c2ecf20Sopenharmony_ci	cpu_latency_qos_remove_request(&substream->latency_pm_qos_req);
8898c2ecf20Sopenharmony_ci unlock:
8908c2ecf20Sopenharmony_ci	snd_pcm_buffer_access_unlock(runtime);
8918c2ecf20Sopenharmony_ci	return result;
8928c2ecf20Sopenharmony_ci}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_cistatic int snd_pcm_sw_params(struct snd_pcm_substream *substream,
8958c2ecf20Sopenharmony_ci			     struct snd_pcm_sw_params *params)
8968c2ecf20Sopenharmony_ci{
8978c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
8988c2ecf20Sopenharmony_ci	int err;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	if (PCM_RUNTIME_CHECK(substream))
9018c2ecf20Sopenharmony_ci		return -ENXIO;
9028c2ecf20Sopenharmony_ci	runtime = substream->runtime;
9038c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
9048c2ecf20Sopenharmony_ci	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
9058c2ecf20Sopenharmony_ci		snd_pcm_stream_unlock_irq(substream);
9068c2ecf20Sopenharmony_ci		return -EBADFD;
9078c2ecf20Sopenharmony_ci	}
9088c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	if (params->tstamp_mode < 0 ||
9118c2ecf20Sopenharmony_ci	    params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
9128c2ecf20Sopenharmony_ci		return -EINVAL;
9138c2ecf20Sopenharmony_ci	if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12) &&
9148c2ecf20Sopenharmony_ci	    params->tstamp_type > SNDRV_PCM_TSTAMP_TYPE_LAST)
9158c2ecf20Sopenharmony_ci		return -EINVAL;
9168c2ecf20Sopenharmony_ci	if (params->avail_min == 0)
9178c2ecf20Sopenharmony_ci		return -EINVAL;
9188c2ecf20Sopenharmony_ci	if (params->silence_size >= runtime->boundary) {
9198c2ecf20Sopenharmony_ci		if (params->silence_threshold != 0)
9208c2ecf20Sopenharmony_ci			return -EINVAL;
9218c2ecf20Sopenharmony_ci	} else {
9228c2ecf20Sopenharmony_ci		if (params->silence_size > params->silence_threshold)
9238c2ecf20Sopenharmony_ci			return -EINVAL;
9248c2ecf20Sopenharmony_ci		if (params->silence_threshold > runtime->buffer_size)
9258c2ecf20Sopenharmony_ci			return -EINVAL;
9268c2ecf20Sopenharmony_ci	}
9278c2ecf20Sopenharmony_ci	err = 0;
9288c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
9298c2ecf20Sopenharmony_ci	runtime->tstamp_mode = params->tstamp_mode;
9308c2ecf20Sopenharmony_ci	if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12))
9318c2ecf20Sopenharmony_ci		runtime->tstamp_type = params->tstamp_type;
9328c2ecf20Sopenharmony_ci	runtime->period_step = params->period_step;
9338c2ecf20Sopenharmony_ci	runtime->control->avail_min = params->avail_min;
9348c2ecf20Sopenharmony_ci	runtime->start_threshold = params->start_threshold;
9358c2ecf20Sopenharmony_ci	runtime->stop_threshold = params->stop_threshold;
9368c2ecf20Sopenharmony_ci	runtime->silence_threshold = params->silence_threshold;
9378c2ecf20Sopenharmony_ci	runtime->silence_size = params->silence_size;
9388c2ecf20Sopenharmony_ci        params->boundary = runtime->boundary;
9398c2ecf20Sopenharmony_ci	if (snd_pcm_running(substream)) {
9408c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
9418c2ecf20Sopenharmony_ci		    runtime->silence_size > 0)
9428c2ecf20Sopenharmony_ci			snd_pcm_playback_silence(substream, ULONG_MAX);
9438c2ecf20Sopenharmony_ci		err = snd_pcm_update_state(substream, runtime);
9448c2ecf20Sopenharmony_ci	}
9458c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
9468c2ecf20Sopenharmony_ci	return err;
9478c2ecf20Sopenharmony_ci}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_cistatic int snd_pcm_sw_params_user(struct snd_pcm_substream *substream,
9508c2ecf20Sopenharmony_ci				  struct snd_pcm_sw_params __user * _params)
9518c2ecf20Sopenharmony_ci{
9528c2ecf20Sopenharmony_ci	struct snd_pcm_sw_params params;
9538c2ecf20Sopenharmony_ci	int err;
9548c2ecf20Sopenharmony_ci	if (copy_from_user(&params, _params, sizeof(params)))
9558c2ecf20Sopenharmony_ci		return -EFAULT;
9568c2ecf20Sopenharmony_ci	err = snd_pcm_sw_params(substream, &params);
9578c2ecf20Sopenharmony_ci	if (copy_to_user(_params, &params, sizeof(params)))
9588c2ecf20Sopenharmony_ci		return -EFAULT;
9598c2ecf20Sopenharmony_ci	return err;
9608c2ecf20Sopenharmony_ci}
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_cistatic inline snd_pcm_uframes_t
9638c2ecf20Sopenharmony_cisnd_pcm_calc_delay(struct snd_pcm_substream *substream)
9648c2ecf20Sopenharmony_ci{
9658c2ecf20Sopenharmony_ci	snd_pcm_uframes_t delay;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
9688c2ecf20Sopenharmony_ci		delay = snd_pcm_playback_hw_avail(substream->runtime);
9698c2ecf20Sopenharmony_ci	else
9708c2ecf20Sopenharmony_ci		delay = snd_pcm_capture_avail(substream->runtime);
9718c2ecf20Sopenharmony_ci	return delay + substream->runtime->delay;
9728c2ecf20Sopenharmony_ci}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ciint snd_pcm_status64(struct snd_pcm_substream *substream,
9758c2ecf20Sopenharmony_ci		     struct snd_pcm_status64 *status)
9768c2ecf20Sopenharmony_ci{
9778c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	snd_pcm_unpack_audio_tstamp_config(status->audio_tstamp_data,
9828c2ecf20Sopenharmony_ci					&runtime->audio_tstamp_config);
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	/* backwards compatible behavior */
9858c2ecf20Sopenharmony_ci	if (runtime->audio_tstamp_config.type_requested ==
9868c2ecf20Sopenharmony_ci		SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT) {
9878c2ecf20Sopenharmony_ci		if (runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)
9888c2ecf20Sopenharmony_ci			runtime->audio_tstamp_config.type_requested =
9898c2ecf20Sopenharmony_ci				SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK;
9908c2ecf20Sopenharmony_ci		else
9918c2ecf20Sopenharmony_ci			runtime->audio_tstamp_config.type_requested =
9928c2ecf20Sopenharmony_ci				SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
9938c2ecf20Sopenharmony_ci		runtime->audio_tstamp_report.valid = 0;
9948c2ecf20Sopenharmony_ci	} else
9958c2ecf20Sopenharmony_ci		runtime->audio_tstamp_report.valid = 1;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	status->state = runtime->status->state;
9988c2ecf20Sopenharmony_ci	status->suspended_state = runtime->status->suspended_state;
9998c2ecf20Sopenharmony_ci	if (status->state == SNDRV_PCM_STATE_OPEN)
10008c2ecf20Sopenharmony_ci		goto _end;
10018c2ecf20Sopenharmony_ci	status->trigger_tstamp_sec = runtime->trigger_tstamp.tv_sec;
10028c2ecf20Sopenharmony_ci	status->trigger_tstamp_nsec = runtime->trigger_tstamp.tv_nsec;
10038c2ecf20Sopenharmony_ci	if (snd_pcm_running(substream)) {
10048c2ecf20Sopenharmony_ci		snd_pcm_update_hw_ptr(substream);
10058c2ecf20Sopenharmony_ci		if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
10068c2ecf20Sopenharmony_ci			status->tstamp_sec = runtime->status->tstamp.tv_sec;
10078c2ecf20Sopenharmony_ci			status->tstamp_nsec =
10088c2ecf20Sopenharmony_ci				runtime->status->tstamp.tv_nsec;
10098c2ecf20Sopenharmony_ci			status->driver_tstamp_sec =
10108c2ecf20Sopenharmony_ci				runtime->driver_tstamp.tv_sec;
10118c2ecf20Sopenharmony_ci			status->driver_tstamp_nsec =
10128c2ecf20Sopenharmony_ci				runtime->driver_tstamp.tv_nsec;
10138c2ecf20Sopenharmony_ci			status->audio_tstamp_sec =
10148c2ecf20Sopenharmony_ci				runtime->status->audio_tstamp.tv_sec;
10158c2ecf20Sopenharmony_ci			status->audio_tstamp_nsec =
10168c2ecf20Sopenharmony_ci				runtime->status->audio_tstamp.tv_nsec;
10178c2ecf20Sopenharmony_ci			if (runtime->audio_tstamp_report.valid == 1)
10188c2ecf20Sopenharmony_ci				/* backwards compatibility, no report provided in COMPAT mode */
10198c2ecf20Sopenharmony_ci				snd_pcm_pack_audio_tstamp_report(&status->audio_tstamp_data,
10208c2ecf20Sopenharmony_ci								&status->audio_tstamp_accuracy,
10218c2ecf20Sopenharmony_ci								&runtime->audio_tstamp_report);
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci			goto _tstamp_end;
10248c2ecf20Sopenharmony_ci		}
10258c2ecf20Sopenharmony_ci	} else {
10268c2ecf20Sopenharmony_ci		/* get tstamp only in fallback mode and only if enabled */
10278c2ecf20Sopenharmony_ci		if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
10288c2ecf20Sopenharmony_ci			struct timespec64 tstamp;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci			snd_pcm_gettime(runtime, &tstamp);
10318c2ecf20Sopenharmony_ci			status->tstamp_sec = tstamp.tv_sec;
10328c2ecf20Sopenharmony_ci			status->tstamp_nsec = tstamp.tv_nsec;
10338c2ecf20Sopenharmony_ci		}
10348c2ecf20Sopenharmony_ci	}
10358c2ecf20Sopenharmony_ci _tstamp_end:
10368c2ecf20Sopenharmony_ci	status->appl_ptr = runtime->control->appl_ptr;
10378c2ecf20Sopenharmony_ci	status->hw_ptr = runtime->status->hw_ptr;
10388c2ecf20Sopenharmony_ci	status->avail = snd_pcm_avail(substream);
10398c2ecf20Sopenharmony_ci	status->delay = snd_pcm_running(substream) ?
10408c2ecf20Sopenharmony_ci		snd_pcm_calc_delay(substream) : 0;
10418c2ecf20Sopenharmony_ci	status->avail_max = runtime->avail_max;
10428c2ecf20Sopenharmony_ci	status->overrange = runtime->overrange;
10438c2ecf20Sopenharmony_ci	runtime->avail_max = 0;
10448c2ecf20Sopenharmony_ci	runtime->overrange = 0;
10458c2ecf20Sopenharmony_ci _end:
10468c2ecf20Sopenharmony_ci 	snd_pcm_stream_unlock_irq(substream);
10478c2ecf20Sopenharmony_ci	return 0;
10488c2ecf20Sopenharmony_ci}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_cistatic int snd_pcm_status_user64(struct snd_pcm_substream *substream,
10518c2ecf20Sopenharmony_ci				 struct snd_pcm_status64 __user * _status,
10528c2ecf20Sopenharmony_ci				 bool ext)
10538c2ecf20Sopenharmony_ci{
10548c2ecf20Sopenharmony_ci	struct snd_pcm_status64 status;
10558c2ecf20Sopenharmony_ci	int res;
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	memset(&status, 0, sizeof(status));
10588c2ecf20Sopenharmony_ci	/*
10598c2ecf20Sopenharmony_ci	 * with extension, parameters are read/write,
10608c2ecf20Sopenharmony_ci	 * get audio_tstamp_data from user,
10618c2ecf20Sopenharmony_ci	 * ignore rest of status structure
10628c2ecf20Sopenharmony_ci	 */
10638c2ecf20Sopenharmony_ci	if (ext && get_user(status.audio_tstamp_data,
10648c2ecf20Sopenharmony_ci				(u32 __user *)(&_status->audio_tstamp_data)))
10658c2ecf20Sopenharmony_ci		return -EFAULT;
10668c2ecf20Sopenharmony_ci	res = snd_pcm_status64(substream, &status);
10678c2ecf20Sopenharmony_ci	if (res < 0)
10688c2ecf20Sopenharmony_ci		return res;
10698c2ecf20Sopenharmony_ci	if (copy_to_user(_status, &status, sizeof(status)))
10708c2ecf20Sopenharmony_ci		return -EFAULT;
10718c2ecf20Sopenharmony_ci	return 0;
10728c2ecf20Sopenharmony_ci}
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_cistatic int snd_pcm_status_user32(struct snd_pcm_substream *substream,
10758c2ecf20Sopenharmony_ci				 struct snd_pcm_status32 __user * _status,
10768c2ecf20Sopenharmony_ci				 bool ext)
10778c2ecf20Sopenharmony_ci{
10788c2ecf20Sopenharmony_ci	struct snd_pcm_status64 status64;
10798c2ecf20Sopenharmony_ci	struct snd_pcm_status32 status32;
10808c2ecf20Sopenharmony_ci	int res;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	memset(&status64, 0, sizeof(status64));
10838c2ecf20Sopenharmony_ci	memset(&status32, 0, sizeof(status32));
10848c2ecf20Sopenharmony_ci	/*
10858c2ecf20Sopenharmony_ci	 * with extension, parameters are read/write,
10868c2ecf20Sopenharmony_ci	 * get audio_tstamp_data from user,
10878c2ecf20Sopenharmony_ci	 * ignore rest of status structure
10888c2ecf20Sopenharmony_ci	 */
10898c2ecf20Sopenharmony_ci	if (ext && get_user(status64.audio_tstamp_data,
10908c2ecf20Sopenharmony_ci			    (u32 __user *)(&_status->audio_tstamp_data)))
10918c2ecf20Sopenharmony_ci		return -EFAULT;
10928c2ecf20Sopenharmony_ci	res = snd_pcm_status64(substream, &status64);
10938c2ecf20Sopenharmony_ci	if (res < 0)
10948c2ecf20Sopenharmony_ci		return res;
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	status32 = (struct snd_pcm_status32) {
10978c2ecf20Sopenharmony_ci		.state = status64.state,
10988c2ecf20Sopenharmony_ci		.trigger_tstamp_sec = status64.trigger_tstamp_sec,
10998c2ecf20Sopenharmony_ci		.trigger_tstamp_nsec = status64.trigger_tstamp_nsec,
11008c2ecf20Sopenharmony_ci		.tstamp_sec = status64.tstamp_sec,
11018c2ecf20Sopenharmony_ci		.tstamp_nsec = status64.tstamp_nsec,
11028c2ecf20Sopenharmony_ci		.appl_ptr = status64.appl_ptr,
11038c2ecf20Sopenharmony_ci		.hw_ptr = status64.hw_ptr,
11048c2ecf20Sopenharmony_ci		.delay = status64.delay,
11058c2ecf20Sopenharmony_ci		.avail = status64.avail,
11068c2ecf20Sopenharmony_ci		.avail_max = status64.avail_max,
11078c2ecf20Sopenharmony_ci		.overrange = status64.overrange,
11088c2ecf20Sopenharmony_ci		.suspended_state = status64.suspended_state,
11098c2ecf20Sopenharmony_ci		.audio_tstamp_data = status64.audio_tstamp_data,
11108c2ecf20Sopenharmony_ci		.audio_tstamp_sec = status64.audio_tstamp_sec,
11118c2ecf20Sopenharmony_ci		.audio_tstamp_nsec = status64.audio_tstamp_nsec,
11128c2ecf20Sopenharmony_ci		.driver_tstamp_sec = status64.audio_tstamp_sec,
11138c2ecf20Sopenharmony_ci		.driver_tstamp_nsec = status64.audio_tstamp_nsec,
11148c2ecf20Sopenharmony_ci		.audio_tstamp_accuracy = status64.audio_tstamp_accuracy,
11158c2ecf20Sopenharmony_ci	};
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	if (copy_to_user(_status, &status32, sizeof(status32)))
11188c2ecf20Sopenharmony_ci		return -EFAULT;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	return 0;
11218c2ecf20Sopenharmony_ci}
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_cistatic int snd_pcm_channel_info(struct snd_pcm_substream *substream,
11248c2ecf20Sopenharmony_ci				struct snd_pcm_channel_info * info)
11258c2ecf20Sopenharmony_ci{
11268c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
11278c2ecf20Sopenharmony_ci	unsigned int channel;
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	channel = info->channel;
11308c2ecf20Sopenharmony_ci	runtime = substream->runtime;
11318c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
11328c2ecf20Sopenharmony_ci	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
11338c2ecf20Sopenharmony_ci		snd_pcm_stream_unlock_irq(substream);
11348c2ecf20Sopenharmony_ci		return -EBADFD;
11358c2ecf20Sopenharmony_ci	}
11368c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
11378c2ecf20Sopenharmony_ci	if (channel >= runtime->channels)
11388c2ecf20Sopenharmony_ci		return -EINVAL;
11398c2ecf20Sopenharmony_ci	memset(info, 0, sizeof(*info));
11408c2ecf20Sopenharmony_ci	info->channel = channel;
11418c2ecf20Sopenharmony_ci	return snd_pcm_ops_ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, info);
11428c2ecf20Sopenharmony_ci}
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_cistatic int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
11458c2ecf20Sopenharmony_ci				     struct snd_pcm_channel_info __user * _info)
11468c2ecf20Sopenharmony_ci{
11478c2ecf20Sopenharmony_ci	struct snd_pcm_channel_info info;
11488c2ecf20Sopenharmony_ci	int res;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	if (copy_from_user(&info, _info, sizeof(info)))
11518c2ecf20Sopenharmony_ci		return -EFAULT;
11528c2ecf20Sopenharmony_ci	res = snd_pcm_channel_info(substream, &info);
11538c2ecf20Sopenharmony_ci	if (res < 0)
11548c2ecf20Sopenharmony_ci		return res;
11558c2ecf20Sopenharmony_ci	if (copy_to_user(_info, &info, sizeof(info)))
11568c2ecf20Sopenharmony_ci		return -EFAULT;
11578c2ecf20Sopenharmony_ci	return 0;
11588c2ecf20Sopenharmony_ci}
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_cistatic void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream)
11618c2ecf20Sopenharmony_ci{
11628c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
11638c2ecf20Sopenharmony_ci	if (runtime->trigger_master == NULL)
11648c2ecf20Sopenharmony_ci		return;
11658c2ecf20Sopenharmony_ci	if (runtime->trigger_master == substream) {
11668c2ecf20Sopenharmony_ci		if (!runtime->trigger_tstamp_latched)
11678c2ecf20Sopenharmony_ci			snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
11688c2ecf20Sopenharmony_ci	} else {
11698c2ecf20Sopenharmony_ci		snd_pcm_trigger_tstamp(runtime->trigger_master);
11708c2ecf20Sopenharmony_ci		runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp;
11718c2ecf20Sopenharmony_ci	}
11728c2ecf20Sopenharmony_ci	runtime->trigger_master = NULL;
11738c2ecf20Sopenharmony_ci}
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci#define ACTION_ARG_IGNORE	(__force snd_pcm_state_t)0
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_cistruct action_ops {
11788c2ecf20Sopenharmony_ci	int (*pre_action)(struct snd_pcm_substream *substream,
11798c2ecf20Sopenharmony_ci			  snd_pcm_state_t state);
11808c2ecf20Sopenharmony_ci	int (*do_action)(struct snd_pcm_substream *substream,
11818c2ecf20Sopenharmony_ci			 snd_pcm_state_t state);
11828c2ecf20Sopenharmony_ci	void (*undo_action)(struct snd_pcm_substream *substream,
11838c2ecf20Sopenharmony_ci			    snd_pcm_state_t state);
11848c2ecf20Sopenharmony_ci	void (*post_action)(struct snd_pcm_substream *substream,
11858c2ecf20Sopenharmony_ci			    snd_pcm_state_t state);
11868c2ecf20Sopenharmony_ci};
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci/*
11898c2ecf20Sopenharmony_ci *  this functions is core for handling of linked stream
11908c2ecf20Sopenharmony_ci *  Note: the stream state might be changed also on failure
11918c2ecf20Sopenharmony_ci *  Note2: call with calling stream lock + link lock
11928c2ecf20Sopenharmony_ci */
11938c2ecf20Sopenharmony_cistatic int snd_pcm_action_group(const struct action_ops *ops,
11948c2ecf20Sopenharmony_ci				struct snd_pcm_substream *substream,
11958c2ecf20Sopenharmony_ci				snd_pcm_state_t state,
11968c2ecf20Sopenharmony_ci				bool stream_lock)
11978c2ecf20Sopenharmony_ci{
11988c2ecf20Sopenharmony_ci	struct snd_pcm_substream *s = NULL;
11998c2ecf20Sopenharmony_ci	struct snd_pcm_substream *s1;
12008c2ecf20Sopenharmony_ci	int res = 0, depth = 1;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	snd_pcm_group_for_each_entry(s, substream) {
12038c2ecf20Sopenharmony_ci		if (s != substream) {
12048c2ecf20Sopenharmony_ci			if (!stream_lock)
12058c2ecf20Sopenharmony_ci				mutex_lock_nested(&s->runtime->buffer_mutex, depth);
12068c2ecf20Sopenharmony_ci			else if (s->pcm->nonatomic)
12078c2ecf20Sopenharmony_ci				mutex_lock_nested(&s->self_group.mutex, depth);
12088c2ecf20Sopenharmony_ci			else
12098c2ecf20Sopenharmony_ci				spin_lock_nested(&s->self_group.lock, depth);
12108c2ecf20Sopenharmony_ci			depth++;
12118c2ecf20Sopenharmony_ci		}
12128c2ecf20Sopenharmony_ci		res = ops->pre_action(s, state);
12138c2ecf20Sopenharmony_ci		if (res < 0)
12148c2ecf20Sopenharmony_ci			goto _unlock;
12158c2ecf20Sopenharmony_ci	}
12168c2ecf20Sopenharmony_ci	snd_pcm_group_for_each_entry(s, substream) {
12178c2ecf20Sopenharmony_ci		res = ops->do_action(s, state);
12188c2ecf20Sopenharmony_ci		if (res < 0) {
12198c2ecf20Sopenharmony_ci			if (ops->undo_action) {
12208c2ecf20Sopenharmony_ci				snd_pcm_group_for_each_entry(s1, substream) {
12218c2ecf20Sopenharmony_ci					if (s1 == s) /* failed stream */
12228c2ecf20Sopenharmony_ci						break;
12238c2ecf20Sopenharmony_ci					ops->undo_action(s1, state);
12248c2ecf20Sopenharmony_ci				}
12258c2ecf20Sopenharmony_ci			}
12268c2ecf20Sopenharmony_ci			s = NULL; /* unlock all */
12278c2ecf20Sopenharmony_ci			goto _unlock;
12288c2ecf20Sopenharmony_ci		}
12298c2ecf20Sopenharmony_ci	}
12308c2ecf20Sopenharmony_ci	snd_pcm_group_for_each_entry(s, substream) {
12318c2ecf20Sopenharmony_ci		ops->post_action(s, state);
12328c2ecf20Sopenharmony_ci	}
12338c2ecf20Sopenharmony_ci _unlock:
12348c2ecf20Sopenharmony_ci	/* unlock streams */
12358c2ecf20Sopenharmony_ci	snd_pcm_group_for_each_entry(s1, substream) {
12368c2ecf20Sopenharmony_ci		if (s1 != substream) {
12378c2ecf20Sopenharmony_ci			if (!stream_lock)
12388c2ecf20Sopenharmony_ci				mutex_unlock(&s1->runtime->buffer_mutex);
12398c2ecf20Sopenharmony_ci			else if (s1->pcm->nonatomic)
12408c2ecf20Sopenharmony_ci				mutex_unlock(&s1->self_group.mutex);
12418c2ecf20Sopenharmony_ci			else
12428c2ecf20Sopenharmony_ci				spin_unlock(&s1->self_group.lock);
12438c2ecf20Sopenharmony_ci		}
12448c2ecf20Sopenharmony_ci		if (s1 == s)	/* end */
12458c2ecf20Sopenharmony_ci			break;
12468c2ecf20Sopenharmony_ci	}
12478c2ecf20Sopenharmony_ci	return res;
12488c2ecf20Sopenharmony_ci}
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci/*
12518c2ecf20Sopenharmony_ci *  Note: call with stream lock
12528c2ecf20Sopenharmony_ci */
12538c2ecf20Sopenharmony_cistatic int snd_pcm_action_single(const struct action_ops *ops,
12548c2ecf20Sopenharmony_ci				 struct snd_pcm_substream *substream,
12558c2ecf20Sopenharmony_ci				 snd_pcm_state_t state)
12568c2ecf20Sopenharmony_ci{
12578c2ecf20Sopenharmony_ci	int res;
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	res = ops->pre_action(substream, state);
12608c2ecf20Sopenharmony_ci	if (res < 0)
12618c2ecf20Sopenharmony_ci		return res;
12628c2ecf20Sopenharmony_ci	res = ops->do_action(substream, state);
12638c2ecf20Sopenharmony_ci	if (res == 0)
12648c2ecf20Sopenharmony_ci		ops->post_action(substream, state);
12658c2ecf20Sopenharmony_ci	else if (ops->undo_action)
12668c2ecf20Sopenharmony_ci		ops->undo_action(substream, state);
12678c2ecf20Sopenharmony_ci	return res;
12688c2ecf20Sopenharmony_ci}
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_cistatic void snd_pcm_group_assign(struct snd_pcm_substream *substream,
12718c2ecf20Sopenharmony_ci				 struct snd_pcm_group *new_group)
12728c2ecf20Sopenharmony_ci{
12738c2ecf20Sopenharmony_ci	substream->group = new_group;
12748c2ecf20Sopenharmony_ci	list_move(&substream->link_list, &new_group->substreams);
12758c2ecf20Sopenharmony_ci}
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci/*
12788c2ecf20Sopenharmony_ci * Unref and unlock the group, but keep the stream lock;
12798c2ecf20Sopenharmony_ci * when the group becomes empty and no longer referred, destroy itself
12808c2ecf20Sopenharmony_ci */
12818c2ecf20Sopenharmony_cistatic void snd_pcm_group_unref(struct snd_pcm_group *group,
12828c2ecf20Sopenharmony_ci				struct snd_pcm_substream *substream)
12838c2ecf20Sopenharmony_ci{
12848c2ecf20Sopenharmony_ci	bool do_free;
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	if (!group)
12878c2ecf20Sopenharmony_ci		return;
12888c2ecf20Sopenharmony_ci	do_free = refcount_dec_and_test(&group->refs);
12898c2ecf20Sopenharmony_ci	snd_pcm_group_unlock(group, substream->pcm->nonatomic);
12908c2ecf20Sopenharmony_ci	if (do_free)
12918c2ecf20Sopenharmony_ci		kfree(group);
12928c2ecf20Sopenharmony_ci}
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci/*
12958c2ecf20Sopenharmony_ci * Lock the group inside a stream lock and reference it;
12968c2ecf20Sopenharmony_ci * return the locked group object, or NULL if not linked
12978c2ecf20Sopenharmony_ci */
12988c2ecf20Sopenharmony_cistatic struct snd_pcm_group *
12998c2ecf20Sopenharmony_cisnd_pcm_stream_group_ref(struct snd_pcm_substream *substream)
13008c2ecf20Sopenharmony_ci{
13018c2ecf20Sopenharmony_ci	bool nonatomic = substream->pcm->nonatomic;
13028c2ecf20Sopenharmony_ci	struct snd_pcm_group *group;
13038c2ecf20Sopenharmony_ci	bool trylock;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	for (;;) {
13068c2ecf20Sopenharmony_ci		if (!snd_pcm_stream_linked(substream))
13078c2ecf20Sopenharmony_ci			return NULL;
13088c2ecf20Sopenharmony_ci		group = substream->group;
13098c2ecf20Sopenharmony_ci		/* block freeing the group object */
13108c2ecf20Sopenharmony_ci		refcount_inc(&group->refs);
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci		trylock = nonatomic ? mutex_trylock(&group->mutex) :
13138c2ecf20Sopenharmony_ci			spin_trylock(&group->lock);
13148c2ecf20Sopenharmony_ci		if (trylock)
13158c2ecf20Sopenharmony_ci			break; /* OK */
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci		/* re-lock for avoiding ABBA deadlock */
13188c2ecf20Sopenharmony_ci		snd_pcm_stream_unlock(substream);
13198c2ecf20Sopenharmony_ci		snd_pcm_group_lock(group, nonatomic);
13208c2ecf20Sopenharmony_ci		snd_pcm_stream_lock(substream);
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci		/* check the group again; the above opens a small race window */
13238c2ecf20Sopenharmony_ci		if (substream->group == group)
13248c2ecf20Sopenharmony_ci			break; /* OK */
13258c2ecf20Sopenharmony_ci		/* group changed, try again */
13268c2ecf20Sopenharmony_ci		snd_pcm_group_unref(group, substream);
13278c2ecf20Sopenharmony_ci	}
13288c2ecf20Sopenharmony_ci	return group;
13298c2ecf20Sopenharmony_ci}
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci/*
13328c2ecf20Sopenharmony_ci *  Note: call with stream lock
13338c2ecf20Sopenharmony_ci */
13348c2ecf20Sopenharmony_cistatic int snd_pcm_action(const struct action_ops *ops,
13358c2ecf20Sopenharmony_ci			  struct snd_pcm_substream *substream,
13368c2ecf20Sopenharmony_ci			  snd_pcm_state_t state)
13378c2ecf20Sopenharmony_ci{
13388c2ecf20Sopenharmony_ci	struct snd_pcm_group *group;
13398c2ecf20Sopenharmony_ci	int res;
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	group = snd_pcm_stream_group_ref(substream);
13428c2ecf20Sopenharmony_ci	if (group)
13438c2ecf20Sopenharmony_ci		res = snd_pcm_action_group(ops, substream, state, true);
13448c2ecf20Sopenharmony_ci	else
13458c2ecf20Sopenharmony_ci		res = snd_pcm_action_single(ops, substream, state);
13468c2ecf20Sopenharmony_ci	snd_pcm_group_unref(group, substream);
13478c2ecf20Sopenharmony_ci	return res;
13488c2ecf20Sopenharmony_ci}
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci/*
13518c2ecf20Sopenharmony_ci *  Note: don't use any locks before
13528c2ecf20Sopenharmony_ci */
13538c2ecf20Sopenharmony_cistatic int snd_pcm_action_lock_irq(const struct action_ops *ops,
13548c2ecf20Sopenharmony_ci				   struct snd_pcm_substream *substream,
13558c2ecf20Sopenharmony_ci				   snd_pcm_state_t state)
13568c2ecf20Sopenharmony_ci{
13578c2ecf20Sopenharmony_ci	int res;
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
13608c2ecf20Sopenharmony_ci	res = snd_pcm_action(ops, substream, state);
13618c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
13628c2ecf20Sopenharmony_ci	return res;
13638c2ecf20Sopenharmony_ci}
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci/*
13668c2ecf20Sopenharmony_ci */
13678c2ecf20Sopenharmony_cistatic int snd_pcm_action_nonatomic(const struct action_ops *ops,
13688c2ecf20Sopenharmony_ci				    struct snd_pcm_substream *substream,
13698c2ecf20Sopenharmony_ci				    snd_pcm_state_t state)
13708c2ecf20Sopenharmony_ci{
13718c2ecf20Sopenharmony_ci	int res;
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	/* Guarantee the group members won't change during non-atomic action */
13748c2ecf20Sopenharmony_ci	down_read(&snd_pcm_link_rwsem);
13758c2ecf20Sopenharmony_ci	res = snd_pcm_buffer_access_lock(substream->runtime);
13768c2ecf20Sopenharmony_ci	if (res < 0)
13778c2ecf20Sopenharmony_ci		goto unlock;
13788c2ecf20Sopenharmony_ci	if (snd_pcm_stream_linked(substream))
13798c2ecf20Sopenharmony_ci		res = snd_pcm_action_group(ops, substream, state, false);
13808c2ecf20Sopenharmony_ci	else
13818c2ecf20Sopenharmony_ci		res = snd_pcm_action_single(ops, substream, state);
13828c2ecf20Sopenharmony_ci	snd_pcm_buffer_access_unlock(substream->runtime);
13838c2ecf20Sopenharmony_ci unlock:
13848c2ecf20Sopenharmony_ci	up_read(&snd_pcm_link_rwsem);
13858c2ecf20Sopenharmony_ci	return res;
13868c2ecf20Sopenharmony_ci}
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci/*
13898c2ecf20Sopenharmony_ci * start callbacks
13908c2ecf20Sopenharmony_ci */
13918c2ecf20Sopenharmony_cistatic int snd_pcm_pre_start(struct snd_pcm_substream *substream,
13928c2ecf20Sopenharmony_ci			     snd_pcm_state_t state)
13938c2ecf20Sopenharmony_ci{
13948c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
13958c2ecf20Sopenharmony_ci	if (runtime->status->state != SNDRV_PCM_STATE_PREPARED)
13968c2ecf20Sopenharmony_ci		return -EBADFD;
13978c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
13988c2ecf20Sopenharmony_ci	    !snd_pcm_playback_data(substream))
13998c2ecf20Sopenharmony_ci		return -EPIPE;
14008c2ecf20Sopenharmony_ci	runtime->trigger_tstamp_latched = false;
14018c2ecf20Sopenharmony_ci	runtime->trigger_master = substream;
14028c2ecf20Sopenharmony_ci	return 0;
14038c2ecf20Sopenharmony_ci}
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_cistatic int snd_pcm_do_start(struct snd_pcm_substream *substream,
14068c2ecf20Sopenharmony_ci			    snd_pcm_state_t state)
14078c2ecf20Sopenharmony_ci{
14088c2ecf20Sopenharmony_ci	if (substream->runtime->trigger_master != substream)
14098c2ecf20Sopenharmony_ci		return 0;
14108c2ecf20Sopenharmony_ci	return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
14118c2ecf20Sopenharmony_ci}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_cistatic void snd_pcm_undo_start(struct snd_pcm_substream *substream,
14148c2ecf20Sopenharmony_ci			       snd_pcm_state_t state)
14158c2ecf20Sopenharmony_ci{
14168c2ecf20Sopenharmony_ci	if (substream->runtime->trigger_master == substream) {
14178c2ecf20Sopenharmony_ci		substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
14188c2ecf20Sopenharmony_ci		substream->runtime->stop_operating = true;
14198c2ecf20Sopenharmony_ci	}
14208c2ecf20Sopenharmony_ci}
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_cistatic void snd_pcm_post_start(struct snd_pcm_substream *substream,
14238c2ecf20Sopenharmony_ci			       snd_pcm_state_t state)
14248c2ecf20Sopenharmony_ci{
14258c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
14268c2ecf20Sopenharmony_ci	snd_pcm_trigger_tstamp(substream);
14278c2ecf20Sopenharmony_ci	runtime->hw_ptr_jiffies = jiffies;
14288c2ecf20Sopenharmony_ci	runtime->hw_ptr_buffer_jiffies = (runtime->buffer_size * HZ) /
14298c2ecf20Sopenharmony_ci							    runtime->rate;
14308c2ecf20Sopenharmony_ci	runtime->status->state = state;
14318c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
14328c2ecf20Sopenharmony_ci	    runtime->silence_size > 0)
14338c2ecf20Sopenharmony_ci		snd_pcm_playback_silence(substream, ULONG_MAX);
14348c2ecf20Sopenharmony_ci	snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART);
14358c2ecf20Sopenharmony_ci}
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_cistatic const struct action_ops snd_pcm_action_start = {
14388c2ecf20Sopenharmony_ci	.pre_action = snd_pcm_pre_start,
14398c2ecf20Sopenharmony_ci	.do_action = snd_pcm_do_start,
14408c2ecf20Sopenharmony_ci	.undo_action = snd_pcm_undo_start,
14418c2ecf20Sopenharmony_ci	.post_action = snd_pcm_post_start
14428c2ecf20Sopenharmony_ci};
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci/**
14458c2ecf20Sopenharmony_ci * snd_pcm_start - start all linked streams
14468c2ecf20Sopenharmony_ci * @substream: the PCM substream instance
14478c2ecf20Sopenharmony_ci *
14488c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code.
14498c2ecf20Sopenharmony_ci * The stream lock must be acquired before calling this function.
14508c2ecf20Sopenharmony_ci */
14518c2ecf20Sopenharmony_ciint snd_pcm_start(struct snd_pcm_substream *substream)
14528c2ecf20Sopenharmony_ci{
14538c2ecf20Sopenharmony_ci	return snd_pcm_action(&snd_pcm_action_start, substream,
14548c2ecf20Sopenharmony_ci			      SNDRV_PCM_STATE_RUNNING);
14558c2ecf20Sopenharmony_ci}
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci/* take the stream lock and start the streams */
14588c2ecf20Sopenharmony_cistatic int snd_pcm_start_lock_irq(struct snd_pcm_substream *substream)
14598c2ecf20Sopenharmony_ci{
14608c2ecf20Sopenharmony_ci	return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream,
14618c2ecf20Sopenharmony_ci				       SNDRV_PCM_STATE_RUNNING);
14628c2ecf20Sopenharmony_ci}
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci/*
14658c2ecf20Sopenharmony_ci * stop callbacks
14668c2ecf20Sopenharmony_ci */
14678c2ecf20Sopenharmony_cistatic int snd_pcm_pre_stop(struct snd_pcm_substream *substream,
14688c2ecf20Sopenharmony_ci			    snd_pcm_state_t state)
14698c2ecf20Sopenharmony_ci{
14708c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
14718c2ecf20Sopenharmony_ci	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
14728c2ecf20Sopenharmony_ci		return -EBADFD;
14738c2ecf20Sopenharmony_ci	runtime->trigger_master = substream;
14748c2ecf20Sopenharmony_ci	return 0;
14758c2ecf20Sopenharmony_ci}
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_cistatic int snd_pcm_do_stop(struct snd_pcm_substream *substream,
14788c2ecf20Sopenharmony_ci			   snd_pcm_state_t state)
14798c2ecf20Sopenharmony_ci{
14808c2ecf20Sopenharmony_ci	if (substream->runtime->trigger_master == substream &&
14818c2ecf20Sopenharmony_ci	    snd_pcm_running(substream)) {
14828c2ecf20Sopenharmony_ci		substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
14838c2ecf20Sopenharmony_ci		substream->runtime->stop_operating = true;
14848c2ecf20Sopenharmony_ci	}
14858c2ecf20Sopenharmony_ci	return 0; /* unconditonally stop all substreams */
14868c2ecf20Sopenharmony_ci}
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_cistatic void snd_pcm_post_stop(struct snd_pcm_substream *substream,
14898c2ecf20Sopenharmony_ci			      snd_pcm_state_t state)
14908c2ecf20Sopenharmony_ci{
14918c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
14928c2ecf20Sopenharmony_ci	if (runtime->status->state != state) {
14938c2ecf20Sopenharmony_ci		snd_pcm_trigger_tstamp(substream);
14948c2ecf20Sopenharmony_ci		runtime->status->state = state;
14958c2ecf20Sopenharmony_ci		snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTOP);
14968c2ecf20Sopenharmony_ci	}
14978c2ecf20Sopenharmony_ci	wake_up(&runtime->sleep);
14988c2ecf20Sopenharmony_ci	wake_up(&runtime->tsleep);
14998c2ecf20Sopenharmony_ci}
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_cistatic const struct action_ops snd_pcm_action_stop = {
15028c2ecf20Sopenharmony_ci	.pre_action = snd_pcm_pre_stop,
15038c2ecf20Sopenharmony_ci	.do_action = snd_pcm_do_stop,
15048c2ecf20Sopenharmony_ci	.post_action = snd_pcm_post_stop
15058c2ecf20Sopenharmony_ci};
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci/**
15088c2ecf20Sopenharmony_ci * snd_pcm_stop - try to stop all running streams in the substream group
15098c2ecf20Sopenharmony_ci * @substream: the PCM substream instance
15108c2ecf20Sopenharmony_ci * @state: PCM state after stopping the stream
15118c2ecf20Sopenharmony_ci *
15128c2ecf20Sopenharmony_ci * The state of each stream is then changed to the given state unconditionally.
15138c2ecf20Sopenharmony_ci *
15148c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code.
15158c2ecf20Sopenharmony_ci */
15168c2ecf20Sopenharmony_ciint snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t state)
15178c2ecf20Sopenharmony_ci{
15188c2ecf20Sopenharmony_ci	return snd_pcm_action(&snd_pcm_action_stop, substream, state);
15198c2ecf20Sopenharmony_ci}
15208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_pcm_stop);
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci/**
15238c2ecf20Sopenharmony_ci * snd_pcm_drain_done - stop the DMA only when the given stream is playback
15248c2ecf20Sopenharmony_ci * @substream: the PCM substream
15258c2ecf20Sopenharmony_ci *
15268c2ecf20Sopenharmony_ci * After stopping, the state is changed to SETUP.
15278c2ecf20Sopenharmony_ci * Unlike snd_pcm_stop(), this affects only the given stream.
15288c2ecf20Sopenharmony_ci *
15298c2ecf20Sopenharmony_ci * Return: Zero if succesful, or a negative error code.
15308c2ecf20Sopenharmony_ci */
15318c2ecf20Sopenharmony_ciint snd_pcm_drain_done(struct snd_pcm_substream *substream)
15328c2ecf20Sopenharmony_ci{
15338c2ecf20Sopenharmony_ci	return snd_pcm_action_single(&snd_pcm_action_stop, substream,
15348c2ecf20Sopenharmony_ci				     SNDRV_PCM_STATE_SETUP);
15358c2ecf20Sopenharmony_ci}
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci/**
15388c2ecf20Sopenharmony_ci * snd_pcm_stop_xrun - stop the running streams as XRUN
15398c2ecf20Sopenharmony_ci * @substream: the PCM substream instance
15408c2ecf20Sopenharmony_ci *
15418c2ecf20Sopenharmony_ci * This stops the given running substream (and all linked substreams) as XRUN.
15428c2ecf20Sopenharmony_ci * Unlike snd_pcm_stop(), this function takes the substream lock by itself.
15438c2ecf20Sopenharmony_ci *
15448c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code.
15458c2ecf20Sopenharmony_ci */
15468c2ecf20Sopenharmony_ciint snd_pcm_stop_xrun(struct snd_pcm_substream *substream)
15478c2ecf20Sopenharmony_ci{
15488c2ecf20Sopenharmony_ci	unsigned long flags;
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irqsave(substream, flags);
15518c2ecf20Sopenharmony_ci	if (substream->runtime && snd_pcm_running(substream))
15528c2ecf20Sopenharmony_ci		__snd_pcm_xrun(substream);
15538c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irqrestore(substream, flags);
15548c2ecf20Sopenharmony_ci	return 0;
15558c2ecf20Sopenharmony_ci}
15568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_pcm_stop_xrun);
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci/*
15598c2ecf20Sopenharmony_ci * pause callbacks: pass boolean (to start pause or resume) as state argument
15608c2ecf20Sopenharmony_ci */
15618c2ecf20Sopenharmony_ci#define pause_pushed(state)	(__force bool)(state)
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_cistatic int snd_pcm_pre_pause(struct snd_pcm_substream *substream,
15648c2ecf20Sopenharmony_ci			     snd_pcm_state_t state)
15658c2ecf20Sopenharmony_ci{
15668c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
15678c2ecf20Sopenharmony_ci	if (!(runtime->info & SNDRV_PCM_INFO_PAUSE))
15688c2ecf20Sopenharmony_ci		return -ENOSYS;
15698c2ecf20Sopenharmony_ci	if (pause_pushed(state)) {
15708c2ecf20Sopenharmony_ci		if (runtime->status->state != SNDRV_PCM_STATE_RUNNING)
15718c2ecf20Sopenharmony_ci			return -EBADFD;
15728c2ecf20Sopenharmony_ci	} else if (runtime->status->state != SNDRV_PCM_STATE_PAUSED)
15738c2ecf20Sopenharmony_ci		return -EBADFD;
15748c2ecf20Sopenharmony_ci	runtime->trigger_master = substream;
15758c2ecf20Sopenharmony_ci	return 0;
15768c2ecf20Sopenharmony_ci}
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_cistatic int snd_pcm_do_pause(struct snd_pcm_substream *substream,
15798c2ecf20Sopenharmony_ci			    snd_pcm_state_t state)
15808c2ecf20Sopenharmony_ci{
15818c2ecf20Sopenharmony_ci	if (substream->runtime->trigger_master != substream)
15828c2ecf20Sopenharmony_ci		return 0;
15838c2ecf20Sopenharmony_ci	/* some drivers might use hw_ptr to recover from the pause -
15848c2ecf20Sopenharmony_ci	   update the hw_ptr now */
15858c2ecf20Sopenharmony_ci	if (pause_pushed(state))
15868c2ecf20Sopenharmony_ci		snd_pcm_update_hw_ptr(substream);
15878c2ecf20Sopenharmony_ci	/* The jiffies check in snd_pcm_update_hw_ptr*() is done by
15888c2ecf20Sopenharmony_ci	 * a delta between the current jiffies, this gives a large enough
15898c2ecf20Sopenharmony_ci	 * delta, effectively to skip the check once.
15908c2ecf20Sopenharmony_ci	 */
15918c2ecf20Sopenharmony_ci	substream->runtime->hw_ptr_jiffies = jiffies - HZ * 1000;
15928c2ecf20Sopenharmony_ci	return substream->ops->trigger(substream,
15938c2ecf20Sopenharmony_ci				       pause_pushed(state) ?
15948c2ecf20Sopenharmony_ci				       SNDRV_PCM_TRIGGER_PAUSE_PUSH :
15958c2ecf20Sopenharmony_ci				       SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
15968c2ecf20Sopenharmony_ci}
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_cistatic void snd_pcm_undo_pause(struct snd_pcm_substream *substream,
15998c2ecf20Sopenharmony_ci			       snd_pcm_state_t state)
16008c2ecf20Sopenharmony_ci{
16018c2ecf20Sopenharmony_ci	if (substream->runtime->trigger_master == substream)
16028c2ecf20Sopenharmony_ci		substream->ops->trigger(substream,
16038c2ecf20Sopenharmony_ci					pause_pushed(state) ?
16048c2ecf20Sopenharmony_ci					SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
16058c2ecf20Sopenharmony_ci					SNDRV_PCM_TRIGGER_PAUSE_PUSH);
16068c2ecf20Sopenharmony_ci}
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_cistatic void snd_pcm_post_pause(struct snd_pcm_substream *substream,
16098c2ecf20Sopenharmony_ci			       snd_pcm_state_t state)
16108c2ecf20Sopenharmony_ci{
16118c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
16128c2ecf20Sopenharmony_ci	snd_pcm_trigger_tstamp(substream);
16138c2ecf20Sopenharmony_ci	if (pause_pushed(state)) {
16148c2ecf20Sopenharmony_ci		runtime->status->state = SNDRV_PCM_STATE_PAUSED;
16158c2ecf20Sopenharmony_ci		snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MPAUSE);
16168c2ecf20Sopenharmony_ci		wake_up(&runtime->sleep);
16178c2ecf20Sopenharmony_ci		wake_up(&runtime->tsleep);
16188c2ecf20Sopenharmony_ci	} else {
16198c2ecf20Sopenharmony_ci		runtime->status->state = SNDRV_PCM_STATE_RUNNING;
16208c2ecf20Sopenharmony_ci		snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MCONTINUE);
16218c2ecf20Sopenharmony_ci	}
16228c2ecf20Sopenharmony_ci}
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_cistatic const struct action_ops snd_pcm_action_pause = {
16258c2ecf20Sopenharmony_ci	.pre_action = snd_pcm_pre_pause,
16268c2ecf20Sopenharmony_ci	.do_action = snd_pcm_do_pause,
16278c2ecf20Sopenharmony_ci	.undo_action = snd_pcm_undo_pause,
16288c2ecf20Sopenharmony_ci	.post_action = snd_pcm_post_pause
16298c2ecf20Sopenharmony_ci};
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci/*
16328c2ecf20Sopenharmony_ci * Push/release the pause for all linked streams.
16338c2ecf20Sopenharmony_ci */
16348c2ecf20Sopenharmony_cistatic int snd_pcm_pause(struct snd_pcm_substream *substream, bool push)
16358c2ecf20Sopenharmony_ci{
16368c2ecf20Sopenharmony_ci	return snd_pcm_action(&snd_pcm_action_pause, substream,
16378c2ecf20Sopenharmony_ci			      (__force snd_pcm_state_t)push);
16388c2ecf20Sopenharmony_ci}
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_cistatic int snd_pcm_pause_lock_irq(struct snd_pcm_substream *substream,
16418c2ecf20Sopenharmony_ci				  bool push)
16428c2ecf20Sopenharmony_ci{
16438c2ecf20Sopenharmony_ci	return snd_pcm_action_lock_irq(&snd_pcm_action_pause, substream,
16448c2ecf20Sopenharmony_ci				       (__force snd_pcm_state_t)push);
16458c2ecf20Sopenharmony_ci}
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
16488c2ecf20Sopenharmony_ci/* suspend callback: state argument ignored */
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_cistatic int snd_pcm_pre_suspend(struct snd_pcm_substream *substream,
16518c2ecf20Sopenharmony_ci			       snd_pcm_state_t state)
16528c2ecf20Sopenharmony_ci{
16538c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
16548c2ecf20Sopenharmony_ci	switch (runtime->status->state) {
16558c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_SUSPENDED:
16568c2ecf20Sopenharmony_ci		return -EBUSY;
16578c2ecf20Sopenharmony_ci	/* unresumable PCM state; return -EBUSY for skipping suspend */
16588c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_OPEN:
16598c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_SETUP:
16608c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_DISCONNECTED:
16618c2ecf20Sopenharmony_ci		return -EBUSY;
16628c2ecf20Sopenharmony_ci	}
16638c2ecf20Sopenharmony_ci	runtime->trigger_master = substream;
16648c2ecf20Sopenharmony_ci	return 0;
16658c2ecf20Sopenharmony_ci}
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_cistatic int snd_pcm_do_suspend(struct snd_pcm_substream *substream,
16688c2ecf20Sopenharmony_ci			      snd_pcm_state_t state)
16698c2ecf20Sopenharmony_ci{
16708c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
16718c2ecf20Sopenharmony_ci	if (runtime->trigger_master != substream)
16728c2ecf20Sopenharmony_ci		return 0;
16738c2ecf20Sopenharmony_ci	if (! snd_pcm_running(substream))
16748c2ecf20Sopenharmony_ci		return 0;
16758c2ecf20Sopenharmony_ci	substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);
16768c2ecf20Sopenharmony_ci	runtime->stop_operating = true;
16778c2ecf20Sopenharmony_ci	return 0; /* suspend unconditionally */
16788c2ecf20Sopenharmony_ci}
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_cistatic void snd_pcm_post_suspend(struct snd_pcm_substream *substream,
16818c2ecf20Sopenharmony_ci				 snd_pcm_state_t state)
16828c2ecf20Sopenharmony_ci{
16838c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
16848c2ecf20Sopenharmony_ci	snd_pcm_trigger_tstamp(substream);
16858c2ecf20Sopenharmony_ci	runtime->status->suspended_state = runtime->status->state;
16868c2ecf20Sopenharmony_ci	runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
16878c2ecf20Sopenharmony_ci	snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSUSPEND);
16888c2ecf20Sopenharmony_ci	wake_up(&runtime->sleep);
16898c2ecf20Sopenharmony_ci	wake_up(&runtime->tsleep);
16908c2ecf20Sopenharmony_ci}
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_cistatic const struct action_ops snd_pcm_action_suspend = {
16938c2ecf20Sopenharmony_ci	.pre_action = snd_pcm_pre_suspend,
16948c2ecf20Sopenharmony_ci	.do_action = snd_pcm_do_suspend,
16958c2ecf20Sopenharmony_ci	.post_action = snd_pcm_post_suspend
16968c2ecf20Sopenharmony_ci};
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci/*
16998c2ecf20Sopenharmony_ci * snd_pcm_suspend - trigger SUSPEND to all linked streams
17008c2ecf20Sopenharmony_ci * @substream: the PCM substream
17018c2ecf20Sopenharmony_ci *
17028c2ecf20Sopenharmony_ci * After this call, all streams are changed to SUSPENDED state.
17038c2ecf20Sopenharmony_ci *
17048c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code.
17058c2ecf20Sopenharmony_ci */
17068c2ecf20Sopenharmony_cistatic int snd_pcm_suspend(struct snd_pcm_substream *substream)
17078c2ecf20Sopenharmony_ci{
17088c2ecf20Sopenharmony_ci	int err;
17098c2ecf20Sopenharmony_ci	unsigned long flags;
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irqsave(substream, flags);
17128c2ecf20Sopenharmony_ci	err = snd_pcm_action(&snd_pcm_action_suspend, substream,
17138c2ecf20Sopenharmony_ci			     ACTION_ARG_IGNORE);
17148c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irqrestore(substream, flags);
17158c2ecf20Sopenharmony_ci	return err;
17168c2ecf20Sopenharmony_ci}
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci/**
17198c2ecf20Sopenharmony_ci * snd_pcm_suspend_all - trigger SUSPEND to all substreams in the given pcm
17208c2ecf20Sopenharmony_ci * @pcm: the PCM instance
17218c2ecf20Sopenharmony_ci *
17228c2ecf20Sopenharmony_ci * After this call, all streams are changed to SUSPENDED state.
17238c2ecf20Sopenharmony_ci *
17248c2ecf20Sopenharmony_ci * Return: Zero if successful (or @pcm is %NULL), or a negative error code.
17258c2ecf20Sopenharmony_ci */
17268c2ecf20Sopenharmony_ciint snd_pcm_suspend_all(struct snd_pcm *pcm)
17278c2ecf20Sopenharmony_ci{
17288c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
17298c2ecf20Sopenharmony_ci	int stream, err = 0;
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci	if (! pcm)
17328c2ecf20Sopenharmony_ci		return 0;
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	for (stream = 0; stream < 2; stream++) {
17358c2ecf20Sopenharmony_ci		for (substream = pcm->streams[stream].substream;
17368c2ecf20Sopenharmony_ci		     substream; substream = substream->next) {
17378c2ecf20Sopenharmony_ci			/* FIXME: the open/close code should lock this as well */
17388c2ecf20Sopenharmony_ci			if (substream->runtime == NULL)
17398c2ecf20Sopenharmony_ci				continue;
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci			/*
17428c2ecf20Sopenharmony_ci			 * Skip BE dai link PCM's that are internal and may
17438c2ecf20Sopenharmony_ci			 * not have their substream ops set.
17448c2ecf20Sopenharmony_ci			 */
17458c2ecf20Sopenharmony_ci			if (!substream->ops)
17468c2ecf20Sopenharmony_ci				continue;
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_ci			err = snd_pcm_suspend(substream);
17498c2ecf20Sopenharmony_ci			if (err < 0 && err != -EBUSY)
17508c2ecf20Sopenharmony_ci				return err;
17518c2ecf20Sopenharmony_ci		}
17528c2ecf20Sopenharmony_ci	}
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	for (stream = 0; stream < 2; stream++)
17558c2ecf20Sopenharmony_ci		for (substream = pcm->streams[stream].substream;
17568c2ecf20Sopenharmony_ci		     substream; substream = substream->next)
17578c2ecf20Sopenharmony_ci			snd_pcm_sync_stop(substream, false);
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci	return 0;
17608c2ecf20Sopenharmony_ci}
17618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_pcm_suspend_all);
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci/* resume callbacks: state argument ignored */
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_cistatic int snd_pcm_pre_resume(struct snd_pcm_substream *substream,
17668c2ecf20Sopenharmony_ci			      snd_pcm_state_t state)
17678c2ecf20Sopenharmony_ci{
17688c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
17698c2ecf20Sopenharmony_ci	if (!(runtime->info & SNDRV_PCM_INFO_RESUME))
17708c2ecf20Sopenharmony_ci		return -ENOSYS;
17718c2ecf20Sopenharmony_ci	runtime->trigger_master = substream;
17728c2ecf20Sopenharmony_ci	return 0;
17738c2ecf20Sopenharmony_ci}
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_cistatic int snd_pcm_do_resume(struct snd_pcm_substream *substream,
17768c2ecf20Sopenharmony_ci			     snd_pcm_state_t state)
17778c2ecf20Sopenharmony_ci{
17788c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
17798c2ecf20Sopenharmony_ci	if (runtime->trigger_master != substream)
17808c2ecf20Sopenharmony_ci		return 0;
17818c2ecf20Sopenharmony_ci	/* DMA not running previously? */
17828c2ecf20Sopenharmony_ci	if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING &&
17838c2ecf20Sopenharmony_ci	    (runtime->status->suspended_state != SNDRV_PCM_STATE_DRAINING ||
17848c2ecf20Sopenharmony_ci	     substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
17858c2ecf20Sopenharmony_ci		return 0;
17868c2ecf20Sopenharmony_ci	return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_RESUME);
17878c2ecf20Sopenharmony_ci}
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_cistatic void snd_pcm_undo_resume(struct snd_pcm_substream *substream,
17908c2ecf20Sopenharmony_ci				snd_pcm_state_t state)
17918c2ecf20Sopenharmony_ci{
17928c2ecf20Sopenharmony_ci	if (substream->runtime->trigger_master == substream &&
17938c2ecf20Sopenharmony_ci	    snd_pcm_running(substream))
17948c2ecf20Sopenharmony_ci		substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);
17958c2ecf20Sopenharmony_ci}
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_cistatic void snd_pcm_post_resume(struct snd_pcm_substream *substream,
17988c2ecf20Sopenharmony_ci				snd_pcm_state_t state)
17998c2ecf20Sopenharmony_ci{
18008c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
18018c2ecf20Sopenharmony_ci	snd_pcm_trigger_tstamp(substream);
18028c2ecf20Sopenharmony_ci	runtime->status->state = runtime->status->suspended_state;
18038c2ecf20Sopenharmony_ci	snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MRESUME);
18048c2ecf20Sopenharmony_ci}
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_cistatic const struct action_ops snd_pcm_action_resume = {
18078c2ecf20Sopenharmony_ci	.pre_action = snd_pcm_pre_resume,
18088c2ecf20Sopenharmony_ci	.do_action = snd_pcm_do_resume,
18098c2ecf20Sopenharmony_ci	.undo_action = snd_pcm_undo_resume,
18108c2ecf20Sopenharmony_ci	.post_action = snd_pcm_post_resume
18118c2ecf20Sopenharmony_ci};
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_cistatic int snd_pcm_resume(struct snd_pcm_substream *substream)
18148c2ecf20Sopenharmony_ci{
18158c2ecf20Sopenharmony_ci	return snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream,
18168c2ecf20Sopenharmony_ci				       ACTION_ARG_IGNORE);
18178c2ecf20Sopenharmony_ci}
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci#else
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_cistatic int snd_pcm_resume(struct snd_pcm_substream *substream)
18228c2ecf20Sopenharmony_ci{
18238c2ecf20Sopenharmony_ci	return -ENOSYS;
18248c2ecf20Sopenharmony_ci}
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci/*
18298c2ecf20Sopenharmony_ci * xrun ioctl
18308c2ecf20Sopenharmony_ci *
18318c2ecf20Sopenharmony_ci * Change the RUNNING stream(s) to XRUN state.
18328c2ecf20Sopenharmony_ci */
18338c2ecf20Sopenharmony_cistatic int snd_pcm_xrun(struct snd_pcm_substream *substream)
18348c2ecf20Sopenharmony_ci{
18358c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
18368c2ecf20Sopenharmony_ci	int result;
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
18398c2ecf20Sopenharmony_ci	switch (runtime->status->state) {
18408c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_XRUN:
18418c2ecf20Sopenharmony_ci		result = 0;	/* already there */
18428c2ecf20Sopenharmony_ci		break;
18438c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_RUNNING:
18448c2ecf20Sopenharmony_ci		__snd_pcm_xrun(substream);
18458c2ecf20Sopenharmony_ci		result = 0;
18468c2ecf20Sopenharmony_ci		break;
18478c2ecf20Sopenharmony_ci	default:
18488c2ecf20Sopenharmony_ci		result = -EBADFD;
18498c2ecf20Sopenharmony_ci	}
18508c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
18518c2ecf20Sopenharmony_ci	return result;
18528c2ecf20Sopenharmony_ci}
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci/*
18558c2ecf20Sopenharmony_ci * reset ioctl
18568c2ecf20Sopenharmony_ci */
18578c2ecf20Sopenharmony_ci/* reset callbacks:  state argument ignored */
18588c2ecf20Sopenharmony_cistatic int snd_pcm_pre_reset(struct snd_pcm_substream *substream,
18598c2ecf20Sopenharmony_ci			     snd_pcm_state_t state)
18608c2ecf20Sopenharmony_ci{
18618c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
18628c2ecf20Sopenharmony_ci	switch (runtime->status->state) {
18638c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_RUNNING:
18648c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_PREPARED:
18658c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_PAUSED:
18668c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_SUSPENDED:
18678c2ecf20Sopenharmony_ci		return 0;
18688c2ecf20Sopenharmony_ci	default:
18698c2ecf20Sopenharmony_ci		return -EBADFD;
18708c2ecf20Sopenharmony_ci	}
18718c2ecf20Sopenharmony_ci}
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_cistatic int snd_pcm_do_reset(struct snd_pcm_substream *substream,
18748c2ecf20Sopenharmony_ci			    snd_pcm_state_t state)
18758c2ecf20Sopenharmony_ci{
18768c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
18778c2ecf20Sopenharmony_ci	int err = snd_pcm_ops_ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL);
18788c2ecf20Sopenharmony_ci	if (err < 0)
18798c2ecf20Sopenharmony_ci		return err;
18808c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
18818c2ecf20Sopenharmony_ci	runtime->hw_ptr_base = 0;
18828c2ecf20Sopenharmony_ci	runtime->hw_ptr_interrupt = runtime->status->hw_ptr -
18838c2ecf20Sopenharmony_ci		runtime->status->hw_ptr % runtime->period_size;
18848c2ecf20Sopenharmony_ci	runtime->silence_start = runtime->status->hw_ptr;
18858c2ecf20Sopenharmony_ci	runtime->silence_filled = 0;
18868c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
18878c2ecf20Sopenharmony_ci	return 0;
18888c2ecf20Sopenharmony_ci}
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_cistatic void snd_pcm_post_reset(struct snd_pcm_substream *substream,
18918c2ecf20Sopenharmony_ci			       snd_pcm_state_t state)
18928c2ecf20Sopenharmony_ci{
18938c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
18948c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
18958c2ecf20Sopenharmony_ci	runtime->control->appl_ptr = runtime->status->hw_ptr;
18968c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
18978c2ecf20Sopenharmony_ci	    runtime->silence_size > 0)
18988c2ecf20Sopenharmony_ci		snd_pcm_playback_silence(substream, ULONG_MAX);
18998c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
19008c2ecf20Sopenharmony_ci}
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_cistatic const struct action_ops snd_pcm_action_reset = {
19038c2ecf20Sopenharmony_ci	.pre_action = snd_pcm_pre_reset,
19048c2ecf20Sopenharmony_ci	.do_action = snd_pcm_do_reset,
19058c2ecf20Sopenharmony_ci	.post_action = snd_pcm_post_reset
19068c2ecf20Sopenharmony_ci};
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_cistatic int snd_pcm_reset(struct snd_pcm_substream *substream)
19098c2ecf20Sopenharmony_ci{
19108c2ecf20Sopenharmony_ci	return snd_pcm_action_nonatomic(&snd_pcm_action_reset, substream,
19118c2ecf20Sopenharmony_ci					ACTION_ARG_IGNORE);
19128c2ecf20Sopenharmony_ci}
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_ci/*
19158c2ecf20Sopenharmony_ci * prepare ioctl
19168c2ecf20Sopenharmony_ci */
19178c2ecf20Sopenharmony_ci/* pass f_flags as state argument */
19188c2ecf20Sopenharmony_cistatic int snd_pcm_pre_prepare(struct snd_pcm_substream *substream,
19198c2ecf20Sopenharmony_ci			       snd_pcm_state_t state)
19208c2ecf20Sopenharmony_ci{
19218c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
19228c2ecf20Sopenharmony_ci	int f_flags = (__force int)state;
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci	if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
19258c2ecf20Sopenharmony_ci	    runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
19268c2ecf20Sopenharmony_ci		return -EBADFD;
19278c2ecf20Sopenharmony_ci	if (snd_pcm_running(substream))
19288c2ecf20Sopenharmony_ci		return -EBUSY;
19298c2ecf20Sopenharmony_ci	substream->f_flags = f_flags;
19308c2ecf20Sopenharmony_ci	return 0;
19318c2ecf20Sopenharmony_ci}
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_cistatic int snd_pcm_do_prepare(struct snd_pcm_substream *substream,
19348c2ecf20Sopenharmony_ci			      snd_pcm_state_t state)
19358c2ecf20Sopenharmony_ci{
19368c2ecf20Sopenharmony_ci	int err;
19378c2ecf20Sopenharmony_ci	snd_pcm_sync_stop(substream, true);
19388c2ecf20Sopenharmony_ci	err = substream->ops->prepare(substream);
19398c2ecf20Sopenharmony_ci	if (err < 0)
19408c2ecf20Sopenharmony_ci		return err;
19418c2ecf20Sopenharmony_ci	return snd_pcm_do_reset(substream, state);
19428c2ecf20Sopenharmony_ci}
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_cistatic void snd_pcm_post_prepare(struct snd_pcm_substream *substream,
19458c2ecf20Sopenharmony_ci				 snd_pcm_state_t state)
19468c2ecf20Sopenharmony_ci{
19478c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
19488c2ecf20Sopenharmony_ci	runtime->control->appl_ptr = runtime->status->hw_ptr;
19498c2ecf20Sopenharmony_ci	snd_pcm_set_state(substream, SNDRV_PCM_STATE_PREPARED);
19508c2ecf20Sopenharmony_ci}
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_cistatic const struct action_ops snd_pcm_action_prepare = {
19538c2ecf20Sopenharmony_ci	.pre_action = snd_pcm_pre_prepare,
19548c2ecf20Sopenharmony_ci	.do_action = snd_pcm_do_prepare,
19558c2ecf20Sopenharmony_ci	.post_action = snd_pcm_post_prepare
19568c2ecf20Sopenharmony_ci};
19578c2ecf20Sopenharmony_ci
19588c2ecf20Sopenharmony_ci/**
19598c2ecf20Sopenharmony_ci * snd_pcm_prepare - prepare the PCM substream to be triggerable
19608c2ecf20Sopenharmony_ci * @substream: the PCM substream instance
19618c2ecf20Sopenharmony_ci * @file: file to refer f_flags
19628c2ecf20Sopenharmony_ci *
19638c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code.
19648c2ecf20Sopenharmony_ci */
19658c2ecf20Sopenharmony_cistatic int snd_pcm_prepare(struct snd_pcm_substream *substream,
19668c2ecf20Sopenharmony_ci			   struct file *file)
19678c2ecf20Sopenharmony_ci{
19688c2ecf20Sopenharmony_ci	int f_flags;
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci	if (file)
19718c2ecf20Sopenharmony_ci		f_flags = file->f_flags;
19728c2ecf20Sopenharmony_ci	else
19738c2ecf20Sopenharmony_ci		f_flags = substream->f_flags;
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
19768c2ecf20Sopenharmony_ci	switch (substream->runtime->status->state) {
19778c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_PAUSED:
19788c2ecf20Sopenharmony_ci		snd_pcm_pause(substream, false);
19798c2ecf20Sopenharmony_ci		fallthrough;
19808c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_SUSPENDED:
19818c2ecf20Sopenharmony_ci		snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
19828c2ecf20Sopenharmony_ci		break;
19838c2ecf20Sopenharmony_ci	}
19848c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ci	return snd_pcm_action_nonatomic(&snd_pcm_action_prepare,
19878c2ecf20Sopenharmony_ci					substream,
19888c2ecf20Sopenharmony_ci					(__force snd_pcm_state_t)f_flags);
19898c2ecf20Sopenharmony_ci}
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_ci/*
19928c2ecf20Sopenharmony_ci * drain ioctl
19938c2ecf20Sopenharmony_ci */
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci/* drain init callbacks: state argument ignored */
19968c2ecf20Sopenharmony_cistatic int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream,
19978c2ecf20Sopenharmony_ci				  snd_pcm_state_t state)
19988c2ecf20Sopenharmony_ci{
19998c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
20008c2ecf20Sopenharmony_ci	switch (runtime->status->state) {
20018c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_OPEN:
20028c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_DISCONNECTED:
20038c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_SUSPENDED:
20048c2ecf20Sopenharmony_ci		return -EBADFD;
20058c2ecf20Sopenharmony_ci	}
20068c2ecf20Sopenharmony_ci	runtime->trigger_master = substream;
20078c2ecf20Sopenharmony_ci	return 0;
20088c2ecf20Sopenharmony_ci}
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_cistatic int snd_pcm_do_drain_init(struct snd_pcm_substream *substream,
20118c2ecf20Sopenharmony_ci				 snd_pcm_state_t state)
20128c2ecf20Sopenharmony_ci{
20138c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
20148c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
20158c2ecf20Sopenharmony_ci		switch (runtime->status->state) {
20168c2ecf20Sopenharmony_ci		case SNDRV_PCM_STATE_PREPARED:
20178c2ecf20Sopenharmony_ci			/* start playback stream if possible */
20188c2ecf20Sopenharmony_ci			if (! snd_pcm_playback_empty(substream)) {
20198c2ecf20Sopenharmony_ci				snd_pcm_do_start(substream, SNDRV_PCM_STATE_DRAINING);
20208c2ecf20Sopenharmony_ci				snd_pcm_post_start(substream, SNDRV_PCM_STATE_DRAINING);
20218c2ecf20Sopenharmony_ci			} else {
20228c2ecf20Sopenharmony_ci				runtime->status->state = SNDRV_PCM_STATE_SETUP;
20238c2ecf20Sopenharmony_ci			}
20248c2ecf20Sopenharmony_ci			break;
20258c2ecf20Sopenharmony_ci		case SNDRV_PCM_STATE_RUNNING:
20268c2ecf20Sopenharmony_ci			runtime->status->state = SNDRV_PCM_STATE_DRAINING;
20278c2ecf20Sopenharmony_ci			break;
20288c2ecf20Sopenharmony_ci		case SNDRV_PCM_STATE_XRUN:
20298c2ecf20Sopenharmony_ci			runtime->status->state = SNDRV_PCM_STATE_SETUP;
20308c2ecf20Sopenharmony_ci			break;
20318c2ecf20Sopenharmony_ci		default:
20328c2ecf20Sopenharmony_ci			break;
20338c2ecf20Sopenharmony_ci		}
20348c2ecf20Sopenharmony_ci	} else {
20358c2ecf20Sopenharmony_ci		/* stop running stream */
20368c2ecf20Sopenharmony_ci		if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) {
20378c2ecf20Sopenharmony_ci			snd_pcm_state_t new_state;
20388c2ecf20Sopenharmony_ci
20398c2ecf20Sopenharmony_ci			new_state = snd_pcm_capture_avail(runtime) > 0 ?
20408c2ecf20Sopenharmony_ci				SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP;
20418c2ecf20Sopenharmony_ci			snd_pcm_do_stop(substream, new_state);
20428c2ecf20Sopenharmony_ci			snd_pcm_post_stop(substream, new_state);
20438c2ecf20Sopenharmony_ci		}
20448c2ecf20Sopenharmony_ci	}
20458c2ecf20Sopenharmony_ci
20468c2ecf20Sopenharmony_ci	if (runtime->status->state == SNDRV_PCM_STATE_DRAINING &&
20478c2ecf20Sopenharmony_ci	    runtime->trigger_master == substream &&
20488c2ecf20Sopenharmony_ci	    (runtime->hw.info & SNDRV_PCM_INFO_DRAIN_TRIGGER))
20498c2ecf20Sopenharmony_ci		return substream->ops->trigger(substream,
20508c2ecf20Sopenharmony_ci					       SNDRV_PCM_TRIGGER_DRAIN);
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci	return 0;
20538c2ecf20Sopenharmony_ci}
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_cistatic void snd_pcm_post_drain_init(struct snd_pcm_substream *substream,
20568c2ecf20Sopenharmony_ci				    snd_pcm_state_t state)
20578c2ecf20Sopenharmony_ci{
20588c2ecf20Sopenharmony_ci}
20598c2ecf20Sopenharmony_ci
20608c2ecf20Sopenharmony_cistatic const struct action_ops snd_pcm_action_drain_init = {
20618c2ecf20Sopenharmony_ci	.pre_action = snd_pcm_pre_drain_init,
20628c2ecf20Sopenharmony_ci	.do_action = snd_pcm_do_drain_init,
20638c2ecf20Sopenharmony_ci	.post_action = snd_pcm_post_drain_init
20648c2ecf20Sopenharmony_ci};
20658c2ecf20Sopenharmony_ci
20668c2ecf20Sopenharmony_ci/*
20678c2ecf20Sopenharmony_ci * Drain the stream(s).
20688c2ecf20Sopenharmony_ci * When the substream is linked, sync until the draining of all playback streams
20698c2ecf20Sopenharmony_ci * is finished.
20708c2ecf20Sopenharmony_ci * After this call, all streams are supposed to be either SETUP or DRAINING
20718c2ecf20Sopenharmony_ci * (capture only) state.
20728c2ecf20Sopenharmony_ci */
20738c2ecf20Sopenharmony_cistatic int snd_pcm_drain(struct snd_pcm_substream *substream,
20748c2ecf20Sopenharmony_ci			 struct file *file)
20758c2ecf20Sopenharmony_ci{
20768c2ecf20Sopenharmony_ci	struct snd_card *card;
20778c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
20788c2ecf20Sopenharmony_ci	struct snd_pcm_substream *s;
20798c2ecf20Sopenharmony_ci	struct snd_pcm_group *group;
20808c2ecf20Sopenharmony_ci	wait_queue_entry_t wait;
20818c2ecf20Sopenharmony_ci	int result = 0;
20828c2ecf20Sopenharmony_ci	int nonblock = 0;
20838c2ecf20Sopenharmony_ci
20848c2ecf20Sopenharmony_ci	card = substream->pcm->card;
20858c2ecf20Sopenharmony_ci	runtime = substream->runtime;
20868c2ecf20Sopenharmony_ci
20878c2ecf20Sopenharmony_ci	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
20888c2ecf20Sopenharmony_ci		return -EBADFD;
20898c2ecf20Sopenharmony_ci
20908c2ecf20Sopenharmony_ci	if (file) {
20918c2ecf20Sopenharmony_ci		if (file->f_flags & O_NONBLOCK)
20928c2ecf20Sopenharmony_ci			nonblock = 1;
20938c2ecf20Sopenharmony_ci	} else if (substream->f_flags & O_NONBLOCK)
20948c2ecf20Sopenharmony_ci		nonblock = 1;
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
20978c2ecf20Sopenharmony_ci	/* resume pause */
20988c2ecf20Sopenharmony_ci	if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
20998c2ecf20Sopenharmony_ci		snd_pcm_pause(substream, false);
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci	/* pre-start/stop - all running streams are changed to DRAINING state */
21028c2ecf20Sopenharmony_ci	result = snd_pcm_action(&snd_pcm_action_drain_init, substream,
21038c2ecf20Sopenharmony_ci				ACTION_ARG_IGNORE);
21048c2ecf20Sopenharmony_ci	if (result < 0)
21058c2ecf20Sopenharmony_ci		goto unlock;
21068c2ecf20Sopenharmony_ci	/* in non-blocking, we don't wait in ioctl but let caller poll */
21078c2ecf20Sopenharmony_ci	if (nonblock) {
21088c2ecf20Sopenharmony_ci		result = -EAGAIN;
21098c2ecf20Sopenharmony_ci		goto unlock;
21108c2ecf20Sopenharmony_ci	}
21118c2ecf20Sopenharmony_ci
21128c2ecf20Sopenharmony_ci	for (;;) {
21138c2ecf20Sopenharmony_ci		long tout;
21148c2ecf20Sopenharmony_ci		struct snd_pcm_runtime *to_check;
21158c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
21168c2ecf20Sopenharmony_ci			result = -ERESTARTSYS;
21178c2ecf20Sopenharmony_ci			break;
21188c2ecf20Sopenharmony_ci		}
21198c2ecf20Sopenharmony_ci		/* find a substream to drain */
21208c2ecf20Sopenharmony_ci		to_check = NULL;
21218c2ecf20Sopenharmony_ci		group = snd_pcm_stream_group_ref(substream);
21228c2ecf20Sopenharmony_ci		snd_pcm_group_for_each_entry(s, substream) {
21238c2ecf20Sopenharmony_ci			if (s->stream != SNDRV_PCM_STREAM_PLAYBACK)
21248c2ecf20Sopenharmony_ci				continue;
21258c2ecf20Sopenharmony_ci			runtime = s->runtime;
21268c2ecf20Sopenharmony_ci			if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
21278c2ecf20Sopenharmony_ci				to_check = runtime;
21288c2ecf20Sopenharmony_ci				break;
21298c2ecf20Sopenharmony_ci			}
21308c2ecf20Sopenharmony_ci		}
21318c2ecf20Sopenharmony_ci		snd_pcm_group_unref(group, substream);
21328c2ecf20Sopenharmony_ci		if (!to_check)
21338c2ecf20Sopenharmony_ci			break; /* all drained */
21348c2ecf20Sopenharmony_ci		init_waitqueue_entry(&wait, current);
21358c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
21368c2ecf20Sopenharmony_ci		add_wait_queue(&to_check->sleep, &wait);
21378c2ecf20Sopenharmony_ci		snd_pcm_stream_unlock_irq(substream);
21388c2ecf20Sopenharmony_ci		if (runtime->no_period_wakeup)
21398c2ecf20Sopenharmony_ci			tout = MAX_SCHEDULE_TIMEOUT;
21408c2ecf20Sopenharmony_ci		else {
21418c2ecf20Sopenharmony_ci			tout = 10;
21428c2ecf20Sopenharmony_ci			if (runtime->rate) {
21438c2ecf20Sopenharmony_ci				long t = runtime->period_size * 2 / runtime->rate;
21448c2ecf20Sopenharmony_ci				tout = max(t, tout);
21458c2ecf20Sopenharmony_ci			}
21468c2ecf20Sopenharmony_ci			tout = msecs_to_jiffies(tout * 1000);
21478c2ecf20Sopenharmony_ci		}
21488c2ecf20Sopenharmony_ci		tout = schedule_timeout(tout);
21498c2ecf20Sopenharmony_ci
21508c2ecf20Sopenharmony_ci		snd_pcm_stream_lock_irq(substream);
21518c2ecf20Sopenharmony_ci		group = snd_pcm_stream_group_ref(substream);
21528c2ecf20Sopenharmony_ci		snd_pcm_group_for_each_entry(s, substream) {
21538c2ecf20Sopenharmony_ci			if (s->runtime == to_check) {
21548c2ecf20Sopenharmony_ci				remove_wait_queue(&to_check->sleep, &wait);
21558c2ecf20Sopenharmony_ci				break;
21568c2ecf20Sopenharmony_ci			}
21578c2ecf20Sopenharmony_ci		}
21588c2ecf20Sopenharmony_ci		snd_pcm_group_unref(group, substream);
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci		if (card->shutdown) {
21618c2ecf20Sopenharmony_ci			result = -ENODEV;
21628c2ecf20Sopenharmony_ci			break;
21638c2ecf20Sopenharmony_ci		}
21648c2ecf20Sopenharmony_ci		if (tout == 0) {
21658c2ecf20Sopenharmony_ci			if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
21668c2ecf20Sopenharmony_ci				result = -ESTRPIPE;
21678c2ecf20Sopenharmony_ci			else {
21688c2ecf20Sopenharmony_ci				dev_dbg(substream->pcm->card->dev,
21698c2ecf20Sopenharmony_ci					"playback drain error (DMA or IRQ trouble?)\n");
21708c2ecf20Sopenharmony_ci				snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
21718c2ecf20Sopenharmony_ci				result = -EIO;
21728c2ecf20Sopenharmony_ci			}
21738c2ecf20Sopenharmony_ci			break;
21748c2ecf20Sopenharmony_ci		}
21758c2ecf20Sopenharmony_ci	}
21768c2ecf20Sopenharmony_ci
21778c2ecf20Sopenharmony_ci unlock:
21788c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
21798c2ecf20Sopenharmony_ci
21808c2ecf20Sopenharmony_ci	return result;
21818c2ecf20Sopenharmony_ci}
21828c2ecf20Sopenharmony_ci
21838c2ecf20Sopenharmony_ci/*
21848c2ecf20Sopenharmony_ci * drop ioctl
21858c2ecf20Sopenharmony_ci *
21868c2ecf20Sopenharmony_ci * Immediately put all linked substreams into SETUP state.
21878c2ecf20Sopenharmony_ci */
21888c2ecf20Sopenharmony_cistatic int snd_pcm_drop(struct snd_pcm_substream *substream)
21898c2ecf20Sopenharmony_ci{
21908c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
21918c2ecf20Sopenharmony_ci	int result = 0;
21928c2ecf20Sopenharmony_ci
21938c2ecf20Sopenharmony_ci	if (PCM_RUNTIME_CHECK(substream))
21948c2ecf20Sopenharmony_ci		return -ENXIO;
21958c2ecf20Sopenharmony_ci	runtime = substream->runtime;
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci	if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
21988c2ecf20Sopenharmony_ci	    runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
21998c2ecf20Sopenharmony_ci		return -EBADFD;
22008c2ecf20Sopenharmony_ci
22018c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
22028c2ecf20Sopenharmony_ci	/* resume pause */
22038c2ecf20Sopenharmony_ci	if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
22048c2ecf20Sopenharmony_ci		snd_pcm_pause(substream, false);
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_ci	snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
22078c2ecf20Sopenharmony_ci	/* runtime->control->appl_ptr = runtime->status->hw_ptr; */
22088c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
22098c2ecf20Sopenharmony_ci
22108c2ecf20Sopenharmony_ci	return result;
22118c2ecf20Sopenharmony_ci}
22128c2ecf20Sopenharmony_ci
22138c2ecf20Sopenharmony_ci
22148c2ecf20Sopenharmony_cistatic bool is_pcm_file(struct file *file)
22158c2ecf20Sopenharmony_ci{
22168c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
22178c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
22188c2ecf20Sopenharmony_ci	unsigned int minor;
22198c2ecf20Sopenharmony_ci
22208c2ecf20Sopenharmony_ci	if (!S_ISCHR(inode->i_mode) || imajor(inode) != snd_major)
22218c2ecf20Sopenharmony_ci		return false;
22228c2ecf20Sopenharmony_ci	minor = iminor(inode);
22238c2ecf20Sopenharmony_ci	pcm = snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
22248c2ecf20Sopenharmony_ci	if (!pcm)
22258c2ecf20Sopenharmony_ci		pcm = snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE);
22268c2ecf20Sopenharmony_ci	if (!pcm)
22278c2ecf20Sopenharmony_ci		return false;
22288c2ecf20Sopenharmony_ci	snd_card_unref(pcm->card);
22298c2ecf20Sopenharmony_ci	return true;
22308c2ecf20Sopenharmony_ci}
22318c2ecf20Sopenharmony_ci
22328c2ecf20Sopenharmony_ci/*
22338c2ecf20Sopenharmony_ci * PCM link handling
22348c2ecf20Sopenharmony_ci */
22358c2ecf20Sopenharmony_cistatic int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
22368c2ecf20Sopenharmony_ci{
22378c2ecf20Sopenharmony_ci	int res = 0;
22388c2ecf20Sopenharmony_ci	struct snd_pcm_file *pcm_file;
22398c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream1;
22408c2ecf20Sopenharmony_ci	struct snd_pcm_group *group, *target_group;
22418c2ecf20Sopenharmony_ci	bool nonatomic = substream->pcm->nonatomic;
22428c2ecf20Sopenharmony_ci	struct fd f = fdget(fd);
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_ci	if (!f.file)
22458c2ecf20Sopenharmony_ci		return -EBADFD;
22468c2ecf20Sopenharmony_ci	if (!is_pcm_file(f.file)) {
22478c2ecf20Sopenharmony_ci		res = -EBADFD;
22488c2ecf20Sopenharmony_ci		goto _badf;
22498c2ecf20Sopenharmony_ci	}
22508c2ecf20Sopenharmony_ci	pcm_file = f.file->private_data;
22518c2ecf20Sopenharmony_ci	substream1 = pcm_file->substream;
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_ci	if (substream == substream1) {
22548c2ecf20Sopenharmony_ci		res = -EINVAL;
22558c2ecf20Sopenharmony_ci		goto _badf;
22568c2ecf20Sopenharmony_ci	}
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci	group = kzalloc(sizeof(*group), GFP_KERNEL);
22598c2ecf20Sopenharmony_ci	if (!group) {
22608c2ecf20Sopenharmony_ci		res = -ENOMEM;
22618c2ecf20Sopenharmony_ci		goto _nolock;
22628c2ecf20Sopenharmony_ci	}
22638c2ecf20Sopenharmony_ci	snd_pcm_group_init(group);
22648c2ecf20Sopenharmony_ci
22658c2ecf20Sopenharmony_ci	down_write(&snd_pcm_link_rwsem);
22668c2ecf20Sopenharmony_ci	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
22678c2ecf20Sopenharmony_ci	    substream->runtime->status->state != substream1->runtime->status->state ||
22688c2ecf20Sopenharmony_ci	    substream->pcm->nonatomic != substream1->pcm->nonatomic) {
22698c2ecf20Sopenharmony_ci		res = -EBADFD;
22708c2ecf20Sopenharmony_ci		goto _end;
22718c2ecf20Sopenharmony_ci	}
22728c2ecf20Sopenharmony_ci	if (snd_pcm_stream_linked(substream1)) {
22738c2ecf20Sopenharmony_ci		res = -EALREADY;
22748c2ecf20Sopenharmony_ci		goto _end;
22758c2ecf20Sopenharmony_ci	}
22768c2ecf20Sopenharmony_ci
22778c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
22788c2ecf20Sopenharmony_ci	if (!snd_pcm_stream_linked(substream)) {
22798c2ecf20Sopenharmony_ci		snd_pcm_group_assign(substream, group);
22808c2ecf20Sopenharmony_ci		group = NULL; /* assigned, don't free this one below */
22818c2ecf20Sopenharmony_ci	}
22828c2ecf20Sopenharmony_ci	target_group = substream->group;
22838c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
22848c2ecf20Sopenharmony_ci
22858c2ecf20Sopenharmony_ci	snd_pcm_group_lock_irq(target_group, nonatomic);
22868c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_nested(substream1);
22878c2ecf20Sopenharmony_ci	snd_pcm_group_assign(substream1, target_group);
22888c2ecf20Sopenharmony_ci	refcount_inc(&target_group->refs);
22898c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock(substream1);
22908c2ecf20Sopenharmony_ci	snd_pcm_group_unlock_irq(target_group, nonatomic);
22918c2ecf20Sopenharmony_ci _end:
22928c2ecf20Sopenharmony_ci	up_write(&snd_pcm_link_rwsem);
22938c2ecf20Sopenharmony_ci _nolock:
22948c2ecf20Sopenharmony_ci	kfree(group);
22958c2ecf20Sopenharmony_ci _badf:
22968c2ecf20Sopenharmony_ci	fdput(f);
22978c2ecf20Sopenharmony_ci	return res;
22988c2ecf20Sopenharmony_ci}
22998c2ecf20Sopenharmony_ci
23008c2ecf20Sopenharmony_cistatic void relink_to_local(struct snd_pcm_substream *substream)
23018c2ecf20Sopenharmony_ci{
23028c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_nested(substream);
23038c2ecf20Sopenharmony_ci	snd_pcm_group_assign(substream, &substream->self_group);
23048c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock(substream);
23058c2ecf20Sopenharmony_ci}
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_cistatic int snd_pcm_unlink(struct snd_pcm_substream *substream)
23088c2ecf20Sopenharmony_ci{
23098c2ecf20Sopenharmony_ci	struct snd_pcm_group *group;
23108c2ecf20Sopenharmony_ci	bool nonatomic = substream->pcm->nonatomic;
23118c2ecf20Sopenharmony_ci	bool do_free = false;
23128c2ecf20Sopenharmony_ci	int res = 0;
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci	down_write(&snd_pcm_link_rwsem);
23158c2ecf20Sopenharmony_ci
23168c2ecf20Sopenharmony_ci	if (!snd_pcm_stream_linked(substream)) {
23178c2ecf20Sopenharmony_ci		res = -EALREADY;
23188c2ecf20Sopenharmony_ci		goto _end;
23198c2ecf20Sopenharmony_ci	}
23208c2ecf20Sopenharmony_ci
23218c2ecf20Sopenharmony_ci	group = substream->group;
23228c2ecf20Sopenharmony_ci	snd_pcm_group_lock_irq(group, nonatomic);
23238c2ecf20Sopenharmony_ci
23248c2ecf20Sopenharmony_ci	relink_to_local(substream);
23258c2ecf20Sopenharmony_ci	refcount_dec(&group->refs);
23268c2ecf20Sopenharmony_ci
23278c2ecf20Sopenharmony_ci	/* detach the last stream, too */
23288c2ecf20Sopenharmony_ci	if (list_is_singular(&group->substreams)) {
23298c2ecf20Sopenharmony_ci		relink_to_local(list_first_entry(&group->substreams,
23308c2ecf20Sopenharmony_ci						 struct snd_pcm_substream,
23318c2ecf20Sopenharmony_ci						 link_list));
23328c2ecf20Sopenharmony_ci		do_free = refcount_dec_and_test(&group->refs);
23338c2ecf20Sopenharmony_ci	}
23348c2ecf20Sopenharmony_ci
23358c2ecf20Sopenharmony_ci	snd_pcm_group_unlock_irq(group, nonatomic);
23368c2ecf20Sopenharmony_ci	if (do_free)
23378c2ecf20Sopenharmony_ci		kfree(group);
23388c2ecf20Sopenharmony_ci
23398c2ecf20Sopenharmony_ci       _end:
23408c2ecf20Sopenharmony_ci	up_write(&snd_pcm_link_rwsem);
23418c2ecf20Sopenharmony_ci	return res;
23428c2ecf20Sopenharmony_ci}
23438c2ecf20Sopenharmony_ci
23448c2ecf20Sopenharmony_ci/*
23458c2ecf20Sopenharmony_ci * hw configurator
23468c2ecf20Sopenharmony_ci */
23478c2ecf20Sopenharmony_cistatic int snd_pcm_hw_rule_mul(struct snd_pcm_hw_params *params,
23488c2ecf20Sopenharmony_ci			       struct snd_pcm_hw_rule *rule)
23498c2ecf20Sopenharmony_ci{
23508c2ecf20Sopenharmony_ci	struct snd_interval t;
23518c2ecf20Sopenharmony_ci	snd_interval_mul(hw_param_interval_c(params, rule->deps[0]),
23528c2ecf20Sopenharmony_ci		     hw_param_interval_c(params, rule->deps[1]), &t);
23538c2ecf20Sopenharmony_ci	return snd_interval_refine(hw_param_interval(params, rule->var), &t);
23548c2ecf20Sopenharmony_ci}
23558c2ecf20Sopenharmony_ci
23568c2ecf20Sopenharmony_cistatic int snd_pcm_hw_rule_div(struct snd_pcm_hw_params *params,
23578c2ecf20Sopenharmony_ci			       struct snd_pcm_hw_rule *rule)
23588c2ecf20Sopenharmony_ci{
23598c2ecf20Sopenharmony_ci	struct snd_interval t;
23608c2ecf20Sopenharmony_ci	snd_interval_div(hw_param_interval_c(params, rule->deps[0]),
23618c2ecf20Sopenharmony_ci		     hw_param_interval_c(params, rule->deps[1]), &t);
23628c2ecf20Sopenharmony_ci	return snd_interval_refine(hw_param_interval(params, rule->var), &t);
23638c2ecf20Sopenharmony_ci}
23648c2ecf20Sopenharmony_ci
23658c2ecf20Sopenharmony_cistatic int snd_pcm_hw_rule_muldivk(struct snd_pcm_hw_params *params,
23668c2ecf20Sopenharmony_ci				   struct snd_pcm_hw_rule *rule)
23678c2ecf20Sopenharmony_ci{
23688c2ecf20Sopenharmony_ci	struct snd_interval t;
23698c2ecf20Sopenharmony_ci	snd_interval_muldivk(hw_param_interval_c(params, rule->deps[0]),
23708c2ecf20Sopenharmony_ci			 hw_param_interval_c(params, rule->deps[1]),
23718c2ecf20Sopenharmony_ci			 (unsigned long) rule->private, &t);
23728c2ecf20Sopenharmony_ci	return snd_interval_refine(hw_param_interval(params, rule->var), &t);
23738c2ecf20Sopenharmony_ci}
23748c2ecf20Sopenharmony_ci
23758c2ecf20Sopenharmony_cistatic int snd_pcm_hw_rule_mulkdiv(struct snd_pcm_hw_params *params,
23768c2ecf20Sopenharmony_ci				   struct snd_pcm_hw_rule *rule)
23778c2ecf20Sopenharmony_ci{
23788c2ecf20Sopenharmony_ci	struct snd_interval t;
23798c2ecf20Sopenharmony_ci	snd_interval_mulkdiv(hw_param_interval_c(params, rule->deps[0]),
23808c2ecf20Sopenharmony_ci			 (unsigned long) rule->private,
23818c2ecf20Sopenharmony_ci			 hw_param_interval_c(params, rule->deps[1]), &t);
23828c2ecf20Sopenharmony_ci	return snd_interval_refine(hw_param_interval(params, rule->var), &t);
23838c2ecf20Sopenharmony_ci}
23848c2ecf20Sopenharmony_ci
23858c2ecf20Sopenharmony_cistatic int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params,
23868c2ecf20Sopenharmony_ci				  struct snd_pcm_hw_rule *rule)
23878c2ecf20Sopenharmony_ci{
23888c2ecf20Sopenharmony_ci	snd_pcm_format_t k;
23898c2ecf20Sopenharmony_ci	const struct snd_interval *i =
23908c2ecf20Sopenharmony_ci				hw_param_interval_c(params, rule->deps[0]);
23918c2ecf20Sopenharmony_ci	struct snd_mask m;
23928c2ecf20Sopenharmony_ci	struct snd_mask *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
23938c2ecf20Sopenharmony_ci	snd_mask_any(&m);
23948c2ecf20Sopenharmony_ci	pcm_for_each_format(k) {
23958c2ecf20Sopenharmony_ci		int bits;
23968c2ecf20Sopenharmony_ci		if (!snd_mask_test_format(mask, k))
23978c2ecf20Sopenharmony_ci			continue;
23988c2ecf20Sopenharmony_ci		bits = snd_pcm_format_physical_width(k);
23998c2ecf20Sopenharmony_ci		if (bits <= 0)
24008c2ecf20Sopenharmony_ci			continue; /* ignore invalid formats */
24018c2ecf20Sopenharmony_ci		if ((unsigned)bits < i->min || (unsigned)bits > i->max)
24028c2ecf20Sopenharmony_ci			snd_mask_reset(&m, (__force unsigned)k);
24038c2ecf20Sopenharmony_ci	}
24048c2ecf20Sopenharmony_ci	return snd_mask_refine(mask, &m);
24058c2ecf20Sopenharmony_ci}
24068c2ecf20Sopenharmony_ci
24078c2ecf20Sopenharmony_cistatic int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
24088c2ecf20Sopenharmony_ci				       struct snd_pcm_hw_rule *rule)
24098c2ecf20Sopenharmony_ci{
24108c2ecf20Sopenharmony_ci	struct snd_interval t;
24118c2ecf20Sopenharmony_ci	snd_pcm_format_t k;
24128c2ecf20Sopenharmony_ci
24138c2ecf20Sopenharmony_ci	t.min = UINT_MAX;
24148c2ecf20Sopenharmony_ci	t.max = 0;
24158c2ecf20Sopenharmony_ci	t.openmin = 0;
24168c2ecf20Sopenharmony_ci	t.openmax = 0;
24178c2ecf20Sopenharmony_ci	pcm_for_each_format(k) {
24188c2ecf20Sopenharmony_ci		int bits;
24198c2ecf20Sopenharmony_ci		if (!snd_mask_test_format(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k))
24208c2ecf20Sopenharmony_ci			continue;
24218c2ecf20Sopenharmony_ci		bits = snd_pcm_format_physical_width(k);
24228c2ecf20Sopenharmony_ci		if (bits <= 0)
24238c2ecf20Sopenharmony_ci			continue; /* ignore invalid formats */
24248c2ecf20Sopenharmony_ci		if (t.min > (unsigned)bits)
24258c2ecf20Sopenharmony_ci			t.min = bits;
24268c2ecf20Sopenharmony_ci		if (t.max < (unsigned)bits)
24278c2ecf20Sopenharmony_ci			t.max = bits;
24288c2ecf20Sopenharmony_ci	}
24298c2ecf20Sopenharmony_ci	t.integer = 1;
24308c2ecf20Sopenharmony_ci	return snd_interval_refine(hw_param_interval(params, rule->var), &t);
24318c2ecf20Sopenharmony_ci}
24328c2ecf20Sopenharmony_ci
24338c2ecf20Sopenharmony_ci#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12
24348c2ecf20Sopenharmony_ci#error "Change this table"
24358c2ecf20Sopenharmony_ci#endif
24368c2ecf20Sopenharmony_ci
24378c2ecf20Sopenharmony_cistatic const unsigned int rates[] = {
24388c2ecf20Sopenharmony_ci	5512, 8000, 11025, 16000, 22050, 32000, 44100,
24398c2ecf20Sopenharmony_ci	48000, 64000, 88200, 96000, 176400, 192000, 352800, 384000
24408c2ecf20Sopenharmony_ci};
24418c2ecf20Sopenharmony_ci
24428c2ecf20Sopenharmony_ciconst struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {
24438c2ecf20Sopenharmony_ci	.count = ARRAY_SIZE(rates),
24448c2ecf20Sopenharmony_ci	.list = rates,
24458c2ecf20Sopenharmony_ci};
24468c2ecf20Sopenharmony_ci
24478c2ecf20Sopenharmony_cistatic int snd_pcm_hw_rule_rate(struct snd_pcm_hw_params *params,
24488c2ecf20Sopenharmony_ci				struct snd_pcm_hw_rule *rule)
24498c2ecf20Sopenharmony_ci{
24508c2ecf20Sopenharmony_ci	struct snd_pcm_hardware *hw = rule->private;
24518c2ecf20Sopenharmony_ci	return snd_interval_list(hw_param_interval(params, rule->var),
24528c2ecf20Sopenharmony_ci				 snd_pcm_known_rates.count,
24538c2ecf20Sopenharmony_ci				 snd_pcm_known_rates.list, hw->rates);
24548c2ecf20Sopenharmony_ci}
24558c2ecf20Sopenharmony_ci
24568c2ecf20Sopenharmony_cistatic int snd_pcm_hw_rule_buffer_bytes_max(struct snd_pcm_hw_params *params,
24578c2ecf20Sopenharmony_ci					    struct snd_pcm_hw_rule *rule)
24588c2ecf20Sopenharmony_ci{
24598c2ecf20Sopenharmony_ci	struct snd_interval t;
24608c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream = rule->private;
24618c2ecf20Sopenharmony_ci	t.min = 0;
24628c2ecf20Sopenharmony_ci	t.max = substream->buffer_bytes_max;
24638c2ecf20Sopenharmony_ci	t.openmin = 0;
24648c2ecf20Sopenharmony_ci	t.openmax = 0;
24658c2ecf20Sopenharmony_ci	t.integer = 1;
24668c2ecf20Sopenharmony_ci	return snd_interval_refine(hw_param_interval(params, rule->var), &t);
24678c2ecf20Sopenharmony_ci}
24688c2ecf20Sopenharmony_ci
24698c2ecf20Sopenharmony_cistatic int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream)
24708c2ecf20Sopenharmony_ci{
24718c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
24728c2ecf20Sopenharmony_ci	struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
24738c2ecf20Sopenharmony_ci	int k, err;
24748c2ecf20Sopenharmony_ci
24758c2ecf20Sopenharmony_ci	for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) {
24768c2ecf20Sopenharmony_ci		snd_mask_any(constrs_mask(constrs, k));
24778c2ecf20Sopenharmony_ci	}
24788c2ecf20Sopenharmony_ci
24798c2ecf20Sopenharmony_ci	for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {
24808c2ecf20Sopenharmony_ci		snd_interval_any(constrs_interval(constrs, k));
24818c2ecf20Sopenharmony_ci	}
24828c2ecf20Sopenharmony_ci
24838c2ecf20Sopenharmony_ci	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_CHANNELS));
24848c2ecf20Sopenharmony_ci	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_SIZE));
24858c2ecf20Sopenharmony_ci	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_BYTES));
24868c2ecf20Sopenharmony_ci	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_SAMPLE_BITS));
24878c2ecf20Sopenharmony_ci	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_FRAME_BITS));
24888c2ecf20Sopenharmony_ci
24898c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
24908c2ecf20Sopenharmony_ci				   snd_pcm_hw_rule_format, NULL,
24918c2ecf20Sopenharmony_ci				   SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
24928c2ecf20Sopenharmony_ci	if (err < 0)
24938c2ecf20Sopenharmony_ci		return err;
24948c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
24958c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_sample_bits, NULL,
24968c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_FORMAT,
24978c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
24988c2ecf20Sopenharmony_ci	if (err < 0)
24998c2ecf20Sopenharmony_ci		return err;
25008c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
25018c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_div, NULL,
25028c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1);
25038c2ecf20Sopenharmony_ci	if (err < 0)
25048c2ecf20Sopenharmony_ci		return err;
25058c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS,
25068c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_mul, NULL,
25078c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_SAMPLE_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1);
25088c2ecf20Sopenharmony_ci	if (err < 0)
25098c2ecf20Sopenharmony_ci		return err;
25108c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS,
25118c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_mulkdiv, (void*) 8,
25128c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
25138c2ecf20Sopenharmony_ci	if (err < 0)
25148c2ecf20Sopenharmony_ci		return err;
25158c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS,
25168c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_mulkdiv, (void*) 8,
25178c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);
25188c2ecf20Sopenharmony_ci	if (err < 0)
25198c2ecf20Sopenharmony_ci		return err;
25208c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
25218c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_div, NULL,
25228c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
25238c2ecf20Sopenharmony_ci	if (err < 0)
25248c2ecf20Sopenharmony_ci		return err;
25258c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
25268c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
25278c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_TIME, -1);
25288c2ecf20Sopenharmony_ci	if (err < 0)
25298c2ecf20Sopenharmony_ci		return err;
25308c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
25318c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
25328c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_BUFFER_TIME, -1);
25338c2ecf20Sopenharmony_ci	if (err < 0)
25348c2ecf20Sopenharmony_ci		return err;
25358c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS,
25368c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_div, NULL,
25378c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
25388c2ecf20Sopenharmony_ci	if (err < 0)
25398c2ecf20Sopenharmony_ci		return err;
25408c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
25418c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_div, NULL,
25428c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1);
25438c2ecf20Sopenharmony_ci	if (err < 0)
25448c2ecf20Sopenharmony_ci		return err;
25458c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
25468c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_mulkdiv, (void*) 8,
25478c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
25488c2ecf20Sopenharmony_ci	if (err < 0)
25498c2ecf20Sopenharmony_ci		return err;
25508c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
25518c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_muldivk, (void*) 1000000,
25528c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_PERIOD_TIME, SNDRV_PCM_HW_PARAM_RATE, -1);
25538c2ecf20Sopenharmony_ci	if (err < 0)
25548c2ecf20Sopenharmony_ci		return err;
25558c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
25568c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_mul, NULL,
25578c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1);
25588c2ecf20Sopenharmony_ci	if (err < 0)
25598c2ecf20Sopenharmony_ci		return err;
25608c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
25618c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_mulkdiv, (void*) 8,
25628c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
25638c2ecf20Sopenharmony_ci	if (err < 0)
25648c2ecf20Sopenharmony_ci		return err;
25658c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
25668c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_muldivk, (void*) 1000000,
25678c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_BUFFER_TIME, SNDRV_PCM_HW_PARAM_RATE, -1);
25688c2ecf20Sopenharmony_ci	if (err < 0)
25698c2ecf20Sopenharmony_ci		return err;
25708c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
25718c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_muldivk, (void*) 8,
25728c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
25738c2ecf20Sopenharmony_ci	if (err < 0)
25748c2ecf20Sopenharmony_ci		return err;
25758c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
25768c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_muldivk, (void*) 8,
25778c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
25788c2ecf20Sopenharmony_ci	if (err < 0)
25798c2ecf20Sopenharmony_ci		return err;
25808c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
25818c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
25828c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1);
25838c2ecf20Sopenharmony_ci	if (err < 0)
25848c2ecf20Sopenharmony_ci		return err;
25858c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_TIME,
25868c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
25878c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1);
25888c2ecf20Sopenharmony_ci	if (err < 0)
25898c2ecf20Sopenharmony_ci		return err;
25908c2ecf20Sopenharmony_ci	return 0;
25918c2ecf20Sopenharmony_ci}
25928c2ecf20Sopenharmony_ci
25938c2ecf20Sopenharmony_cistatic int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
25948c2ecf20Sopenharmony_ci{
25958c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
25968c2ecf20Sopenharmony_ci	struct snd_pcm_hardware *hw = &runtime->hw;
25978c2ecf20Sopenharmony_ci	int err;
25988c2ecf20Sopenharmony_ci	unsigned int mask = 0;
25998c2ecf20Sopenharmony_ci
26008c2ecf20Sopenharmony_ci        if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)
26018c2ecf20Sopenharmony_ci		mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_RW_INTERLEAVED);
26028c2ecf20Sopenharmony_ci        if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
26038c2ecf20Sopenharmony_ci		mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
26048c2ecf20Sopenharmony_ci	if (hw_support_mmap(substream)) {
26058c2ecf20Sopenharmony_ci		if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)
26068c2ecf20Sopenharmony_ci			mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
26078c2ecf20Sopenharmony_ci		if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
26088c2ecf20Sopenharmony_ci			mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED);
26098c2ecf20Sopenharmony_ci		if (hw->info & SNDRV_PCM_INFO_COMPLEX)
26108c2ecf20Sopenharmony_ci			mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_MMAP_COMPLEX);
26118c2ecf20Sopenharmony_ci	}
26128c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask);
26138c2ecf20Sopenharmony_ci	if (err < 0)
26148c2ecf20Sopenharmony_ci		return err;
26158c2ecf20Sopenharmony_ci
26168c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats);
26178c2ecf20Sopenharmony_ci	if (err < 0)
26188c2ecf20Sopenharmony_ci		return err;
26198c2ecf20Sopenharmony_ci
26208c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT,
26218c2ecf20Sopenharmony_ci					 PARAM_MASK_BIT(SNDRV_PCM_SUBFORMAT_STD));
26228c2ecf20Sopenharmony_ci	if (err < 0)
26238c2ecf20Sopenharmony_ci		return err;
26248c2ecf20Sopenharmony_ci
26258c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
26268c2ecf20Sopenharmony_ci					   hw->channels_min, hw->channels_max);
26278c2ecf20Sopenharmony_ci	if (err < 0)
26288c2ecf20Sopenharmony_ci		return err;
26298c2ecf20Sopenharmony_ci
26308c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
26318c2ecf20Sopenharmony_ci					   hw->rate_min, hw->rate_max);
26328c2ecf20Sopenharmony_ci	if (err < 0)
26338c2ecf20Sopenharmony_ci		return err;
26348c2ecf20Sopenharmony_ci
26358c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
26368c2ecf20Sopenharmony_ci					   hw->period_bytes_min, hw->period_bytes_max);
26378c2ecf20Sopenharmony_ci	if (err < 0)
26388c2ecf20Sopenharmony_ci		return err;
26398c2ecf20Sopenharmony_ci
26408c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS,
26418c2ecf20Sopenharmony_ci					   hw->periods_min, hw->periods_max);
26428c2ecf20Sopenharmony_ci	if (err < 0)
26438c2ecf20Sopenharmony_ci		return err;
26448c2ecf20Sopenharmony_ci
26458c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
26468c2ecf20Sopenharmony_ci					   hw->period_bytes_min, hw->buffer_bytes_max);
26478c2ecf20Sopenharmony_ci	if (err < 0)
26488c2ecf20Sopenharmony_ci		return err;
26498c2ecf20Sopenharmony_ci
26508c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
26518c2ecf20Sopenharmony_ci				  snd_pcm_hw_rule_buffer_bytes_max, substream,
26528c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1);
26538c2ecf20Sopenharmony_ci	if (err < 0)
26548c2ecf20Sopenharmony_ci		return err;
26558c2ecf20Sopenharmony_ci
26568c2ecf20Sopenharmony_ci	/* FIXME: remove */
26578c2ecf20Sopenharmony_ci	if (runtime->dma_bytes) {
26588c2ecf20Sopenharmony_ci		err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes);
26598c2ecf20Sopenharmony_ci		if (err < 0)
26608c2ecf20Sopenharmony_ci			return err;
26618c2ecf20Sopenharmony_ci	}
26628c2ecf20Sopenharmony_ci
26638c2ecf20Sopenharmony_ci	if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) {
26648c2ecf20Sopenharmony_ci		err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
26658c2ecf20Sopenharmony_ci					  snd_pcm_hw_rule_rate, hw,
26668c2ecf20Sopenharmony_ci					  SNDRV_PCM_HW_PARAM_RATE, -1);
26678c2ecf20Sopenharmony_ci		if (err < 0)
26688c2ecf20Sopenharmony_ci			return err;
26698c2ecf20Sopenharmony_ci	}
26708c2ecf20Sopenharmony_ci
26718c2ecf20Sopenharmony_ci	/* FIXME: this belong to lowlevel */
26728c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
26738c2ecf20Sopenharmony_ci
26748c2ecf20Sopenharmony_ci	return 0;
26758c2ecf20Sopenharmony_ci}
26768c2ecf20Sopenharmony_ci
26778c2ecf20Sopenharmony_cistatic void pcm_release_private(struct snd_pcm_substream *substream)
26788c2ecf20Sopenharmony_ci{
26798c2ecf20Sopenharmony_ci	if (snd_pcm_stream_linked(substream))
26808c2ecf20Sopenharmony_ci		snd_pcm_unlink(substream);
26818c2ecf20Sopenharmony_ci}
26828c2ecf20Sopenharmony_ci
26838c2ecf20Sopenharmony_civoid snd_pcm_release_substream(struct snd_pcm_substream *substream)
26848c2ecf20Sopenharmony_ci{
26858c2ecf20Sopenharmony_ci	substream->ref_count--;
26868c2ecf20Sopenharmony_ci	if (substream->ref_count > 0)
26878c2ecf20Sopenharmony_ci		return;
26888c2ecf20Sopenharmony_ci
26898c2ecf20Sopenharmony_ci	snd_pcm_drop(substream);
26908c2ecf20Sopenharmony_ci	if (substream->hw_opened) {
26918c2ecf20Sopenharmony_ci		if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
26928c2ecf20Sopenharmony_ci			do_hw_free(substream);
26938c2ecf20Sopenharmony_ci		substream->ops->close(substream);
26948c2ecf20Sopenharmony_ci		substream->hw_opened = 0;
26958c2ecf20Sopenharmony_ci	}
26968c2ecf20Sopenharmony_ci	if (cpu_latency_qos_request_active(&substream->latency_pm_qos_req))
26978c2ecf20Sopenharmony_ci		cpu_latency_qos_remove_request(&substream->latency_pm_qos_req);
26988c2ecf20Sopenharmony_ci	if (substream->pcm_release) {
26998c2ecf20Sopenharmony_ci		substream->pcm_release(substream);
27008c2ecf20Sopenharmony_ci		substream->pcm_release = NULL;
27018c2ecf20Sopenharmony_ci	}
27028c2ecf20Sopenharmony_ci	snd_pcm_detach_substream(substream);
27038c2ecf20Sopenharmony_ci}
27048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_pcm_release_substream);
27058c2ecf20Sopenharmony_ci
27068c2ecf20Sopenharmony_ciint snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
27078c2ecf20Sopenharmony_ci			   struct file *file,
27088c2ecf20Sopenharmony_ci			   struct snd_pcm_substream **rsubstream)
27098c2ecf20Sopenharmony_ci{
27108c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
27118c2ecf20Sopenharmony_ci	int err;
27128c2ecf20Sopenharmony_ci
27138c2ecf20Sopenharmony_ci	err = snd_pcm_attach_substream(pcm, stream, file, &substream);
27148c2ecf20Sopenharmony_ci	if (err < 0)
27158c2ecf20Sopenharmony_ci		return err;
27168c2ecf20Sopenharmony_ci	if (substream->ref_count > 1) {
27178c2ecf20Sopenharmony_ci		*rsubstream = substream;
27188c2ecf20Sopenharmony_ci		return 0;
27198c2ecf20Sopenharmony_ci	}
27208c2ecf20Sopenharmony_ci
27218c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraints_init(substream);
27228c2ecf20Sopenharmony_ci	if (err < 0) {
27238c2ecf20Sopenharmony_ci		pcm_dbg(pcm, "snd_pcm_hw_constraints_init failed\n");
27248c2ecf20Sopenharmony_ci		goto error;
27258c2ecf20Sopenharmony_ci	}
27268c2ecf20Sopenharmony_ci
27278c2ecf20Sopenharmony_ci	if ((err = substream->ops->open(substream)) < 0)
27288c2ecf20Sopenharmony_ci		goto error;
27298c2ecf20Sopenharmony_ci
27308c2ecf20Sopenharmony_ci	substream->hw_opened = 1;
27318c2ecf20Sopenharmony_ci
27328c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraints_complete(substream);
27338c2ecf20Sopenharmony_ci	if (err < 0) {
27348c2ecf20Sopenharmony_ci		pcm_dbg(pcm, "snd_pcm_hw_constraints_complete failed\n");
27358c2ecf20Sopenharmony_ci		goto error;
27368c2ecf20Sopenharmony_ci	}
27378c2ecf20Sopenharmony_ci
27388c2ecf20Sopenharmony_ci	*rsubstream = substream;
27398c2ecf20Sopenharmony_ci	return 0;
27408c2ecf20Sopenharmony_ci
27418c2ecf20Sopenharmony_ci error:
27428c2ecf20Sopenharmony_ci	snd_pcm_release_substream(substream);
27438c2ecf20Sopenharmony_ci	return err;
27448c2ecf20Sopenharmony_ci}
27458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_pcm_open_substream);
27468c2ecf20Sopenharmony_ci
27478c2ecf20Sopenharmony_cistatic int snd_pcm_open_file(struct file *file,
27488c2ecf20Sopenharmony_ci			     struct snd_pcm *pcm,
27498c2ecf20Sopenharmony_ci			     int stream)
27508c2ecf20Sopenharmony_ci{
27518c2ecf20Sopenharmony_ci	struct snd_pcm_file *pcm_file;
27528c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
27538c2ecf20Sopenharmony_ci	int err;
27548c2ecf20Sopenharmony_ci
27558c2ecf20Sopenharmony_ci	err = snd_pcm_open_substream(pcm, stream, file, &substream);
27568c2ecf20Sopenharmony_ci	if (err < 0)
27578c2ecf20Sopenharmony_ci		return err;
27588c2ecf20Sopenharmony_ci
27598c2ecf20Sopenharmony_ci	pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
27608c2ecf20Sopenharmony_ci	if (pcm_file == NULL) {
27618c2ecf20Sopenharmony_ci		snd_pcm_release_substream(substream);
27628c2ecf20Sopenharmony_ci		return -ENOMEM;
27638c2ecf20Sopenharmony_ci	}
27648c2ecf20Sopenharmony_ci	pcm_file->substream = substream;
27658c2ecf20Sopenharmony_ci	if (substream->ref_count == 1)
27668c2ecf20Sopenharmony_ci		substream->pcm_release = pcm_release_private;
27678c2ecf20Sopenharmony_ci	file->private_data = pcm_file;
27688c2ecf20Sopenharmony_ci
27698c2ecf20Sopenharmony_ci	return 0;
27708c2ecf20Sopenharmony_ci}
27718c2ecf20Sopenharmony_ci
27728c2ecf20Sopenharmony_cistatic int snd_pcm_playback_open(struct inode *inode, struct file *file)
27738c2ecf20Sopenharmony_ci{
27748c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
27758c2ecf20Sopenharmony_ci	int err = nonseekable_open(inode, file);
27768c2ecf20Sopenharmony_ci	if (err < 0)
27778c2ecf20Sopenharmony_ci		return err;
27788c2ecf20Sopenharmony_ci	pcm = snd_lookup_minor_data(iminor(inode),
27798c2ecf20Sopenharmony_ci				    SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
27808c2ecf20Sopenharmony_ci	err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
27818c2ecf20Sopenharmony_ci	if (pcm)
27828c2ecf20Sopenharmony_ci		snd_card_unref(pcm->card);
27838c2ecf20Sopenharmony_ci	return err;
27848c2ecf20Sopenharmony_ci}
27858c2ecf20Sopenharmony_ci
27868c2ecf20Sopenharmony_cistatic int snd_pcm_capture_open(struct inode *inode, struct file *file)
27878c2ecf20Sopenharmony_ci{
27888c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
27898c2ecf20Sopenharmony_ci	int err = nonseekable_open(inode, file);
27908c2ecf20Sopenharmony_ci	if (err < 0)
27918c2ecf20Sopenharmony_ci		return err;
27928c2ecf20Sopenharmony_ci	pcm = snd_lookup_minor_data(iminor(inode),
27938c2ecf20Sopenharmony_ci				    SNDRV_DEVICE_TYPE_PCM_CAPTURE);
27948c2ecf20Sopenharmony_ci	err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);
27958c2ecf20Sopenharmony_ci	if (pcm)
27968c2ecf20Sopenharmony_ci		snd_card_unref(pcm->card);
27978c2ecf20Sopenharmony_ci	return err;
27988c2ecf20Sopenharmony_ci}
27998c2ecf20Sopenharmony_ci
28008c2ecf20Sopenharmony_cistatic int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
28018c2ecf20Sopenharmony_ci{
28028c2ecf20Sopenharmony_ci	int err;
28038c2ecf20Sopenharmony_ci	wait_queue_entry_t wait;
28048c2ecf20Sopenharmony_ci
28058c2ecf20Sopenharmony_ci	if (pcm == NULL) {
28068c2ecf20Sopenharmony_ci		err = -ENODEV;
28078c2ecf20Sopenharmony_ci		goto __error1;
28088c2ecf20Sopenharmony_ci	}
28098c2ecf20Sopenharmony_ci	err = snd_card_file_add(pcm->card, file);
28108c2ecf20Sopenharmony_ci	if (err < 0)
28118c2ecf20Sopenharmony_ci		goto __error1;
28128c2ecf20Sopenharmony_ci	if (!try_module_get(pcm->card->module)) {
28138c2ecf20Sopenharmony_ci		err = -EFAULT;
28148c2ecf20Sopenharmony_ci		goto __error2;
28158c2ecf20Sopenharmony_ci	}
28168c2ecf20Sopenharmony_ci	init_waitqueue_entry(&wait, current);
28178c2ecf20Sopenharmony_ci	add_wait_queue(&pcm->open_wait, &wait);
28188c2ecf20Sopenharmony_ci	mutex_lock(&pcm->open_mutex);
28198c2ecf20Sopenharmony_ci	while (1) {
28208c2ecf20Sopenharmony_ci		err = snd_pcm_open_file(file, pcm, stream);
28218c2ecf20Sopenharmony_ci		if (err >= 0)
28228c2ecf20Sopenharmony_ci			break;
28238c2ecf20Sopenharmony_ci		if (err == -EAGAIN) {
28248c2ecf20Sopenharmony_ci			if (file->f_flags & O_NONBLOCK) {
28258c2ecf20Sopenharmony_ci				err = -EBUSY;
28268c2ecf20Sopenharmony_ci				break;
28278c2ecf20Sopenharmony_ci			}
28288c2ecf20Sopenharmony_ci		} else
28298c2ecf20Sopenharmony_ci			break;
28308c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
28318c2ecf20Sopenharmony_ci		mutex_unlock(&pcm->open_mutex);
28328c2ecf20Sopenharmony_ci		schedule();
28338c2ecf20Sopenharmony_ci		mutex_lock(&pcm->open_mutex);
28348c2ecf20Sopenharmony_ci		if (pcm->card->shutdown) {
28358c2ecf20Sopenharmony_ci			err = -ENODEV;
28368c2ecf20Sopenharmony_ci			break;
28378c2ecf20Sopenharmony_ci		}
28388c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
28398c2ecf20Sopenharmony_ci			err = -ERESTARTSYS;
28408c2ecf20Sopenharmony_ci			break;
28418c2ecf20Sopenharmony_ci		}
28428c2ecf20Sopenharmony_ci	}
28438c2ecf20Sopenharmony_ci	remove_wait_queue(&pcm->open_wait, &wait);
28448c2ecf20Sopenharmony_ci	mutex_unlock(&pcm->open_mutex);
28458c2ecf20Sopenharmony_ci	if (err < 0)
28468c2ecf20Sopenharmony_ci		goto __error;
28478c2ecf20Sopenharmony_ci	return err;
28488c2ecf20Sopenharmony_ci
28498c2ecf20Sopenharmony_ci      __error:
28508c2ecf20Sopenharmony_ci	module_put(pcm->card->module);
28518c2ecf20Sopenharmony_ci      __error2:
28528c2ecf20Sopenharmony_ci      	snd_card_file_remove(pcm->card, file);
28538c2ecf20Sopenharmony_ci      __error1:
28548c2ecf20Sopenharmony_ci      	return err;
28558c2ecf20Sopenharmony_ci}
28568c2ecf20Sopenharmony_ci
28578c2ecf20Sopenharmony_cistatic int snd_pcm_release(struct inode *inode, struct file *file)
28588c2ecf20Sopenharmony_ci{
28598c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
28608c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
28618c2ecf20Sopenharmony_ci	struct snd_pcm_file *pcm_file;
28628c2ecf20Sopenharmony_ci
28638c2ecf20Sopenharmony_ci	pcm_file = file->private_data;
28648c2ecf20Sopenharmony_ci	substream = pcm_file->substream;
28658c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!substream))
28668c2ecf20Sopenharmony_ci		return -ENXIO;
28678c2ecf20Sopenharmony_ci	pcm = substream->pcm;
28688c2ecf20Sopenharmony_ci	mutex_lock(&pcm->open_mutex);
28698c2ecf20Sopenharmony_ci	snd_pcm_release_substream(substream);
28708c2ecf20Sopenharmony_ci	kfree(pcm_file);
28718c2ecf20Sopenharmony_ci	mutex_unlock(&pcm->open_mutex);
28728c2ecf20Sopenharmony_ci	wake_up(&pcm->open_wait);
28738c2ecf20Sopenharmony_ci	module_put(pcm->card->module);
28748c2ecf20Sopenharmony_ci	snd_card_file_remove(pcm->card, file);
28758c2ecf20Sopenharmony_ci	return 0;
28768c2ecf20Sopenharmony_ci}
28778c2ecf20Sopenharmony_ci
28788c2ecf20Sopenharmony_ci/* check and update PCM state; return 0 or a negative error
28798c2ecf20Sopenharmony_ci * call this inside PCM lock
28808c2ecf20Sopenharmony_ci */
28818c2ecf20Sopenharmony_cistatic int do_pcm_hwsync(struct snd_pcm_substream *substream)
28828c2ecf20Sopenharmony_ci{
28838c2ecf20Sopenharmony_ci	switch (substream->runtime->status->state) {
28848c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_DRAINING:
28858c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
28868c2ecf20Sopenharmony_ci			return -EBADFD;
28878c2ecf20Sopenharmony_ci		fallthrough;
28888c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_RUNNING:
28898c2ecf20Sopenharmony_ci		return snd_pcm_update_hw_ptr(substream);
28908c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_PREPARED:
28918c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_PAUSED:
28928c2ecf20Sopenharmony_ci		return 0;
28938c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_SUSPENDED:
28948c2ecf20Sopenharmony_ci		return -ESTRPIPE;
28958c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_XRUN:
28968c2ecf20Sopenharmony_ci		return -EPIPE;
28978c2ecf20Sopenharmony_ci	default:
28988c2ecf20Sopenharmony_ci		return -EBADFD;
28998c2ecf20Sopenharmony_ci	}
29008c2ecf20Sopenharmony_ci}
29018c2ecf20Sopenharmony_ci
29028c2ecf20Sopenharmony_ci/* increase the appl_ptr; returns the processed frames or a negative error */
29038c2ecf20Sopenharmony_cistatic snd_pcm_sframes_t forward_appl_ptr(struct snd_pcm_substream *substream,
29048c2ecf20Sopenharmony_ci					  snd_pcm_uframes_t frames,
29058c2ecf20Sopenharmony_ci					   snd_pcm_sframes_t avail)
29068c2ecf20Sopenharmony_ci{
29078c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
29088c2ecf20Sopenharmony_ci	snd_pcm_sframes_t appl_ptr;
29098c2ecf20Sopenharmony_ci	int ret;
29108c2ecf20Sopenharmony_ci
29118c2ecf20Sopenharmony_ci	if (avail <= 0)
29128c2ecf20Sopenharmony_ci		return 0;
29138c2ecf20Sopenharmony_ci	if (frames > (snd_pcm_uframes_t)avail)
29148c2ecf20Sopenharmony_ci		frames = avail;
29158c2ecf20Sopenharmony_ci	appl_ptr = runtime->control->appl_ptr + frames;
29168c2ecf20Sopenharmony_ci	if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
29178c2ecf20Sopenharmony_ci		appl_ptr -= runtime->boundary;
29188c2ecf20Sopenharmony_ci	ret = pcm_lib_apply_appl_ptr(substream, appl_ptr);
29198c2ecf20Sopenharmony_ci	return ret < 0 ? ret : frames;
29208c2ecf20Sopenharmony_ci}
29218c2ecf20Sopenharmony_ci
29228c2ecf20Sopenharmony_ci/* decrease the appl_ptr; returns the processed frames or zero for error */
29238c2ecf20Sopenharmony_cistatic snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream,
29248c2ecf20Sopenharmony_ci					 snd_pcm_uframes_t frames,
29258c2ecf20Sopenharmony_ci					 snd_pcm_sframes_t avail)
29268c2ecf20Sopenharmony_ci{
29278c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
29288c2ecf20Sopenharmony_ci	snd_pcm_sframes_t appl_ptr;
29298c2ecf20Sopenharmony_ci	int ret;
29308c2ecf20Sopenharmony_ci
29318c2ecf20Sopenharmony_ci	if (avail <= 0)
29328c2ecf20Sopenharmony_ci		return 0;
29338c2ecf20Sopenharmony_ci	if (frames > (snd_pcm_uframes_t)avail)
29348c2ecf20Sopenharmony_ci		frames = avail;
29358c2ecf20Sopenharmony_ci	appl_ptr = runtime->control->appl_ptr - frames;
29368c2ecf20Sopenharmony_ci	if (appl_ptr < 0)
29378c2ecf20Sopenharmony_ci		appl_ptr += runtime->boundary;
29388c2ecf20Sopenharmony_ci	ret = pcm_lib_apply_appl_ptr(substream, appl_ptr);
29398c2ecf20Sopenharmony_ci	/* NOTE: we return zero for errors because PulseAudio gets depressed
29408c2ecf20Sopenharmony_ci	 * upon receiving an error from rewind ioctl and stops processing
29418c2ecf20Sopenharmony_ci	 * any longer.  Returning zero means that no rewind is done, so
29428c2ecf20Sopenharmony_ci	 * it's not absolutely wrong to answer like that.
29438c2ecf20Sopenharmony_ci	 */
29448c2ecf20Sopenharmony_ci	return ret < 0 ? 0 : frames;
29458c2ecf20Sopenharmony_ci}
29468c2ecf20Sopenharmony_ci
29478c2ecf20Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_rewind(struct snd_pcm_substream *substream,
29488c2ecf20Sopenharmony_ci					snd_pcm_uframes_t frames)
29498c2ecf20Sopenharmony_ci{
29508c2ecf20Sopenharmony_ci	snd_pcm_sframes_t ret;
29518c2ecf20Sopenharmony_ci
29528c2ecf20Sopenharmony_ci	if (frames == 0)
29538c2ecf20Sopenharmony_ci		return 0;
29548c2ecf20Sopenharmony_ci
29558c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
29568c2ecf20Sopenharmony_ci	ret = do_pcm_hwsync(substream);
29578c2ecf20Sopenharmony_ci	if (!ret)
29588c2ecf20Sopenharmony_ci		ret = rewind_appl_ptr(substream, frames,
29598c2ecf20Sopenharmony_ci				      snd_pcm_hw_avail(substream));
29608c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
29618c2ecf20Sopenharmony_ci	return ret;
29628c2ecf20Sopenharmony_ci}
29638c2ecf20Sopenharmony_ci
29648c2ecf20Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_forward(struct snd_pcm_substream *substream,
29658c2ecf20Sopenharmony_ci					 snd_pcm_uframes_t frames)
29668c2ecf20Sopenharmony_ci{
29678c2ecf20Sopenharmony_ci	snd_pcm_sframes_t ret;
29688c2ecf20Sopenharmony_ci
29698c2ecf20Sopenharmony_ci	if (frames == 0)
29708c2ecf20Sopenharmony_ci		return 0;
29718c2ecf20Sopenharmony_ci
29728c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
29738c2ecf20Sopenharmony_ci	ret = do_pcm_hwsync(substream);
29748c2ecf20Sopenharmony_ci	if (!ret)
29758c2ecf20Sopenharmony_ci		ret = forward_appl_ptr(substream, frames,
29768c2ecf20Sopenharmony_ci				       snd_pcm_avail(substream));
29778c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
29788c2ecf20Sopenharmony_ci	return ret;
29798c2ecf20Sopenharmony_ci}
29808c2ecf20Sopenharmony_ci
29818c2ecf20Sopenharmony_cistatic int snd_pcm_hwsync(struct snd_pcm_substream *substream)
29828c2ecf20Sopenharmony_ci{
29838c2ecf20Sopenharmony_ci	int err;
29848c2ecf20Sopenharmony_ci
29858c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
29868c2ecf20Sopenharmony_ci	err = do_pcm_hwsync(substream);
29878c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
29888c2ecf20Sopenharmony_ci	return err;
29898c2ecf20Sopenharmony_ci}
29908c2ecf20Sopenharmony_ci
29918c2ecf20Sopenharmony_cistatic int snd_pcm_delay(struct snd_pcm_substream *substream,
29928c2ecf20Sopenharmony_ci			 snd_pcm_sframes_t *delay)
29938c2ecf20Sopenharmony_ci{
29948c2ecf20Sopenharmony_ci	int err;
29958c2ecf20Sopenharmony_ci	snd_pcm_sframes_t n = 0;
29968c2ecf20Sopenharmony_ci
29978c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
29988c2ecf20Sopenharmony_ci	err = do_pcm_hwsync(substream);
29998c2ecf20Sopenharmony_ci	if (!err)
30008c2ecf20Sopenharmony_ci		n = snd_pcm_calc_delay(substream);
30018c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
30028c2ecf20Sopenharmony_ci	if (!err)
30038c2ecf20Sopenharmony_ci		*delay = n;
30048c2ecf20Sopenharmony_ci	return err;
30058c2ecf20Sopenharmony_ci}
30068c2ecf20Sopenharmony_ci
30078c2ecf20Sopenharmony_cistatic int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
30088c2ecf20Sopenharmony_ci			    struct snd_pcm_sync_ptr __user *_sync_ptr)
30098c2ecf20Sopenharmony_ci{
30108c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
30118c2ecf20Sopenharmony_ci	struct snd_pcm_sync_ptr sync_ptr;
30128c2ecf20Sopenharmony_ci	volatile struct snd_pcm_mmap_status *status;
30138c2ecf20Sopenharmony_ci	volatile struct snd_pcm_mmap_control *control;
30148c2ecf20Sopenharmony_ci	int err;
30158c2ecf20Sopenharmony_ci
30168c2ecf20Sopenharmony_ci	memset(&sync_ptr, 0, sizeof(sync_ptr));
30178c2ecf20Sopenharmony_ci	if (get_user(sync_ptr.flags, (unsigned __user *)&(_sync_ptr->flags)))
30188c2ecf20Sopenharmony_ci		return -EFAULT;
30198c2ecf20Sopenharmony_ci	if (copy_from_user(&sync_ptr.c.control, &(_sync_ptr->c.control), sizeof(struct snd_pcm_mmap_control)))
30208c2ecf20Sopenharmony_ci		return -EFAULT;
30218c2ecf20Sopenharmony_ci	status = runtime->status;
30228c2ecf20Sopenharmony_ci	control = runtime->control;
30238c2ecf20Sopenharmony_ci	if (sync_ptr.flags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
30248c2ecf20Sopenharmony_ci		err = snd_pcm_hwsync(substream);
30258c2ecf20Sopenharmony_ci		if (err < 0)
30268c2ecf20Sopenharmony_ci			return err;
30278c2ecf20Sopenharmony_ci	}
30288c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
30298c2ecf20Sopenharmony_ci	if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) {
30308c2ecf20Sopenharmony_ci		err = pcm_lib_apply_appl_ptr(substream,
30318c2ecf20Sopenharmony_ci					     sync_ptr.c.control.appl_ptr);
30328c2ecf20Sopenharmony_ci		if (err < 0) {
30338c2ecf20Sopenharmony_ci			snd_pcm_stream_unlock_irq(substream);
30348c2ecf20Sopenharmony_ci			return err;
30358c2ecf20Sopenharmony_ci		}
30368c2ecf20Sopenharmony_ci	} else {
30378c2ecf20Sopenharmony_ci		sync_ptr.c.control.appl_ptr = control->appl_ptr;
30388c2ecf20Sopenharmony_ci	}
30398c2ecf20Sopenharmony_ci	if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
30408c2ecf20Sopenharmony_ci		control->avail_min = sync_ptr.c.control.avail_min;
30418c2ecf20Sopenharmony_ci	else
30428c2ecf20Sopenharmony_ci		sync_ptr.c.control.avail_min = control->avail_min;
30438c2ecf20Sopenharmony_ci	sync_ptr.s.status.state = status->state;
30448c2ecf20Sopenharmony_ci	sync_ptr.s.status.hw_ptr = status->hw_ptr;
30458c2ecf20Sopenharmony_ci	sync_ptr.s.status.tstamp = status->tstamp;
30468c2ecf20Sopenharmony_ci	sync_ptr.s.status.suspended_state = status->suspended_state;
30478c2ecf20Sopenharmony_ci	sync_ptr.s.status.audio_tstamp = status->audio_tstamp;
30488c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
30498c2ecf20Sopenharmony_ci	if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr)))
30508c2ecf20Sopenharmony_ci		return -EFAULT;
30518c2ecf20Sopenharmony_ci	return 0;
30528c2ecf20Sopenharmony_ci}
30538c2ecf20Sopenharmony_ci
30548c2ecf20Sopenharmony_cistruct snd_pcm_mmap_status32 {
30558c2ecf20Sopenharmony_ci	snd_pcm_state_t state;
30568c2ecf20Sopenharmony_ci	s32 pad1;
30578c2ecf20Sopenharmony_ci	u32 hw_ptr;
30588c2ecf20Sopenharmony_ci	s32 tstamp_sec;
30598c2ecf20Sopenharmony_ci	s32 tstamp_nsec;
30608c2ecf20Sopenharmony_ci	snd_pcm_state_t suspended_state;
30618c2ecf20Sopenharmony_ci	s32 audio_tstamp_sec;
30628c2ecf20Sopenharmony_ci	s32 audio_tstamp_nsec;
30638c2ecf20Sopenharmony_ci} __attribute__((packed));
30648c2ecf20Sopenharmony_ci
30658c2ecf20Sopenharmony_cistruct snd_pcm_mmap_control32 {
30668c2ecf20Sopenharmony_ci	u32 appl_ptr;
30678c2ecf20Sopenharmony_ci	u32 avail_min;
30688c2ecf20Sopenharmony_ci};
30698c2ecf20Sopenharmony_ci
30708c2ecf20Sopenharmony_cistruct snd_pcm_sync_ptr32 {
30718c2ecf20Sopenharmony_ci	u32 flags;
30728c2ecf20Sopenharmony_ci	union {
30738c2ecf20Sopenharmony_ci		struct snd_pcm_mmap_status32 status;
30748c2ecf20Sopenharmony_ci		unsigned char reserved[64];
30758c2ecf20Sopenharmony_ci	} s;
30768c2ecf20Sopenharmony_ci	union {
30778c2ecf20Sopenharmony_ci		struct snd_pcm_mmap_control32 control;
30788c2ecf20Sopenharmony_ci		unsigned char reserved[64];
30798c2ecf20Sopenharmony_ci	} c;
30808c2ecf20Sopenharmony_ci} __attribute__((packed));
30818c2ecf20Sopenharmony_ci
30828c2ecf20Sopenharmony_ci/* recalcuate the boundary within 32bit */
30838c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t recalculate_boundary(struct snd_pcm_runtime *runtime)
30848c2ecf20Sopenharmony_ci{
30858c2ecf20Sopenharmony_ci	snd_pcm_uframes_t boundary;
30868c2ecf20Sopenharmony_ci
30878c2ecf20Sopenharmony_ci	if (! runtime->buffer_size)
30888c2ecf20Sopenharmony_ci		return 0;
30898c2ecf20Sopenharmony_ci	boundary = runtime->buffer_size;
30908c2ecf20Sopenharmony_ci	while (boundary * 2 <= 0x7fffffffUL - runtime->buffer_size)
30918c2ecf20Sopenharmony_ci		boundary *= 2;
30928c2ecf20Sopenharmony_ci	return boundary;
30938c2ecf20Sopenharmony_ci}
30948c2ecf20Sopenharmony_ci
30958c2ecf20Sopenharmony_cistatic int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
30968c2ecf20Sopenharmony_ci					 struct snd_pcm_sync_ptr32 __user *src)
30978c2ecf20Sopenharmony_ci{
30988c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
30998c2ecf20Sopenharmony_ci	volatile struct snd_pcm_mmap_status *status;
31008c2ecf20Sopenharmony_ci	volatile struct snd_pcm_mmap_control *control;
31018c2ecf20Sopenharmony_ci	u32 sflags;
31028c2ecf20Sopenharmony_ci	struct snd_pcm_mmap_control scontrol;
31038c2ecf20Sopenharmony_ci	struct snd_pcm_mmap_status sstatus;
31048c2ecf20Sopenharmony_ci	snd_pcm_uframes_t boundary;
31058c2ecf20Sopenharmony_ci	int err;
31068c2ecf20Sopenharmony_ci
31078c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!runtime))
31088c2ecf20Sopenharmony_ci		return -EINVAL;
31098c2ecf20Sopenharmony_ci
31108c2ecf20Sopenharmony_ci	if (get_user(sflags, &src->flags) ||
31118c2ecf20Sopenharmony_ci	    get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
31128c2ecf20Sopenharmony_ci	    get_user(scontrol.avail_min, &src->c.control.avail_min))
31138c2ecf20Sopenharmony_ci		return -EFAULT;
31148c2ecf20Sopenharmony_ci	if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
31158c2ecf20Sopenharmony_ci		err = snd_pcm_hwsync(substream);
31168c2ecf20Sopenharmony_ci		if (err < 0)
31178c2ecf20Sopenharmony_ci			return err;
31188c2ecf20Sopenharmony_ci	}
31198c2ecf20Sopenharmony_ci	status = runtime->status;
31208c2ecf20Sopenharmony_ci	control = runtime->control;
31218c2ecf20Sopenharmony_ci	boundary = recalculate_boundary(runtime);
31228c2ecf20Sopenharmony_ci	if (! boundary)
31238c2ecf20Sopenharmony_ci		boundary = 0x7fffffff;
31248c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
31258c2ecf20Sopenharmony_ci	/* FIXME: we should consider the boundary for the sync from app */
31268c2ecf20Sopenharmony_ci	if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) {
31278c2ecf20Sopenharmony_ci		err = pcm_lib_apply_appl_ptr(substream,
31288c2ecf20Sopenharmony_ci				scontrol.appl_ptr);
31298c2ecf20Sopenharmony_ci		if (err < 0) {
31308c2ecf20Sopenharmony_ci			snd_pcm_stream_unlock_irq(substream);
31318c2ecf20Sopenharmony_ci			return err;
31328c2ecf20Sopenharmony_ci		}
31338c2ecf20Sopenharmony_ci	} else
31348c2ecf20Sopenharmony_ci		scontrol.appl_ptr = control->appl_ptr % boundary;
31358c2ecf20Sopenharmony_ci	if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
31368c2ecf20Sopenharmony_ci		control->avail_min = scontrol.avail_min;
31378c2ecf20Sopenharmony_ci	else
31388c2ecf20Sopenharmony_ci		scontrol.avail_min = control->avail_min;
31398c2ecf20Sopenharmony_ci	sstatus.state = status->state;
31408c2ecf20Sopenharmony_ci	sstatus.hw_ptr = status->hw_ptr % boundary;
31418c2ecf20Sopenharmony_ci	sstatus.tstamp = status->tstamp;
31428c2ecf20Sopenharmony_ci	sstatus.suspended_state = status->suspended_state;
31438c2ecf20Sopenharmony_ci	sstatus.audio_tstamp = status->audio_tstamp;
31448c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
31458c2ecf20Sopenharmony_ci	if (put_user(sstatus.state, &src->s.status.state) ||
31468c2ecf20Sopenharmony_ci	    put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
31478c2ecf20Sopenharmony_ci	    put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) ||
31488c2ecf20Sopenharmony_ci	    put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp_nsec) ||
31498c2ecf20Sopenharmony_ci	    put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
31508c2ecf20Sopenharmony_ci	    put_user(sstatus.audio_tstamp.tv_sec, &src->s.status.audio_tstamp_sec) ||
31518c2ecf20Sopenharmony_ci	    put_user(sstatus.audio_tstamp.tv_nsec, &src->s.status.audio_tstamp_nsec) ||
31528c2ecf20Sopenharmony_ci	    put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
31538c2ecf20Sopenharmony_ci	    put_user(scontrol.avail_min, &src->c.control.avail_min))
31548c2ecf20Sopenharmony_ci		return -EFAULT;
31558c2ecf20Sopenharmony_ci
31568c2ecf20Sopenharmony_ci	return 0;
31578c2ecf20Sopenharmony_ci}
31588c2ecf20Sopenharmony_ci#define __SNDRV_PCM_IOCTL_SYNC_PTR32 _IOWR('A', 0x23, struct snd_pcm_sync_ptr32)
31598c2ecf20Sopenharmony_ci
31608c2ecf20Sopenharmony_cistatic int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg)
31618c2ecf20Sopenharmony_ci{
31628c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
31638c2ecf20Sopenharmony_ci	int arg;
31648c2ecf20Sopenharmony_ci
31658c2ecf20Sopenharmony_ci	if (get_user(arg, _arg))
31668c2ecf20Sopenharmony_ci		return -EFAULT;
31678c2ecf20Sopenharmony_ci	if (arg < 0 || arg > SNDRV_PCM_TSTAMP_TYPE_LAST)
31688c2ecf20Sopenharmony_ci		return -EINVAL;
31698c2ecf20Sopenharmony_ci	runtime->tstamp_type = arg;
31708c2ecf20Sopenharmony_ci	return 0;
31718c2ecf20Sopenharmony_ci}
31728c2ecf20Sopenharmony_ci
31738c2ecf20Sopenharmony_cistatic int snd_pcm_xferi_frames_ioctl(struct snd_pcm_substream *substream,
31748c2ecf20Sopenharmony_ci				      struct snd_xferi __user *_xferi)
31758c2ecf20Sopenharmony_ci{
31768c2ecf20Sopenharmony_ci	struct snd_xferi xferi;
31778c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
31788c2ecf20Sopenharmony_ci	snd_pcm_sframes_t result;
31798c2ecf20Sopenharmony_ci
31808c2ecf20Sopenharmony_ci	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
31818c2ecf20Sopenharmony_ci		return -EBADFD;
31828c2ecf20Sopenharmony_ci	if (put_user(0, &_xferi->result))
31838c2ecf20Sopenharmony_ci		return -EFAULT;
31848c2ecf20Sopenharmony_ci	if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
31858c2ecf20Sopenharmony_ci		return -EFAULT;
31868c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
31878c2ecf20Sopenharmony_ci		result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
31888c2ecf20Sopenharmony_ci	else
31898c2ecf20Sopenharmony_ci		result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
31908c2ecf20Sopenharmony_ci	if (put_user(result, &_xferi->result))
31918c2ecf20Sopenharmony_ci		return -EFAULT;
31928c2ecf20Sopenharmony_ci	return result < 0 ? result : 0;
31938c2ecf20Sopenharmony_ci}
31948c2ecf20Sopenharmony_ci
31958c2ecf20Sopenharmony_cistatic int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream,
31968c2ecf20Sopenharmony_ci				      struct snd_xfern __user *_xfern)
31978c2ecf20Sopenharmony_ci{
31988c2ecf20Sopenharmony_ci	struct snd_xfern xfern;
31998c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
32008c2ecf20Sopenharmony_ci	void *bufs;
32018c2ecf20Sopenharmony_ci	snd_pcm_sframes_t result;
32028c2ecf20Sopenharmony_ci
32038c2ecf20Sopenharmony_ci	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
32048c2ecf20Sopenharmony_ci		return -EBADFD;
32058c2ecf20Sopenharmony_ci	if (runtime->channels > 128)
32068c2ecf20Sopenharmony_ci		return -EINVAL;
32078c2ecf20Sopenharmony_ci	if (put_user(0, &_xfern->result))
32088c2ecf20Sopenharmony_ci		return -EFAULT;
32098c2ecf20Sopenharmony_ci	if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
32108c2ecf20Sopenharmony_ci		return -EFAULT;
32118c2ecf20Sopenharmony_ci
32128c2ecf20Sopenharmony_ci	bufs = memdup_user(xfern.bufs, sizeof(void *) * runtime->channels);
32138c2ecf20Sopenharmony_ci	if (IS_ERR(bufs))
32148c2ecf20Sopenharmony_ci		return PTR_ERR(bufs);
32158c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
32168c2ecf20Sopenharmony_ci		result = snd_pcm_lib_writev(substream, bufs, xfern.frames);
32178c2ecf20Sopenharmony_ci	else
32188c2ecf20Sopenharmony_ci		result = snd_pcm_lib_readv(substream, bufs, xfern.frames);
32198c2ecf20Sopenharmony_ci	kfree(bufs);
32208c2ecf20Sopenharmony_ci	if (put_user(result, &_xfern->result))
32218c2ecf20Sopenharmony_ci		return -EFAULT;
32228c2ecf20Sopenharmony_ci	return result < 0 ? result : 0;
32238c2ecf20Sopenharmony_ci}
32248c2ecf20Sopenharmony_ci
32258c2ecf20Sopenharmony_cistatic int snd_pcm_rewind_ioctl(struct snd_pcm_substream *substream,
32268c2ecf20Sopenharmony_ci				snd_pcm_uframes_t __user *_frames)
32278c2ecf20Sopenharmony_ci{
32288c2ecf20Sopenharmony_ci	snd_pcm_uframes_t frames;
32298c2ecf20Sopenharmony_ci	snd_pcm_sframes_t result;
32308c2ecf20Sopenharmony_ci
32318c2ecf20Sopenharmony_ci	if (get_user(frames, _frames))
32328c2ecf20Sopenharmony_ci		return -EFAULT;
32338c2ecf20Sopenharmony_ci	if (put_user(0, _frames))
32348c2ecf20Sopenharmony_ci		return -EFAULT;
32358c2ecf20Sopenharmony_ci	result = snd_pcm_rewind(substream, frames);
32368c2ecf20Sopenharmony_ci	if (put_user(result, _frames))
32378c2ecf20Sopenharmony_ci		return -EFAULT;
32388c2ecf20Sopenharmony_ci	return result < 0 ? result : 0;
32398c2ecf20Sopenharmony_ci}
32408c2ecf20Sopenharmony_ci
32418c2ecf20Sopenharmony_cistatic int snd_pcm_forward_ioctl(struct snd_pcm_substream *substream,
32428c2ecf20Sopenharmony_ci				 snd_pcm_uframes_t __user *_frames)
32438c2ecf20Sopenharmony_ci{
32448c2ecf20Sopenharmony_ci	snd_pcm_uframes_t frames;
32458c2ecf20Sopenharmony_ci	snd_pcm_sframes_t result;
32468c2ecf20Sopenharmony_ci
32478c2ecf20Sopenharmony_ci	if (get_user(frames, _frames))
32488c2ecf20Sopenharmony_ci		return -EFAULT;
32498c2ecf20Sopenharmony_ci	if (put_user(0, _frames))
32508c2ecf20Sopenharmony_ci		return -EFAULT;
32518c2ecf20Sopenharmony_ci	result = snd_pcm_forward(substream, frames);
32528c2ecf20Sopenharmony_ci	if (put_user(result, _frames))
32538c2ecf20Sopenharmony_ci		return -EFAULT;
32548c2ecf20Sopenharmony_ci	return result < 0 ? result : 0;
32558c2ecf20Sopenharmony_ci}
32568c2ecf20Sopenharmony_ci
32578c2ecf20Sopenharmony_cistatic int snd_pcm_common_ioctl(struct file *file,
32588c2ecf20Sopenharmony_ci				 struct snd_pcm_substream *substream,
32598c2ecf20Sopenharmony_ci				 unsigned int cmd, void __user *arg)
32608c2ecf20Sopenharmony_ci{
32618c2ecf20Sopenharmony_ci	struct snd_pcm_file *pcm_file = file->private_data;
32628c2ecf20Sopenharmony_ci	int res;
32638c2ecf20Sopenharmony_ci
32648c2ecf20Sopenharmony_ci	if (PCM_RUNTIME_CHECK(substream))
32658c2ecf20Sopenharmony_ci		return -ENXIO;
32668c2ecf20Sopenharmony_ci
32678c2ecf20Sopenharmony_ci	res = snd_power_wait(substream->pcm->card, SNDRV_CTL_POWER_D0);
32688c2ecf20Sopenharmony_ci	if (res < 0)
32698c2ecf20Sopenharmony_ci		return res;
32708c2ecf20Sopenharmony_ci
32718c2ecf20Sopenharmony_ci	switch (cmd) {
32728c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_PVERSION:
32738c2ecf20Sopenharmony_ci		return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0;
32748c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_INFO:
32758c2ecf20Sopenharmony_ci		return snd_pcm_info_user(substream, arg);
32768c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_TSTAMP:	/* just for compatibility */
32778c2ecf20Sopenharmony_ci		return 0;
32788c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_TTSTAMP:
32798c2ecf20Sopenharmony_ci		return snd_pcm_tstamp(substream, arg);
32808c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_USER_PVERSION:
32818c2ecf20Sopenharmony_ci		if (get_user(pcm_file->user_pversion,
32828c2ecf20Sopenharmony_ci			     (unsigned int __user *)arg))
32838c2ecf20Sopenharmony_ci			return -EFAULT;
32848c2ecf20Sopenharmony_ci		return 0;
32858c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_HW_REFINE:
32868c2ecf20Sopenharmony_ci		return snd_pcm_hw_refine_user(substream, arg);
32878c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_HW_PARAMS:
32888c2ecf20Sopenharmony_ci		return snd_pcm_hw_params_user(substream, arg);
32898c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_HW_FREE:
32908c2ecf20Sopenharmony_ci		return snd_pcm_hw_free(substream);
32918c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_SW_PARAMS:
32928c2ecf20Sopenharmony_ci		return snd_pcm_sw_params_user(substream, arg);
32938c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_STATUS32:
32948c2ecf20Sopenharmony_ci		return snd_pcm_status_user32(substream, arg, false);
32958c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_STATUS_EXT32:
32968c2ecf20Sopenharmony_ci		return snd_pcm_status_user32(substream, arg, true);
32978c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_STATUS64:
32988c2ecf20Sopenharmony_ci		return snd_pcm_status_user64(substream, arg, false);
32998c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_STATUS_EXT64:
33008c2ecf20Sopenharmony_ci		return snd_pcm_status_user64(substream, arg, true);
33018c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_CHANNEL_INFO:
33028c2ecf20Sopenharmony_ci		return snd_pcm_channel_info_user(substream, arg);
33038c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_PREPARE:
33048c2ecf20Sopenharmony_ci		return snd_pcm_prepare(substream, file);
33058c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_RESET:
33068c2ecf20Sopenharmony_ci		return snd_pcm_reset(substream);
33078c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_START:
33088c2ecf20Sopenharmony_ci		return snd_pcm_start_lock_irq(substream);
33098c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_LINK:
33108c2ecf20Sopenharmony_ci		return snd_pcm_link(substream, (int)(unsigned long) arg);
33118c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_UNLINK:
33128c2ecf20Sopenharmony_ci		return snd_pcm_unlink(substream);
33138c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_RESUME:
33148c2ecf20Sopenharmony_ci		return snd_pcm_resume(substream);
33158c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_XRUN:
33168c2ecf20Sopenharmony_ci		return snd_pcm_xrun(substream);
33178c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_HWSYNC:
33188c2ecf20Sopenharmony_ci		return snd_pcm_hwsync(substream);
33198c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_DELAY:
33208c2ecf20Sopenharmony_ci	{
33218c2ecf20Sopenharmony_ci		snd_pcm_sframes_t delay;
33228c2ecf20Sopenharmony_ci		snd_pcm_sframes_t __user *res = arg;
33238c2ecf20Sopenharmony_ci		int err;
33248c2ecf20Sopenharmony_ci
33258c2ecf20Sopenharmony_ci		err = snd_pcm_delay(substream, &delay);
33268c2ecf20Sopenharmony_ci		if (err)
33278c2ecf20Sopenharmony_ci			return err;
33288c2ecf20Sopenharmony_ci		if (put_user(delay, res))
33298c2ecf20Sopenharmony_ci			return -EFAULT;
33308c2ecf20Sopenharmony_ci		return 0;
33318c2ecf20Sopenharmony_ci	}
33328c2ecf20Sopenharmony_ci	case __SNDRV_PCM_IOCTL_SYNC_PTR32:
33338c2ecf20Sopenharmony_ci		return snd_pcm_ioctl_sync_ptr_compat(substream, arg);
33348c2ecf20Sopenharmony_ci	case __SNDRV_PCM_IOCTL_SYNC_PTR64:
33358c2ecf20Sopenharmony_ci		return snd_pcm_sync_ptr(substream, arg);
33368c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_SUPPORT_OLD_API
33378c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_HW_REFINE_OLD:
33388c2ecf20Sopenharmony_ci		return snd_pcm_hw_refine_old_user(substream, arg);
33398c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_HW_PARAMS_OLD:
33408c2ecf20Sopenharmony_ci		return snd_pcm_hw_params_old_user(substream, arg);
33418c2ecf20Sopenharmony_ci#endif
33428c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_DRAIN:
33438c2ecf20Sopenharmony_ci		return snd_pcm_drain(substream, file);
33448c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_DROP:
33458c2ecf20Sopenharmony_ci		return snd_pcm_drop(substream);
33468c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_PAUSE:
33478c2ecf20Sopenharmony_ci		return snd_pcm_pause_lock_irq(substream, (unsigned long)arg);
33488c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
33498c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_READI_FRAMES:
33508c2ecf20Sopenharmony_ci		return snd_pcm_xferi_frames_ioctl(substream, arg);
33518c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
33528c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_READN_FRAMES:
33538c2ecf20Sopenharmony_ci		return snd_pcm_xfern_frames_ioctl(substream, arg);
33548c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_REWIND:
33558c2ecf20Sopenharmony_ci		return snd_pcm_rewind_ioctl(substream, arg);
33568c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_FORWARD:
33578c2ecf20Sopenharmony_ci		return snd_pcm_forward_ioctl(substream, arg);
33588c2ecf20Sopenharmony_ci	}
33598c2ecf20Sopenharmony_ci	pcm_dbg(substream->pcm, "unknown ioctl = 0x%x\n", cmd);
33608c2ecf20Sopenharmony_ci	return -ENOTTY;
33618c2ecf20Sopenharmony_ci}
33628c2ecf20Sopenharmony_ci
33638c2ecf20Sopenharmony_cistatic long snd_pcm_ioctl(struct file *file, unsigned int cmd,
33648c2ecf20Sopenharmony_ci			  unsigned long arg)
33658c2ecf20Sopenharmony_ci{
33668c2ecf20Sopenharmony_ci	struct snd_pcm_file *pcm_file;
33678c2ecf20Sopenharmony_ci
33688c2ecf20Sopenharmony_ci	pcm_file = file->private_data;
33698c2ecf20Sopenharmony_ci
33708c2ecf20Sopenharmony_ci	if (((cmd >> 8) & 0xff) != 'A')
33718c2ecf20Sopenharmony_ci		return -ENOTTY;
33728c2ecf20Sopenharmony_ci
33738c2ecf20Sopenharmony_ci	return snd_pcm_common_ioctl(file, pcm_file->substream, cmd,
33748c2ecf20Sopenharmony_ci				     (void __user *)arg);
33758c2ecf20Sopenharmony_ci}
33768c2ecf20Sopenharmony_ci
33778c2ecf20Sopenharmony_ci/**
33788c2ecf20Sopenharmony_ci * snd_pcm_kernel_ioctl - Execute PCM ioctl in the kernel-space
33798c2ecf20Sopenharmony_ci * @substream: PCM substream
33808c2ecf20Sopenharmony_ci * @cmd: IOCTL cmd
33818c2ecf20Sopenharmony_ci * @arg: IOCTL argument
33828c2ecf20Sopenharmony_ci *
33838c2ecf20Sopenharmony_ci * The function is provided primarily for OSS layer and USB gadget drivers,
33848c2ecf20Sopenharmony_ci * and it allows only the limited set of ioctls (hw_params, sw_params,
33858c2ecf20Sopenharmony_ci * prepare, start, drain, drop, forward).
33868c2ecf20Sopenharmony_ci */
33878c2ecf20Sopenharmony_ciint snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream,
33888c2ecf20Sopenharmony_ci			 unsigned int cmd, void *arg)
33898c2ecf20Sopenharmony_ci{
33908c2ecf20Sopenharmony_ci	snd_pcm_uframes_t *frames = arg;
33918c2ecf20Sopenharmony_ci	snd_pcm_sframes_t result;
33928c2ecf20Sopenharmony_ci
33938c2ecf20Sopenharmony_ci	switch (cmd) {
33948c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_FORWARD:
33958c2ecf20Sopenharmony_ci	{
33968c2ecf20Sopenharmony_ci		/* provided only for OSS; capture-only and no value returned */
33978c2ecf20Sopenharmony_ci		if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
33988c2ecf20Sopenharmony_ci			return -EINVAL;
33998c2ecf20Sopenharmony_ci		result = snd_pcm_forward(substream, *frames);
34008c2ecf20Sopenharmony_ci		return result < 0 ? result : 0;
34018c2ecf20Sopenharmony_ci	}
34028c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_HW_PARAMS:
34038c2ecf20Sopenharmony_ci		return snd_pcm_hw_params(substream, arg);
34048c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_SW_PARAMS:
34058c2ecf20Sopenharmony_ci		return snd_pcm_sw_params(substream, arg);
34068c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_PREPARE:
34078c2ecf20Sopenharmony_ci		return snd_pcm_prepare(substream, NULL);
34088c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_START:
34098c2ecf20Sopenharmony_ci		return snd_pcm_start_lock_irq(substream);
34108c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_DRAIN:
34118c2ecf20Sopenharmony_ci		return snd_pcm_drain(substream, NULL);
34128c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_DROP:
34138c2ecf20Sopenharmony_ci		return snd_pcm_drop(substream);
34148c2ecf20Sopenharmony_ci	case SNDRV_PCM_IOCTL_DELAY:
34158c2ecf20Sopenharmony_ci		return snd_pcm_delay(substream, frames);
34168c2ecf20Sopenharmony_ci	default:
34178c2ecf20Sopenharmony_ci		return -EINVAL;
34188c2ecf20Sopenharmony_ci	}
34198c2ecf20Sopenharmony_ci}
34208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_pcm_kernel_ioctl);
34218c2ecf20Sopenharmony_ci
34228c2ecf20Sopenharmony_cistatic ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count,
34238c2ecf20Sopenharmony_ci			    loff_t * offset)
34248c2ecf20Sopenharmony_ci{
34258c2ecf20Sopenharmony_ci	struct snd_pcm_file *pcm_file;
34268c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
34278c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
34288c2ecf20Sopenharmony_ci	snd_pcm_sframes_t result;
34298c2ecf20Sopenharmony_ci
34308c2ecf20Sopenharmony_ci	pcm_file = file->private_data;
34318c2ecf20Sopenharmony_ci	substream = pcm_file->substream;
34328c2ecf20Sopenharmony_ci	if (PCM_RUNTIME_CHECK(substream))
34338c2ecf20Sopenharmony_ci		return -ENXIO;
34348c2ecf20Sopenharmony_ci	runtime = substream->runtime;
34358c2ecf20Sopenharmony_ci	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
34368c2ecf20Sopenharmony_ci		return -EBADFD;
34378c2ecf20Sopenharmony_ci	if (!frame_aligned(runtime, count))
34388c2ecf20Sopenharmony_ci		return -EINVAL;
34398c2ecf20Sopenharmony_ci	count = bytes_to_frames(runtime, count);
34408c2ecf20Sopenharmony_ci	result = snd_pcm_lib_read(substream, buf, count);
34418c2ecf20Sopenharmony_ci	if (result > 0)
34428c2ecf20Sopenharmony_ci		result = frames_to_bytes(runtime, result);
34438c2ecf20Sopenharmony_ci	return result;
34448c2ecf20Sopenharmony_ci}
34458c2ecf20Sopenharmony_ci
34468c2ecf20Sopenharmony_cistatic ssize_t snd_pcm_write(struct file *file, const char __user *buf,
34478c2ecf20Sopenharmony_ci			     size_t count, loff_t * offset)
34488c2ecf20Sopenharmony_ci{
34498c2ecf20Sopenharmony_ci	struct snd_pcm_file *pcm_file;
34508c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
34518c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
34528c2ecf20Sopenharmony_ci	snd_pcm_sframes_t result;
34538c2ecf20Sopenharmony_ci
34548c2ecf20Sopenharmony_ci	pcm_file = file->private_data;
34558c2ecf20Sopenharmony_ci	substream = pcm_file->substream;
34568c2ecf20Sopenharmony_ci	if (PCM_RUNTIME_CHECK(substream))
34578c2ecf20Sopenharmony_ci		return -ENXIO;
34588c2ecf20Sopenharmony_ci	runtime = substream->runtime;
34598c2ecf20Sopenharmony_ci	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
34608c2ecf20Sopenharmony_ci		return -EBADFD;
34618c2ecf20Sopenharmony_ci	if (!frame_aligned(runtime, count))
34628c2ecf20Sopenharmony_ci		return -EINVAL;
34638c2ecf20Sopenharmony_ci	count = bytes_to_frames(runtime, count);
34648c2ecf20Sopenharmony_ci	result = snd_pcm_lib_write(substream, buf, count);
34658c2ecf20Sopenharmony_ci	if (result > 0)
34668c2ecf20Sopenharmony_ci		result = frames_to_bytes(runtime, result);
34678c2ecf20Sopenharmony_ci	return result;
34688c2ecf20Sopenharmony_ci}
34698c2ecf20Sopenharmony_ci
34708c2ecf20Sopenharmony_cistatic ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to)
34718c2ecf20Sopenharmony_ci{
34728c2ecf20Sopenharmony_ci	struct snd_pcm_file *pcm_file;
34738c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
34748c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
34758c2ecf20Sopenharmony_ci	snd_pcm_sframes_t result;
34768c2ecf20Sopenharmony_ci	unsigned long i;
34778c2ecf20Sopenharmony_ci	void __user **bufs;
34788c2ecf20Sopenharmony_ci	snd_pcm_uframes_t frames;
34798c2ecf20Sopenharmony_ci
34808c2ecf20Sopenharmony_ci	pcm_file = iocb->ki_filp->private_data;
34818c2ecf20Sopenharmony_ci	substream = pcm_file->substream;
34828c2ecf20Sopenharmony_ci	if (PCM_RUNTIME_CHECK(substream))
34838c2ecf20Sopenharmony_ci		return -ENXIO;
34848c2ecf20Sopenharmony_ci	runtime = substream->runtime;
34858c2ecf20Sopenharmony_ci	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
34868c2ecf20Sopenharmony_ci		return -EBADFD;
34878c2ecf20Sopenharmony_ci	if (!iter_is_iovec(to))
34888c2ecf20Sopenharmony_ci		return -EINVAL;
34898c2ecf20Sopenharmony_ci	if (to->nr_segs > 1024 || to->nr_segs != runtime->channels)
34908c2ecf20Sopenharmony_ci		return -EINVAL;
34918c2ecf20Sopenharmony_ci	if (!frame_aligned(runtime, to->iov->iov_len))
34928c2ecf20Sopenharmony_ci		return -EINVAL;
34938c2ecf20Sopenharmony_ci	frames = bytes_to_samples(runtime, to->iov->iov_len);
34948c2ecf20Sopenharmony_ci	bufs = kmalloc_array(to->nr_segs, sizeof(void *), GFP_KERNEL);
34958c2ecf20Sopenharmony_ci	if (bufs == NULL)
34968c2ecf20Sopenharmony_ci		return -ENOMEM;
34978c2ecf20Sopenharmony_ci	for (i = 0; i < to->nr_segs; ++i)
34988c2ecf20Sopenharmony_ci		bufs[i] = to->iov[i].iov_base;
34998c2ecf20Sopenharmony_ci	result = snd_pcm_lib_readv(substream, bufs, frames);
35008c2ecf20Sopenharmony_ci	if (result > 0)
35018c2ecf20Sopenharmony_ci		result = frames_to_bytes(runtime, result);
35028c2ecf20Sopenharmony_ci	kfree(bufs);
35038c2ecf20Sopenharmony_ci	return result;
35048c2ecf20Sopenharmony_ci}
35058c2ecf20Sopenharmony_ci
35068c2ecf20Sopenharmony_cistatic ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from)
35078c2ecf20Sopenharmony_ci{
35088c2ecf20Sopenharmony_ci	struct snd_pcm_file *pcm_file;
35098c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
35108c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
35118c2ecf20Sopenharmony_ci	snd_pcm_sframes_t result;
35128c2ecf20Sopenharmony_ci	unsigned long i;
35138c2ecf20Sopenharmony_ci	void __user **bufs;
35148c2ecf20Sopenharmony_ci	snd_pcm_uframes_t frames;
35158c2ecf20Sopenharmony_ci
35168c2ecf20Sopenharmony_ci	pcm_file = iocb->ki_filp->private_data;
35178c2ecf20Sopenharmony_ci	substream = pcm_file->substream;
35188c2ecf20Sopenharmony_ci	if (PCM_RUNTIME_CHECK(substream))
35198c2ecf20Sopenharmony_ci		return -ENXIO;
35208c2ecf20Sopenharmony_ci	runtime = substream->runtime;
35218c2ecf20Sopenharmony_ci	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
35228c2ecf20Sopenharmony_ci		return -EBADFD;
35238c2ecf20Sopenharmony_ci	if (!iter_is_iovec(from))
35248c2ecf20Sopenharmony_ci		return -EINVAL;
35258c2ecf20Sopenharmony_ci	if (from->nr_segs > 128 || from->nr_segs != runtime->channels ||
35268c2ecf20Sopenharmony_ci	    !frame_aligned(runtime, from->iov->iov_len))
35278c2ecf20Sopenharmony_ci		return -EINVAL;
35288c2ecf20Sopenharmony_ci	frames = bytes_to_samples(runtime, from->iov->iov_len);
35298c2ecf20Sopenharmony_ci	bufs = kmalloc_array(from->nr_segs, sizeof(void *), GFP_KERNEL);
35308c2ecf20Sopenharmony_ci	if (bufs == NULL)
35318c2ecf20Sopenharmony_ci		return -ENOMEM;
35328c2ecf20Sopenharmony_ci	for (i = 0; i < from->nr_segs; ++i)
35338c2ecf20Sopenharmony_ci		bufs[i] = from->iov[i].iov_base;
35348c2ecf20Sopenharmony_ci	result = snd_pcm_lib_writev(substream, bufs, frames);
35358c2ecf20Sopenharmony_ci	if (result > 0)
35368c2ecf20Sopenharmony_ci		result = frames_to_bytes(runtime, result);
35378c2ecf20Sopenharmony_ci	kfree(bufs);
35388c2ecf20Sopenharmony_ci	return result;
35398c2ecf20Sopenharmony_ci}
35408c2ecf20Sopenharmony_ci
35418c2ecf20Sopenharmony_cistatic __poll_t snd_pcm_poll(struct file *file, poll_table *wait)
35428c2ecf20Sopenharmony_ci{
35438c2ecf20Sopenharmony_ci	struct snd_pcm_file *pcm_file;
35448c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
35458c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
35468c2ecf20Sopenharmony_ci	__poll_t mask, ok;
35478c2ecf20Sopenharmony_ci	snd_pcm_uframes_t avail;
35488c2ecf20Sopenharmony_ci
35498c2ecf20Sopenharmony_ci	pcm_file = file->private_data;
35508c2ecf20Sopenharmony_ci
35518c2ecf20Sopenharmony_ci	substream = pcm_file->substream;
35528c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
35538c2ecf20Sopenharmony_ci		ok = EPOLLOUT | EPOLLWRNORM;
35548c2ecf20Sopenharmony_ci	else
35558c2ecf20Sopenharmony_ci		ok = EPOLLIN | EPOLLRDNORM;
35568c2ecf20Sopenharmony_ci	if (PCM_RUNTIME_CHECK(substream))
35578c2ecf20Sopenharmony_ci		return ok | EPOLLERR;
35588c2ecf20Sopenharmony_ci
35598c2ecf20Sopenharmony_ci	runtime = substream->runtime;
35608c2ecf20Sopenharmony_ci	poll_wait(file, &runtime->sleep, wait);
35618c2ecf20Sopenharmony_ci
35628c2ecf20Sopenharmony_ci	mask = 0;
35638c2ecf20Sopenharmony_ci	snd_pcm_stream_lock_irq(substream);
35648c2ecf20Sopenharmony_ci	avail = snd_pcm_avail(substream);
35658c2ecf20Sopenharmony_ci	switch (runtime->status->state) {
35668c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_RUNNING:
35678c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_PREPARED:
35688c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_PAUSED:
35698c2ecf20Sopenharmony_ci		if (avail >= runtime->control->avail_min)
35708c2ecf20Sopenharmony_ci			mask = ok;
35718c2ecf20Sopenharmony_ci		break;
35728c2ecf20Sopenharmony_ci	case SNDRV_PCM_STATE_DRAINING:
35738c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
35748c2ecf20Sopenharmony_ci			mask = ok;
35758c2ecf20Sopenharmony_ci			if (!avail)
35768c2ecf20Sopenharmony_ci				mask |= EPOLLERR;
35778c2ecf20Sopenharmony_ci		}
35788c2ecf20Sopenharmony_ci		break;
35798c2ecf20Sopenharmony_ci	default:
35808c2ecf20Sopenharmony_ci		mask = ok | EPOLLERR;
35818c2ecf20Sopenharmony_ci		break;
35828c2ecf20Sopenharmony_ci	}
35838c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock_irq(substream);
35848c2ecf20Sopenharmony_ci	return mask;
35858c2ecf20Sopenharmony_ci}
35868c2ecf20Sopenharmony_ci
35878c2ecf20Sopenharmony_ci/*
35888c2ecf20Sopenharmony_ci * mmap support
35898c2ecf20Sopenharmony_ci */
35908c2ecf20Sopenharmony_ci
35918c2ecf20Sopenharmony_ci/*
35928c2ecf20Sopenharmony_ci * Only on coherent architectures, we can mmap the status and the control records
35938c2ecf20Sopenharmony_ci * for effcient data transfer.  On others, we have to use HWSYNC ioctl...
35948c2ecf20Sopenharmony_ci */
35958c2ecf20Sopenharmony_ci#if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_ALPHA)
35968c2ecf20Sopenharmony_ci/*
35978c2ecf20Sopenharmony_ci * mmap status record
35988c2ecf20Sopenharmony_ci */
35998c2ecf20Sopenharmony_cistatic vm_fault_t snd_pcm_mmap_status_fault(struct vm_fault *vmf)
36008c2ecf20Sopenharmony_ci{
36018c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream = vmf->vma->vm_private_data;
36028c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
36038c2ecf20Sopenharmony_ci
36048c2ecf20Sopenharmony_ci	if (substream == NULL)
36058c2ecf20Sopenharmony_ci		return VM_FAULT_SIGBUS;
36068c2ecf20Sopenharmony_ci	runtime = substream->runtime;
36078c2ecf20Sopenharmony_ci	vmf->page = virt_to_page(runtime->status);
36088c2ecf20Sopenharmony_ci	get_page(vmf->page);
36098c2ecf20Sopenharmony_ci	return 0;
36108c2ecf20Sopenharmony_ci}
36118c2ecf20Sopenharmony_ci
36128c2ecf20Sopenharmony_cistatic const struct vm_operations_struct snd_pcm_vm_ops_status =
36138c2ecf20Sopenharmony_ci{
36148c2ecf20Sopenharmony_ci	.fault =	snd_pcm_mmap_status_fault,
36158c2ecf20Sopenharmony_ci};
36168c2ecf20Sopenharmony_ci
36178c2ecf20Sopenharmony_cistatic int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file,
36188c2ecf20Sopenharmony_ci			       struct vm_area_struct *area)
36198c2ecf20Sopenharmony_ci{
36208c2ecf20Sopenharmony_ci	long size;
36218c2ecf20Sopenharmony_ci	if (!(area->vm_flags & VM_READ))
36228c2ecf20Sopenharmony_ci		return -EINVAL;
36238c2ecf20Sopenharmony_ci	size = area->vm_end - area->vm_start;
36248c2ecf20Sopenharmony_ci	if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)))
36258c2ecf20Sopenharmony_ci		return -EINVAL;
36268c2ecf20Sopenharmony_ci	area->vm_ops = &snd_pcm_vm_ops_status;
36278c2ecf20Sopenharmony_ci	area->vm_private_data = substream;
36288c2ecf20Sopenharmony_ci	area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
36298c2ecf20Sopenharmony_ci	return 0;
36308c2ecf20Sopenharmony_ci}
36318c2ecf20Sopenharmony_ci
36328c2ecf20Sopenharmony_ci/*
36338c2ecf20Sopenharmony_ci * mmap control record
36348c2ecf20Sopenharmony_ci */
36358c2ecf20Sopenharmony_cistatic vm_fault_t snd_pcm_mmap_control_fault(struct vm_fault *vmf)
36368c2ecf20Sopenharmony_ci{
36378c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream = vmf->vma->vm_private_data;
36388c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
36398c2ecf20Sopenharmony_ci
36408c2ecf20Sopenharmony_ci	if (substream == NULL)
36418c2ecf20Sopenharmony_ci		return VM_FAULT_SIGBUS;
36428c2ecf20Sopenharmony_ci	runtime = substream->runtime;
36438c2ecf20Sopenharmony_ci	vmf->page = virt_to_page(runtime->control);
36448c2ecf20Sopenharmony_ci	get_page(vmf->page);
36458c2ecf20Sopenharmony_ci	return 0;
36468c2ecf20Sopenharmony_ci}
36478c2ecf20Sopenharmony_ci
36488c2ecf20Sopenharmony_cistatic const struct vm_operations_struct snd_pcm_vm_ops_control =
36498c2ecf20Sopenharmony_ci{
36508c2ecf20Sopenharmony_ci	.fault =	snd_pcm_mmap_control_fault,
36518c2ecf20Sopenharmony_ci};
36528c2ecf20Sopenharmony_ci
36538c2ecf20Sopenharmony_cistatic int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file,
36548c2ecf20Sopenharmony_ci				struct vm_area_struct *area)
36558c2ecf20Sopenharmony_ci{
36568c2ecf20Sopenharmony_ci	long size;
36578c2ecf20Sopenharmony_ci	if (!(area->vm_flags & VM_READ))
36588c2ecf20Sopenharmony_ci		return -EINVAL;
36598c2ecf20Sopenharmony_ci	size = area->vm_end - area->vm_start;
36608c2ecf20Sopenharmony_ci	if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)))
36618c2ecf20Sopenharmony_ci		return -EINVAL;
36628c2ecf20Sopenharmony_ci	area->vm_ops = &snd_pcm_vm_ops_control;
36638c2ecf20Sopenharmony_ci	area->vm_private_data = substream;
36648c2ecf20Sopenharmony_ci	area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
36658c2ecf20Sopenharmony_ci	return 0;
36668c2ecf20Sopenharmony_ci}
36678c2ecf20Sopenharmony_ci
36688c2ecf20Sopenharmony_cistatic bool pcm_status_mmap_allowed(struct snd_pcm_file *pcm_file)
36698c2ecf20Sopenharmony_ci{
36708c2ecf20Sopenharmony_ci	/* See pcm_control_mmap_allowed() below.
36718c2ecf20Sopenharmony_ci	 * Since older alsa-lib requires both status and control mmaps to be
36728c2ecf20Sopenharmony_ci	 * coupled, we have to disable the status mmap for old alsa-lib, too.
36738c2ecf20Sopenharmony_ci	 */
36748c2ecf20Sopenharmony_ci	if (pcm_file->user_pversion < SNDRV_PROTOCOL_VERSION(2, 0, 14) &&
36758c2ecf20Sopenharmony_ci	    (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_SYNC_APPLPTR))
36768c2ecf20Sopenharmony_ci		return false;
36778c2ecf20Sopenharmony_ci	return true;
36788c2ecf20Sopenharmony_ci}
36798c2ecf20Sopenharmony_ci
36808c2ecf20Sopenharmony_cistatic bool pcm_control_mmap_allowed(struct snd_pcm_file *pcm_file)
36818c2ecf20Sopenharmony_ci{
36828c2ecf20Sopenharmony_ci	if (pcm_file->no_compat_mmap)
36838c2ecf20Sopenharmony_ci		return false;
36848c2ecf20Sopenharmony_ci	/* Disallow the control mmap when SYNC_APPLPTR flag is set;
36858c2ecf20Sopenharmony_ci	 * it enforces the user-space to fall back to snd_pcm_sync_ptr(),
36868c2ecf20Sopenharmony_ci	 * thus it effectively assures the manual update of appl_ptr.
36878c2ecf20Sopenharmony_ci	 */
36888c2ecf20Sopenharmony_ci	if (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_SYNC_APPLPTR)
36898c2ecf20Sopenharmony_ci		return false;
36908c2ecf20Sopenharmony_ci	return true;
36918c2ecf20Sopenharmony_ci}
36928c2ecf20Sopenharmony_ci
36938c2ecf20Sopenharmony_ci#else /* ! coherent mmap */
36948c2ecf20Sopenharmony_ci/*
36958c2ecf20Sopenharmony_ci * don't support mmap for status and control records.
36968c2ecf20Sopenharmony_ci */
36978c2ecf20Sopenharmony_ci#define pcm_status_mmap_allowed(pcm_file)	false
36988c2ecf20Sopenharmony_ci#define pcm_control_mmap_allowed(pcm_file)	false
36998c2ecf20Sopenharmony_ci
37008c2ecf20Sopenharmony_cistatic int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file,
37018c2ecf20Sopenharmony_ci			       struct vm_area_struct *area)
37028c2ecf20Sopenharmony_ci{
37038c2ecf20Sopenharmony_ci	return -ENXIO;
37048c2ecf20Sopenharmony_ci}
37058c2ecf20Sopenharmony_cistatic int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file,
37068c2ecf20Sopenharmony_ci				struct vm_area_struct *area)
37078c2ecf20Sopenharmony_ci{
37088c2ecf20Sopenharmony_ci	return -ENXIO;
37098c2ecf20Sopenharmony_ci}
37108c2ecf20Sopenharmony_ci#endif /* coherent mmap */
37118c2ecf20Sopenharmony_ci
37128c2ecf20Sopenharmony_cistatic inline struct page *
37138c2ecf20Sopenharmony_cisnd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs)
37148c2ecf20Sopenharmony_ci{
37158c2ecf20Sopenharmony_ci	void *vaddr = substream->runtime->dma_area + ofs;
37168c2ecf20Sopenharmony_ci
37178c2ecf20Sopenharmony_ci	switch (substream->dma_buffer.dev.type) {
37188c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DMA_SGBUF
37198c2ecf20Sopenharmony_ci	case SNDRV_DMA_TYPE_DEV_SG:
37208c2ecf20Sopenharmony_ci	case SNDRV_DMA_TYPE_DEV_UC_SG:
37218c2ecf20Sopenharmony_ci		return snd_pcm_sgbuf_ops_page(substream, ofs);
37228c2ecf20Sopenharmony_ci#endif /* CONFIG_SND_DMA_SGBUF */
37238c2ecf20Sopenharmony_ci	case SNDRV_DMA_TYPE_VMALLOC:
37248c2ecf20Sopenharmony_ci		return vmalloc_to_page(vaddr);
37258c2ecf20Sopenharmony_ci	default:
37268c2ecf20Sopenharmony_ci		return virt_to_page(vaddr);
37278c2ecf20Sopenharmony_ci	}
37288c2ecf20Sopenharmony_ci}
37298c2ecf20Sopenharmony_ci
37308c2ecf20Sopenharmony_ci/*
37318c2ecf20Sopenharmony_ci * fault callback for mmapping a RAM page
37328c2ecf20Sopenharmony_ci */
37338c2ecf20Sopenharmony_cistatic vm_fault_t snd_pcm_mmap_data_fault(struct vm_fault *vmf)
37348c2ecf20Sopenharmony_ci{
37358c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream = vmf->vma->vm_private_data;
37368c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
37378c2ecf20Sopenharmony_ci	unsigned long offset;
37388c2ecf20Sopenharmony_ci	struct page * page;
37398c2ecf20Sopenharmony_ci	size_t dma_bytes;
37408c2ecf20Sopenharmony_ci
37418c2ecf20Sopenharmony_ci	if (substream == NULL)
37428c2ecf20Sopenharmony_ci		return VM_FAULT_SIGBUS;
37438c2ecf20Sopenharmony_ci	runtime = substream->runtime;
37448c2ecf20Sopenharmony_ci	offset = vmf->pgoff << PAGE_SHIFT;
37458c2ecf20Sopenharmony_ci	dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
37468c2ecf20Sopenharmony_ci	if (offset > dma_bytes - PAGE_SIZE)
37478c2ecf20Sopenharmony_ci		return VM_FAULT_SIGBUS;
37488c2ecf20Sopenharmony_ci	if (substream->ops->page)
37498c2ecf20Sopenharmony_ci		page = substream->ops->page(substream, offset);
37508c2ecf20Sopenharmony_ci	else
37518c2ecf20Sopenharmony_ci		page = snd_pcm_default_page_ops(substream, offset);
37528c2ecf20Sopenharmony_ci	if (!page)
37538c2ecf20Sopenharmony_ci		return VM_FAULT_SIGBUS;
37548c2ecf20Sopenharmony_ci	get_page(page);
37558c2ecf20Sopenharmony_ci	vmf->page = page;
37568c2ecf20Sopenharmony_ci	return 0;
37578c2ecf20Sopenharmony_ci}
37588c2ecf20Sopenharmony_ci
37598c2ecf20Sopenharmony_cistatic const struct vm_operations_struct snd_pcm_vm_ops_data = {
37608c2ecf20Sopenharmony_ci	.open =		snd_pcm_mmap_data_open,
37618c2ecf20Sopenharmony_ci	.close =	snd_pcm_mmap_data_close,
37628c2ecf20Sopenharmony_ci};
37638c2ecf20Sopenharmony_ci
37648c2ecf20Sopenharmony_cistatic const struct vm_operations_struct snd_pcm_vm_ops_data_fault = {
37658c2ecf20Sopenharmony_ci	.open =		snd_pcm_mmap_data_open,
37668c2ecf20Sopenharmony_ci	.close =	snd_pcm_mmap_data_close,
37678c2ecf20Sopenharmony_ci	.fault =	snd_pcm_mmap_data_fault,
37688c2ecf20Sopenharmony_ci};
37698c2ecf20Sopenharmony_ci
37708c2ecf20Sopenharmony_ci/*
37718c2ecf20Sopenharmony_ci * mmap the DMA buffer on RAM
37728c2ecf20Sopenharmony_ci */
37738c2ecf20Sopenharmony_ci
37748c2ecf20Sopenharmony_ci/**
37758c2ecf20Sopenharmony_ci * snd_pcm_lib_default_mmap - Default PCM data mmap function
37768c2ecf20Sopenharmony_ci * @substream: PCM substream
37778c2ecf20Sopenharmony_ci * @area: VMA
37788c2ecf20Sopenharmony_ci *
37798c2ecf20Sopenharmony_ci * This is the default mmap handler for PCM data.  When mmap pcm_ops is NULL,
37808c2ecf20Sopenharmony_ci * this function is invoked implicitly.
37818c2ecf20Sopenharmony_ci */
37828c2ecf20Sopenharmony_ciint snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
37838c2ecf20Sopenharmony_ci			     struct vm_area_struct *area)
37848c2ecf20Sopenharmony_ci{
37858c2ecf20Sopenharmony_ci	area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
37868c2ecf20Sopenharmony_ci#ifdef CONFIG_GENERIC_ALLOCATOR
37878c2ecf20Sopenharmony_ci	if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_IRAM) {
37888c2ecf20Sopenharmony_ci		area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
37898c2ecf20Sopenharmony_ci		return remap_pfn_range(area, area->vm_start,
37908c2ecf20Sopenharmony_ci				substream->dma_buffer.addr >> PAGE_SHIFT,
37918c2ecf20Sopenharmony_ci				area->vm_end - area->vm_start, area->vm_page_prot);
37928c2ecf20Sopenharmony_ci	}
37938c2ecf20Sopenharmony_ci#endif /* CONFIG_GENERIC_ALLOCATOR */
37948c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_HAS_DMA) && !substream->ops->page &&
37958c2ecf20Sopenharmony_ci	    (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV ||
37968c2ecf20Sopenharmony_ci	     substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_UC))
37978c2ecf20Sopenharmony_ci		return dma_mmap_coherent(substream->dma_buffer.dev.dev,
37988c2ecf20Sopenharmony_ci					 area,
37998c2ecf20Sopenharmony_ci					 substream->runtime->dma_area,
38008c2ecf20Sopenharmony_ci					 substream->runtime->dma_addr,
38018c2ecf20Sopenharmony_ci					 substream->runtime->dma_bytes);
38028c2ecf20Sopenharmony_ci	/* mmap with fault handler */
38038c2ecf20Sopenharmony_ci	area->vm_ops = &snd_pcm_vm_ops_data_fault;
38048c2ecf20Sopenharmony_ci	return 0;
38058c2ecf20Sopenharmony_ci}
38068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_pcm_lib_default_mmap);
38078c2ecf20Sopenharmony_ci
38088c2ecf20Sopenharmony_ci/*
38098c2ecf20Sopenharmony_ci * mmap the DMA buffer on I/O memory area
38108c2ecf20Sopenharmony_ci */
38118c2ecf20Sopenharmony_ci#if SNDRV_PCM_INFO_MMAP_IOMEM
38128c2ecf20Sopenharmony_ci/**
38138c2ecf20Sopenharmony_ci * snd_pcm_lib_mmap_iomem - Default PCM data mmap function for I/O mem
38148c2ecf20Sopenharmony_ci * @substream: PCM substream
38158c2ecf20Sopenharmony_ci * @area: VMA
38168c2ecf20Sopenharmony_ci *
38178c2ecf20Sopenharmony_ci * When your hardware uses the iomapped pages as the hardware buffer and
38188c2ecf20Sopenharmony_ci * wants to mmap it, pass this function as mmap pcm_ops.  Note that this
38198c2ecf20Sopenharmony_ci * is supposed to work only on limited architectures.
38208c2ecf20Sopenharmony_ci */
38218c2ecf20Sopenharmony_ciint snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
38228c2ecf20Sopenharmony_ci			   struct vm_area_struct *area)
38238c2ecf20Sopenharmony_ci{
38248c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
38258c2ecf20Sopenharmony_ci
38268c2ecf20Sopenharmony_ci	area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
38278c2ecf20Sopenharmony_ci	return vm_iomap_memory(area, runtime->dma_addr, runtime->dma_bytes);
38288c2ecf20Sopenharmony_ci}
38298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_pcm_lib_mmap_iomem);
38308c2ecf20Sopenharmony_ci#endif /* SNDRV_PCM_INFO_MMAP */
38318c2ecf20Sopenharmony_ci
38328c2ecf20Sopenharmony_ci/*
38338c2ecf20Sopenharmony_ci * mmap DMA buffer
38348c2ecf20Sopenharmony_ci */
38358c2ecf20Sopenharmony_ciint snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
38368c2ecf20Sopenharmony_ci		      struct vm_area_struct *area)
38378c2ecf20Sopenharmony_ci{
38388c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
38398c2ecf20Sopenharmony_ci	long size;
38408c2ecf20Sopenharmony_ci	unsigned long offset;
38418c2ecf20Sopenharmony_ci	size_t dma_bytes;
38428c2ecf20Sopenharmony_ci	int err;
38438c2ecf20Sopenharmony_ci
38448c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
38458c2ecf20Sopenharmony_ci		if (!(area->vm_flags & (VM_WRITE|VM_READ)))
38468c2ecf20Sopenharmony_ci			return -EINVAL;
38478c2ecf20Sopenharmony_ci	} else {
38488c2ecf20Sopenharmony_ci		if (!(area->vm_flags & VM_READ))
38498c2ecf20Sopenharmony_ci			return -EINVAL;
38508c2ecf20Sopenharmony_ci	}
38518c2ecf20Sopenharmony_ci	runtime = substream->runtime;
38528c2ecf20Sopenharmony_ci	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
38538c2ecf20Sopenharmony_ci		return -EBADFD;
38548c2ecf20Sopenharmony_ci	if (!(runtime->info & SNDRV_PCM_INFO_MMAP))
38558c2ecf20Sopenharmony_ci		return -ENXIO;
38568c2ecf20Sopenharmony_ci	if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
38578c2ecf20Sopenharmony_ci	    runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
38588c2ecf20Sopenharmony_ci		return -EINVAL;
38598c2ecf20Sopenharmony_ci	size = area->vm_end - area->vm_start;
38608c2ecf20Sopenharmony_ci	offset = area->vm_pgoff << PAGE_SHIFT;
38618c2ecf20Sopenharmony_ci	dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
38628c2ecf20Sopenharmony_ci	if ((size_t)size > dma_bytes)
38638c2ecf20Sopenharmony_ci		return -EINVAL;
38648c2ecf20Sopenharmony_ci	if (offset > dma_bytes - size)
38658c2ecf20Sopenharmony_ci		return -EINVAL;
38668c2ecf20Sopenharmony_ci
38678c2ecf20Sopenharmony_ci	area->vm_ops = &snd_pcm_vm_ops_data;
38688c2ecf20Sopenharmony_ci	area->vm_private_data = substream;
38698c2ecf20Sopenharmony_ci	if (substream->ops->mmap)
38708c2ecf20Sopenharmony_ci		err = substream->ops->mmap(substream, area);
38718c2ecf20Sopenharmony_ci	else
38728c2ecf20Sopenharmony_ci		err = snd_pcm_lib_default_mmap(substream, area);
38738c2ecf20Sopenharmony_ci	if (!err)
38748c2ecf20Sopenharmony_ci		atomic_inc(&substream->mmap_count);
38758c2ecf20Sopenharmony_ci	return err;
38768c2ecf20Sopenharmony_ci}
38778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_pcm_mmap_data);
38788c2ecf20Sopenharmony_ci
38798c2ecf20Sopenharmony_cistatic int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
38808c2ecf20Sopenharmony_ci{
38818c2ecf20Sopenharmony_ci	struct snd_pcm_file * pcm_file;
38828c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
38838c2ecf20Sopenharmony_ci	unsigned long offset;
38848c2ecf20Sopenharmony_ci
38858c2ecf20Sopenharmony_ci	pcm_file = file->private_data;
38868c2ecf20Sopenharmony_ci	substream = pcm_file->substream;
38878c2ecf20Sopenharmony_ci	if (PCM_RUNTIME_CHECK(substream))
38888c2ecf20Sopenharmony_ci		return -ENXIO;
38898c2ecf20Sopenharmony_ci
38908c2ecf20Sopenharmony_ci	offset = area->vm_pgoff << PAGE_SHIFT;
38918c2ecf20Sopenharmony_ci	switch (offset) {
38928c2ecf20Sopenharmony_ci	case SNDRV_PCM_MMAP_OFFSET_STATUS_OLD:
38938c2ecf20Sopenharmony_ci		if (pcm_file->no_compat_mmap || !IS_ENABLED(CONFIG_64BIT))
38948c2ecf20Sopenharmony_ci			return -ENXIO;
38958c2ecf20Sopenharmony_ci		fallthrough;
38968c2ecf20Sopenharmony_ci	case SNDRV_PCM_MMAP_OFFSET_STATUS_NEW:
38978c2ecf20Sopenharmony_ci		if (!pcm_status_mmap_allowed(pcm_file))
38988c2ecf20Sopenharmony_ci			return -ENXIO;
38998c2ecf20Sopenharmony_ci		return snd_pcm_mmap_status(substream, file, area);
39008c2ecf20Sopenharmony_ci	case SNDRV_PCM_MMAP_OFFSET_CONTROL_OLD:
39018c2ecf20Sopenharmony_ci		if (pcm_file->no_compat_mmap || !IS_ENABLED(CONFIG_64BIT))
39028c2ecf20Sopenharmony_ci			return -ENXIO;
39038c2ecf20Sopenharmony_ci		fallthrough;
39048c2ecf20Sopenharmony_ci	case SNDRV_PCM_MMAP_OFFSET_CONTROL_NEW:
39058c2ecf20Sopenharmony_ci		if (!pcm_control_mmap_allowed(pcm_file))
39068c2ecf20Sopenharmony_ci			return -ENXIO;
39078c2ecf20Sopenharmony_ci		return snd_pcm_mmap_control(substream, file, area);
39088c2ecf20Sopenharmony_ci	default:
39098c2ecf20Sopenharmony_ci		return snd_pcm_mmap_data(substream, file, area);
39108c2ecf20Sopenharmony_ci	}
39118c2ecf20Sopenharmony_ci	return 0;
39128c2ecf20Sopenharmony_ci}
39138c2ecf20Sopenharmony_ci
39148c2ecf20Sopenharmony_cistatic int snd_pcm_fasync(int fd, struct file * file, int on)
39158c2ecf20Sopenharmony_ci{
39168c2ecf20Sopenharmony_ci	struct snd_pcm_file * pcm_file;
39178c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
39188c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
39198c2ecf20Sopenharmony_ci
39208c2ecf20Sopenharmony_ci	pcm_file = file->private_data;
39218c2ecf20Sopenharmony_ci	substream = pcm_file->substream;
39228c2ecf20Sopenharmony_ci	if (PCM_RUNTIME_CHECK(substream))
39238c2ecf20Sopenharmony_ci		return -ENXIO;
39248c2ecf20Sopenharmony_ci	runtime = substream->runtime;
39258c2ecf20Sopenharmony_ci	return fasync_helper(fd, file, on, &runtime->fasync);
39268c2ecf20Sopenharmony_ci}
39278c2ecf20Sopenharmony_ci
39288c2ecf20Sopenharmony_ci/*
39298c2ecf20Sopenharmony_ci * ioctl32 compat
39308c2ecf20Sopenharmony_ci */
39318c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
39328c2ecf20Sopenharmony_ci#include "pcm_compat.c"
39338c2ecf20Sopenharmony_ci#else
39348c2ecf20Sopenharmony_ci#define snd_pcm_ioctl_compat	NULL
39358c2ecf20Sopenharmony_ci#endif
39368c2ecf20Sopenharmony_ci
39378c2ecf20Sopenharmony_ci/*
39388c2ecf20Sopenharmony_ci *  To be removed helpers to keep binary compatibility
39398c2ecf20Sopenharmony_ci */
39408c2ecf20Sopenharmony_ci
39418c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_SUPPORT_OLD_API
39428c2ecf20Sopenharmony_ci#define __OLD_TO_NEW_MASK(x) ((x&7)|((x&0x07fffff8)<<5))
39438c2ecf20Sopenharmony_ci#define __NEW_TO_OLD_MASK(x) ((x&7)|((x&0xffffff00)>>5))
39448c2ecf20Sopenharmony_ci
39458c2ecf20Sopenharmony_cistatic void snd_pcm_hw_convert_from_old_params(struct snd_pcm_hw_params *params,
39468c2ecf20Sopenharmony_ci					       struct snd_pcm_hw_params_old *oparams)
39478c2ecf20Sopenharmony_ci{
39488c2ecf20Sopenharmony_ci	unsigned int i;
39498c2ecf20Sopenharmony_ci
39508c2ecf20Sopenharmony_ci	memset(params, 0, sizeof(*params));
39518c2ecf20Sopenharmony_ci	params->flags = oparams->flags;
39528c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(oparams->masks); i++)
39538c2ecf20Sopenharmony_ci		params->masks[i].bits[0] = oparams->masks[i];
39548c2ecf20Sopenharmony_ci	memcpy(params->intervals, oparams->intervals, sizeof(oparams->intervals));
39558c2ecf20Sopenharmony_ci	params->rmask = __OLD_TO_NEW_MASK(oparams->rmask);
39568c2ecf20Sopenharmony_ci	params->cmask = __OLD_TO_NEW_MASK(oparams->cmask);
39578c2ecf20Sopenharmony_ci	params->info = oparams->info;
39588c2ecf20Sopenharmony_ci	params->msbits = oparams->msbits;
39598c2ecf20Sopenharmony_ci	params->rate_num = oparams->rate_num;
39608c2ecf20Sopenharmony_ci	params->rate_den = oparams->rate_den;
39618c2ecf20Sopenharmony_ci	params->fifo_size = oparams->fifo_size;
39628c2ecf20Sopenharmony_ci}
39638c2ecf20Sopenharmony_ci
39648c2ecf20Sopenharmony_cistatic void snd_pcm_hw_convert_to_old_params(struct snd_pcm_hw_params_old *oparams,
39658c2ecf20Sopenharmony_ci					     struct snd_pcm_hw_params *params)
39668c2ecf20Sopenharmony_ci{
39678c2ecf20Sopenharmony_ci	unsigned int i;
39688c2ecf20Sopenharmony_ci
39698c2ecf20Sopenharmony_ci	memset(oparams, 0, sizeof(*oparams));
39708c2ecf20Sopenharmony_ci	oparams->flags = params->flags;
39718c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(oparams->masks); i++)
39728c2ecf20Sopenharmony_ci		oparams->masks[i] = params->masks[i].bits[0];
39738c2ecf20Sopenharmony_ci	memcpy(oparams->intervals, params->intervals, sizeof(oparams->intervals));
39748c2ecf20Sopenharmony_ci	oparams->rmask = __NEW_TO_OLD_MASK(params->rmask);
39758c2ecf20Sopenharmony_ci	oparams->cmask = __NEW_TO_OLD_MASK(params->cmask);
39768c2ecf20Sopenharmony_ci	oparams->info = params->info;
39778c2ecf20Sopenharmony_ci	oparams->msbits = params->msbits;
39788c2ecf20Sopenharmony_ci	oparams->rate_num = params->rate_num;
39798c2ecf20Sopenharmony_ci	oparams->rate_den = params->rate_den;
39808c2ecf20Sopenharmony_ci	oparams->fifo_size = params->fifo_size;
39818c2ecf20Sopenharmony_ci}
39828c2ecf20Sopenharmony_ci
39838c2ecf20Sopenharmony_cistatic int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream,
39848c2ecf20Sopenharmony_ci				      struct snd_pcm_hw_params_old __user * _oparams)
39858c2ecf20Sopenharmony_ci{
39868c2ecf20Sopenharmony_ci	struct snd_pcm_hw_params *params;
39878c2ecf20Sopenharmony_ci	struct snd_pcm_hw_params_old *oparams = NULL;
39888c2ecf20Sopenharmony_ci	int err;
39898c2ecf20Sopenharmony_ci
39908c2ecf20Sopenharmony_ci	params = kmalloc(sizeof(*params), GFP_KERNEL);
39918c2ecf20Sopenharmony_ci	if (!params)
39928c2ecf20Sopenharmony_ci		return -ENOMEM;
39938c2ecf20Sopenharmony_ci
39948c2ecf20Sopenharmony_ci	oparams = memdup_user(_oparams, sizeof(*oparams));
39958c2ecf20Sopenharmony_ci	if (IS_ERR(oparams)) {
39968c2ecf20Sopenharmony_ci		err = PTR_ERR(oparams);
39978c2ecf20Sopenharmony_ci		goto out;
39988c2ecf20Sopenharmony_ci	}
39998c2ecf20Sopenharmony_ci	snd_pcm_hw_convert_from_old_params(params, oparams);
40008c2ecf20Sopenharmony_ci	err = snd_pcm_hw_refine(substream, params);
40018c2ecf20Sopenharmony_ci	if (err < 0)
40028c2ecf20Sopenharmony_ci		goto out_old;
40038c2ecf20Sopenharmony_ci
40048c2ecf20Sopenharmony_ci	err = fixup_unreferenced_params(substream, params);
40058c2ecf20Sopenharmony_ci	if (err < 0)
40068c2ecf20Sopenharmony_ci		goto out_old;
40078c2ecf20Sopenharmony_ci
40088c2ecf20Sopenharmony_ci	snd_pcm_hw_convert_to_old_params(oparams, params);
40098c2ecf20Sopenharmony_ci	if (copy_to_user(_oparams, oparams, sizeof(*oparams)))
40108c2ecf20Sopenharmony_ci		err = -EFAULT;
40118c2ecf20Sopenharmony_ciout_old:
40128c2ecf20Sopenharmony_ci	kfree(oparams);
40138c2ecf20Sopenharmony_ciout:
40148c2ecf20Sopenharmony_ci	kfree(params);
40158c2ecf20Sopenharmony_ci	return err;
40168c2ecf20Sopenharmony_ci}
40178c2ecf20Sopenharmony_ci
40188c2ecf20Sopenharmony_cistatic int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
40198c2ecf20Sopenharmony_ci				      struct snd_pcm_hw_params_old __user * _oparams)
40208c2ecf20Sopenharmony_ci{
40218c2ecf20Sopenharmony_ci	struct snd_pcm_hw_params *params;
40228c2ecf20Sopenharmony_ci	struct snd_pcm_hw_params_old *oparams = NULL;
40238c2ecf20Sopenharmony_ci	int err;
40248c2ecf20Sopenharmony_ci
40258c2ecf20Sopenharmony_ci	params = kmalloc(sizeof(*params), GFP_KERNEL);
40268c2ecf20Sopenharmony_ci	if (!params)
40278c2ecf20Sopenharmony_ci		return -ENOMEM;
40288c2ecf20Sopenharmony_ci
40298c2ecf20Sopenharmony_ci	oparams = memdup_user(_oparams, sizeof(*oparams));
40308c2ecf20Sopenharmony_ci	if (IS_ERR(oparams)) {
40318c2ecf20Sopenharmony_ci		err = PTR_ERR(oparams);
40328c2ecf20Sopenharmony_ci		goto out;
40338c2ecf20Sopenharmony_ci	}
40348c2ecf20Sopenharmony_ci
40358c2ecf20Sopenharmony_ci	snd_pcm_hw_convert_from_old_params(params, oparams);
40368c2ecf20Sopenharmony_ci	err = snd_pcm_hw_params(substream, params);
40378c2ecf20Sopenharmony_ci	if (err < 0)
40388c2ecf20Sopenharmony_ci		goto out_old;
40398c2ecf20Sopenharmony_ci
40408c2ecf20Sopenharmony_ci	snd_pcm_hw_convert_to_old_params(oparams, params);
40418c2ecf20Sopenharmony_ci	if (copy_to_user(_oparams, oparams, sizeof(*oparams)))
40428c2ecf20Sopenharmony_ci		err = -EFAULT;
40438c2ecf20Sopenharmony_ciout_old:
40448c2ecf20Sopenharmony_ci	kfree(oparams);
40458c2ecf20Sopenharmony_ciout:
40468c2ecf20Sopenharmony_ci	kfree(params);
40478c2ecf20Sopenharmony_ci	return err;
40488c2ecf20Sopenharmony_ci}
40498c2ecf20Sopenharmony_ci#endif /* CONFIG_SND_SUPPORT_OLD_API */
40508c2ecf20Sopenharmony_ci
40518c2ecf20Sopenharmony_ci#ifndef CONFIG_MMU
40528c2ecf20Sopenharmony_cistatic unsigned long snd_pcm_get_unmapped_area(struct file *file,
40538c2ecf20Sopenharmony_ci					       unsigned long addr,
40548c2ecf20Sopenharmony_ci					       unsigned long len,
40558c2ecf20Sopenharmony_ci					       unsigned long pgoff,
40568c2ecf20Sopenharmony_ci					       unsigned long flags)
40578c2ecf20Sopenharmony_ci{
40588c2ecf20Sopenharmony_ci	struct snd_pcm_file *pcm_file = file->private_data;
40598c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream = pcm_file->substream;
40608c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
40618c2ecf20Sopenharmony_ci	unsigned long offset = pgoff << PAGE_SHIFT;
40628c2ecf20Sopenharmony_ci
40638c2ecf20Sopenharmony_ci	switch (offset) {
40648c2ecf20Sopenharmony_ci	case SNDRV_PCM_MMAP_OFFSET_STATUS_NEW:
40658c2ecf20Sopenharmony_ci		return (unsigned long)runtime->status;
40668c2ecf20Sopenharmony_ci	case SNDRV_PCM_MMAP_OFFSET_CONTROL_NEW:
40678c2ecf20Sopenharmony_ci		return (unsigned long)runtime->control;
40688c2ecf20Sopenharmony_ci	default:
40698c2ecf20Sopenharmony_ci		return (unsigned long)runtime->dma_area + offset;
40708c2ecf20Sopenharmony_ci	}
40718c2ecf20Sopenharmony_ci}
40728c2ecf20Sopenharmony_ci#else
40738c2ecf20Sopenharmony_ci# define snd_pcm_get_unmapped_area NULL
40748c2ecf20Sopenharmony_ci#endif
40758c2ecf20Sopenharmony_ci
40768c2ecf20Sopenharmony_ci/*
40778c2ecf20Sopenharmony_ci *  Register section
40788c2ecf20Sopenharmony_ci */
40798c2ecf20Sopenharmony_ci
40808c2ecf20Sopenharmony_ciconst struct file_operations snd_pcm_f_ops[2] = {
40818c2ecf20Sopenharmony_ci	{
40828c2ecf20Sopenharmony_ci		.owner =		THIS_MODULE,
40838c2ecf20Sopenharmony_ci		.write =		snd_pcm_write,
40848c2ecf20Sopenharmony_ci		.write_iter =		snd_pcm_writev,
40858c2ecf20Sopenharmony_ci		.open =			snd_pcm_playback_open,
40868c2ecf20Sopenharmony_ci		.release =		snd_pcm_release,
40878c2ecf20Sopenharmony_ci		.llseek =		no_llseek,
40888c2ecf20Sopenharmony_ci		.poll =			snd_pcm_poll,
40898c2ecf20Sopenharmony_ci		.unlocked_ioctl =	snd_pcm_ioctl,
40908c2ecf20Sopenharmony_ci		.compat_ioctl = 	snd_pcm_ioctl_compat,
40918c2ecf20Sopenharmony_ci		.mmap =			snd_pcm_mmap,
40928c2ecf20Sopenharmony_ci		.fasync =		snd_pcm_fasync,
40938c2ecf20Sopenharmony_ci		.get_unmapped_area =	snd_pcm_get_unmapped_area,
40948c2ecf20Sopenharmony_ci	},
40958c2ecf20Sopenharmony_ci	{
40968c2ecf20Sopenharmony_ci		.owner =		THIS_MODULE,
40978c2ecf20Sopenharmony_ci		.read =			snd_pcm_read,
40988c2ecf20Sopenharmony_ci		.read_iter =		snd_pcm_readv,
40998c2ecf20Sopenharmony_ci		.open =			snd_pcm_capture_open,
41008c2ecf20Sopenharmony_ci		.release =		snd_pcm_release,
41018c2ecf20Sopenharmony_ci		.llseek =		no_llseek,
41028c2ecf20Sopenharmony_ci		.poll =			snd_pcm_poll,
41038c2ecf20Sopenharmony_ci		.unlocked_ioctl =	snd_pcm_ioctl,
41048c2ecf20Sopenharmony_ci		.compat_ioctl = 	snd_pcm_ioctl_compat,
41058c2ecf20Sopenharmony_ci		.mmap =			snd_pcm_mmap,
41068c2ecf20Sopenharmony_ci		.fasync =		snd_pcm_fasync,
41078c2ecf20Sopenharmony_ci		.get_unmapped_area =	snd_pcm_get_unmapped_area,
41088c2ecf20Sopenharmony_ci	}
41098c2ecf20Sopenharmony_ci};
4110