18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Timers abstract layer 48c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/delay.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/time.h> 118c2ecf20Sopenharmony_ci#include <linux/mutex.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 168c2ecf20Sopenharmony_ci#include <sound/core.h> 178c2ecf20Sopenharmony_ci#include <sound/timer.h> 188c2ecf20Sopenharmony_ci#include <sound/control.h> 198c2ecf20Sopenharmony_ci#include <sound/info.h> 208c2ecf20Sopenharmony_ci#include <sound/minors.h> 218c2ecf20Sopenharmony_ci#include <sound/initval.h> 228c2ecf20Sopenharmony_ci#include <linux/kmod.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* internal flags */ 258c2ecf20Sopenharmony_ci#define SNDRV_TIMER_IFLG_PAUSED 0x00010000 268c2ecf20Sopenharmony_ci#define SNDRV_TIMER_IFLG_DEAD 0x00020000 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_HRTIMER) 298c2ecf20Sopenharmony_ci#define DEFAULT_TIMER_LIMIT 4 308c2ecf20Sopenharmony_ci#else 318c2ecf20Sopenharmony_ci#define DEFAULT_TIMER_LIMIT 1 328c2ecf20Sopenharmony_ci#endif 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int timer_limit = DEFAULT_TIMER_LIMIT; 358c2ecf20Sopenharmony_cistatic int timer_tstamp_monotonic = 1; 368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>"); 378c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALSA timer interface"); 388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 398c2ecf20Sopenharmony_cimodule_param(timer_limit, int, 0444); 408c2ecf20Sopenharmony_ciMODULE_PARM_DESC(timer_limit, "Maximum global timers in system."); 418c2ecf20Sopenharmony_cimodule_param(timer_tstamp_monotonic, int, 0444); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default)."); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ciMODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER); 458c2ecf20Sopenharmony_ciMODULE_ALIAS("devname:snd/timer"); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cienum timer_tread_format { 488c2ecf20Sopenharmony_ci TREAD_FORMAT_NONE = 0, 498c2ecf20Sopenharmony_ci TREAD_FORMAT_TIME64, 508c2ecf20Sopenharmony_ci TREAD_FORMAT_TIME32, 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistruct snd_timer_tread32 { 548c2ecf20Sopenharmony_ci int event; 558c2ecf20Sopenharmony_ci s32 tstamp_sec; 568c2ecf20Sopenharmony_ci s32 tstamp_nsec; 578c2ecf20Sopenharmony_ci unsigned int val; 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct snd_timer_tread64 { 618c2ecf20Sopenharmony_ci int event; 628c2ecf20Sopenharmony_ci u8 pad1[4]; 638c2ecf20Sopenharmony_ci s64 tstamp_sec; 648c2ecf20Sopenharmony_ci s64 tstamp_nsec; 658c2ecf20Sopenharmony_ci unsigned int val; 668c2ecf20Sopenharmony_ci u8 pad2[4]; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistruct snd_timer_user { 708c2ecf20Sopenharmony_ci struct snd_timer_instance *timeri; 718c2ecf20Sopenharmony_ci int tread; /* enhanced read with timestamps and events */ 728c2ecf20Sopenharmony_ci unsigned long ticks; 738c2ecf20Sopenharmony_ci unsigned long overrun; 748c2ecf20Sopenharmony_ci int qhead; 758c2ecf20Sopenharmony_ci int qtail; 768c2ecf20Sopenharmony_ci int qused; 778c2ecf20Sopenharmony_ci int queue_size; 788c2ecf20Sopenharmony_ci bool disconnected; 798c2ecf20Sopenharmony_ci struct snd_timer_read *queue; 808c2ecf20Sopenharmony_ci struct snd_timer_tread64 *tqueue; 818c2ecf20Sopenharmony_ci spinlock_t qlock; 828c2ecf20Sopenharmony_ci unsigned long last_resolution; 838c2ecf20Sopenharmony_ci unsigned int filter; 848c2ecf20Sopenharmony_ci struct timespec64 tstamp; /* trigger tstamp */ 858c2ecf20Sopenharmony_ci wait_queue_head_t qchange_sleep; 868c2ecf20Sopenharmony_ci struct snd_fasync *fasync; 878c2ecf20Sopenharmony_ci struct mutex ioctl_lock; 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistruct snd_timer_status32 { 918c2ecf20Sopenharmony_ci s32 tstamp_sec; /* Timestamp - last update */ 928c2ecf20Sopenharmony_ci s32 tstamp_nsec; 938c2ecf20Sopenharmony_ci unsigned int resolution; /* current period resolution in ns */ 948c2ecf20Sopenharmony_ci unsigned int lost; /* counter of master tick lost */ 958c2ecf20Sopenharmony_ci unsigned int overrun; /* count of read queue overruns */ 968c2ecf20Sopenharmony_ci unsigned int queue; /* used queue size */ 978c2ecf20Sopenharmony_ci unsigned char reserved[64]; /* reserved */ 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define SNDRV_TIMER_IOCTL_STATUS32 _IOR('T', 0x14, struct snd_timer_status32) 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistruct snd_timer_status64 { 1038c2ecf20Sopenharmony_ci s64 tstamp_sec; /* Timestamp - last update */ 1048c2ecf20Sopenharmony_ci s64 tstamp_nsec; 1058c2ecf20Sopenharmony_ci unsigned int resolution; /* current period resolution in ns */ 1068c2ecf20Sopenharmony_ci unsigned int lost; /* counter of master tick lost */ 1078c2ecf20Sopenharmony_ci unsigned int overrun; /* count of read queue overruns */ 1088c2ecf20Sopenharmony_ci unsigned int queue; /* used queue size */ 1098c2ecf20Sopenharmony_ci unsigned char reserved[64]; /* reserved */ 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#define SNDRV_TIMER_IOCTL_STATUS64 _IOR('T', 0x14, struct snd_timer_status64) 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* list of timers */ 1158c2ecf20Sopenharmony_cistatic LIST_HEAD(snd_timer_list); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* list of slave instances */ 1188c2ecf20Sopenharmony_cistatic LIST_HEAD(snd_timer_slave_list); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* lock for slave active lists */ 1218c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(slave_active_lock); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#define MAX_SLAVE_INSTANCES 1000 1248c2ecf20Sopenharmony_cistatic int num_slaves; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(register_mutex); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int snd_timer_free(struct snd_timer *timer); 1298c2ecf20Sopenharmony_cistatic int snd_timer_dev_free(struct snd_device *device); 1308c2ecf20Sopenharmony_cistatic int snd_timer_dev_register(struct snd_device *device); 1318c2ecf20Sopenharmony_cistatic int snd_timer_dev_disconnect(struct snd_device *device); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* 1368c2ecf20Sopenharmony_ci * create a timer instance with the given owner string. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_cistruct snd_timer_instance *snd_timer_instance_new(const char *owner) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct snd_timer_instance *timeri; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci timeri = kzalloc(sizeof(*timeri), GFP_KERNEL); 1438c2ecf20Sopenharmony_ci if (timeri == NULL) 1448c2ecf20Sopenharmony_ci return NULL; 1458c2ecf20Sopenharmony_ci timeri->owner = kstrdup(owner, GFP_KERNEL); 1468c2ecf20Sopenharmony_ci if (! timeri->owner) { 1478c2ecf20Sopenharmony_ci kfree(timeri); 1488c2ecf20Sopenharmony_ci return NULL; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&timeri->open_list); 1518c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&timeri->active_list); 1528c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&timeri->ack_list); 1538c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&timeri->slave_list_head); 1548c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&timeri->slave_active_head); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return timeri; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_timer_instance_new); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_civoid snd_timer_instance_free(struct snd_timer_instance *timeri) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci if (timeri) { 1638c2ecf20Sopenharmony_ci if (timeri->private_free) 1648c2ecf20Sopenharmony_ci timeri->private_free(timeri); 1658c2ecf20Sopenharmony_ci kfree(timeri->owner); 1668c2ecf20Sopenharmony_ci kfree(timeri); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_timer_instance_free); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* 1728c2ecf20Sopenharmony_ci * find a timer instance from the given timer id 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_cistatic struct snd_timer *snd_timer_find(struct snd_timer_id *tid) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct snd_timer *timer; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci list_for_each_entry(timer, &snd_timer_list, device_list) { 1798c2ecf20Sopenharmony_ci if (timer->tmr_class != tid->dev_class) 1808c2ecf20Sopenharmony_ci continue; 1818c2ecf20Sopenharmony_ci if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD || 1828c2ecf20Sopenharmony_ci timer->tmr_class == SNDRV_TIMER_CLASS_PCM) && 1838c2ecf20Sopenharmony_ci (timer->card == NULL || 1848c2ecf20Sopenharmony_ci timer->card->number != tid->card)) 1858c2ecf20Sopenharmony_ci continue; 1868c2ecf20Sopenharmony_ci if (timer->tmr_device != tid->device) 1878c2ecf20Sopenharmony_ci continue; 1888c2ecf20Sopenharmony_ci if (timer->tmr_subdevice != tid->subdevice) 1898c2ecf20Sopenharmony_ci continue; 1908c2ecf20Sopenharmony_ci return timer; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci return NULL; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci#ifdef CONFIG_MODULES 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic void snd_timer_request(struct snd_timer_id *tid) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci switch (tid->dev_class) { 2008c2ecf20Sopenharmony_ci case SNDRV_TIMER_CLASS_GLOBAL: 2018c2ecf20Sopenharmony_ci if (tid->device < timer_limit) 2028c2ecf20Sopenharmony_ci request_module("snd-timer-%i", tid->device); 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci case SNDRV_TIMER_CLASS_CARD: 2058c2ecf20Sopenharmony_ci case SNDRV_TIMER_CLASS_PCM: 2068c2ecf20Sopenharmony_ci if (tid->card < snd_ecards_limit) 2078c2ecf20Sopenharmony_ci request_module("snd-card-%i", tid->card); 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci default: 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci#endif 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* move the slave if it belongs to the master; return 1 if match */ 2178c2ecf20Sopenharmony_cistatic int check_matching_master_slave(struct snd_timer_instance *master, 2188c2ecf20Sopenharmony_ci struct snd_timer_instance *slave) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci if (slave->slave_class != master->slave_class || 2218c2ecf20Sopenharmony_ci slave->slave_id != master->slave_id) 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci if (master->timer->num_instances >= master->timer->max_instances) 2248c2ecf20Sopenharmony_ci return -EBUSY; 2258c2ecf20Sopenharmony_ci list_move_tail(&slave->open_list, &master->slave_list_head); 2268c2ecf20Sopenharmony_ci master->timer->num_instances++; 2278c2ecf20Sopenharmony_ci spin_lock_irq(&slave_active_lock); 2288c2ecf20Sopenharmony_ci spin_lock(&master->timer->lock); 2298c2ecf20Sopenharmony_ci slave->master = master; 2308c2ecf20Sopenharmony_ci slave->timer = master->timer; 2318c2ecf20Sopenharmony_ci if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) 2328c2ecf20Sopenharmony_ci list_add_tail(&slave->active_list, &master->slave_active_head); 2338c2ecf20Sopenharmony_ci spin_unlock(&master->timer->lock); 2348c2ecf20Sopenharmony_ci spin_unlock_irq(&slave_active_lock); 2358c2ecf20Sopenharmony_ci return 1; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/* 2398c2ecf20Sopenharmony_ci * look for a master instance matching with the slave id of the given slave. 2408c2ecf20Sopenharmony_ci * when found, relink the open_link of the slave. 2418c2ecf20Sopenharmony_ci * 2428c2ecf20Sopenharmony_ci * call this with register_mutex down. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_cistatic int snd_timer_check_slave(struct snd_timer_instance *slave) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct snd_timer *timer; 2478c2ecf20Sopenharmony_ci struct snd_timer_instance *master; 2488c2ecf20Sopenharmony_ci int err = 0; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* FIXME: it's really dumb to look up all entries.. */ 2518c2ecf20Sopenharmony_ci list_for_each_entry(timer, &snd_timer_list, device_list) { 2528c2ecf20Sopenharmony_ci list_for_each_entry(master, &timer->open_list_head, open_list) { 2538c2ecf20Sopenharmony_ci err = check_matching_master_slave(master, slave); 2548c2ecf20Sopenharmony_ci if (err != 0) /* match found or error */ 2558c2ecf20Sopenharmony_ci goto out; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci out: 2598c2ecf20Sopenharmony_ci return err < 0 ? err : 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/* 2638c2ecf20Sopenharmony_ci * look for slave instances matching with the slave id of the given master. 2648c2ecf20Sopenharmony_ci * when found, relink the open_link of slaves. 2658c2ecf20Sopenharmony_ci * 2668c2ecf20Sopenharmony_ci * call this with register_mutex down. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_cistatic int snd_timer_check_master(struct snd_timer_instance *master) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct snd_timer_instance *slave, *tmp; 2718c2ecf20Sopenharmony_ci int err = 0; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* check all pending slaves */ 2748c2ecf20Sopenharmony_ci list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) { 2758c2ecf20Sopenharmony_ci err = check_matching_master_slave(master, slave); 2768c2ecf20Sopenharmony_ci if (err < 0) 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci return err < 0 ? err : 0; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic void snd_timer_close_locked(struct snd_timer_instance *timeri, 2838c2ecf20Sopenharmony_ci struct device **card_devp_to_put); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/* 2868c2ecf20Sopenharmony_ci * open a timer instance 2878c2ecf20Sopenharmony_ci * when opening a master, the slave id must be here given. 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_ciint snd_timer_open(struct snd_timer_instance *timeri, 2908c2ecf20Sopenharmony_ci struct snd_timer_id *tid, 2918c2ecf20Sopenharmony_ci unsigned int slave_id) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct snd_timer *timer; 2948c2ecf20Sopenharmony_ci struct device *card_dev_to_put = NULL; 2958c2ecf20Sopenharmony_ci int err; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 2988c2ecf20Sopenharmony_ci if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) { 2998c2ecf20Sopenharmony_ci /* open a slave instance */ 3008c2ecf20Sopenharmony_ci if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE || 3018c2ecf20Sopenharmony_ci tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) { 3028c2ecf20Sopenharmony_ci pr_debug("ALSA: timer: invalid slave class %i\n", 3038c2ecf20Sopenharmony_ci tid->dev_sclass); 3048c2ecf20Sopenharmony_ci err = -EINVAL; 3058c2ecf20Sopenharmony_ci goto unlock; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci if (num_slaves >= MAX_SLAVE_INSTANCES) { 3088c2ecf20Sopenharmony_ci err = -EBUSY; 3098c2ecf20Sopenharmony_ci goto unlock; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci timeri->slave_class = tid->dev_sclass; 3128c2ecf20Sopenharmony_ci timeri->slave_id = tid->device; 3138c2ecf20Sopenharmony_ci timeri->flags |= SNDRV_TIMER_IFLG_SLAVE; 3148c2ecf20Sopenharmony_ci list_add_tail(&timeri->open_list, &snd_timer_slave_list); 3158c2ecf20Sopenharmony_ci num_slaves++; 3168c2ecf20Sopenharmony_ci err = snd_timer_check_slave(timeri); 3178c2ecf20Sopenharmony_ci goto list_added; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* open a master instance */ 3218c2ecf20Sopenharmony_ci timer = snd_timer_find(tid); 3228c2ecf20Sopenharmony_ci#ifdef CONFIG_MODULES 3238c2ecf20Sopenharmony_ci if (!timer) { 3248c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 3258c2ecf20Sopenharmony_ci snd_timer_request(tid); 3268c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 3278c2ecf20Sopenharmony_ci timer = snd_timer_find(tid); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci#endif 3308c2ecf20Sopenharmony_ci if (!timer) { 3318c2ecf20Sopenharmony_ci err = -ENODEV; 3328c2ecf20Sopenharmony_ci goto unlock; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci if (!list_empty(&timer->open_list_head)) { 3358c2ecf20Sopenharmony_ci struct snd_timer_instance *t = 3368c2ecf20Sopenharmony_ci list_entry(timer->open_list_head.next, 3378c2ecf20Sopenharmony_ci struct snd_timer_instance, open_list); 3388c2ecf20Sopenharmony_ci if (t->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) { 3398c2ecf20Sopenharmony_ci err = -EBUSY; 3408c2ecf20Sopenharmony_ci goto unlock; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci if (timer->num_instances >= timer->max_instances) { 3448c2ecf20Sopenharmony_ci err = -EBUSY; 3458c2ecf20Sopenharmony_ci goto unlock; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci if (!try_module_get(timer->module)) { 3488c2ecf20Sopenharmony_ci err = -EBUSY; 3498c2ecf20Sopenharmony_ci goto unlock; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci /* take a card refcount for safe disconnection */ 3528c2ecf20Sopenharmony_ci if (timer->card) { 3538c2ecf20Sopenharmony_ci get_device(&timer->card->card_dev); 3548c2ecf20Sopenharmony_ci card_dev_to_put = &timer->card->card_dev; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (list_empty(&timer->open_list_head) && timer->hw.open) { 3588c2ecf20Sopenharmony_ci err = timer->hw.open(timer); 3598c2ecf20Sopenharmony_ci if (err) { 3608c2ecf20Sopenharmony_ci module_put(timer->module); 3618c2ecf20Sopenharmony_ci goto unlock; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci timeri->timer = timer; 3668c2ecf20Sopenharmony_ci timeri->slave_class = tid->dev_sclass; 3678c2ecf20Sopenharmony_ci timeri->slave_id = slave_id; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci list_add_tail(&timeri->open_list, &timer->open_list_head); 3708c2ecf20Sopenharmony_ci timer->num_instances++; 3718c2ecf20Sopenharmony_ci err = snd_timer_check_master(timeri); 3728c2ecf20Sopenharmony_cilist_added: 3738c2ecf20Sopenharmony_ci if (err < 0) 3748c2ecf20Sopenharmony_ci snd_timer_close_locked(timeri, &card_dev_to_put); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci unlock: 3778c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 3788c2ecf20Sopenharmony_ci /* put_device() is called after unlock for avoiding deadlock */ 3798c2ecf20Sopenharmony_ci if (err < 0 && card_dev_to_put) 3808c2ecf20Sopenharmony_ci put_device(card_dev_to_put); 3818c2ecf20Sopenharmony_ci return err; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_timer_open); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci/* 3868c2ecf20Sopenharmony_ci * close a timer instance 3878c2ecf20Sopenharmony_ci * call this with register_mutex down. 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_cistatic void snd_timer_close_locked(struct snd_timer_instance *timeri, 3908c2ecf20Sopenharmony_ci struct device **card_devp_to_put) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct snd_timer *timer = timeri->timer; 3938c2ecf20Sopenharmony_ci struct snd_timer_instance *slave, *tmp; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (timer) { 3968c2ecf20Sopenharmony_ci spin_lock_irq(&timer->lock); 3978c2ecf20Sopenharmony_ci timeri->flags |= SNDRV_TIMER_IFLG_DEAD; 3988c2ecf20Sopenharmony_ci spin_unlock_irq(&timer->lock); 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (!list_empty(&timeri->open_list)) { 4028c2ecf20Sopenharmony_ci list_del_init(&timeri->open_list); 4038c2ecf20Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 4048c2ecf20Sopenharmony_ci num_slaves--; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* force to stop the timer */ 4088c2ecf20Sopenharmony_ci snd_timer_stop(timeri); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (timer) { 4118c2ecf20Sopenharmony_ci timer->num_instances--; 4128c2ecf20Sopenharmony_ci /* wait, until the active callback is finished */ 4138c2ecf20Sopenharmony_ci spin_lock_irq(&timer->lock); 4148c2ecf20Sopenharmony_ci while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { 4158c2ecf20Sopenharmony_ci spin_unlock_irq(&timer->lock); 4168c2ecf20Sopenharmony_ci udelay(10); 4178c2ecf20Sopenharmony_ci spin_lock_irq(&timer->lock); 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci spin_unlock_irq(&timer->lock); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* remove slave links */ 4228c2ecf20Sopenharmony_ci spin_lock_irq(&slave_active_lock); 4238c2ecf20Sopenharmony_ci spin_lock(&timer->lock); 4248c2ecf20Sopenharmony_ci timeri->timer = NULL; 4258c2ecf20Sopenharmony_ci list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head, 4268c2ecf20Sopenharmony_ci open_list) { 4278c2ecf20Sopenharmony_ci list_move_tail(&slave->open_list, &snd_timer_slave_list); 4288c2ecf20Sopenharmony_ci timer->num_instances--; 4298c2ecf20Sopenharmony_ci slave->master = NULL; 4308c2ecf20Sopenharmony_ci slave->timer = NULL; 4318c2ecf20Sopenharmony_ci list_del_init(&slave->ack_list); 4328c2ecf20Sopenharmony_ci list_del_init(&slave->active_list); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci spin_unlock(&timer->lock); 4358c2ecf20Sopenharmony_ci spin_unlock_irq(&slave_active_lock); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* slave doesn't need to release timer resources below */ 4388c2ecf20Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 4398c2ecf20Sopenharmony_ci timer = NULL; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (timer) { 4438c2ecf20Sopenharmony_ci if (list_empty(&timer->open_list_head) && timer->hw.close) 4448c2ecf20Sopenharmony_ci timer->hw.close(timer); 4458c2ecf20Sopenharmony_ci /* release a card refcount for safe disconnection */ 4468c2ecf20Sopenharmony_ci if (timer->card) 4478c2ecf20Sopenharmony_ci *card_devp_to_put = &timer->card->card_dev; 4488c2ecf20Sopenharmony_ci module_put(timer->module); 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci/* 4538c2ecf20Sopenharmony_ci * close a timer instance 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_civoid snd_timer_close(struct snd_timer_instance *timeri) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct device *card_dev_to_put = NULL; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (snd_BUG_ON(!timeri)) 4608c2ecf20Sopenharmony_ci return; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 4638c2ecf20Sopenharmony_ci snd_timer_close_locked(timeri, &card_dev_to_put); 4648c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 4658c2ecf20Sopenharmony_ci /* put_device() is called after unlock for avoiding deadlock */ 4668c2ecf20Sopenharmony_ci if (card_dev_to_put) 4678c2ecf20Sopenharmony_ci put_device(card_dev_to_put); 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_timer_close); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic unsigned long snd_timer_hw_resolution(struct snd_timer *timer) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci if (timer->hw.c_resolution) 4748c2ecf20Sopenharmony_ci return timer->hw.c_resolution(timer); 4758c2ecf20Sopenharmony_ci else 4768c2ecf20Sopenharmony_ci return timer->hw.resolution; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ciunsigned long snd_timer_resolution(struct snd_timer_instance *timeri) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct snd_timer * timer; 4828c2ecf20Sopenharmony_ci unsigned long ret = 0; 4838c2ecf20Sopenharmony_ci unsigned long flags; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (timeri == NULL) 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci timer = timeri->timer; 4888c2ecf20Sopenharmony_ci if (timer) { 4898c2ecf20Sopenharmony_ci spin_lock_irqsave(&timer->lock, flags); 4908c2ecf20Sopenharmony_ci ret = snd_timer_hw_resolution(timer); 4918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&timer->lock, flags); 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci return ret; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_timer_resolution); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic void snd_timer_notify1(struct snd_timer_instance *ti, int event) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct snd_timer *timer = ti->timer; 5008c2ecf20Sopenharmony_ci unsigned long resolution = 0; 5018c2ecf20Sopenharmony_ci struct snd_timer_instance *ts; 5028c2ecf20Sopenharmony_ci struct timespec64 tstamp; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (timer_tstamp_monotonic) 5058c2ecf20Sopenharmony_ci ktime_get_ts64(&tstamp); 5068c2ecf20Sopenharmony_ci else 5078c2ecf20Sopenharmony_ci ktime_get_real_ts64(&tstamp); 5088c2ecf20Sopenharmony_ci if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START || 5098c2ecf20Sopenharmony_ci event > SNDRV_TIMER_EVENT_PAUSE)) 5108c2ecf20Sopenharmony_ci return; 5118c2ecf20Sopenharmony_ci if (timer && 5128c2ecf20Sopenharmony_ci (event == SNDRV_TIMER_EVENT_START || 5138c2ecf20Sopenharmony_ci event == SNDRV_TIMER_EVENT_CONTINUE)) 5148c2ecf20Sopenharmony_ci resolution = snd_timer_hw_resolution(timer); 5158c2ecf20Sopenharmony_ci if (ti->ccallback) 5168c2ecf20Sopenharmony_ci ti->ccallback(ti, event, &tstamp, resolution); 5178c2ecf20Sopenharmony_ci if (ti->flags & SNDRV_TIMER_IFLG_SLAVE) 5188c2ecf20Sopenharmony_ci return; 5198c2ecf20Sopenharmony_ci if (timer == NULL) 5208c2ecf20Sopenharmony_ci return; 5218c2ecf20Sopenharmony_ci if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 5228c2ecf20Sopenharmony_ci return; 5238c2ecf20Sopenharmony_ci event += 10; /* convert to SNDRV_TIMER_EVENT_MXXX */ 5248c2ecf20Sopenharmony_ci list_for_each_entry(ts, &ti->slave_active_head, active_list) 5258c2ecf20Sopenharmony_ci if (ts->ccallback) 5268c2ecf20Sopenharmony_ci ts->ccallback(ts, event, &tstamp, resolution); 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci/* start/continue a master timer */ 5308c2ecf20Sopenharmony_cistatic int snd_timer_start1(struct snd_timer_instance *timeri, 5318c2ecf20Sopenharmony_ci bool start, unsigned long ticks) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct snd_timer *timer; 5348c2ecf20Sopenharmony_ci int result; 5358c2ecf20Sopenharmony_ci unsigned long flags; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci timer = timeri->timer; 5388c2ecf20Sopenharmony_ci if (!timer) 5398c2ecf20Sopenharmony_ci return -EINVAL; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci spin_lock_irqsave(&timer->lock, flags); 5428c2ecf20Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { 5438c2ecf20Sopenharmony_ci result = -EINVAL; 5448c2ecf20Sopenharmony_ci goto unlock; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci if (timer->card && timer->card->shutdown) { 5478c2ecf20Sopenharmony_ci result = -ENODEV; 5488c2ecf20Sopenharmony_ci goto unlock; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | 5518c2ecf20Sopenharmony_ci SNDRV_TIMER_IFLG_START)) { 5528c2ecf20Sopenharmony_ci result = -EBUSY; 5538c2ecf20Sopenharmony_ci goto unlock; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (start) 5578c2ecf20Sopenharmony_ci timeri->ticks = timeri->cticks = ticks; 5588c2ecf20Sopenharmony_ci else if (!timeri->cticks) 5598c2ecf20Sopenharmony_ci timeri->cticks = 1; 5608c2ecf20Sopenharmony_ci timeri->pticks = 0; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci list_move_tail(&timeri->active_list, &timer->active_list_head); 5638c2ecf20Sopenharmony_ci if (timer->running) { 5648c2ecf20Sopenharmony_ci if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 5658c2ecf20Sopenharmony_ci goto __start_now; 5668c2ecf20Sopenharmony_ci timer->flags |= SNDRV_TIMER_FLG_RESCHED; 5678c2ecf20Sopenharmony_ci timeri->flags |= SNDRV_TIMER_IFLG_START; 5688c2ecf20Sopenharmony_ci result = 1; /* delayed start */ 5698c2ecf20Sopenharmony_ci } else { 5708c2ecf20Sopenharmony_ci if (start) 5718c2ecf20Sopenharmony_ci timer->sticks = ticks; 5728c2ecf20Sopenharmony_ci timer->hw.start(timer); 5738c2ecf20Sopenharmony_ci __start_now: 5748c2ecf20Sopenharmony_ci timer->running++; 5758c2ecf20Sopenharmony_ci timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; 5768c2ecf20Sopenharmony_ci result = 0; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : 5798c2ecf20Sopenharmony_ci SNDRV_TIMER_EVENT_CONTINUE); 5808c2ecf20Sopenharmony_ci unlock: 5818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&timer->lock, flags); 5828c2ecf20Sopenharmony_ci return result; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci/* start/continue a slave timer */ 5868c2ecf20Sopenharmony_cistatic int snd_timer_start_slave(struct snd_timer_instance *timeri, 5878c2ecf20Sopenharmony_ci bool start) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci unsigned long flags; 5908c2ecf20Sopenharmony_ci int err; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci spin_lock_irqsave(&slave_active_lock, flags); 5938c2ecf20Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { 5948c2ecf20Sopenharmony_ci err = -EINVAL; 5958c2ecf20Sopenharmony_ci goto unlock; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { 5988c2ecf20Sopenharmony_ci err = -EBUSY; 5998c2ecf20Sopenharmony_ci goto unlock; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; 6028c2ecf20Sopenharmony_ci if (timeri->master && timeri->timer) { 6038c2ecf20Sopenharmony_ci spin_lock(&timeri->timer->lock); 6048c2ecf20Sopenharmony_ci list_add_tail(&timeri->active_list, 6058c2ecf20Sopenharmony_ci &timeri->master->slave_active_head); 6068c2ecf20Sopenharmony_ci snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : 6078c2ecf20Sopenharmony_ci SNDRV_TIMER_EVENT_CONTINUE); 6088c2ecf20Sopenharmony_ci spin_unlock(&timeri->timer->lock); 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci err = 1; /* delayed start */ 6118c2ecf20Sopenharmony_ci unlock: 6128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&slave_active_lock, flags); 6138c2ecf20Sopenharmony_ci return err; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci/* stop/pause a master timer */ 6178c2ecf20Sopenharmony_cistatic int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct snd_timer *timer; 6208c2ecf20Sopenharmony_ci int result = 0; 6218c2ecf20Sopenharmony_ci unsigned long flags; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci timer = timeri->timer; 6248c2ecf20Sopenharmony_ci if (!timer) 6258c2ecf20Sopenharmony_ci return -EINVAL; 6268c2ecf20Sopenharmony_ci spin_lock_irqsave(&timer->lock, flags); 6278c2ecf20Sopenharmony_ci list_del_init(&timeri->ack_list); 6288c2ecf20Sopenharmony_ci list_del_init(&timeri->active_list); 6298c2ecf20Sopenharmony_ci if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | 6308c2ecf20Sopenharmony_ci SNDRV_TIMER_IFLG_START))) { 6318c2ecf20Sopenharmony_ci result = -EBUSY; 6328c2ecf20Sopenharmony_ci goto unlock; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci if (timer->card && timer->card->shutdown) 6358c2ecf20Sopenharmony_ci goto unlock; 6368c2ecf20Sopenharmony_ci if (stop) { 6378c2ecf20Sopenharmony_ci timeri->cticks = timeri->ticks; 6388c2ecf20Sopenharmony_ci timeri->pticks = 0; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && 6418c2ecf20Sopenharmony_ci !(--timer->running)) { 6428c2ecf20Sopenharmony_ci timer->hw.stop(timer); 6438c2ecf20Sopenharmony_ci if (timer->flags & SNDRV_TIMER_FLG_RESCHED) { 6448c2ecf20Sopenharmony_ci timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; 6458c2ecf20Sopenharmony_ci snd_timer_reschedule(timer, 0); 6468c2ecf20Sopenharmony_ci if (timer->flags & SNDRV_TIMER_FLG_CHANGE) { 6478c2ecf20Sopenharmony_ci timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; 6488c2ecf20Sopenharmony_ci timer->hw.start(timer); 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START); 6538c2ecf20Sopenharmony_ci if (stop) 6548c2ecf20Sopenharmony_ci timeri->flags &= ~SNDRV_TIMER_IFLG_PAUSED; 6558c2ecf20Sopenharmony_ci else 6568c2ecf20Sopenharmony_ci timeri->flags |= SNDRV_TIMER_IFLG_PAUSED; 6578c2ecf20Sopenharmony_ci snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : 6588c2ecf20Sopenharmony_ci SNDRV_TIMER_EVENT_PAUSE); 6598c2ecf20Sopenharmony_ci unlock: 6608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&timer->lock, flags); 6618c2ecf20Sopenharmony_ci return result; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci/* stop/pause a slave timer */ 6658c2ecf20Sopenharmony_cistatic int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci unsigned long flags; 6688c2ecf20Sopenharmony_ci bool running; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci spin_lock_irqsave(&slave_active_lock, flags); 6718c2ecf20Sopenharmony_ci running = timeri->flags & SNDRV_TIMER_IFLG_RUNNING; 6728c2ecf20Sopenharmony_ci timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; 6738c2ecf20Sopenharmony_ci if (timeri->timer) { 6748c2ecf20Sopenharmony_ci spin_lock(&timeri->timer->lock); 6758c2ecf20Sopenharmony_ci list_del_init(&timeri->ack_list); 6768c2ecf20Sopenharmony_ci list_del_init(&timeri->active_list); 6778c2ecf20Sopenharmony_ci if (running) 6788c2ecf20Sopenharmony_ci snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : 6798c2ecf20Sopenharmony_ci SNDRV_TIMER_EVENT_PAUSE); 6808c2ecf20Sopenharmony_ci spin_unlock(&timeri->timer->lock); 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&slave_active_lock, flags); 6838c2ecf20Sopenharmony_ci return running ? 0 : -EBUSY; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci/* 6878c2ecf20Sopenharmony_ci * start the timer instance 6888c2ecf20Sopenharmony_ci */ 6898c2ecf20Sopenharmony_ciint snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci if (timeri == NULL || ticks < 1) 6928c2ecf20Sopenharmony_ci return -EINVAL; 6938c2ecf20Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 6948c2ecf20Sopenharmony_ci return snd_timer_start_slave(timeri, true); 6958c2ecf20Sopenharmony_ci else 6968c2ecf20Sopenharmony_ci return snd_timer_start1(timeri, true, ticks); 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_timer_start); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci/* 7018c2ecf20Sopenharmony_ci * stop the timer instance. 7028c2ecf20Sopenharmony_ci * 7038c2ecf20Sopenharmony_ci * do not call this from the timer callback! 7048c2ecf20Sopenharmony_ci */ 7058c2ecf20Sopenharmony_ciint snd_timer_stop(struct snd_timer_instance *timeri) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 7088c2ecf20Sopenharmony_ci return snd_timer_stop_slave(timeri, true); 7098c2ecf20Sopenharmony_ci else 7108c2ecf20Sopenharmony_ci return snd_timer_stop1(timeri, true); 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_timer_stop); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci/* 7158c2ecf20Sopenharmony_ci * start again.. the tick is kept. 7168c2ecf20Sopenharmony_ci */ 7178c2ecf20Sopenharmony_ciint snd_timer_continue(struct snd_timer_instance *timeri) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci /* timer can continue only after pause */ 7208c2ecf20Sopenharmony_ci if (!(timeri->flags & SNDRV_TIMER_IFLG_PAUSED)) 7218c2ecf20Sopenharmony_ci return -EINVAL; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 7248c2ecf20Sopenharmony_ci return snd_timer_start_slave(timeri, false); 7258c2ecf20Sopenharmony_ci else 7268c2ecf20Sopenharmony_ci return snd_timer_start1(timeri, false, 0); 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_timer_continue); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci/* 7318c2ecf20Sopenharmony_ci * pause.. remember the ticks left 7328c2ecf20Sopenharmony_ci */ 7338c2ecf20Sopenharmony_ciint snd_timer_pause(struct snd_timer_instance * timeri) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 7368c2ecf20Sopenharmony_ci return snd_timer_stop_slave(timeri, false); 7378c2ecf20Sopenharmony_ci else 7388c2ecf20Sopenharmony_ci return snd_timer_stop1(timeri, false); 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_timer_pause); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci/* 7438c2ecf20Sopenharmony_ci * reschedule the timer 7448c2ecf20Sopenharmony_ci * 7458c2ecf20Sopenharmony_ci * start pending instances and check the scheduling ticks. 7468c2ecf20Sopenharmony_ci * when the scheduling ticks is changed set CHANGE flag to reprogram the timer. 7478c2ecf20Sopenharmony_ci */ 7488c2ecf20Sopenharmony_cistatic void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci struct snd_timer_instance *ti; 7518c2ecf20Sopenharmony_ci unsigned long ticks = ~0UL; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci list_for_each_entry(ti, &timer->active_list_head, active_list) { 7548c2ecf20Sopenharmony_ci if (ti->flags & SNDRV_TIMER_IFLG_START) { 7558c2ecf20Sopenharmony_ci ti->flags &= ~SNDRV_TIMER_IFLG_START; 7568c2ecf20Sopenharmony_ci ti->flags |= SNDRV_TIMER_IFLG_RUNNING; 7578c2ecf20Sopenharmony_ci timer->running++; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) { 7608c2ecf20Sopenharmony_ci if (ticks > ti->cticks) 7618c2ecf20Sopenharmony_ci ticks = ti->cticks; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci if (ticks == ~0UL) { 7658c2ecf20Sopenharmony_ci timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; 7668c2ecf20Sopenharmony_ci return; 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci if (ticks > timer->hw.ticks) 7698c2ecf20Sopenharmony_ci ticks = timer->hw.ticks; 7708c2ecf20Sopenharmony_ci if (ticks_left != ticks) 7718c2ecf20Sopenharmony_ci timer->flags |= SNDRV_TIMER_FLG_CHANGE; 7728c2ecf20Sopenharmony_ci timer->sticks = ticks; 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci/* call callbacks in timer ack list */ 7768c2ecf20Sopenharmony_cistatic void snd_timer_process_callbacks(struct snd_timer *timer, 7778c2ecf20Sopenharmony_ci struct list_head *head) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci struct snd_timer_instance *ti; 7808c2ecf20Sopenharmony_ci unsigned long resolution, ticks; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci while (!list_empty(head)) { 7838c2ecf20Sopenharmony_ci ti = list_first_entry(head, struct snd_timer_instance, 7848c2ecf20Sopenharmony_ci ack_list); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci /* remove from ack_list and make empty */ 7878c2ecf20Sopenharmony_ci list_del_init(&ti->ack_list); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) { 7908c2ecf20Sopenharmony_ci ticks = ti->pticks; 7918c2ecf20Sopenharmony_ci ti->pticks = 0; 7928c2ecf20Sopenharmony_ci resolution = ti->resolution; 7938c2ecf20Sopenharmony_ci ti->flags |= SNDRV_TIMER_IFLG_CALLBACK; 7948c2ecf20Sopenharmony_ci spin_unlock(&timer->lock); 7958c2ecf20Sopenharmony_ci if (ti->callback) 7968c2ecf20Sopenharmony_ci ti->callback(ti, resolution, ticks); 7978c2ecf20Sopenharmony_ci spin_lock(&timer->lock); 7988c2ecf20Sopenharmony_ci ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK; 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci} 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci/* clear pending instances from ack list */ 8048c2ecf20Sopenharmony_cistatic void snd_timer_clear_callbacks(struct snd_timer *timer, 8058c2ecf20Sopenharmony_ci struct list_head *head) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci unsigned long flags; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci spin_lock_irqsave(&timer->lock, flags); 8108c2ecf20Sopenharmony_ci while (!list_empty(head)) 8118c2ecf20Sopenharmony_ci list_del_init(head->next); 8128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&timer->lock, flags); 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci/* 8168c2ecf20Sopenharmony_ci * timer work 8178c2ecf20Sopenharmony_ci * 8188c2ecf20Sopenharmony_ci */ 8198c2ecf20Sopenharmony_cistatic void snd_timer_work(struct work_struct *work) 8208c2ecf20Sopenharmony_ci{ 8218c2ecf20Sopenharmony_ci struct snd_timer *timer = container_of(work, struct snd_timer, task_work); 8228c2ecf20Sopenharmony_ci unsigned long flags; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci if (timer->card && timer->card->shutdown) { 8258c2ecf20Sopenharmony_ci snd_timer_clear_callbacks(timer, &timer->sack_list_head); 8268c2ecf20Sopenharmony_ci return; 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci spin_lock_irqsave(&timer->lock, flags); 8308c2ecf20Sopenharmony_ci snd_timer_process_callbacks(timer, &timer->sack_list_head); 8318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&timer->lock, flags); 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci/* 8358c2ecf20Sopenharmony_ci * timer interrupt 8368c2ecf20Sopenharmony_ci * 8378c2ecf20Sopenharmony_ci * ticks_left is usually equal to timer->sticks. 8388c2ecf20Sopenharmony_ci * 8398c2ecf20Sopenharmony_ci */ 8408c2ecf20Sopenharmony_civoid snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci struct snd_timer_instance *ti, *ts, *tmp; 8438c2ecf20Sopenharmony_ci unsigned long resolution; 8448c2ecf20Sopenharmony_ci struct list_head *ack_list_head; 8458c2ecf20Sopenharmony_ci unsigned long flags; 8468c2ecf20Sopenharmony_ci bool use_work = false; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (timer == NULL) 8498c2ecf20Sopenharmony_ci return; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci if (timer->card && timer->card->shutdown) { 8528c2ecf20Sopenharmony_ci snd_timer_clear_callbacks(timer, &timer->ack_list_head); 8538c2ecf20Sopenharmony_ci return; 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci spin_lock_irqsave(&timer->lock, flags); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci /* remember the current resolution */ 8598c2ecf20Sopenharmony_ci resolution = snd_timer_hw_resolution(timer); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci /* loop for all active instances 8628c2ecf20Sopenharmony_ci * Here we cannot use list_for_each_entry because the active_list of a 8638c2ecf20Sopenharmony_ci * processed instance is relinked to done_list_head before the callback 8648c2ecf20Sopenharmony_ci * is called. 8658c2ecf20Sopenharmony_ci */ 8668c2ecf20Sopenharmony_ci list_for_each_entry_safe(ti, tmp, &timer->active_list_head, 8678c2ecf20Sopenharmony_ci active_list) { 8688c2ecf20Sopenharmony_ci if (ti->flags & SNDRV_TIMER_IFLG_DEAD) 8698c2ecf20Sopenharmony_ci continue; 8708c2ecf20Sopenharmony_ci if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING)) 8718c2ecf20Sopenharmony_ci continue; 8728c2ecf20Sopenharmony_ci ti->pticks += ticks_left; 8738c2ecf20Sopenharmony_ci ti->resolution = resolution; 8748c2ecf20Sopenharmony_ci if (ti->cticks < ticks_left) 8758c2ecf20Sopenharmony_ci ti->cticks = 0; 8768c2ecf20Sopenharmony_ci else 8778c2ecf20Sopenharmony_ci ti->cticks -= ticks_left; 8788c2ecf20Sopenharmony_ci if (ti->cticks) /* not expired */ 8798c2ecf20Sopenharmony_ci continue; 8808c2ecf20Sopenharmony_ci if (ti->flags & SNDRV_TIMER_IFLG_AUTO) { 8818c2ecf20Sopenharmony_ci ti->cticks = ti->ticks; 8828c2ecf20Sopenharmony_ci } else { 8838c2ecf20Sopenharmony_ci ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING; 8848c2ecf20Sopenharmony_ci --timer->running; 8858c2ecf20Sopenharmony_ci list_del_init(&ti->active_list); 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci if ((timer->hw.flags & SNDRV_TIMER_HW_WORK) || 8888c2ecf20Sopenharmony_ci (ti->flags & SNDRV_TIMER_IFLG_FAST)) 8898c2ecf20Sopenharmony_ci ack_list_head = &timer->ack_list_head; 8908c2ecf20Sopenharmony_ci else 8918c2ecf20Sopenharmony_ci ack_list_head = &timer->sack_list_head; 8928c2ecf20Sopenharmony_ci if (list_empty(&ti->ack_list)) 8938c2ecf20Sopenharmony_ci list_add_tail(&ti->ack_list, ack_list_head); 8948c2ecf20Sopenharmony_ci list_for_each_entry(ts, &ti->slave_active_head, active_list) { 8958c2ecf20Sopenharmony_ci ts->pticks = ti->pticks; 8968c2ecf20Sopenharmony_ci ts->resolution = resolution; 8978c2ecf20Sopenharmony_ci if (list_empty(&ts->ack_list)) 8988c2ecf20Sopenharmony_ci list_add_tail(&ts->ack_list, ack_list_head); 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci if (timer->flags & SNDRV_TIMER_FLG_RESCHED) 9028c2ecf20Sopenharmony_ci snd_timer_reschedule(timer, timer->sticks); 9038c2ecf20Sopenharmony_ci if (timer->running) { 9048c2ecf20Sopenharmony_ci if (timer->hw.flags & SNDRV_TIMER_HW_STOP) { 9058c2ecf20Sopenharmony_ci timer->hw.stop(timer); 9068c2ecf20Sopenharmony_ci timer->flags |= SNDRV_TIMER_FLG_CHANGE; 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) || 9098c2ecf20Sopenharmony_ci (timer->flags & SNDRV_TIMER_FLG_CHANGE)) { 9108c2ecf20Sopenharmony_ci /* restart timer */ 9118c2ecf20Sopenharmony_ci timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; 9128c2ecf20Sopenharmony_ci timer->hw.start(timer); 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci } else { 9158c2ecf20Sopenharmony_ci timer->hw.stop(timer); 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci /* now process all fast callbacks */ 9198c2ecf20Sopenharmony_ci snd_timer_process_callbacks(timer, &timer->ack_list_head); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci /* do we have any slow callbacks? */ 9228c2ecf20Sopenharmony_ci use_work = !list_empty(&timer->sack_list_head); 9238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&timer->lock, flags); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci if (use_work) 9268c2ecf20Sopenharmony_ci queue_work(system_highpri_wq, &timer->task_work); 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_timer_interrupt); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci/* 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci */ 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ciint snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, 9358c2ecf20Sopenharmony_ci struct snd_timer **rtimer) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci struct snd_timer *timer; 9388c2ecf20Sopenharmony_ci int err; 9398c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 9408c2ecf20Sopenharmony_ci .dev_free = snd_timer_dev_free, 9418c2ecf20Sopenharmony_ci .dev_register = snd_timer_dev_register, 9428c2ecf20Sopenharmony_ci .dev_disconnect = snd_timer_dev_disconnect, 9438c2ecf20Sopenharmony_ci }; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci if (snd_BUG_ON(!tid)) 9468c2ecf20Sopenharmony_ci return -EINVAL; 9478c2ecf20Sopenharmony_ci if (tid->dev_class == SNDRV_TIMER_CLASS_CARD || 9488c2ecf20Sopenharmony_ci tid->dev_class == SNDRV_TIMER_CLASS_PCM) { 9498c2ecf20Sopenharmony_ci if (WARN_ON(!card)) 9508c2ecf20Sopenharmony_ci return -EINVAL; 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci if (rtimer) 9538c2ecf20Sopenharmony_ci *rtimer = NULL; 9548c2ecf20Sopenharmony_ci timer = kzalloc(sizeof(*timer), GFP_KERNEL); 9558c2ecf20Sopenharmony_ci if (!timer) 9568c2ecf20Sopenharmony_ci return -ENOMEM; 9578c2ecf20Sopenharmony_ci timer->tmr_class = tid->dev_class; 9588c2ecf20Sopenharmony_ci timer->card = card; 9598c2ecf20Sopenharmony_ci timer->tmr_device = tid->device; 9608c2ecf20Sopenharmony_ci timer->tmr_subdevice = tid->subdevice; 9618c2ecf20Sopenharmony_ci if (id) 9628c2ecf20Sopenharmony_ci strlcpy(timer->id, id, sizeof(timer->id)); 9638c2ecf20Sopenharmony_ci timer->sticks = 1; 9648c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&timer->device_list); 9658c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&timer->open_list_head); 9668c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&timer->active_list_head); 9678c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&timer->ack_list_head); 9688c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&timer->sack_list_head); 9698c2ecf20Sopenharmony_ci spin_lock_init(&timer->lock); 9708c2ecf20Sopenharmony_ci INIT_WORK(&timer->task_work, snd_timer_work); 9718c2ecf20Sopenharmony_ci timer->max_instances = 1000; /* default limit per timer */ 9728c2ecf20Sopenharmony_ci if (card != NULL) { 9738c2ecf20Sopenharmony_ci timer->module = card->module; 9748c2ecf20Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops); 9758c2ecf20Sopenharmony_ci if (err < 0) { 9768c2ecf20Sopenharmony_ci snd_timer_free(timer); 9778c2ecf20Sopenharmony_ci return err; 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci if (rtimer) 9818c2ecf20Sopenharmony_ci *rtimer = timer; 9828c2ecf20Sopenharmony_ci return 0; 9838c2ecf20Sopenharmony_ci} 9848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_timer_new); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_cistatic int snd_timer_free(struct snd_timer *timer) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci if (!timer) 9898c2ecf20Sopenharmony_ci return 0; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 9928c2ecf20Sopenharmony_ci if (! list_empty(&timer->open_list_head)) { 9938c2ecf20Sopenharmony_ci struct list_head *p, *n; 9948c2ecf20Sopenharmony_ci struct snd_timer_instance *ti; 9958c2ecf20Sopenharmony_ci pr_warn("ALSA: timer %p is busy?\n", timer); 9968c2ecf20Sopenharmony_ci list_for_each_safe(p, n, &timer->open_list_head) { 9978c2ecf20Sopenharmony_ci list_del_init(p); 9988c2ecf20Sopenharmony_ci ti = list_entry(p, struct snd_timer_instance, open_list); 9998c2ecf20Sopenharmony_ci ti->timer = NULL; 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci list_del(&timer->device_list); 10038c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci if (timer->private_free) 10068c2ecf20Sopenharmony_ci timer->private_free(timer); 10078c2ecf20Sopenharmony_ci kfree(timer); 10088c2ecf20Sopenharmony_ci return 0; 10098c2ecf20Sopenharmony_ci} 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_cistatic int snd_timer_dev_free(struct snd_device *device) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci struct snd_timer *timer = device->device_data; 10148c2ecf20Sopenharmony_ci return snd_timer_free(timer); 10158c2ecf20Sopenharmony_ci} 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_cistatic int snd_timer_dev_register(struct snd_device *dev) 10188c2ecf20Sopenharmony_ci{ 10198c2ecf20Sopenharmony_ci struct snd_timer *timer = dev->device_data; 10208c2ecf20Sopenharmony_ci struct snd_timer *timer1; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (snd_BUG_ON(!timer || !timer->hw.start || !timer->hw.stop)) 10238c2ecf20Sopenharmony_ci return -ENXIO; 10248c2ecf20Sopenharmony_ci if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) && 10258c2ecf20Sopenharmony_ci !timer->hw.resolution && timer->hw.c_resolution == NULL) 10268c2ecf20Sopenharmony_ci return -EINVAL; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 10298c2ecf20Sopenharmony_ci list_for_each_entry(timer1, &snd_timer_list, device_list) { 10308c2ecf20Sopenharmony_ci if (timer1->tmr_class > timer->tmr_class) 10318c2ecf20Sopenharmony_ci break; 10328c2ecf20Sopenharmony_ci if (timer1->tmr_class < timer->tmr_class) 10338c2ecf20Sopenharmony_ci continue; 10348c2ecf20Sopenharmony_ci if (timer1->card && timer->card) { 10358c2ecf20Sopenharmony_ci if (timer1->card->number > timer->card->number) 10368c2ecf20Sopenharmony_ci break; 10378c2ecf20Sopenharmony_ci if (timer1->card->number < timer->card->number) 10388c2ecf20Sopenharmony_ci continue; 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci if (timer1->tmr_device > timer->tmr_device) 10418c2ecf20Sopenharmony_ci break; 10428c2ecf20Sopenharmony_ci if (timer1->tmr_device < timer->tmr_device) 10438c2ecf20Sopenharmony_ci continue; 10448c2ecf20Sopenharmony_ci if (timer1->tmr_subdevice > timer->tmr_subdevice) 10458c2ecf20Sopenharmony_ci break; 10468c2ecf20Sopenharmony_ci if (timer1->tmr_subdevice < timer->tmr_subdevice) 10478c2ecf20Sopenharmony_ci continue; 10488c2ecf20Sopenharmony_ci /* conflicts.. */ 10498c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 10508c2ecf20Sopenharmony_ci return -EBUSY; 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci list_add_tail(&timer->device_list, &timer1->device_list); 10538c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 10548c2ecf20Sopenharmony_ci return 0; 10558c2ecf20Sopenharmony_ci} 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_cistatic int snd_timer_dev_disconnect(struct snd_device *device) 10588c2ecf20Sopenharmony_ci{ 10598c2ecf20Sopenharmony_ci struct snd_timer *timer = device->device_data; 10608c2ecf20Sopenharmony_ci struct snd_timer_instance *ti; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 10638c2ecf20Sopenharmony_ci list_del_init(&timer->device_list); 10648c2ecf20Sopenharmony_ci /* wake up pending sleepers */ 10658c2ecf20Sopenharmony_ci list_for_each_entry(ti, &timer->open_list_head, open_list) { 10668c2ecf20Sopenharmony_ci if (ti->disconnect) 10678c2ecf20Sopenharmony_ci ti->disconnect(ti); 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 10708c2ecf20Sopenharmony_ci return 0; 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_civoid snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tstamp) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci unsigned long flags; 10768c2ecf20Sopenharmony_ci unsigned long resolution = 0; 10778c2ecf20Sopenharmony_ci struct snd_timer_instance *ti, *ts; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci if (timer->card && timer->card->shutdown) 10808c2ecf20Sopenharmony_ci return; 10818c2ecf20Sopenharmony_ci if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)) 10828c2ecf20Sopenharmony_ci return; 10838c2ecf20Sopenharmony_ci if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART || 10848c2ecf20Sopenharmony_ci event > SNDRV_TIMER_EVENT_MRESUME)) 10858c2ecf20Sopenharmony_ci return; 10868c2ecf20Sopenharmony_ci spin_lock_irqsave(&timer->lock, flags); 10878c2ecf20Sopenharmony_ci if (event == SNDRV_TIMER_EVENT_MSTART || 10888c2ecf20Sopenharmony_ci event == SNDRV_TIMER_EVENT_MCONTINUE || 10898c2ecf20Sopenharmony_ci event == SNDRV_TIMER_EVENT_MRESUME) 10908c2ecf20Sopenharmony_ci resolution = snd_timer_hw_resolution(timer); 10918c2ecf20Sopenharmony_ci list_for_each_entry(ti, &timer->active_list_head, active_list) { 10928c2ecf20Sopenharmony_ci if (ti->ccallback) 10938c2ecf20Sopenharmony_ci ti->ccallback(ti, event, tstamp, resolution); 10948c2ecf20Sopenharmony_ci list_for_each_entry(ts, &ti->slave_active_head, active_list) 10958c2ecf20Sopenharmony_ci if (ts->ccallback) 10968c2ecf20Sopenharmony_ci ts->ccallback(ts, event, tstamp, resolution); 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&timer->lock, flags); 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_timer_notify); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci/* 11038c2ecf20Sopenharmony_ci * exported functions for global timers 11048c2ecf20Sopenharmony_ci */ 11058c2ecf20Sopenharmony_ciint snd_timer_global_new(char *id, int device, struct snd_timer **rtimer) 11068c2ecf20Sopenharmony_ci{ 11078c2ecf20Sopenharmony_ci struct snd_timer_id tid; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; 11108c2ecf20Sopenharmony_ci tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; 11118c2ecf20Sopenharmony_ci tid.card = -1; 11128c2ecf20Sopenharmony_ci tid.device = device; 11138c2ecf20Sopenharmony_ci tid.subdevice = 0; 11148c2ecf20Sopenharmony_ci return snd_timer_new(NULL, id, &tid, rtimer); 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_timer_global_new); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ciint snd_timer_global_free(struct snd_timer *timer) 11198c2ecf20Sopenharmony_ci{ 11208c2ecf20Sopenharmony_ci return snd_timer_free(timer); 11218c2ecf20Sopenharmony_ci} 11228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_timer_global_free); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ciint snd_timer_global_register(struct snd_timer *timer) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci struct snd_device dev; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci memset(&dev, 0, sizeof(dev)); 11298c2ecf20Sopenharmony_ci dev.device_data = timer; 11308c2ecf20Sopenharmony_ci return snd_timer_dev_register(&dev); 11318c2ecf20Sopenharmony_ci} 11328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_timer_global_register); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci/* 11358c2ecf20Sopenharmony_ci * System timer 11368c2ecf20Sopenharmony_ci */ 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_cistruct snd_timer_system_private { 11398c2ecf20Sopenharmony_ci struct timer_list tlist; 11408c2ecf20Sopenharmony_ci struct snd_timer *snd_timer; 11418c2ecf20Sopenharmony_ci unsigned long last_expires; 11428c2ecf20Sopenharmony_ci unsigned long last_jiffies; 11438c2ecf20Sopenharmony_ci unsigned long correction; 11448c2ecf20Sopenharmony_ci}; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_cistatic void snd_timer_s_function(struct timer_list *t) 11478c2ecf20Sopenharmony_ci{ 11488c2ecf20Sopenharmony_ci struct snd_timer_system_private *priv = from_timer(priv, t, 11498c2ecf20Sopenharmony_ci tlist); 11508c2ecf20Sopenharmony_ci struct snd_timer *timer = priv->snd_timer; 11518c2ecf20Sopenharmony_ci unsigned long jiff = jiffies; 11528c2ecf20Sopenharmony_ci if (time_after(jiff, priv->last_expires)) 11538c2ecf20Sopenharmony_ci priv->correction += (long)jiff - (long)priv->last_expires; 11548c2ecf20Sopenharmony_ci snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies); 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_cistatic int snd_timer_s_start(struct snd_timer * timer) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci struct snd_timer_system_private *priv; 11608c2ecf20Sopenharmony_ci unsigned long njiff; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci priv = (struct snd_timer_system_private *) timer->private_data; 11638c2ecf20Sopenharmony_ci njiff = (priv->last_jiffies = jiffies); 11648c2ecf20Sopenharmony_ci if (priv->correction > timer->sticks - 1) { 11658c2ecf20Sopenharmony_ci priv->correction -= timer->sticks - 1; 11668c2ecf20Sopenharmony_ci njiff++; 11678c2ecf20Sopenharmony_ci } else { 11688c2ecf20Sopenharmony_ci njiff += timer->sticks - priv->correction; 11698c2ecf20Sopenharmony_ci priv->correction = 0; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci priv->last_expires = njiff; 11728c2ecf20Sopenharmony_ci mod_timer(&priv->tlist, njiff); 11738c2ecf20Sopenharmony_ci return 0; 11748c2ecf20Sopenharmony_ci} 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_cistatic int snd_timer_s_stop(struct snd_timer * timer) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci struct snd_timer_system_private *priv; 11798c2ecf20Sopenharmony_ci unsigned long jiff; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci priv = (struct snd_timer_system_private *) timer->private_data; 11828c2ecf20Sopenharmony_ci del_timer(&priv->tlist); 11838c2ecf20Sopenharmony_ci jiff = jiffies; 11848c2ecf20Sopenharmony_ci if (time_before(jiff, priv->last_expires)) 11858c2ecf20Sopenharmony_ci timer->sticks = priv->last_expires - jiff; 11868c2ecf20Sopenharmony_ci else 11878c2ecf20Sopenharmony_ci timer->sticks = 1; 11888c2ecf20Sopenharmony_ci priv->correction = 0; 11898c2ecf20Sopenharmony_ci return 0; 11908c2ecf20Sopenharmony_ci} 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_cistatic int snd_timer_s_close(struct snd_timer *timer) 11938c2ecf20Sopenharmony_ci{ 11948c2ecf20Sopenharmony_ci struct snd_timer_system_private *priv; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci priv = (struct snd_timer_system_private *)timer->private_data; 11978c2ecf20Sopenharmony_ci del_timer_sync(&priv->tlist); 11988c2ecf20Sopenharmony_ci return 0; 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_cistatic const struct snd_timer_hardware snd_timer_system = 12028c2ecf20Sopenharmony_ci{ 12038c2ecf20Sopenharmony_ci .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_WORK, 12048c2ecf20Sopenharmony_ci .resolution = 1000000000L / HZ, 12058c2ecf20Sopenharmony_ci .ticks = 10000000L, 12068c2ecf20Sopenharmony_ci .close = snd_timer_s_close, 12078c2ecf20Sopenharmony_ci .start = snd_timer_s_start, 12088c2ecf20Sopenharmony_ci .stop = snd_timer_s_stop 12098c2ecf20Sopenharmony_ci}; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_cistatic void snd_timer_free_system(struct snd_timer *timer) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci kfree(timer->private_data); 12148c2ecf20Sopenharmony_ci} 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_cistatic int snd_timer_register_system(void) 12178c2ecf20Sopenharmony_ci{ 12188c2ecf20Sopenharmony_ci struct snd_timer *timer; 12198c2ecf20Sopenharmony_ci struct snd_timer_system_private *priv; 12208c2ecf20Sopenharmony_ci int err; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer); 12238c2ecf20Sopenharmony_ci if (err < 0) 12248c2ecf20Sopenharmony_ci return err; 12258c2ecf20Sopenharmony_ci strcpy(timer->name, "system timer"); 12268c2ecf20Sopenharmony_ci timer->hw = snd_timer_system; 12278c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 12288c2ecf20Sopenharmony_ci if (priv == NULL) { 12298c2ecf20Sopenharmony_ci snd_timer_free(timer); 12308c2ecf20Sopenharmony_ci return -ENOMEM; 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci priv->snd_timer = timer; 12338c2ecf20Sopenharmony_ci timer_setup(&priv->tlist, snd_timer_s_function, 0); 12348c2ecf20Sopenharmony_ci timer->private_data = priv; 12358c2ecf20Sopenharmony_ci timer->private_free = snd_timer_free_system; 12368c2ecf20Sopenharmony_ci return snd_timer_global_register(timer); 12378c2ecf20Sopenharmony_ci} 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_PROC_FS 12408c2ecf20Sopenharmony_ci/* 12418c2ecf20Sopenharmony_ci * Info interface 12428c2ecf20Sopenharmony_ci */ 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_cistatic void snd_timer_proc_read(struct snd_info_entry *entry, 12458c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 12468c2ecf20Sopenharmony_ci{ 12478c2ecf20Sopenharmony_ci struct snd_timer *timer; 12488c2ecf20Sopenharmony_ci struct snd_timer_instance *ti; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 12518c2ecf20Sopenharmony_ci list_for_each_entry(timer, &snd_timer_list, device_list) { 12528c2ecf20Sopenharmony_ci if (timer->card && timer->card->shutdown) 12538c2ecf20Sopenharmony_ci continue; 12548c2ecf20Sopenharmony_ci switch (timer->tmr_class) { 12558c2ecf20Sopenharmony_ci case SNDRV_TIMER_CLASS_GLOBAL: 12568c2ecf20Sopenharmony_ci snd_iprintf(buffer, "G%i: ", timer->tmr_device); 12578c2ecf20Sopenharmony_ci break; 12588c2ecf20Sopenharmony_ci case SNDRV_TIMER_CLASS_CARD: 12598c2ecf20Sopenharmony_ci snd_iprintf(buffer, "C%i-%i: ", 12608c2ecf20Sopenharmony_ci timer->card->number, timer->tmr_device); 12618c2ecf20Sopenharmony_ci break; 12628c2ecf20Sopenharmony_ci case SNDRV_TIMER_CLASS_PCM: 12638c2ecf20Sopenharmony_ci snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number, 12648c2ecf20Sopenharmony_ci timer->tmr_device, timer->tmr_subdevice); 12658c2ecf20Sopenharmony_ci break; 12668c2ecf20Sopenharmony_ci default: 12678c2ecf20Sopenharmony_ci snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class, 12688c2ecf20Sopenharmony_ci timer->card ? timer->card->number : -1, 12698c2ecf20Sopenharmony_ci timer->tmr_device, timer->tmr_subdevice); 12708c2ecf20Sopenharmony_ci } 12718c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%s :", timer->name); 12728c2ecf20Sopenharmony_ci if (timer->hw.resolution) 12738c2ecf20Sopenharmony_ci snd_iprintf(buffer, " %lu.%03luus (%lu ticks)", 12748c2ecf20Sopenharmony_ci timer->hw.resolution / 1000, 12758c2ecf20Sopenharmony_ci timer->hw.resolution % 1000, 12768c2ecf20Sopenharmony_ci timer->hw.ticks); 12778c2ecf20Sopenharmony_ci if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 12788c2ecf20Sopenharmony_ci snd_iprintf(buffer, " SLAVE"); 12798c2ecf20Sopenharmony_ci snd_iprintf(buffer, "\n"); 12808c2ecf20Sopenharmony_ci list_for_each_entry(ti, &timer->open_list_head, open_list) 12818c2ecf20Sopenharmony_ci snd_iprintf(buffer, " Client %s : %s\n", 12828c2ecf20Sopenharmony_ci ti->owner ? ti->owner : "unknown", 12838c2ecf20Sopenharmony_ci (ti->flags & (SNDRV_TIMER_IFLG_START | 12848c2ecf20Sopenharmony_ci SNDRV_TIMER_IFLG_RUNNING)) 12858c2ecf20Sopenharmony_ci ? "running" : "stopped"); 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 12888c2ecf20Sopenharmony_ci} 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_cistatic struct snd_info_entry *snd_timer_proc_entry; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_cistatic void __init snd_timer_proc_init(void) 12938c2ecf20Sopenharmony_ci{ 12948c2ecf20Sopenharmony_ci struct snd_info_entry *entry; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL); 12978c2ecf20Sopenharmony_ci if (entry != NULL) { 12988c2ecf20Sopenharmony_ci entry->c.text.read = snd_timer_proc_read; 12998c2ecf20Sopenharmony_ci if (snd_info_register(entry) < 0) { 13008c2ecf20Sopenharmony_ci snd_info_free_entry(entry); 13018c2ecf20Sopenharmony_ci entry = NULL; 13028c2ecf20Sopenharmony_ci } 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci snd_timer_proc_entry = entry; 13058c2ecf20Sopenharmony_ci} 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_cistatic void __exit snd_timer_proc_done(void) 13088c2ecf20Sopenharmony_ci{ 13098c2ecf20Sopenharmony_ci snd_info_free_entry(snd_timer_proc_entry); 13108c2ecf20Sopenharmony_ci} 13118c2ecf20Sopenharmony_ci#else /* !CONFIG_SND_PROC_FS */ 13128c2ecf20Sopenharmony_ci#define snd_timer_proc_init() 13138c2ecf20Sopenharmony_ci#define snd_timer_proc_done() 13148c2ecf20Sopenharmony_ci#endif 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci/* 13178c2ecf20Sopenharmony_ci * USER SPACE interface 13188c2ecf20Sopenharmony_ci */ 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_cistatic void snd_timer_user_interrupt(struct snd_timer_instance *timeri, 13218c2ecf20Sopenharmony_ci unsigned long resolution, 13228c2ecf20Sopenharmony_ci unsigned long ticks) 13238c2ecf20Sopenharmony_ci{ 13248c2ecf20Sopenharmony_ci struct snd_timer_user *tu = timeri->callback_data; 13258c2ecf20Sopenharmony_ci struct snd_timer_read *r; 13268c2ecf20Sopenharmony_ci int prev; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci spin_lock(&tu->qlock); 13298c2ecf20Sopenharmony_ci if (tu->qused > 0) { 13308c2ecf20Sopenharmony_ci prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; 13318c2ecf20Sopenharmony_ci r = &tu->queue[prev]; 13328c2ecf20Sopenharmony_ci if (r->resolution == resolution) { 13338c2ecf20Sopenharmony_ci r->ticks += ticks; 13348c2ecf20Sopenharmony_ci goto __wake; 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci if (tu->qused >= tu->queue_size) { 13388c2ecf20Sopenharmony_ci tu->overrun++; 13398c2ecf20Sopenharmony_ci } else { 13408c2ecf20Sopenharmony_ci r = &tu->queue[tu->qtail++]; 13418c2ecf20Sopenharmony_ci tu->qtail %= tu->queue_size; 13428c2ecf20Sopenharmony_ci r->resolution = resolution; 13438c2ecf20Sopenharmony_ci r->ticks = ticks; 13448c2ecf20Sopenharmony_ci tu->qused++; 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci __wake: 13478c2ecf20Sopenharmony_ci spin_unlock(&tu->qlock); 13488c2ecf20Sopenharmony_ci snd_kill_fasync(tu->fasync, SIGIO, POLL_IN); 13498c2ecf20Sopenharmony_ci wake_up(&tu->qchange_sleep); 13508c2ecf20Sopenharmony_ci} 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_cistatic void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu, 13538c2ecf20Sopenharmony_ci struct snd_timer_tread64 *tread) 13548c2ecf20Sopenharmony_ci{ 13558c2ecf20Sopenharmony_ci if (tu->qused >= tu->queue_size) { 13568c2ecf20Sopenharmony_ci tu->overrun++; 13578c2ecf20Sopenharmony_ci } else { 13588c2ecf20Sopenharmony_ci memcpy(&tu->tqueue[tu->qtail++], tread, sizeof(*tread)); 13598c2ecf20Sopenharmony_ci tu->qtail %= tu->queue_size; 13608c2ecf20Sopenharmony_ci tu->qused++; 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci} 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_cistatic void snd_timer_user_ccallback(struct snd_timer_instance *timeri, 13658c2ecf20Sopenharmony_ci int event, 13668c2ecf20Sopenharmony_ci struct timespec64 *tstamp, 13678c2ecf20Sopenharmony_ci unsigned long resolution) 13688c2ecf20Sopenharmony_ci{ 13698c2ecf20Sopenharmony_ci struct snd_timer_user *tu = timeri->callback_data; 13708c2ecf20Sopenharmony_ci struct snd_timer_tread64 r1; 13718c2ecf20Sopenharmony_ci unsigned long flags; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci if (event >= SNDRV_TIMER_EVENT_START && 13748c2ecf20Sopenharmony_ci event <= SNDRV_TIMER_EVENT_PAUSE) 13758c2ecf20Sopenharmony_ci tu->tstamp = *tstamp; 13768c2ecf20Sopenharmony_ci if ((tu->filter & (1 << event)) == 0 || !tu->tread) 13778c2ecf20Sopenharmony_ci return; 13788c2ecf20Sopenharmony_ci memset(&r1, 0, sizeof(r1)); 13798c2ecf20Sopenharmony_ci r1.event = event; 13808c2ecf20Sopenharmony_ci r1.tstamp_sec = tstamp->tv_sec; 13818c2ecf20Sopenharmony_ci r1.tstamp_nsec = tstamp->tv_nsec; 13828c2ecf20Sopenharmony_ci r1.val = resolution; 13838c2ecf20Sopenharmony_ci spin_lock_irqsave(&tu->qlock, flags); 13848c2ecf20Sopenharmony_ci snd_timer_user_append_to_tqueue(tu, &r1); 13858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&tu->qlock, flags); 13868c2ecf20Sopenharmony_ci snd_kill_fasync(tu->fasync, SIGIO, POLL_IN); 13878c2ecf20Sopenharmony_ci wake_up(&tu->qchange_sleep); 13888c2ecf20Sopenharmony_ci} 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_cistatic void snd_timer_user_disconnect(struct snd_timer_instance *timeri) 13918c2ecf20Sopenharmony_ci{ 13928c2ecf20Sopenharmony_ci struct snd_timer_user *tu = timeri->callback_data; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci tu->disconnected = true; 13958c2ecf20Sopenharmony_ci wake_up(&tu->qchange_sleep); 13968c2ecf20Sopenharmony_ci} 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_cistatic void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, 13998c2ecf20Sopenharmony_ci unsigned long resolution, 14008c2ecf20Sopenharmony_ci unsigned long ticks) 14018c2ecf20Sopenharmony_ci{ 14028c2ecf20Sopenharmony_ci struct snd_timer_user *tu = timeri->callback_data; 14038c2ecf20Sopenharmony_ci struct snd_timer_tread64 *r, r1; 14048c2ecf20Sopenharmony_ci struct timespec64 tstamp; 14058c2ecf20Sopenharmony_ci int prev, append = 0; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci memset(&r1, 0, sizeof(r1)); 14088c2ecf20Sopenharmony_ci memset(&tstamp, 0, sizeof(tstamp)); 14098c2ecf20Sopenharmony_ci spin_lock(&tu->qlock); 14108c2ecf20Sopenharmony_ci if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) | 14118c2ecf20Sopenharmony_ci (1 << SNDRV_TIMER_EVENT_TICK))) == 0) { 14128c2ecf20Sopenharmony_ci spin_unlock(&tu->qlock); 14138c2ecf20Sopenharmony_ci return; 14148c2ecf20Sopenharmony_ci } 14158c2ecf20Sopenharmony_ci if (tu->last_resolution != resolution || ticks > 0) { 14168c2ecf20Sopenharmony_ci if (timer_tstamp_monotonic) 14178c2ecf20Sopenharmony_ci ktime_get_ts64(&tstamp); 14188c2ecf20Sopenharmony_ci else 14198c2ecf20Sopenharmony_ci ktime_get_real_ts64(&tstamp); 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && 14228c2ecf20Sopenharmony_ci tu->last_resolution != resolution) { 14238c2ecf20Sopenharmony_ci r1.event = SNDRV_TIMER_EVENT_RESOLUTION; 14248c2ecf20Sopenharmony_ci r1.tstamp_sec = tstamp.tv_sec; 14258c2ecf20Sopenharmony_ci r1.tstamp_nsec = tstamp.tv_nsec; 14268c2ecf20Sopenharmony_ci r1.val = resolution; 14278c2ecf20Sopenharmony_ci snd_timer_user_append_to_tqueue(tu, &r1); 14288c2ecf20Sopenharmony_ci tu->last_resolution = resolution; 14298c2ecf20Sopenharmony_ci append++; 14308c2ecf20Sopenharmony_ci } 14318c2ecf20Sopenharmony_ci if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0) 14328c2ecf20Sopenharmony_ci goto __wake; 14338c2ecf20Sopenharmony_ci if (ticks == 0) 14348c2ecf20Sopenharmony_ci goto __wake; 14358c2ecf20Sopenharmony_ci if (tu->qused > 0) { 14368c2ecf20Sopenharmony_ci prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; 14378c2ecf20Sopenharmony_ci r = &tu->tqueue[prev]; 14388c2ecf20Sopenharmony_ci if (r->event == SNDRV_TIMER_EVENT_TICK) { 14398c2ecf20Sopenharmony_ci r->tstamp_sec = tstamp.tv_sec; 14408c2ecf20Sopenharmony_ci r->tstamp_nsec = tstamp.tv_nsec; 14418c2ecf20Sopenharmony_ci r->val += ticks; 14428c2ecf20Sopenharmony_ci append++; 14438c2ecf20Sopenharmony_ci goto __wake; 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci } 14468c2ecf20Sopenharmony_ci r1.event = SNDRV_TIMER_EVENT_TICK; 14478c2ecf20Sopenharmony_ci r1.tstamp_sec = tstamp.tv_sec; 14488c2ecf20Sopenharmony_ci r1.tstamp_nsec = tstamp.tv_nsec; 14498c2ecf20Sopenharmony_ci r1.val = ticks; 14508c2ecf20Sopenharmony_ci snd_timer_user_append_to_tqueue(tu, &r1); 14518c2ecf20Sopenharmony_ci append++; 14528c2ecf20Sopenharmony_ci __wake: 14538c2ecf20Sopenharmony_ci spin_unlock(&tu->qlock); 14548c2ecf20Sopenharmony_ci if (append == 0) 14558c2ecf20Sopenharmony_ci return; 14568c2ecf20Sopenharmony_ci snd_kill_fasync(tu->fasync, SIGIO, POLL_IN); 14578c2ecf20Sopenharmony_ci wake_up(&tu->qchange_sleep); 14588c2ecf20Sopenharmony_ci} 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_cistatic int realloc_user_queue(struct snd_timer_user *tu, int size) 14618c2ecf20Sopenharmony_ci{ 14628c2ecf20Sopenharmony_ci struct snd_timer_read *queue = NULL; 14638c2ecf20Sopenharmony_ci struct snd_timer_tread64 *tqueue = NULL; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci if (tu->tread) { 14668c2ecf20Sopenharmony_ci tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL); 14678c2ecf20Sopenharmony_ci if (!tqueue) 14688c2ecf20Sopenharmony_ci return -ENOMEM; 14698c2ecf20Sopenharmony_ci } else { 14708c2ecf20Sopenharmony_ci queue = kcalloc(size, sizeof(*queue), GFP_KERNEL); 14718c2ecf20Sopenharmony_ci if (!queue) 14728c2ecf20Sopenharmony_ci return -ENOMEM; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci spin_lock_irq(&tu->qlock); 14768c2ecf20Sopenharmony_ci kfree(tu->queue); 14778c2ecf20Sopenharmony_ci kfree(tu->tqueue); 14788c2ecf20Sopenharmony_ci tu->queue_size = size; 14798c2ecf20Sopenharmony_ci tu->queue = queue; 14808c2ecf20Sopenharmony_ci tu->tqueue = tqueue; 14818c2ecf20Sopenharmony_ci tu->qhead = tu->qtail = tu->qused = 0; 14828c2ecf20Sopenharmony_ci spin_unlock_irq(&tu->qlock); 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci return 0; 14858c2ecf20Sopenharmony_ci} 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_cistatic int snd_timer_user_open(struct inode *inode, struct file *file) 14888c2ecf20Sopenharmony_ci{ 14898c2ecf20Sopenharmony_ci struct snd_timer_user *tu; 14908c2ecf20Sopenharmony_ci int err; 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci err = stream_open(inode, file); 14938c2ecf20Sopenharmony_ci if (err < 0) 14948c2ecf20Sopenharmony_ci return err; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci tu = kzalloc(sizeof(*tu), GFP_KERNEL); 14978c2ecf20Sopenharmony_ci if (tu == NULL) 14988c2ecf20Sopenharmony_ci return -ENOMEM; 14998c2ecf20Sopenharmony_ci spin_lock_init(&tu->qlock); 15008c2ecf20Sopenharmony_ci init_waitqueue_head(&tu->qchange_sleep); 15018c2ecf20Sopenharmony_ci mutex_init(&tu->ioctl_lock); 15028c2ecf20Sopenharmony_ci tu->ticks = 1; 15038c2ecf20Sopenharmony_ci if (realloc_user_queue(tu, 128) < 0) { 15048c2ecf20Sopenharmony_ci kfree(tu); 15058c2ecf20Sopenharmony_ci return -ENOMEM; 15068c2ecf20Sopenharmony_ci } 15078c2ecf20Sopenharmony_ci file->private_data = tu; 15088c2ecf20Sopenharmony_ci return 0; 15098c2ecf20Sopenharmony_ci} 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_cistatic int snd_timer_user_release(struct inode *inode, struct file *file) 15128c2ecf20Sopenharmony_ci{ 15138c2ecf20Sopenharmony_ci struct snd_timer_user *tu; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci if (file->private_data) { 15168c2ecf20Sopenharmony_ci tu = file->private_data; 15178c2ecf20Sopenharmony_ci file->private_data = NULL; 15188c2ecf20Sopenharmony_ci mutex_lock(&tu->ioctl_lock); 15198c2ecf20Sopenharmony_ci if (tu->timeri) { 15208c2ecf20Sopenharmony_ci snd_timer_close(tu->timeri); 15218c2ecf20Sopenharmony_ci snd_timer_instance_free(tu->timeri); 15228c2ecf20Sopenharmony_ci } 15238c2ecf20Sopenharmony_ci mutex_unlock(&tu->ioctl_lock); 15248c2ecf20Sopenharmony_ci snd_fasync_free(tu->fasync); 15258c2ecf20Sopenharmony_ci kfree(tu->queue); 15268c2ecf20Sopenharmony_ci kfree(tu->tqueue); 15278c2ecf20Sopenharmony_ci kfree(tu); 15288c2ecf20Sopenharmony_ci } 15298c2ecf20Sopenharmony_ci return 0; 15308c2ecf20Sopenharmony_ci} 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_cistatic void snd_timer_user_zero_id(struct snd_timer_id *id) 15338c2ecf20Sopenharmony_ci{ 15348c2ecf20Sopenharmony_ci id->dev_class = SNDRV_TIMER_CLASS_NONE; 15358c2ecf20Sopenharmony_ci id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; 15368c2ecf20Sopenharmony_ci id->card = -1; 15378c2ecf20Sopenharmony_ci id->device = -1; 15388c2ecf20Sopenharmony_ci id->subdevice = -1; 15398c2ecf20Sopenharmony_ci} 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_cistatic void snd_timer_user_copy_id(struct snd_timer_id *id, struct snd_timer *timer) 15428c2ecf20Sopenharmony_ci{ 15438c2ecf20Sopenharmony_ci id->dev_class = timer->tmr_class; 15448c2ecf20Sopenharmony_ci id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; 15458c2ecf20Sopenharmony_ci id->card = timer->card ? timer->card->number : -1; 15468c2ecf20Sopenharmony_ci id->device = timer->tmr_device; 15478c2ecf20Sopenharmony_ci id->subdevice = timer->tmr_subdevice; 15488c2ecf20Sopenharmony_ci} 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_cistatic int snd_timer_user_next_device(struct snd_timer_id __user *_tid) 15518c2ecf20Sopenharmony_ci{ 15528c2ecf20Sopenharmony_ci struct snd_timer_id id; 15538c2ecf20Sopenharmony_ci struct snd_timer *timer; 15548c2ecf20Sopenharmony_ci struct list_head *p; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci if (copy_from_user(&id, _tid, sizeof(id))) 15578c2ecf20Sopenharmony_ci return -EFAULT; 15588c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 15598c2ecf20Sopenharmony_ci if (id.dev_class < 0) { /* first item */ 15608c2ecf20Sopenharmony_ci if (list_empty(&snd_timer_list)) 15618c2ecf20Sopenharmony_ci snd_timer_user_zero_id(&id); 15628c2ecf20Sopenharmony_ci else { 15638c2ecf20Sopenharmony_ci timer = list_entry(snd_timer_list.next, 15648c2ecf20Sopenharmony_ci struct snd_timer, device_list); 15658c2ecf20Sopenharmony_ci snd_timer_user_copy_id(&id, timer); 15668c2ecf20Sopenharmony_ci } 15678c2ecf20Sopenharmony_ci } else { 15688c2ecf20Sopenharmony_ci switch (id.dev_class) { 15698c2ecf20Sopenharmony_ci case SNDRV_TIMER_CLASS_GLOBAL: 15708c2ecf20Sopenharmony_ci id.device = id.device < 0 ? 0 : id.device + 1; 15718c2ecf20Sopenharmony_ci list_for_each(p, &snd_timer_list) { 15728c2ecf20Sopenharmony_ci timer = list_entry(p, struct snd_timer, device_list); 15738c2ecf20Sopenharmony_ci if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) { 15748c2ecf20Sopenharmony_ci snd_timer_user_copy_id(&id, timer); 15758c2ecf20Sopenharmony_ci break; 15768c2ecf20Sopenharmony_ci } 15778c2ecf20Sopenharmony_ci if (timer->tmr_device >= id.device) { 15788c2ecf20Sopenharmony_ci snd_timer_user_copy_id(&id, timer); 15798c2ecf20Sopenharmony_ci break; 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci } 15828c2ecf20Sopenharmony_ci if (p == &snd_timer_list) 15838c2ecf20Sopenharmony_ci snd_timer_user_zero_id(&id); 15848c2ecf20Sopenharmony_ci break; 15858c2ecf20Sopenharmony_ci case SNDRV_TIMER_CLASS_CARD: 15868c2ecf20Sopenharmony_ci case SNDRV_TIMER_CLASS_PCM: 15878c2ecf20Sopenharmony_ci if (id.card < 0) { 15888c2ecf20Sopenharmony_ci id.card = 0; 15898c2ecf20Sopenharmony_ci } else { 15908c2ecf20Sopenharmony_ci if (id.device < 0) { 15918c2ecf20Sopenharmony_ci id.device = 0; 15928c2ecf20Sopenharmony_ci } else { 15938c2ecf20Sopenharmony_ci if (id.subdevice < 0) 15948c2ecf20Sopenharmony_ci id.subdevice = 0; 15958c2ecf20Sopenharmony_ci else if (id.subdevice < INT_MAX) 15968c2ecf20Sopenharmony_ci id.subdevice++; 15978c2ecf20Sopenharmony_ci } 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci list_for_each(p, &snd_timer_list) { 16008c2ecf20Sopenharmony_ci timer = list_entry(p, struct snd_timer, device_list); 16018c2ecf20Sopenharmony_ci if (timer->tmr_class > id.dev_class) { 16028c2ecf20Sopenharmony_ci snd_timer_user_copy_id(&id, timer); 16038c2ecf20Sopenharmony_ci break; 16048c2ecf20Sopenharmony_ci } 16058c2ecf20Sopenharmony_ci if (timer->tmr_class < id.dev_class) 16068c2ecf20Sopenharmony_ci continue; 16078c2ecf20Sopenharmony_ci if (timer->card->number > id.card) { 16088c2ecf20Sopenharmony_ci snd_timer_user_copy_id(&id, timer); 16098c2ecf20Sopenharmony_ci break; 16108c2ecf20Sopenharmony_ci } 16118c2ecf20Sopenharmony_ci if (timer->card->number < id.card) 16128c2ecf20Sopenharmony_ci continue; 16138c2ecf20Sopenharmony_ci if (timer->tmr_device > id.device) { 16148c2ecf20Sopenharmony_ci snd_timer_user_copy_id(&id, timer); 16158c2ecf20Sopenharmony_ci break; 16168c2ecf20Sopenharmony_ci } 16178c2ecf20Sopenharmony_ci if (timer->tmr_device < id.device) 16188c2ecf20Sopenharmony_ci continue; 16198c2ecf20Sopenharmony_ci if (timer->tmr_subdevice > id.subdevice) { 16208c2ecf20Sopenharmony_ci snd_timer_user_copy_id(&id, timer); 16218c2ecf20Sopenharmony_ci break; 16228c2ecf20Sopenharmony_ci } 16238c2ecf20Sopenharmony_ci if (timer->tmr_subdevice < id.subdevice) 16248c2ecf20Sopenharmony_ci continue; 16258c2ecf20Sopenharmony_ci snd_timer_user_copy_id(&id, timer); 16268c2ecf20Sopenharmony_ci break; 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci if (p == &snd_timer_list) 16298c2ecf20Sopenharmony_ci snd_timer_user_zero_id(&id); 16308c2ecf20Sopenharmony_ci break; 16318c2ecf20Sopenharmony_ci default: 16328c2ecf20Sopenharmony_ci snd_timer_user_zero_id(&id); 16338c2ecf20Sopenharmony_ci } 16348c2ecf20Sopenharmony_ci } 16358c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 16368c2ecf20Sopenharmony_ci if (copy_to_user(_tid, &id, sizeof(*_tid))) 16378c2ecf20Sopenharmony_ci return -EFAULT; 16388c2ecf20Sopenharmony_ci return 0; 16398c2ecf20Sopenharmony_ci} 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_cistatic int snd_timer_user_ginfo(struct file *file, 16428c2ecf20Sopenharmony_ci struct snd_timer_ginfo __user *_ginfo) 16438c2ecf20Sopenharmony_ci{ 16448c2ecf20Sopenharmony_ci struct snd_timer_ginfo *ginfo; 16458c2ecf20Sopenharmony_ci struct snd_timer_id tid; 16468c2ecf20Sopenharmony_ci struct snd_timer *t; 16478c2ecf20Sopenharmony_ci struct list_head *p; 16488c2ecf20Sopenharmony_ci int err = 0; 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci ginfo = memdup_user(_ginfo, sizeof(*ginfo)); 16518c2ecf20Sopenharmony_ci if (IS_ERR(ginfo)) 16528c2ecf20Sopenharmony_ci return PTR_ERR(ginfo); 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci tid = ginfo->tid; 16558c2ecf20Sopenharmony_ci memset(ginfo, 0, sizeof(*ginfo)); 16568c2ecf20Sopenharmony_ci ginfo->tid = tid; 16578c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 16588c2ecf20Sopenharmony_ci t = snd_timer_find(&tid); 16598c2ecf20Sopenharmony_ci if (t != NULL) { 16608c2ecf20Sopenharmony_ci ginfo->card = t->card ? t->card->number : -1; 16618c2ecf20Sopenharmony_ci if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) 16628c2ecf20Sopenharmony_ci ginfo->flags |= SNDRV_TIMER_FLG_SLAVE; 16638c2ecf20Sopenharmony_ci strlcpy(ginfo->id, t->id, sizeof(ginfo->id)); 16648c2ecf20Sopenharmony_ci strlcpy(ginfo->name, t->name, sizeof(ginfo->name)); 16658c2ecf20Sopenharmony_ci ginfo->resolution = t->hw.resolution; 16668c2ecf20Sopenharmony_ci if (t->hw.resolution_min > 0) { 16678c2ecf20Sopenharmony_ci ginfo->resolution_min = t->hw.resolution_min; 16688c2ecf20Sopenharmony_ci ginfo->resolution_max = t->hw.resolution_max; 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci list_for_each(p, &t->open_list_head) { 16718c2ecf20Sopenharmony_ci ginfo->clients++; 16728c2ecf20Sopenharmony_ci } 16738c2ecf20Sopenharmony_ci } else { 16748c2ecf20Sopenharmony_ci err = -ENODEV; 16758c2ecf20Sopenharmony_ci } 16768c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 16778c2ecf20Sopenharmony_ci if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo))) 16788c2ecf20Sopenharmony_ci err = -EFAULT; 16798c2ecf20Sopenharmony_ci kfree(ginfo); 16808c2ecf20Sopenharmony_ci return err; 16818c2ecf20Sopenharmony_ci} 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_cistatic int timer_set_gparams(struct snd_timer_gparams *gparams) 16848c2ecf20Sopenharmony_ci{ 16858c2ecf20Sopenharmony_ci struct snd_timer *t; 16868c2ecf20Sopenharmony_ci int err; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 16898c2ecf20Sopenharmony_ci t = snd_timer_find(&gparams->tid); 16908c2ecf20Sopenharmony_ci if (!t) { 16918c2ecf20Sopenharmony_ci err = -ENODEV; 16928c2ecf20Sopenharmony_ci goto _error; 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci if (!list_empty(&t->open_list_head)) { 16958c2ecf20Sopenharmony_ci err = -EBUSY; 16968c2ecf20Sopenharmony_ci goto _error; 16978c2ecf20Sopenharmony_ci } 16988c2ecf20Sopenharmony_ci if (!t->hw.set_period) { 16998c2ecf20Sopenharmony_ci err = -ENOSYS; 17008c2ecf20Sopenharmony_ci goto _error; 17018c2ecf20Sopenharmony_ci } 17028c2ecf20Sopenharmony_ci err = t->hw.set_period(t, gparams->period_num, gparams->period_den); 17038c2ecf20Sopenharmony_ci_error: 17048c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 17058c2ecf20Sopenharmony_ci return err; 17068c2ecf20Sopenharmony_ci} 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_cistatic int snd_timer_user_gparams(struct file *file, 17098c2ecf20Sopenharmony_ci struct snd_timer_gparams __user *_gparams) 17108c2ecf20Sopenharmony_ci{ 17118c2ecf20Sopenharmony_ci struct snd_timer_gparams gparams; 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci if (copy_from_user(&gparams, _gparams, sizeof(gparams))) 17148c2ecf20Sopenharmony_ci return -EFAULT; 17158c2ecf20Sopenharmony_ci return timer_set_gparams(&gparams); 17168c2ecf20Sopenharmony_ci} 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_cistatic int snd_timer_user_gstatus(struct file *file, 17198c2ecf20Sopenharmony_ci struct snd_timer_gstatus __user *_gstatus) 17208c2ecf20Sopenharmony_ci{ 17218c2ecf20Sopenharmony_ci struct snd_timer_gstatus gstatus; 17228c2ecf20Sopenharmony_ci struct snd_timer_id tid; 17238c2ecf20Sopenharmony_ci struct snd_timer *t; 17248c2ecf20Sopenharmony_ci int err = 0; 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus))) 17278c2ecf20Sopenharmony_ci return -EFAULT; 17288c2ecf20Sopenharmony_ci tid = gstatus.tid; 17298c2ecf20Sopenharmony_ci memset(&gstatus, 0, sizeof(gstatus)); 17308c2ecf20Sopenharmony_ci gstatus.tid = tid; 17318c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 17328c2ecf20Sopenharmony_ci t = snd_timer_find(&tid); 17338c2ecf20Sopenharmony_ci if (t != NULL) { 17348c2ecf20Sopenharmony_ci spin_lock_irq(&t->lock); 17358c2ecf20Sopenharmony_ci gstatus.resolution = snd_timer_hw_resolution(t); 17368c2ecf20Sopenharmony_ci if (t->hw.precise_resolution) { 17378c2ecf20Sopenharmony_ci t->hw.precise_resolution(t, &gstatus.resolution_num, 17388c2ecf20Sopenharmony_ci &gstatus.resolution_den); 17398c2ecf20Sopenharmony_ci } else { 17408c2ecf20Sopenharmony_ci gstatus.resolution_num = gstatus.resolution; 17418c2ecf20Sopenharmony_ci gstatus.resolution_den = 1000000000uL; 17428c2ecf20Sopenharmony_ci } 17438c2ecf20Sopenharmony_ci spin_unlock_irq(&t->lock); 17448c2ecf20Sopenharmony_ci } else { 17458c2ecf20Sopenharmony_ci err = -ENODEV; 17468c2ecf20Sopenharmony_ci } 17478c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 17488c2ecf20Sopenharmony_ci if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus))) 17498c2ecf20Sopenharmony_ci err = -EFAULT; 17508c2ecf20Sopenharmony_ci return err; 17518c2ecf20Sopenharmony_ci} 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_cistatic int snd_timer_user_tselect(struct file *file, 17548c2ecf20Sopenharmony_ci struct snd_timer_select __user *_tselect) 17558c2ecf20Sopenharmony_ci{ 17568c2ecf20Sopenharmony_ci struct snd_timer_user *tu; 17578c2ecf20Sopenharmony_ci struct snd_timer_select tselect; 17588c2ecf20Sopenharmony_ci char str[32]; 17598c2ecf20Sopenharmony_ci int err = 0; 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci tu = file->private_data; 17628c2ecf20Sopenharmony_ci if (tu->timeri) { 17638c2ecf20Sopenharmony_ci snd_timer_close(tu->timeri); 17648c2ecf20Sopenharmony_ci snd_timer_instance_free(tu->timeri); 17658c2ecf20Sopenharmony_ci tu->timeri = NULL; 17668c2ecf20Sopenharmony_ci } 17678c2ecf20Sopenharmony_ci if (copy_from_user(&tselect, _tselect, sizeof(tselect))) { 17688c2ecf20Sopenharmony_ci err = -EFAULT; 17698c2ecf20Sopenharmony_ci goto __err; 17708c2ecf20Sopenharmony_ci } 17718c2ecf20Sopenharmony_ci sprintf(str, "application %i", current->pid); 17728c2ecf20Sopenharmony_ci if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE) 17738c2ecf20Sopenharmony_ci tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION; 17748c2ecf20Sopenharmony_ci tu->timeri = snd_timer_instance_new(str); 17758c2ecf20Sopenharmony_ci if (!tu->timeri) { 17768c2ecf20Sopenharmony_ci err = -ENOMEM; 17778c2ecf20Sopenharmony_ci goto __err; 17788c2ecf20Sopenharmony_ci } 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST; 17818c2ecf20Sopenharmony_ci tu->timeri->callback = tu->tread 17828c2ecf20Sopenharmony_ci ? snd_timer_user_tinterrupt : snd_timer_user_interrupt; 17838c2ecf20Sopenharmony_ci tu->timeri->ccallback = snd_timer_user_ccallback; 17848c2ecf20Sopenharmony_ci tu->timeri->callback_data = (void *)tu; 17858c2ecf20Sopenharmony_ci tu->timeri->disconnect = snd_timer_user_disconnect; 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci err = snd_timer_open(tu->timeri, &tselect.id, current->pid); 17888c2ecf20Sopenharmony_ci if (err < 0) { 17898c2ecf20Sopenharmony_ci snd_timer_instance_free(tu->timeri); 17908c2ecf20Sopenharmony_ci tu->timeri = NULL; 17918c2ecf20Sopenharmony_ci } 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci __err: 17948c2ecf20Sopenharmony_ci return err; 17958c2ecf20Sopenharmony_ci} 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_cistatic int snd_timer_user_info(struct file *file, 17988c2ecf20Sopenharmony_ci struct snd_timer_info __user *_info) 17998c2ecf20Sopenharmony_ci{ 18008c2ecf20Sopenharmony_ci struct snd_timer_user *tu; 18018c2ecf20Sopenharmony_ci struct snd_timer_info *info; 18028c2ecf20Sopenharmony_ci struct snd_timer *t; 18038c2ecf20Sopenharmony_ci int err = 0; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci tu = file->private_data; 18068c2ecf20Sopenharmony_ci if (!tu->timeri) 18078c2ecf20Sopenharmony_ci return -EBADFD; 18088c2ecf20Sopenharmony_ci t = tu->timeri->timer; 18098c2ecf20Sopenharmony_ci if (!t) 18108c2ecf20Sopenharmony_ci return -EBADFD; 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci info = kzalloc(sizeof(*info), GFP_KERNEL); 18138c2ecf20Sopenharmony_ci if (! info) 18148c2ecf20Sopenharmony_ci return -ENOMEM; 18158c2ecf20Sopenharmony_ci info->card = t->card ? t->card->number : -1; 18168c2ecf20Sopenharmony_ci if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) 18178c2ecf20Sopenharmony_ci info->flags |= SNDRV_TIMER_FLG_SLAVE; 18188c2ecf20Sopenharmony_ci strlcpy(info->id, t->id, sizeof(info->id)); 18198c2ecf20Sopenharmony_ci strlcpy(info->name, t->name, sizeof(info->name)); 18208c2ecf20Sopenharmony_ci info->resolution = t->hw.resolution; 18218c2ecf20Sopenharmony_ci if (copy_to_user(_info, info, sizeof(*_info))) 18228c2ecf20Sopenharmony_ci err = -EFAULT; 18238c2ecf20Sopenharmony_ci kfree(info); 18248c2ecf20Sopenharmony_ci return err; 18258c2ecf20Sopenharmony_ci} 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_cistatic int snd_timer_user_params(struct file *file, 18288c2ecf20Sopenharmony_ci struct snd_timer_params __user *_params) 18298c2ecf20Sopenharmony_ci{ 18308c2ecf20Sopenharmony_ci struct snd_timer_user *tu; 18318c2ecf20Sopenharmony_ci struct snd_timer_params params; 18328c2ecf20Sopenharmony_ci struct snd_timer *t; 18338c2ecf20Sopenharmony_ci int err; 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci tu = file->private_data; 18368c2ecf20Sopenharmony_ci if (!tu->timeri) 18378c2ecf20Sopenharmony_ci return -EBADFD; 18388c2ecf20Sopenharmony_ci t = tu->timeri->timer; 18398c2ecf20Sopenharmony_ci if (!t) 18408c2ecf20Sopenharmony_ci return -EBADFD; 18418c2ecf20Sopenharmony_ci if (copy_from_user(¶ms, _params, sizeof(params))) 18428c2ecf20Sopenharmony_ci return -EFAULT; 18438c2ecf20Sopenharmony_ci if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) { 18448c2ecf20Sopenharmony_ci u64 resolution; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci if (params.ticks < 1) { 18478c2ecf20Sopenharmony_ci err = -EINVAL; 18488c2ecf20Sopenharmony_ci goto _end; 18498c2ecf20Sopenharmony_ci } 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci /* Don't allow resolution less than 1ms */ 18528c2ecf20Sopenharmony_ci resolution = snd_timer_resolution(tu->timeri); 18538c2ecf20Sopenharmony_ci resolution *= params.ticks; 18548c2ecf20Sopenharmony_ci if (resolution < 1000000) { 18558c2ecf20Sopenharmony_ci err = -EINVAL; 18568c2ecf20Sopenharmony_ci goto _end; 18578c2ecf20Sopenharmony_ci } 18588c2ecf20Sopenharmony_ci } 18598c2ecf20Sopenharmony_ci if (params.queue_size > 0 && 18608c2ecf20Sopenharmony_ci (params.queue_size < 32 || params.queue_size > 1024)) { 18618c2ecf20Sopenharmony_ci err = -EINVAL; 18628c2ecf20Sopenharmony_ci goto _end; 18638c2ecf20Sopenharmony_ci } 18648c2ecf20Sopenharmony_ci if (params.filter & ~((1<<SNDRV_TIMER_EVENT_RESOLUTION)| 18658c2ecf20Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_TICK)| 18668c2ecf20Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_START)| 18678c2ecf20Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_STOP)| 18688c2ecf20Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_CONTINUE)| 18698c2ecf20Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_PAUSE)| 18708c2ecf20Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_SUSPEND)| 18718c2ecf20Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_RESUME)| 18728c2ecf20Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_MSTART)| 18738c2ecf20Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_MSTOP)| 18748c2ecf20Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_MCONTINUE)| 18758c2ecf20Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_MPAUSE)| 18768c2ecf20Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_MSUSPEND)| 18778c2ecf20Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_MRESUME))) { 18788c2ecf20Sopenharmony_ci err = -EINVAL; 18798c2ecf20Sopenharmony_ci goto _end; 18808c2ecf20Sopenharmony_ci } 18818c2ecf20Sopenharmony_ci snd_timer_stop(tu->timeri); 18828c2ecf20Sopenharmony_ci spin_lock_irq(&t->lock); 18838c2ecf20Sopenharmony_ci tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO| 18848c2ecf20Sopenharmony_ci SNDRV_TIMER_IFLG_EXCLUSIVE| 18858c2ecf20Sopenharmony_ci SNDRV_TIMER_IFLG_EARLY_EVENT); 18868c2ecf20Sopenharmony_ci if (params.flags & SNDRV_TIMER_PSFLG_AUTO) 18878c2ecf20Sopenharmony_ci tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO; 18888c2ecf20Sopenharmony_ci if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE) 18898c2ecf20Sopenharmony_ci tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE; 18908c2ecf20Sopenharmony_ci if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT) 18918c2ecf20Sopenharmony_ci tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT; 18928c2ecf20Sopenharmony_ci spin_unlock_irq(&t->lock); 18938c2ecf20Sopenharmony_ci if (params.queue_size > 0 && 18948c2ecf20Sopenharmony_ci (unsigned int)tu->queue_size != params.queue_size) { 18958c2ecf20Sopenharmony_ci err = realloc_user_queue(tu, params.queue_size); 18968c2ecf20Sopenharmony_ci if (err < 0) 18978c2ecf20Sopenharmony_ci goto _end; 18988c2ecf20Sopenharmony_ci } 18998c2ecf20Sopenharmony_ci spin_lock_irq(&tu->qlock); 19008c2ecf20Sopenharmony_ci tu->qhead = tu->qtail = tu->qused = 0; 19018c2ecf20Sopenharmony_ci if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { 19028c2ecf20Sopenharmony_ci if (tu->tread) { 19038c2ecf20Sopenharmony_ci struct snd_timer_tread64 tread; 19048c2ecf20Sopenharmony_ci memset(&tread, 0, sizeof(tread)); 19058c2ecf20Sopenharmony_ci tread.event = SNDRV_TIMER_EVENT_EARLY; 19068c2ecf20Sopenharmony_ci tread.tstamp_sec = 0; 19078c2ecf20Sopenharmony_ci tread.tstamp_nsec = 0; 19088c2ecf20Sopenharmony_ci tread.val = 0; 19098c2ecf20Sopenharmony_ci snd_timer_user_append_to_tqueue(tu, &tread); 19108c2ecf20Sopenharmony_ci } else { 19118c2ecf20Sopenharmony_ci struct snd_timer_read *r = &tu->queue[0]; 19128c2ecf20Sopenharmony_ci r->resolution = 0; 19138c2ecf20Sopenharmony_ci r->ticks = 0; 19148c2ecf20Sopenharmony_ci tu->qused++; 19158c2ecf20Sopenharmony_ci tu->qtail++; 19168c2ecf20Sopenharmony_ci } 19178c2ecf20Sopenharmony_ci } 19188c2ecf20Sopenharmony_ci tu->filter = params.filter; 19198c2ecf20Sopenharmony_ci tu->ticks = params.ticks; 19208c2ecf20Sopenharmony_ci spin_unlock_irq(&tu->qlock); 19218c2ecf20Sopenharmony_ci err = 0; 19228c2ecf20Sopenharmony_ci _end: 19238c2ecf20Sopenharmony_ci if (copy_to_user(_params, ¶ms, sizeof(params))) 19248c2ecf20Sopenharmony_ci return -EFAULT; 19258c2ecf20Sopenharmony_ci return err; 19268c2ecf20Sopenharmony_ci} 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_cistatic int snd_timer_user_status32(struct file *file, 19298c2ecf20Sopenharmony_ci struct snd_timer_status32 __user *_status) 19308c2ecf20Sopenharmony_ci { 19318c2ecf20Sopenharmony_ci struct snd_timer_user *tu; 19328c2ecf20Sopenharmony_ci struct snd_timer_status32 status; 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci tu = file->private_data; 19358c2ecf20Sopenharmony_ci if (!tu->timeri) 19368c2ecf20Sopenharmony_ci return -EBADFD; 19378c2ecf20Sopenharmony_ci memset(&status, 0, sizeof(status)); 19388c2ecf20Sopenharmony_ci status.tstamp_sec = tu->tstamp.tv_sec; 19398c2ecf20Sopenharmony_ci status.tstamp_nsec = tu->tstamp.tv_nsec; 19408c2ecf20Sopenharmony_ci status.resolution = snd_timer_resolution(tu->timeri); 19418c2ecf20Sopenharmony_ci status.lost = tu->timeri->lost; 19428c2ecf20Sopenharmony_ci status.overrun = tu->overrun; 19438c2ecf20Sopenharmony_ci spin_lock_irq(&tu->qlock); 19448c2ecf20Sopenharmony_ci status.queue = tu->qused; 19458c2ecf20Sopenharmony_ci spin_unlock_irq(&tu->qlock); 19468c2ecf20Sopenharmony_ci if (copy_to_user(_status, &status, sizeof(status))) 19478c2ecf20Sopenharmony_ci return -EFAULT; 19488c2ecf20Sopenharmony_ci return 0; 19498c2ecf20Sopenharmony_ci} 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_cistatic int snd_timer_user_status64(struct file *file, 19528c2ecf20Sopenharmony_ci struct snd_timer_status64 __user *_status) 19538c2ecf20Sopenharmony_ci{ 19548c2ecf20Sopenharmony_ci struct snd_timer_user *tu; 19558c2ecf20Sopenharmony_ci struct snd_timer_status64 status; 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci tu = file->private_data; 19588c2ecf20Sopenharmony_ci if (!tu->timeri) 19598c2ecf20Sopenharmony_ci return -EBADFD; 19608c2ecf20Sopenharmony_ci memset(&status, 0, sizeof(status)); 19618c2ecf20Sopenharmony_ci status.tstamp_sec = tu->tstamp.tv_sec; 19628c2ecf20Sopenharmony_ci status.tstamp_nsec = tu->tstamp.tv_nsec; 19638c2ecf20Sopenharmony_ci status.resolution = snd_timer_resolution(tu->timeri); 19648c2ecf20Sopenharmony_ci status.lost = tu->timeri->lost; 19658c2ecf20Sopenharmony_ci status.overrun = tu->overrun; 19668c2ecf20Sopenharmony_ci spin_lock_irq(&tu->qlock); 19678c2ecf20Sopenharmony_ci status.queue = tu->qused; 19688c2ecf20Sopenharmony_ci spin_unlock_irq(&tu->qlock); 19698c2ecf20Sopenharmony_ci if (copy_to_user(_status, &status, sizeof(status))) 19708c2ecf20Sopenharmony_ci return -EFAULT; 19718c2ecf20Sopenharmony_ci return 0; 19728c2ecf20Sopenharmony_ci} 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_cistatic int snd_timer_user_start(struct file *file) 19758c2ecf20Sopenharmony_ci{ 19768c2ecf20Sopenharmony_ci int err; 19778c2ecf20Sopenharmony_ci struct snd_timer_user *tu; 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci tu = file->private_data; 19808c2ecf20Sopenharmony_ci if (!tu->timeri) 19818c2ecf20Sopenharmony_ci return -EBADFD; 19828c2ecf20Sopenharmony_ci snd_timer_stop(tu->timeri); 19838c2ecf20Sopenharmony_ci tu->timeri->lost = 0; 19848c2ecf20Sopenharmony_ci tu->last_resolution = 0; 19858c2ecf20Sopenharmony_ci err = snd_timer_start(tu->timeri, tu->ticks); 19868c2ecf20Sopenharmony_ci if (err < 0) 19878c2ecf20Sopenharmony_ci return err; 19888c2ecf20Sopenharmony_ci return 0; 19898c2ecf20Sopenharmony_ci} 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_cistatic int snd_timer_user_stop(struct file *file) 19928c2ecf20Sopenharmony_ci{ 19938c2ecf20Sopenharmony_ci int err; 19948c2ecf20Sopenharmony_ci struct snd_timer_user *tu; 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci tu = file->private_data; 19978c2ecf20Sopenharmony_ci if (!tu->timeri) 19988c2ecf20Sopenharmony_ci return -EBADFD; 19998c2ecf20Sopenharmony_ci err = snd_timer_stop(tu->timeri); 20008c2ecf20Sopenharmony_ci if (err < 0) 20018c2ecf20Sopenharmony_ci return err; 20028c2ecf20Sopenharmony_ci return 0; 20038c2ecf20Sopenharmony_ci} 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_cistatic int snd_timer_user_continue(struct file *file) 20068c2ecf20Sopenharmony_ci{ 20078c2ecf20Sopenharmony_ci int err; 20088c2ecf20Sopenharmony_ci struct snd_timer_user *tu; 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ci tu = file->private_data; 20118c2ecf20Sopenharmony_ci if (!tu->timeri) 20128c2ecf20Sopenharmony_ci return -EBADFD; 20138c2ecf20Sopenharmony_ci /* start timer instead of continue if it's not used before */ 20148c2ecf20Sopenharmony_ci if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED)) 20158c2ecf20Sopenharmony_ci return snd_timer_user_start(file); 20168c2ecf20Sopenharmony_ci tu->timeri->lost = 0; 20178c2ecf20Sopenharmony_ci err = snd_timer_continue(tu->timeri); 20188c2ecf20Sopenharmony_ci if (err < 0) 20198c2ecf20Sopenharmony_ci return err; 20208c2ecf20Sopenharmony_ci return 0; 20218c2ecf20Sopenharmony_ci} 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_cistatic int snd_timer_user_pause(struct file *file) 20248c2ecf20Sopenharmony_ci{ 20258c2ecf20Sopenharmony_ci int err; 20268c2ecf20Sopenharmony_ci struct snd_timer_user *tu; 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci tu = file->private_data; 20298c2ecf20Sopenharmony_ci if (!tu->timeri) 20308c2ecf20Sopenharmony_ci return -EBADFD; 20318c2ecf20Sopenharmony_ci err = snd_timer_pause(tu->timeri); 20328c2ecf20Sopenharmony_ci if (err < 0) 20338c2ecf20Sopenharmony_ci return err; 20348c2ecf20Sopenharmony_ci return 0; 20358c2ecf20Sopenharmony_ci} 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_cistatic int snd_timer_user_tread(void __user *argp, struct snd_timer_user *tu, 20388c2ecf20Sopenharmony_ci unsigned int cmd, bool compat) 20398c2ecf20Sopenharmony_ci{ 20408c2ecf20Sopenharmony_ci int __user *p = argp; 20418c2ecf20Sopenharmony_ci int xarg, old_tread; 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci if (tu->timeri) /* too late */ 20448c2ecf20Sopenharmony_ci return -EBUSY; 20458c2ecf20Sopenharmony_ci if (get_user(xarg, p)) 20468c2ecf20Sopenharmony_ci return -EFAULT; 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci old_tread = tu->tread; 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci if (!xarg) 20518c2ecf20Sopenharmony_ci tu->tread = TREAD_FORMAT_NONE; 20528c2ecf20Sopenharmony_ci else if (cmd == SNDRV_TIMER_IOCTL_TREAD64 || 20538c2ecf20Sopenharmony_ci (IS_ENABLED(CONFIG_64BIT) && !compat)) 20548c2ecf20Sopenharmony_ci tu->tread = TREAD_FORMAT_TIME64; 20558c2ecf20Sopenharmony_ci else 20568c2ecf20Sopenharmony_ci tu->tread = TREAD_FORMAT_TIME32; 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci if (tu->tread != old_tread && 20598c2ecf20Sopenharmony_ci realloc_user_queue(tu, tu->queue_size) < 0) { 20608c2ecf20Sopenharmony_ci tu->tread = old_tread; 20618c2ecf20Sopenharmony_ci return -ENOMEM; 20628c2ecf20Sopenharmony_ci } 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci return 0; 20658c2ecf20Sopenharmony_ci} 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_cienum { 20688c2ecf20Sopenharmony_ci SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20), 20698c2ecf20Sopenharmony_ci SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21), 20708c2ecf20Sopenharmony_ci SNDRV_TIMER_IOCTL_CONTINUE_OLD = _IO('T', 0x22), 20718c2ecf20Sopenharmony_ci SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23), 20728c2ecf20Sopenharmony_ci}; 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_cistatic long __snd_timer_user_ioctl(struct file *file, unsigned int cmd, 20758c2ecf20Sopenharmony_ci unsigned long arg, bool compat) 20768c2ecf20Sopenharmony_ci{ 20778c2ecf20Sopenharmony_ci struct snd_timer_user *tu; 20788c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 20798c2ecf20Sopenharmony_ci int __user *p = argp; 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci tu = file->private_data; 20828c2ecf20Sopenharmony_ci switch (cmd) { 20838c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_PVERSION: 20848c2ecf20Sopenharmony_ci return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0; 20858c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_NEXT_DEVICE: 20868c2ecf20Sopenharmony_ci return snd_timer_user_next_device(argp); 20878c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_TREAD_OLD: 20888c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_TREAD64: 20898c2ecf20Sopenharmony_ci return snd_timer_user_tread(argp, tu, cmd, compat); 20908c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_GINFO: 20918c2ecf20Sopenharmony_ci return snd_timer_user_ginfo(file, argp); 20928c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_GPARAMS: 20938c2ecf20Sopenharmony_ci return snd_timer_user_gparams(file, argp); 20948c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_GSTATUS: 20958c2ecf20Sopenharmony_ci return snd_timer_user_gstatus(file, argp); 20968c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_SELECT: 20978c2ecf20Sopenharmony_ci return snd_timer_user_tselect(file, argp); 20988c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_INFO: 20998c2ecf20Sopenharmony_ci return snd_timer_user_info(file, argp); 21008c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_PARAMS: 21018c2ecf20Sopenharmony_ci return snd_timer_user_params(file, argp); 21028c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_STATUS32: 21038c2ecf20Sopenharmony_ci return snd_timer_user_status32(file, argp); 21048c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_STATUS64: 21058c2ecf20Sopenharmony_ci return snd_timer_user_status64(file, argp); 21068c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_START: 21078c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_START_OLD: 21088c2ecf20Sopenharmony_ci return snd_timer_user_start(file); 21098c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_STOP: 21108c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_STOP_OLD: 21118c2ecf20Sopenharmony_ci return snd_timer_user_stop(file); 21128c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_CONTINUE: 21138c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_CONTINUE_OLD: 21148c2ecf20Sopenharmony_ci return snd_timer_user_continue(file); 21158c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_PAUSE: 21168c2ecf20Sopenharmony_ci case SNDRV_TIMER_IOCTL_PAUSE_OLD: 21178c2ecf20Sopenharmony_ci return snd_timer_user_pause(file); 21188c2ecf20Sopenharmony_ci } 21198c2ecf20Sopenharmony_ci return -ENOTTY; 21208c2ecf20Sopenharmony_ci} 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_cistatic long snd_timer_user_ioctl(struct file *file, unsigned int cmd, 21238c2ecf20Sopenharmony_ci unsigned long arg) 21248c2ecf20Sopenharmony_ci{ 21258c2ecf20Sopenharmony_ci struct snd_timer_user *tu = file->private_data; 21268c2ecf20Sopenharmony_ci long ret; 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_ci mutex_lock(&tu->ioctl_lock); 21298c2ecf20Sopenharmony_ci ret = __snd_timer_user_ioctl(file, cmd, arg, false); 21308c2ecf20Sopenharmony_ci mutex_unlock(&tu->ioctl_lock); 21318c2ecf20Sopenharmony_ci return ret; 21328c2ecf20Sopenharmony_ci} 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_cistatic int snd_timer_user_fasync(int fd, struct file * file, int on) 21358c2ecf20Sopenharmony_ci{ 21368c2ecf20Sopenharmony_ci struct snd_timer_user *tu; 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci tu = file->private_data; 21398c2ecf20Sopenharmony_ci return snd_fasync_helper(fd, file, on, &tu->fasync); 21408c2ecf20Sopenharmony_ci} 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_cistatic ssize_t snd_timer_user_read(struct file *file, char __user *buffer, 21438c2ecf20Sopenharmony_ci size_t count, loff_t *offset) 21448c2ecf20Sopenharmony_ci{ 21458c2ecf20Sopenharmony_ci struct snd_timer_tread64 *tread; 21468c2ecf20Sopenharmony_ci struct snd_timer_tread32 tread32; 21478c2ecf20Sopenharmony_ci struct snd_timer_user *tu; 21488c2ecf20Sopenharmony_ci long result = 0, unit; 21498c2ecf20Sopenharmony_ci int qhead; 21508c2ecf20Sopenharmony_ci int err = 0; 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci tu = file->private_data; 21538c2ecf20Sopenharmony_ci switch (tu->tread) { 21548c2ecf20Sopenharmony_ci case TREAD_FORMAT_TIME64: 21558c2ecf20Sopenharmony_ci unit = sizeof(struct snd_timer_tread64); 21568c2ecf20Sopenharmony_ci break; 21578c2ecf20Sopenharmony_ci case TREAD_FORMAT_TIME32: 21588c2ecf20Sopenharmony_ci unit = sizeof(struct snd_timer_tread32); 21598c2ecf20Sopenharmony_ci break; 21608c2ecf20Sopenharmony_ci case TREAD_FORMAT_NONE: 21618c2ecf20Sopenharmony_ci unit = sizeof(struct snd_timer_read); 21628c2ecf20Sopenharmony_ci break; 21638c2ecf20Sopenharmony_ci default: 21648c2ecf20Sopenharmony_ci WARN_ONCE(1, "Corrupt snd_timer_user\n"); 21658c2ecf20Sopenharmony_ci return -ENOTSUPP; 21668c2ecf20Sopenharmony_ci } 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_ci mutex_lock(&tu->ioctl_lock); 21698c2ecf20Sopenharmony_ci spin_lock_irq(&tu->qlock); 21708c2ecf20Sopenharmony_ci while ((long)count - result >= unit) { 21718c2ecf20Sopenharmony_ci while (!tu->qused) { 21728c2ecf20Sopenharmony_ci wait_queue_entry_t wait; 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { 21758c2ecf20Sopenharmony_ci err = -EAGAIN; 21768c2ecf20Sopenharmony_ci goto _error; 21778c2ecf20Sopenharmony_ci } 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 21808c2ecf20Sopenharmony_ci init_waitqueue_entry(&wait, current); 21818c2ecf20Sopenharmony_ci add_wait_queue(&tu->qchange_sleep, &wait); 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci spin_unlock_irq(&tu->qlock); 21848c2ecf20Sopenharmony_ci mutex_unlock(&tu->ioctl_lock); 21858c2ecf20Sopenharmony_ci schedule(); 21868c2ecf20Sopenharmony_ci mutex_lock(&tu->ioctl_lock); 21878c2ecf20Sopenharmony_ci spin_lock_irq(&tu->qlock); 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci remove_wait_queue(&tu->qchange_sleep, &wait); 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci if (tu->disconnected) { 21928c2ecf20Sopenharmony_ci err = -ENODEV; 21938c2ecf20Sopenharmony_ci goto _error; 21948c2ecf20Sopenharmony_ci } 21958c2ecf20Sopenharmony_ci if (signal_pending(current)) { 21968c2ecf20Sopenharmony_ci err = -ERESTARTSYS; 21978c2ecf20Sopenharmony_ci goto _error; 21988c2ecf20Sopenharmony_ci } 21998c2ecf20Sopenharmony_ci } 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci qhead = tu->qhead++; 22028c2ecf20Sopenharmony_ci tu->qhead %= tu->queue_size; 22038c2ecf20Sopenharmony_ci tu->qused--; 22048c2ecf20Sopenharmony_ci spin_unlock_irq(&tu->qlock); 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci tread = &tu->tqueue[qhead]; 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci switch (tu->tread) { 22098c2ecf20Sopenharmony_ci case TREAD_FORMAT_TIME64: 22108c2ecf20Sopenharmony_ci if (copy_to_user(buffer, tread, 22118c2ecf20Sopenharmony_ci sizeof(struct snd_timer_tread64))) 22128c2ecf20Sopenharmony_ci err = -EFAULT; 22138c2ecf20Sopenharmony_ci break; 22148c2ecf20Sopenharmony_ci case TREAD_FORMAT_TIME32: 22158c2ecf20Sopenharmony_ci memset(&tread32, 0, sizeof(tread32)); 22168c2ecf20Sopenharmony_ci tread32 = (struct snd_timer_tread32) { 22178c2ecf20Sopenharmony_ci .event = tread->event, 22188c2ecf20Sopenharmony_ci .tstamp_sec = tread->tstamp_sec, 22198c2ecf20Sopenharmony_ci .tstamp_nsec = tread->tstamp_nsec, 22208c2ecf20Sopenharmony_ci .val = tread->val, 22218c2ecf20Sopenharmony_ci }; 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci if (copy_to_user(buffer, &tread32, sizeof(tread32))) 22248c2ecf20Sopenharmony_ci err = -EFAULT; 22258c2ecf20Sopenharmony_ci break; 22268c2ecf20Sopenharmony_ci case TREAD_FORMAT_NONE: 22278c2ecf20Sopenharmony_ci if (copy_to_user(buffer, &tu->queue[qhead], 22288c2ecf20Sopenharmony_ci sizeof(struct snd_timer_read))) 22298c2ecf20Sopenharmony_ci err = -EFAULT; 22308c2ecf20Sopenharmony_ci break; 22318c2ecf20Sopenharmony_ci default: 22328c2ecf20Sopenharmony_ci err = -ENOTSUPP; 22338c2ecf20Sopenharmony_ci break; 22348c2ecf20Sopenharmony_ci } 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci spin_lock_irq(&tu->qlock); 22378c2ecf20Sopenharmony_ci if (err < 0) 22388c2ecf20Sopenharmony_ci goto _error; 22398c2ecf20Sopenharmony_ci result += unit; 22408c2ecf20Sopenharmony_ci buffer += unit; 22418c2ecf20Sopenharmony_ci } 22428c2ecf20Sopenharmony_ci _error: 22438c2ecf20Sopenharmony_ci spin_unlock_irq(&tu->qlock); 22448c2ecf20Sopenharmony_ci mutex_unlock(&tu->ioctl_lock); 22458c2ecf20Sopenharmony_ci return result > 0 ? result : err; 22468c2ecf20Sopenharmony_ci} 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_cistatic __poll_t snd_timer_user_poll(struct file *file, poll_table * wait) 22498c2ecf20Sopenharmony_ci{ 22508c2ecf20Sopenharmony_ci __poll_t mask; 22518c2ecf20Sopenharmony_ci struct snd_timer_user *tu; 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci tu = file->private_data; 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci poll_wait(file, &tu->qchange_sleep, wait); 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_ci mask = 0; 22588c2ecf20Sopenharmony_ci spin_lock_irq(&tu->qlock); 22598c2ecf20Sopenharmony_ci if (tu->qused) 22608c2ecf20Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 22618c2ecf20Sopenharmony_ci if (tu->disconnected) 22628c2ecf20Sopenharmony_ci mask |= EPOLLERR; 22638c2ecf20Sopenharmony_ci spin_unlock_irq(&tu->qlock); 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci return mask; 22668c2ecf20Sopenharmony_ci} 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 22698c2ecf20Sopenharmony_ci#include "timer_compat.c" 22708c2ecf20Sopenharmony_ci#else 22718c2ecf20Sopenharmony_ci#define snd_timer_user_ioctl_compat NULL 22728c2ecf20Sopenharmony_ci#endif 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_cistatic const struct file_operations snd_timer_f_ops = 22758c2ecf20Sopenharmony_ci{ 22768c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 22778c2ecf20Sopenharmony_ci .read = snd_timer_user_read, 22788c2ecf20Sopenharmony_ci .open = snd_timer_user_open, 22798c2ecf20Sopenharmony_ci .release = snd_timer_user_release, 22808c2ecf20Sopenharmony_ci .llseek = no_llseek, 22818c2ecf20Sopenharmony_ci .poll = snd_timer_user_poll, 22828c2ecf20Sopenharmony_ci .unlocked_ioctl = snd_timer_user_ioctl, 22838c2ecf20Sopenharmony_ci .compat_ioctl = snd_timer_user_ioctl_compat, 22848c2ecf20Sopenharmony_ci .fasync = snd_timer_user_fasync, 22858c2ecf20Sopenharmony_ci}; 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_ci/* unregister the system timer */ 22888c2ecf20Sopenharmony_cistatic void snd_timer_free_all(void) 22898c2ecf20Sopenharmony_ci{ 22908c2ecf20Sopenharmony_ci struct snd_timer *timer, *n; 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci list_for_each_entry_safe(timer, n, &snd_timer_list, device_list) 22938c2ecf20Sopenharmony_ci snd_timer_free(timer); 22948c2ecf20Sopenharmony_ci} 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_cistatic struct device timer_dev; 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci/* 22998c2ecf20Sopenharmony_ci * ENTRY functions 23008c2ecf20Sopenharmony_ci */ 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_cistatic int __init alsa_timer_init(void) 23038c2ecf20Sopenharmony_ci{ 23048c2ecf20Sopenharmony_ci int err; 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_ci snd_device_initialize(&timer_dev, NULL); 23078c2ecf20Sopenharmony_ci dev_set_name(&timer_dev, "timer"); 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci#ifdef SNDRV_OSS_INFO_DEV_TIMERS 23108c2ecf20Sopenharmony_ci snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1, 23118c2ecf20Sopenharmony_ci "system timer"); 23128c2ecf20Sopenharmony_ci#endif 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci err = snd_timer_register_system(); 23158c2ecf20Sopenharmony_ci if (err < 0) { 23168c2ecf20Sopenharmony_ci pr_err("ALSA: unable to register system timer (%i)\n", err); 23178c2ecf20Sopenharmony_ci goto put_timer; 23188c2ecf20Sopenharmony_ci } 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0, 23218c2ecf20Sopenharmony_ci &snd_timer_f_ops, NULL, &timer_dev); 23228c2ecf20Sopenharmony_ci if (err < 0) { 23238c2ecf20Sopenharmony_ci pr_err("ALSA: unable to register timer device (%i)\n", err); 23248c2ecf20Sopenharmony_ci snd_timer_free_all(); 23258c2ecf20Sopenharmony_ci goto put_timer; 23268c2ecf20Sopenharmony_ci } 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci snd_timer_proc_init(); 23298c2ecf20Sopenharmony_ci return 0; 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ciput_timer: 23328c2ecf20Sopenharmony_ci put_device(&timer_dev); 23338c2ecf20Sopenharmony_ci return err; 23348c2ecf20Sopenharmony_ci} 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_cistatic void __exit alsa_timer_exit(void) 23378c2ecf20Sopenharmony_ci{ 23388c2ecf20Sopenharmony_ci snd_unregister_device(&timer_dev); 23398c2ecf20Sopenharmony_ci snd_timer_free_all(); 23408c2ecf20Sopenharmony_ci put_device(&timer_dev); 23418c2ecf20Sopenharmony_ci snd_timer_proc_done(); 23428c2ecf20Sopenharmony_ci#ifdef SNDRV_OSS_INFO_DEV_TIMERS 23438c2ecf20Sopenharmony_ci snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1); 23448c2ecf20Sopenharmony_ci#endif 23458c2ecf20Sopenharmony_ci} 23468c2ecf20Sopenharmony_ci 23478c2ecf20Sopenharmony_cimodule_init(alsa_timer_init) 23488c2ecf20Sopenharmony_cimodule_exit(alsa_timer_exit) 2349