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(¶ms, _params, sizeof(params))) 9558c2ecf20Sopenharmony_ci return -EFAULT; 9568c2ecf20Sopenharmony_ci err = snd_pcm_sw_params(substream, ¶ms); 9578c2ecf20Sopenharmony_ci if (copy_to_user(_params, ¶ms, 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