162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Timers abstract layer 462306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/delay.h> 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/time.h> 1162306a36Sopenharmony_ci#include <linux/mutex.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <linux/sched/signal.h> 1662306a36Sopenharmony_ci#include <sound/core.h> 1762306a36Sopenharmony_ci#include <sound/timer.h> 1862306a36Sopenharmony_ci#include <sound/control.h> 1962306a36Sopenharmony_ci#include <sound/info.h> 2062306a36Sopenharmony_ci#include <sound/minors.h> 2162306a36Sopenharmony_ci#include <sound/initval.h> 2262306a36Sopenharmony_ci#include <linux/kmod.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* internal flags */ 2562306a36Sopenharmony_ci#define SNDRV_TIMER_IFLG_PAUSED 0x00010000 2662306a36Sopenharmony_ci#define SNDRV_TIMER_IFLG_DEAD 0x00020000 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_HRTIMER) 2962306a36Sopenharmony_ci#define DEFAULT_TIMER_LIMIT 4 3062306a36Sopenharmony_ci#else 3162306a36Sopenharmony_ci#define DEFAULT_TIMER_LIMIT 1 3262306a36Sopenharmony_ci#endif 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic int timer_limit = DEFAULT_TIMER_LIMIT; 3562306a36Sopenharmony_cistatic int timer_tstamp_monotonic = 1; 3662306a36Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>"); 3762306a36Sopenharmony_ciMODULE_DESCRIPTION("ALSA timer interface"); 3862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3962306a36Sopenharmony_cimodule_param(timer_limit, int, 0444); 4062306a36Sopenharmony_ciMODULE_PARM_DESC(timer_limit, "Maximum global timers in system."); 4162306a36Sopenharmony_cimodule_param(timer_tstamp_monotonic, int, 0444); 4262306a36Sopenharmony_ciMODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default)."); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ciMODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER); 4562306a36Sopenharmony_ciMODULE_ALIAS("devname:snd/timer"); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cienum timer_tread_format { 4862306a36Sopenharmony_ci TREAD_FORMAT_NONE = 0, 4962306a36Sopenharmony_ci TREAD_FORMAT_TIME64, 5062306a36Sopenharmony_ci TREAD_FORMAT_TIME32, 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct snd_timer_tread32 { 5462306a36Sopenharmony_ci int event; 5562306a36Sopenharmony_ci s32 tstamp_sec; 5662306a36Sopenharmony_ci s32 tstamp_nsec; 5762306a36Sopenharmony_ci unsigned int val; 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct snd_timer_tread64 { 6162306a36Sopenharmony_ci int event; 6262306a36Sopenharmony_ci u8 pad1[4]; 6362306a36Sopenharmony_ci s64 tstamp_sec; 6462306a36Sopenharmony_ci s64 tstamp_nsec; 6562306a36Sopenharmony_ci unsigned int val; 6662306a36Sopenharmony_ci u8 pad2[4]; 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistruct snd_timer_user { 7062306a36Sopenharmony_ci struct snd_timer_instance *timeri; 7162306a36Sopenharmony_ci int tread; /* enhanced read with timestamps and events */ 7262306a36Sopenharmony_ci unsigned long ticks; 7362306a36Sopenharmony_ci unsigned long overrun; 7462306a36Sopenharmony_ci int qhead; 7562306a36Sopenharmony_ci int qtail; 7662306a36Sopenharmony_ci int qused; 7762306a36Sopenharmony_ci int queue_size; 7862306a36Sopenharmony_ci bool disconnected; 7962306a36Sopenharmony_ci struct snd_timer_read *queue; 8062306a36Sopenharmony_ci struct snd_timer_tread64 *tqueue; 8162306a36Sopenharmony_ci spinlock_t qlock; 8262306a36Sopenharmony_ci unsigned long last_resolution; 8362306a36Sopenharmony_ci unsigned int filter; 8462306a36Sopenharmony_ci struct timespec64 tstamp; /* trigger tstamp */ 8562306a36Sopenharmony_ci wait_queue_head_t qchange_sleep; 8662306a36Sopenharmony_ci struct snd_fasync *fasync; 8762306a36Sopenharmony_ci struct mutex ioctl_lock; 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistruct snd_timer_status32 { 9162306a36Sopenharmony_ci s32 tstamp_sec; /* Timestamp - last update */ 9262306a36Sopenharmony_ci s32 tstamp_nsec; 9362306a36Sopenharmony_ci unsigned int resolution; /* current period resolution in ns */ 9462306a36Sopenharmony_ci unsigned int lost; /* counter of master tick lost */ 9562306a36Sopenharmony_ci unsigned int overrun; /* count of read queue overruns */ 9662306a36Sopenharmony_ci unsigned int queue; /* used queue size */ 9762306a36Sopenharmony_ci unsigned char reserved[64]; /* reserved */ 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define SNDRV_TIMER_IOCTL_STATUS32 _IOR('T', 0x14, struct snd_timer_status32) 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct snd_timer_status64 { 10362306a36Sopenharmony_ci s64 tstamp_sec; /* Timestamp - last update */ 10462306a36Sopenharmony_ci s64 tstamp_nsec; 10562306a36Sopenharmony_ci unsigned int resolution; /* current period resolution in ns */ 10662306a36Sopenharmony_ci unsigned int lost; /* counter of master tick lost */ 10762306a36Sopenharmony_ci unsigned int overrun; /* count of read queue overruns */ 10862306a36Sopenharmony_ci unsigned int queue; /* used queue size */ 10962306a36Sopenharmony_ci unsigned char reserved[64]; /* reserved */ 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#define SNDRV_TIMER_IOCTL_STATUS64 _IOR('T', 0x14, struct snd_timer_status64) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* list of timers */ 11562306a36Sopenharmony_cistatic LIST_HEAD(snd_timer_list); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* list of slave instances */ 11862306a36Sopenharmony_cistatic LIST_HEAD(snd_timer_slave_list); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* lock for slave active lists */ 12162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(slave_active_lock); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci#define MAX_SLAVE_INSTANCES 1000 12462306a36Sopenharmony_cistatic int num_slaves; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic DEFINE_MUTEX(register_mutex); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int snd_timer_free(struct snd_timer *timer); 12962306a36Sopenharmony_cistatic int snd_timer_dev_free(struct snd_device *device); 13062306a36Sopenharmony_cistatic int snd_timer_dev_register(struct snd_device *device); 13162306a36Sopenharmony_cistatic int snd_timer_dev_disconnect(struct snd_device *device); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* 13662306a36Sopenharmony_ci * create a timer instance with the given owner string. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_cistruct snd_timer_instance *snd_timer_instance_new(const char *owner) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct snd_timer_instance *timeri; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci timeri = kzalloc(sizeof(*timeri), GFP_KERNEL); 14362306a36Sopenharmony_ci if (timeri == NULL) 14462306a36Sopenharmony_ci return NULL; 14562306a36Sopenharmony_ci timeri->owner = kstrdup(owner, GFP_KERNEL); 14662306a36Sopenharmony_ci if (! timeri->owner) { 14762306a36Sopenharmony_ci kfree(timeri); 14862306a36Sopenharmony_ci return NULL; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci INIT_LIST_HEAD(&timeri->open_list); 15162306a36Sopenharmony_ci INIT_LIST_HEAD(&timeri->active_list); 15262306a36Sopenharmony_ci INIT_LIST_HEAD(&timeri->ack_list); 15362306a36Sopenharmony_ci INIT_LIST_HEAD(&timeri->slave_list_head); 15462306a36Sopenharmony_ci INIT_LIST_HEAD(&timeri->slave_active_head); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return timeri; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_timer_instance_new); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_civoid snd_timer_instance_free(struct snd_timer_instance *timeri) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci if (timeri) { 16362306a36Sopenharmony_ci if (timeri->private_free) 16462306a36Sopenharmony_ci timeri->private_free(timeri); 16562306a36Sopenharmony_ci kfree(timeri->owner); 16662306a36Sopenharmony_ci kfree(timeri); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_timer_instance_free); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/* 17262306a36Sopenharmony_ci * find a timer instance from the given timer id 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_cistatic struct snd_timer *snd_timer_find(struct snd_timer_id *tid) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct snd_timer *timer; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci list_for_each_entry(timer, &snd_timer_list, device_list) { 17962306a36Sopenharmony_ci if (timer->tmr_class != tid->dev_class) 18062306a36Sopenharmony_ci continue; 18162306a36Sopenharmony_ci if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD || 18262306a36Sopenharmony_ci timer->tmr_class == SNDRV_TIMER_CLASS_PCM) && 18362306a36Sopenharmony_ci (timer->card == NULL || 18462306a36Sopenharmony_ci timer->card->number != tid->card)) 18562306a36Sopenharmony_ci continue; 18662306a36Sopenharmony_ci if (timer->tmr_device != tid->device) 18762306a36Sopenharmony_ci continue; 18862306a36Sopenharmony_ci if (timer->tmr_subdevice != tid->subdevice) 18962306a36Sopenharmony_ci continue; 19062306a36Sopenharmony_ci return timer; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci return NULL; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci#ifdef CONFIG_MODULES 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void snd_timer_request(struct snd_timer_id *tid) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci switch (tid->dev_class) { 20062306a36Sopenharmony_ci case SNDRV_TIMER_CLASS_GLOBAL: 20162306a36Sopenharmony_ci if (tid->device < timer_limit) 20262306a36Sopenharmony_ci request_module("snd-timer-%i", tid->device); 20362306a36Sopenharmony_ci break; 20462306a36Sopenharmony_ci case SNDRV_TIMER_CLASS_CARD: 20562306a36Sopenharmony_ci case SNDRV_TIMER_CLASS_PCM: 20662306a36Sopenharmony_ci if (tid->card < snd_ecards_limit) 20762306a36Sopenharmony_ci request_module("snd-card-%i", tid->card); 20862306a36Sopenharmony_ci break; 20962306a36Sopenharmony_ci default: 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci#endif 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/* move the slave if it belongs to the master; return 1 if match */ 21762306a36Sopenharmony_cistatic int check_matching_master_slave(struct snd_timer_instance *master, 21862306a36Sopenharmony_ci struct snd_timer_instance *slave) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci if (slave->slave_class != master->slave_class || 22162306a36Sopenharmony_ci slave->slave_id != master->slave_id) 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci if (master->timer->num_instances >= master->timer->max_instances) 22462306a36Sopenharmony_ci return -EBUSY; 22562306a36Sopenharmony_ci list_move_tail(&slave->open_list, &master->slave_list_head); 22662306a36Sopenharmony_ci master->timer->num_instances++; 22762306a36Sopenharmony_ci spin_lock_irq(&slave_active_lock); 22862306a36Sopenharmony_ci spin_lock(&master->timer->lock); 22962306a36Sopenharmony_ci slave->master = master; 23062306a36Sopenharmony_ci slave->timer = master->timer; 23162306a36Sopenharmony_ci if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) 23262306a36Sopenharmony_ci list_add_tail(&slave->active_list, &master->slave_active_head); 23362306a36Sopenharmony_ci spin_unlock(&master->timer->lock); 23462306a36Sopenharmony_ci spin_unlock_irq(&slave_active_lock); 23562306a36Sopenharmony_ci return 1; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci/* 23962306a36Sopenharmony_ci * look for a master instance matching with the slave id of the given slave. 24062306a36Sopenharmony_ci * when found, relink the open_link of the slave. 24162306a36Sopenharmony_ci * 24262306a36Sopenharmony_ci * call this with register_mutex down. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_cistatic int snd_timer_check_slave(struct snd_timer_instance *slave) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct snd_timer *timer; 24762306a36Sopenharmony_ci struct snd_timer_instance *master; 24862306a36Sopenharmony_ci int err = 0; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* FIXME: it's really dumb to look up all entries.. */ 25162306a36Sopenharmony_ci list_for_each_entry(timer, &snd_timer_list, device_list) { 25262306a36Sopenharmony_ci list_for_each_entry(master, &timer->open_list_head, open_list) { 25362306a36Sopenharmony_ci err = check_matching_master_slave(master, slave); 25462306a36Sopenharmony_ci if (err != 0) /* match found or error */ 25562306a36Sopenharmony_ci goto out; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci out: 25962306a36Sopenharmony_ci return err < 0 ? err : 0; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* 26362306a36Sopenharmony_ci * look for slave instances matching with the slave id of the given master. 26462306a36Sopenharmony_ci * when found, relink the open_link of slaves. 26562306a36Sopenharmony_ci * 26662306a36Sopenharmony_ci * call this with register_mutex down. 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_cistatic int snd_timer_check_master(struct snd_timer_instance *master) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct snd_timer_instance *slave, *tmp; 27162306a36Sopenharmony_ci int err = 0; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* check all pending slaves */ 27462306a36Sopenharmony_ci list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) { 27562306a36Sopenharmony_ci err = check_matching_master_slave(master, slave); 27662306a36Sopenharmony_ci if (err < 0) 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci return err < 0 ? err : 0; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic void snd_timer_close_locked(struct snd_timer_instance *timeri, 28362306a36Sopenharmony_ci struct device **card_devp_to_put); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/* 28662306a36Sopenharmony_ci * open a timer instance 28762306a36Sopenharmony_ci * when opening a master, the slave id must be here given. 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ciint snd_timer_open(struct snd_timer_instance *timeri, 29062306a36Sopenharmony_ci struct snd_timer_id *tid, 29162306a36Sopenharmony_ci unsigned int slave_id) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci struct snd_timer *timer; 29462306a36Sopenharmony_ci struct device *card_dev_to_put = NULL; 29562306a36Sopenharmony_ci int err; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci mutex_lock(®ister_mutex); 29862306a36Sopenharmony_ci if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) { 29962306a36Sopenharmony_ci /* open a slave instance */ 30062306a36Sopenharmony_ci if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE || 30162306a36Sopenharmony_ci tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) { 30262306a36Sopenharmony_ci pr_debug("ALSA: timer: invalid slave class %i\n", 30362306a36Sopenharmony_ci tid->dev_sclass); 30462306a36Sopenharmony_ci err = -EINVAL; 30562306a36Sopenharmony_ci goto unlock; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci if (num_slaves >= MAX_SLAVE_INSTANCES) { 30862306a36Sopenharmony_ci err = -EBUSY; 30962306a36Sopenharmony_ci goto unlock; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci timeri->slave_class = tid->dev_sclass; 31262306a36Sopenharmony_ci timeri->slave_id = tid->device; 31362306a36Sopenharmony_ci timeri->flags |= SNDRV_TIMER_IFLG_SLAVE; 31462306a36Sopenharmony_ci list_add_tail(&timeri->open_list, &snd_timer_slave_list); 31562306a36Sopenharmony_ci num_slaves++; 31662306a36Sopenharmony_ci err = snd_timer_check_slave(timeri); 31762306a36Sopenharmony_ci goto list_added; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* open a master instance */ 32162306a36Sopenharmony_ci timer = snd_timer_find(tid); 32262306a36Sopenharmony_ci#ifdef CONFIG_MODULES 32362306a36Sopenharmony_ci if (!timer) { 32462306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 32562306a36Sopenharmony_ci snd_timer_request(tid); 32662306a36Sopenharmony_ci mutex_lock(®ister_mutex); 32762306a36Sopenharmony_ci timer = snd_timer_find(tid); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci#endif 33062306a36Sopenharmony_ci if (!timer) { 33162306a36Sopenharmony_ci err = -ENODEV; 33262306a36Sopenharmony_ci goto unlock; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci if (!list_empty(&timer->open_list_head)) { 33562306a36Sopenharmony_ci struct snd_timer_instance *t = 33662306a36Sopenharmony_ci list_entry(timer->open_list_head.next, 33762306a36Sopenharmony_ci struct snd_timer_instance, open_list); 33862306a36Sopenharmony_ci if (t->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) { 33962306a36Sopenharmony_ci err = -EBUSY; 34062306a36Sopenharmony_ci goto unlock; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci if (timer->num_instances >= timer->max_instances) { 34462306a36Sopenharmony_ci err = -EBUSY; 34562306a36Sopenharmony_ci goto unlock; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci if (!try_module_get(timer->module)) { 34862306a36Sopenharmony_ci err = -EBUSY; 34962306a36Sopenharmony_ci goto unlock; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci /* take a card refcount for safe disconnection */ 35262306a36Sopenharmony_ci if (timer->card) { 35362306a36Sopenharmony_ci get_device(&timer->card->card_dev); 35462306a36Sopenharmony_ci card_dev_to_put = &timer->card->card_dev; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (list_empty(&timer->open_list_head) && timer->hw.open) { 35862306a36Sopenharmony_ci err = timer->hw.open(timer); 35962306a36Sopenharmony_ci if (err) { 36062306a36Sopenharmony_ci module_put(timer->module); 36162306a36Sopenharmony_ci goto unlock; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci timeri->timer = timer; 36662306a36Sopenharmony_ci timeri->slave_class = tid->dev_sclass; 36762306a36Sopenharmony_ci timeri->slave_id = slave_id; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci list_add_tail(&timeri->open_list, &timer->open_list_head); 37062306a36Sopenharmony_ci timer->num_instances++; 37162306a36Sopenharmony_ci err = snd_timer_check_master(timeri); 37262306a36Sopenharmony_cilist_added: 37362306a36Sopenharmony_ci if (err < 0) 37462306a36Sopenharmony_ci snd_timer_close_locked(timeri, &card_dev_to_put); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci unlock: 37762306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 37862306a36Sopenharmony_ci /* put_device() is called after unlock for avoiding deadlock */ 37962306a36Sopenharmony_ci if (err < 0 && card_dev_to_put) 38062306a36Sopenharmony_ci put_device(card_dev_to_put); 38162306a36Sopenharmony_ci return err; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_timer_open); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci/* 38662306a36Sopenharmony_ci * close a timer instance 38762306a36Sopenharmony_ci * call this with register_mutex down. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_cistatic void snd_timer_close_locked(struct snd_timer_instance *timeri, 39062306a36Sopenharmony_ci struct device **card_devp_to_put) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct snd_timer *timer = timeri->timer; 39362306a36Sopenharmony_ci struct snd_timer_instance *slave, *tmp; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (timer) { 39662306a36Sopenharmony_ci spin_lock_irq(&timer->lock); 39762306a36Sopenharmony_ci timeri->flags |= SNDRV_TIMER_IFLG_DEAD; 39862306a36Sopenharmony_ci spin_unlock_irq(&timer->lock); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (!list_empty(&timeri->open_list)) { 40262306a36Sopenharmony_ci list_del_init(&timeri->open_list); 40362306a36Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 40462306a36Sopenharmony_ci num_slaves--; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* force to stop the timer */ 40862306a36Sopenharmony_ci snd_timer_stop(timeri); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (timer) { 41162306a36Sopenharmony_ci timer->num_instances--; 41262306a36Sopenharmony_ci /* wait, until the active callback is finished */ 41362306a36Sopenharmony_ci spin_lock_irq(&timer->lock); 41462306a36Sopenharmony_ci while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { 41562306a36Sopenharmony_ci spin_unlock_irq(&timer->lock); 41662306a36Sopenharmony_ci udelay(10); 41762306a36Sopenharmony_ci spin_lock_irq(&timer->lock); 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci spin_unlock_irq(&timer->lock); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* remove slave links */ 42262306a36Sopenharmony_ci spin_lock_irq(&slave_active_lock); 42362306a36Sopenharmony_ci spin_lock(&timer->lock); 42462306a36Sopenharmony_ci timeri->timer = NULL; 42562306a36Sopenharmony_ci list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head, 42662306a36Sopenharmony_ci open_list) { 42762306a36Sopenharmony_ci list_move_tail(&slave->open_list, &snd_timer_slave_list); 42862306a36Sopenharmony_ci timer->num_instances--; 42962306a36Sopenharmony_ci slave->master = NULL; 43062306a36Sopenharmony_ci slave->timer = NULL; 43162306a36Sopenharmony_ci list_del_init(&slave->ack_list); 43262306a36Sopenharmony_ci list_del_init(&slave->active_list); 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci spin_unlock(&timer->lock); 43562306a36Sopenharmony_ci spin_unlock_irq(&slave_active_lock); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* slave doesn't need to release timer resources below */ 43862306a36Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 43962306a36Sopenharmony_ci timer = NULL; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (timer) { 44362306a36Sopenharmony_ci if (list_empty(&timer->open_list_head) && timer->hw.close) 44462306a36Sopenharmony_ci timer->hw.close(timer); 44562306a36Sopenharmony_ci /* release a card refcount for safe disconnection */ 44662306a36Sopenharmony_ci if (timer->card) 44762306a36Sopenharmony_ci *card_devp_to_put = &timer->card->card_dev; 44862306a36Sopenharmony_ci module_put(timer->module); 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci/* 45362306a36Sopenharmony_ci * close a timer instance 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_civoid snd_timer_close(struct snd_timer_instance *timeri) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct device *card_dev_to_put = NULL; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (snd_BUG_ON(!timeri)) 46062306a36Sopenharmony_ci return; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci mutex_lock(®ister_mutex); 46362306a36Sopenharmony_ci snd_timer_close_locked(timeri, &card_dev_to_put); 46462306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 46562306a36Sopenharmony_ci /* put_device() is called after unlock for avoiding deadlock */ 46662306a36Sopenharmony_ci if (card_dev_to_put) 46762306a36Sopenharmony_ci put_device(card_dev_to_put); 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_timer_close); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic unsigned long snd_timer_hw_resolution(struct snd_timer *timer) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci if (timer->hw.c_resolution) 47462306a36Sopenharmony_ci return timer->hw.c_resolution(timer); 47562306a36Sopenharmony_ci else 47662306a36Sopenharmony_ci return timer->hw.resolution; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ciunsigned long snd_timer_resolution(struct snd_timer_instance *timeri) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct snd_timer * timer; 48262306a36Sopenharmony_ci unsigned long ret = 0; 48362306a36Sopenharmony_ci unsigned long flags; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (timeri == NULL) 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci timer = timeri->timer; 48862306a36Sopenharmony_ci if (timer) { 48962306a36Sopenharmony_ci spin_lock_irqsave(&timer->lock, flags); 49062306a36Sopenharmony_ci ret = snd_timer_hw_resolution(timer); 49162306a36Sopenharmony_ci spin_unlock_irqrestore(&timer->lock, flags); 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci return ret; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ciEXPORT_SYMBOL(snd_timer_resolution); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic void snd_timer_notify1(struct snd_timer_instance *ti, int event) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct snd_timer *timer = ti->timer; 50062306a36Sopenharmony_ci unsigned long resolution = 0; 50162306a36Sopenharmony_ci struct snd_timer_instance *ts; 50262306a36Sopenharmony_ci struct timespec64 tstamp; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (timer_tstamp_monotonic) 50562306a36Sopenharmony_ci ktime_get_ts64(&tstamp); 50662306a36Sopenharmony_ci else 50762306a36Sopenharmony_ci ktime_get_real_ts64(&tstamp); 50862306a36Sopenharmony_ci if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START || 50962306a36Sopenharmony_ci event > SNDRV_TIMER_EVENT_PAUSE)) 51062306a36Sopenharmony_ci return; 51162306a36Sopenharmony_ci if (timer && 51262306a36Sopenharmony_ci (event == SNDRV_TIMER_EVENT_START || 51362306a36Sopenharmony_ci event == SNDRV_TIMER_EVENT_CONTINUE)) 51462306a36Sopenharmony_ci resolution = snd_timer_hw_resolution(timer); 51562306a36Sopenharmony_ci if (ti->ccallback) 51662306a36Sopenharmony_ci ti->ccallback(ti, event, &tstamp, resolution); 51762306a36Sopenharmony_ci if (ti->flags & SNDRV_TIMER_IFLG_SLAVE) 51862306a36Sopenharmony_ci return; 51962306a36Sopenharmony_ci if (timer == NULL) 52062306a36Sopenharmony_ci return; 52162306a36Sopenharmony_ci if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 52262306a36Sopenharmony_ci return; 52362306a36Sopenharmony_ci event += 10; /* convert to SNDRV_TIMER_EVENT_MXXX */ 52462306a36Sopenharmony_ci list_for_each_entry(ts, &ti->slave_active_head, active_list) 52562306a36Sopenharmony_ci if (ts->ccallback) 52662306a36Sopenharmony_ci ts->ccallback(ts, event, &tstamp, resolution); 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci/* start/continue a master timer */ 53062306a36Sopenharmony_cistatic int snd_timer_start1(struct snd_timer_instance *timeri, 53162306a36Sopenharmony_ci bool start, unsigned long ticks) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct snd_timer *timer; 53462306a36Sopenharmony_ci int result; 53562306a36Sopenharmony_ci unsigned long flags; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci timer = timeri->timer; 53862306a36Sopenharmony_ci if (!timer) 53962306a36Sopenharmony_ci return -EINVAL; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci spin_lock_irqsave(&timer->lock, flags); 54262306a36Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { 54362306a36Sopenharmony_ci result = -EINVAL; 54462306a36Sopenharmony_ci goto unlock; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci if (timer->card && timer->card->shutdown) { 54762306a36Sopenharmony_ci result = -ENODEV; 54862306a36Sopenharmony_ci goto unlock; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | 55162306a36Sopenharmony_ci SNDRV_TIMER_IFLG_START)) { 55262306a36Sopenharmony_ci result = -EBUSY; 55362306a36Sopenharmony_ci goto unlock; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (start) 55762306a36Sopenharmony_ci timeri->ticks = timeri->cticks = ticks; 55862306a36Sopenharmony_ci else if (!timeri->cticks) 55962306a36Sopenharmony_ci timeri->cticks = 1; 56062306a36Sopenharmony_ci timeri->pticks = 0; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci list_move_tail(&timeri->active_list, &timer->active_list_head); 56362306a36Sopenharmony_ci if (timer->running) { 56462306a36Sopenharmony_ci if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 56562306a36Sopenharmony_ci goto __start_now; 56662306a36Sopenharmony_ci timer->flags |= SNDRV_TIMER_FLG_RESCHED; 56762306a36Sopenharmony_ci timeri->flags |= SNDRV_TIMER_IFLG_START; 56862306a36Sopenharmony_ci result = 1; /* delayed start */ 56962306a36Sopenharmony_ci } else { 57062306a36Sopenharmony_ci if (start) 57162306a36Sopenharmony_ci timer->sticks = ticks; 57262306a36Sopenharmony_ci timer->hw.start(timer); 57362306a36Sopenharmony_ci __start_now: 57462306a36Sopenharmony_ci timer->running++; 57562306a36Sopenharmony_ci timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; 57662306a36Sopenharmony_ci result = 0; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : 57962306a36Sopenharmony_ci SNDRV_TIMER_EVENT_CONTINUE); 58062306a36Sopenharmony_ci unlock: 58162306a36Sopenharmony_ci spin_unlock_irqrestore(&timer->lock, flags); 58262306a36Sopenharmony_ci return result; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci/* start/continue a slave timer */ 58662306a36Sopenharmony_cistatic int snd_timer_start_slave(struct snd_timer_instance *timeri, 58762306a36Sopenharmony_ci bool start) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci unsigned long flags; 59062306a36Sopenharmony_ci int err; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci spin_lock_irqsave(&slave_active_lock, flags); 59362306a36Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { 59462306a36Sopenharmony_ci err = -EINVAL; 59562306a36Sopenharmony_ci goto unlock; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { 59862306a36Sopenharmony_ci err = -EBUSY; 59962306a36Sopenharmony_ci goto unlock; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; 60262306a36Sopenharmony_ci if (timeri->master && timeri->timer) { 60362306a36Sopenharmony_ci spin_lock(&timeri->timer->lock); 60462306a36Sopenharmony_ci list_add_tail(&timeri->active_list, 60562306a36Sopenharmony_ci &timeri->master->slave_active_head); 60662306a36Sopenharmony_ci snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : 60762306a36Sopenharmony_ci SNDRV_TIMER_EVENT_CONTINUE); 60862306a36Sopenharmony_ci spin_unlock(&timeri->timer->lock); 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci err = 1; /* delayed start */ 61162306a36Sopenharmony_ci unlock: 61262306a36Sopenharmony_ci spin_unlock_irqrestore(&slave_active_lock, flags); 61362306a36Sopenharmony_ci return err; 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci/* stop/pause a master timer */ 61762306a36Sopenharmony_cistatic int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci struct snd_timer *timer; 62062306a36Sopenharmony_ci int result = 0; 62162306a36Sopenharmony_ci unsigned long flags; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci timer = timeri->timer; 62462306a36Sopenharmony_ci if (!timer) 62562306a36Sopenharmony_ci return -EINVAL; 62662306a36Sopenharmony_ci spin_lock_irqsave(&timer->lock, flags); 62762306a36Sopenharmony_ci list_del_init(&timeri->ack_list); 62862306a36Sopenharmony_ci list_del_init(&timeri->active_list); 62962306a36Sopenharmony_ci if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | 63062306a36Sopenharmony_ci SNDRV_TIMER_IFLG_START))) { 63162306a36Sopenharmony_ci result = -EBUSY; 63262306a36Sopenharmony_ci goto unlock; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci if (timer->card && timer->card->shutdown) 63562306a36Sopenharmony_ci goto unlock; 63662306a36Sopenharmony_ci if (stop) { 63762306a36Sopenharmony_ci timeri->cticks = timeri->ticks; 63862306a36Sopenharmony_ci timeri->pticks = 0; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && 64162306a36Sopenharmony_ci !(--timer->running)) { 64262306a36Sopenharmony_ci timer->hw.stop(timer); 64362306a36Sopenharmony_ci if (timer->flags & SNDRV_TIMER_FLG_RESCHED) { 64462306a36Sopenharmony_ci timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; 64562306a36Sopenharmony_ci snd_timer_reschedule(timer, 0); 64662306a36Sopenharmony_ci if (timer->flags & SNDRV_TIMER_FLG_CHANGE) { 64762306a36Sopenharmony_ci timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; 64862306a36Sopenharmony_ci timer->hw.start(timer); 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START); 65362306a36Sopenharmony_ci if (stop) 65462306a36Sopenharmony_ci timeri->flags &= ~SNDRV_TIMER_IFLG_PAUSED; 65562306a36Sopenharmony_ci else 65662306a36Sopenharmony_ci timeri->flags |= SNDRV_TIMER_IFLG_PAUSED; 65762306a36Sopenharmony_ci snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : 65862306a36Sopenharmony_ci SNDRV_TIMER_EVENT_PAUSE); 65962306a36Sopenharmony_ci unlock: 66062306a36Sopenharmony_ci spin_unlock_irqrestore(&timer->lock, flags); 66162306a36Sopenharmony_ci return result; 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci/* stop/pause a slave timer */ 66562306a36Sopenharmony_cistatic int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci unsigned long flags; 66862306a36Sopenharmony_ci bool running; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci spin_lock_irqsave(&slave_active_lock, flags); 67162306a36Sopenharmony_ci running = timeri->flags & SNDRV_TIMER_IFLG_RUNNING; 67262306a36Sopenharmony_ci timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; 67362306a36Sopenharmony_ci if (timeri->timer) { 67462306a36Sopenharmony_ci spin_lock(&timeri->timer->lock); 67562306a36Sopenharmony_ci list_del_init(&timeri->ack_list); 67662306a36Sopenharmony_ci list_del_init(&timeri->active_list); 67762306a36Sopenharmony_ci if (running) 67862306a36Sopenharmony_ci snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : 67962306a36Sopenharmony_ci SNDRV_TIMER_EVENT_PAUSE); 68062306a36Sopenharmony_ci spin_unlock(&timeri->timer->lock); 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci spin_unlock_irqrestore(&slave_active_lock, flags); 68362306a36Sopenharmony_ci return running ? 0 : -EBUSY; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci/* 68762306a36Sopenharmony_ci * start the timer instance 68862306a36Sopenharmony_ci */ 68962306a36Sopenharmony_ciint snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci if (timeri == NULL || ticks < 1) 69262306a36Sopenharmony_ci return -EINVAL; 69362306a36Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 69462306a36Sopenharmony_ci return snd_timer_start_slave(timeri, true); 69562306a36Sopenharmony_ci else 69662306a36Sopenharmony_ci return snd_timer_start1(timeri, true, ticks); 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_timer_start); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci/* 70162306a36Sopenharmony_ci * stop the timer instance. 70262306a36Sopenharmony_ci * 70362306a36Sopenharmony_ci * do not call this from the timer callback! 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_ciint snd_timer_stop(struct snd_timer_instance *timeri) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 70862306a36Sopenharmony_ci return snd_timer_stop_slave(timeri, true); 70962306a36Sopenharmony_ci else 71062306a36Sopenharmony_ci return snd_timer_stop1(timeri, true); 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ciEXPORT_SYMBOL(snd_timer_stop); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci/* 71562306a36Sopenharmony_ci * start again.. the tick is kept. 71662306a36Sopenharmony_ci */ 71762306a36Sopenharmony_ciint snd_timer_continue(struct snd_timer_instance *timeri) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci /* timer can continue only after pause */ 72062306a36Sopenharmony_ci if (!(timeri->flags & SNDRV_TIMER_IFLG_PAUSED)) 72162306a36Sopenharmony_ci return -EINVAL; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 72462306a36Sopenharmony_ci return snd_timer_start_slave(timeri, false); 72562306a36Sopenharmony_ci else 72662306a36Sopenharmony_ci return snd_timer_start1(timeri, false, 0); 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_timer_continue); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci/* 73162306a36Sopenharmony_ci * pause.. remember the ticks left 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_ciint snd_timer_pause(struct snd_timer_instance * timeri) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 73662306a36Sopenharmony_ci return snd_timer_stop_slave(timeri, false); 73762306a36Sopenharmony_ci else 73862306a36Sopenharmony_ci return snd_timer_stop1(timeri, false); 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ciEXPORT_SYMBOL(snd_timer_pause); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci/* 74362306a36Sopenharmony_ci * reschedule the timer 74462306a36Sopenharmony_ci * 74562306a36Sopenharmony_ci * start pending instances and check the scheduling ticks. 74662306a36Sopenharmony_ci * when the scheduling ticks is changed set CHANGE flag to reprogram the timer. 74762306a36Sopenharmony_ci */ 74862306a36Sopenharmony_cistatic void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci struct snd_timer_instance *ti; 75162306a36Sopenharmony_ci unsigned long ticks = ~0UL; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci list_for_each_entry(ti, &timer->active_list_head, active_list) { 75462306a36Sopenharmony_ci if (ti->flags & SNDRV_TIMER_IFLG_START) { 75562306a36Sopenharmony_ci ti->flags &= ~SNDRV_TIMER_IFLG_START; 75662306a36Sopenharmony_ci ti->flags |= SNDRV_TIMER_IFLG_RUNNING; 75762306a36Sopenharmony_ci timer->running++; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) { 76062306a36Sopenharmony_ci if (ticks > ti->cticks) 76162306a36Sopenharmony_ci ticks = ti->cticks; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci if (ticks == ~0UL) { 76562306a36Sopenharmony_ci timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; 76662306a36Sopenharmony_ci return; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci if (ticks > timer->hw.ticks) 76962306a36Sopenharmony_ci ticks = timer->hw.ticks; 77062306a36Sopenharmony_ci if (ticks_left != ticks) 77162306a36Sopenharmony_ci timer->flags |= SNDRV_TIMER_FLG_CHANGE; 77262306a36Sopenharmony_ci timer->sticks = ticks; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci/* call callbacks in timer ack list */ 77662306a36Sopenharmony_cistatic void snd_timer_process_callbacks(struct snd_timer *timer, 77762306a36Sopenharmony_ci struct list_head *head) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci struct snd_timer_instance *ti; 78062306a36Sopenharmony_ci unsigned long resolution, ticks; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci while (!list_empty(head)) { 78362306a36Sopenharmony_ci ti = list_first_entry(head, struct snd_timer_instance, 78462306a36Sopenharmony_ci ack_list); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci /* remove from ack_list and make empty */ 78762306a36Sopenharmony_ci list_del_init(&ti->ack_list); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) { 79062306a36Sopenharmony_ci ticks = ti->pticks; 79162306a36Sopenharmony_ci ti->pticks = 0; 79262306a36Sopenharmony_ci resolution = ti->resolution; 79362306a36Sopenharmony_ci ti->flags |= SNDRV_TIMER_IFLG_CALLBACK; 79462306a36Sopenharmony_ci spin_unlock(&timer->lock); 79562306a36Sopenharmony_ci if (ti->callback) 79662306a36Sopenharmony_ci ti->callback(ti, resolution, ticks); 79762306a36Sopenharmony_ci spin_lock(&timer->lock); 79862306a36Sopenharmony_ci ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci/* clear pending instances from ack list */ 80462306a36Sopenharmony_cistatic void snd_timer_clear_callbacks(struct snd_timer *timer, 80562306a36Sopenharmony_ci struct list_head *head) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci unsigned long flags; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci spin_lock_irqsave(&timer->lock, flags); 81062306a36Sopenharmony_ci while (!list_empty(head)) 81162306a36Sopenharmony_ci list_del_init(head->next); 81262306a36Sopenharmony_ci spin_unlock_irqrestore(&timer->lock, flags); 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci/* 81662306a36Sopenharmony_ci * timer work 81762306a36Sopenharmony_ci * 81862306a36Sopenharmony_ci */ 81962306a36Sopenharmony_cistatic void snd_timer_work(struct work_struct *work) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci struct snd_timer *timer = container_of(work, struct snd_timer, task_work); 82262306a36Sopenharmony_ci unsigned long flags; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci if (timer->card && timer->card->shutdown) { 82562306a36Sopenharmony_ci snd_timer_clear_callbacks(timer, &timer->sack_list_head); 82662306a36Sopenharmony_ci return; 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci spin_lock_irqsave(&timer->lock, flags); 83062306a36Sopenharmony_ci snd_timer_process_callbacks(timer, &timer->sack_list_head); 83162306a36Sopenharmony_ci spin_unlock_irqrestore(&timer->lock, flags); 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci/* 83562306a36Sopenharmony_ci * timer interrupt 83662306a36Sopenharmony_ci * 83762306a36Sopenharmony_ci * ticks_left is usually equal to timer->sticks. 83862306a36Sopenharmony_ci * 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_civoid snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci struct snd_timer_instance *ti, *ts, *tmp; 84362306a36Sopenharmony_ci unsigned long resolution; 84462306a36Sopenharmony_ci struct list_head *ack_list_head; 84562306a36Sopenharmony_ci unsigned long flags; 84662306a36Sopenharmony_ci bool use_work = false; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (timer == NULL) 84962306a36Sopenharmony_ci return; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (timer->card && timer->card->shutdown) { 85262306a36Sopenharmony_ci snd_timer_clear_callbacks(timer, &timer->ack_list_head); 85362306a36Sopenharmony_ci return; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci spin_lock_irqsave(&timer->lock, flags); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci /* remember the current resolution */ 85962306a36Sopenharmony_ci resolution = snd_timer_hw_resolution(timer); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* loop for all active instances 86262306a36Sopenharmony_ci * Here we cannot use list_for_each_entry because the active_list of a 86362306a36Sopenharmony_ci * processed instance is relinked to done_list_head before the callback 86462306a36Sopenharmony_ci * is called. 86562306a36Sopenharmony_ci */ 86662306a36Sopenharmony_ci list_for_each_entry_safe(ti, tmp, &timer->active_list_head, 86762306a36Sopenharmony_ci active_list) { 86862306a36Sopenharmony_ci if (ti->flags & SNDRV_TIMER_IFLG_DEAD) 86962306a36Sopenharmony_ci continue; 87062306a36Sopenharmony_ci if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING)) 87162306a36Sopenharmony_ci continue; 87262306a36Sopenharmony_ci ti->pticks += ticks_left; 87362306a36Sopenharmony_ci ti->resolution = resolution; 87462306a36Sopenharmony_ci if (ti->cticks < ticks_left) 87562306a36Sopenharmony_ci ti->cticks = 0; 87662306a36Sopenharmony_ci else 87762306a36Sopenharmony_ci ti->cticks -= ticks_left; 87862306a36Sopenharmony_ci if (ti->cticks) /* not expired */ 87962306a36Sopenharmony_ci continue; 88062306a36Sopenharmony_ci if (ti->flags & SNDRV_TIMER_IFLG_AUTO) { 88162306a36Sopenharmony_ci ti->cticks = ti->ticks; 88262306a36Sopenharmony_ci } else { 88362306a36Sopenharmony_ci ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING; 88462306a36Sopenharmony_ci --timer->running; 88562306a36Sopenharmony_ci list_del_init(&ti->active_list); 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci if ((timer->hw.flags & SNDRV_TIMER_HW_WORK) || 88862306a36Sopenharmony_ci (ti->flags & SNDRV_TIMER_IFLG_FAST)) 88962306a36Sopenharmony_ci ack_list_head = &timer->ack_list_head; 89062306a36Sopenharmony_ci else 89162306a36Sopenharmony_ci ack_list_head = &timer->sack_list_head; 89262306a36Sopenharmony_ci if (list_empty(&ti->ack_list)) 89362306a36Sopenharmony_ci list_add_tail(&ti->ack_list, ack_list_head); 89462306a36Sopenharmony_ci list_for_each_entry(ts, &ti->slave_active_head, active_list) { 89562306a36Sopenharmony_ci ts->pticks = ti->pticks; 89662306a36Sopenharmony_ci ts->resolution = resolution; 89762306a36Sopenharmony_ci if (list_empty(&ts->ack_list)) 89862306a36Sopenharmony_ci list_add_tail(&ts->ack_list, ack_list_head); 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci if (timer->flags & SNDRV_TIMER_FLG_RESCHED) 90262306a36Sopenharmony_ci snd_timer_reschedule(timer, timer->sticks); 90362306a36Sopenharmony_ci if (timer->running) { 90462306a36Sopenharmony_ci if (timer->hw.flags & SNDRV_TIMER_HW_STOP) { 90562306a36Sopenharmony_ci timer->hw.stop(timer); 90662306a36Sopenharmony_ci timer->flags |= SNDRV_TIMER_FLG_CHANGE; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) || 90962306a36Sopenharmony_ci (timer->flags & SNDRV_TIMER_FLG_CHANGE)) { 91062306a36Sopenharmony_ci /* restart timer */ 91162306a36Sopenharmony_ci timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; 91262306a36Sopenharmony_ci timer->hw.start(timer); 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci } else { 91562306a36Sopenharmony_ci timer->hw.stop(timer); 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci /* now process all fast callbacks */ 91962306a36Sopenharmony_ci snd_timer_process_callbacks(timer, &timer->ack_list_head); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci /* do we have any slow callbacks? */ 92262306a36Sopenharmony_ci use_work = !list_empty(&timer->sack_list_head); 92362306a36Sopenharmony_ci spin_unlock_irqrestore(&timer->lock, flags); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (use_work) 92662306a36Sopenharmony_ci queue_work(system_highpri_wq, &timer->task_work); 92762306a36Sopenharmony_ci} 92862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_timer_interrupt); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci/* 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci */ 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ciint snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, 93562306a36Sopenharmony_ci struct snd_timer **rtimer) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci struct snd_timer *timer; 93862306a36Sopenharmony_ci int err; 93962306a36Sopenharmony_ci static const struct snd_device_ops ops = { 94062306a36Sopenharmony_ci .dev_free = snd_timer_dev_free, 94162306a36Sopenharmony_ci .dev_register = snd_timer_dev_register, 94262306a36Sopenharmony_ci .dev_disconnect = snd_timer_dev_disconnect, 94362306a36Sopenharmony_ci }; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci if (snd_BUG_ON(!tid)) 94662306a36Sopenharmony_ci return -EINVAL; 94762306a36Sopenharmony_ci if (tid->dev_class == SNDRV_TIMER_CLASS_CARD || 94862306a36Sopenharmony_ci tid->dev_class == SNDRV_TIMER_CLASS_PCM) { 94962306a36Sopenharmony_ci if (WARN_ON(!card)) 95062306a36Sopenharmony_ci return -EINVAL; 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci if (rtimer) 95362306a36Sopenharmony_ci *rtimer = NULL; 95462306a36Sopenharmony_ci timer = kzalloc(sizeof(*timer), GFP_KERNEL); 95562306a36Sopenharmony_ci if (!timer) 95662306a36Sopenharmony_ci return -ENOMEM; 95762306a36Sopenharmony_ci timer->tmr_class = tid->dev_class; 95862306a36Sopenharmony_ci timer->card = card; 95962306a36Sopenharmony_ci timer->tmr_device = tid->device; 96062306a36Sopenharmony_ci timer->tmr_subdevice = tid->subdevice; 96162306a36Sopenharmony_ci if (id) 96262306a36Sopenharmony_ci strscpy(timer->id, id, sizeof(timer->id)); 96362306a36Sopenharmony_ci timer->sticks = 1; 96462306a36Sopenharmony_ci INIT_LIST_HEAD(&timer->device_list); 96562306a36Sopenharmony_ci INIT_LIST_HEAD(&timer->open_list_head); 96662306a36Sopenharmony_ci INIT_LIST_HEAD(&timer->active_list_head); 96762306a36Sopenharmony_ci INIT_LIST_HEAD(&timer->ack_list_head); 96862306a36Sopenharmony_ci INIT_LIST_HEAD(&timer->sack_list_head); 96962306a36Sopenharmony_ci spin_lock_init(&timer->lock); 97062306a36Sopenharmony_ci INIT_WORK(&timer->task_work, snd_timer_work); 97162306a36Sopenharmony_ci timer->max_instances = 1000; /* default limit per timer */ 97262306a36Sopenharmony_ci if (card != NULL) { 97362306a36Sopenharmony_ci timer->module = card->module; 97462306a36Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops); 97562306a36Sopenharmony_ci if (err < 0) { 97662306a36Sopenharmony_ci snd_timer_free(timer); 97762306a36Sopenharmony_ci return err; 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci if (rtimer) 98162306a36Sopenharmony_ci *rtimer = timer; 98262306a36Sopenharmony_ci return 0; 98362306a36Sopenharmony_ci} 98462306a36Sopenharmony_ciEXPORT_SYMBOL(snd_timer_new); 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_cistatic int snd_timer_free(struct snd_timer *timer) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci if (!timer) 98962306a36Sopenharmony_ci return 0; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci mutex_lock(®ister_mutex); 99262306a36Sopenharmony_ci if (! list_empty(&timer->open_list_head)) { 99362306a36Sopenharmony_ci struct list_head *p, *n; 99462306a36Sopenharmony_ci struct snd_timer_instance *ti; 99562306a36Sopenharmony_ci pr_warn("ALSA: timer %p is busy?\n", timer); 99662306a36Sopenharmony_ci list_for_each_safe(p, n, &timer->open_list_head) { 99762306a36Sopenharmony_ci list_del_init(p); 99862306a36Sopenharmony_ci ti = list_entry(p, struct snd_timer_instance, open_list); 99962306a36Sopenharmony_ci ti->timer = NULL; 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci list_del(&timer->device_list); 100362306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci if (timer->private_free) 100662306a36Sopenharmony_ci timer->private_free(timer); 100762306a36Sopenharmony_ci kfree(timer); 100862306a36Sopenharmony_ci return 0; 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cistatic int snd_timer_dev_free(struct snd_device *device) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci struct snd_timer *timer = device->device_data; 101462306a36Sopenharmony_ci return snd_timer_free(timer); 101562306a36Sopenharmony_ci} 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_cistatic int snd_timer_dev_register(struct snd_device *dev) 101862306a36Sopenharmony_ci{ 101962306a36Sopenharmony_ci struct snd_timer *timer = dev->device_data; 102062306a36Sopenharmony_ci struct snd_timer *timer1; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci if (snd_BUG_ON(!timer || !timer->hw.start || !timer->hw.stop)) 102362306a36Sopenharmony_ci return -ENXIO; 102462306a36Sopenharmony_ci if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) && 102562306a36Sopenharmony_ci !timer->hw.resolution && timer->hw.c_resolution == NULL) 102662306a36Sopenharmony_ci return -EINVAL; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci mutex_lock(®ister_mutex); 102962306a36Sopenharmony_ci list_for_each_entry(timer1, &snd_timer_list, device_list) { 103062306a36Sopenharmony_ci if (timer1->tmr_class > timer->tmr_class) 103162306a36Sopenharmony_ci break; 103262306a36Sopenharmony_ci if (timer1->tmr_class < timer->tmr_class) 103362306a36Sopenharmony_ci continue; 103462306a36Sopenharmony_ci if (timer1->card && timer->card) { 103562306a36Sopenharmony_ci if (timer1->card->number > timer->card->number) 103662306a36Sopenharmony_ci break; 103762306a36Sopenharmony_ci if (timer1->card->number < timer->card->number) 103862306a36Sopenharmony_ci continue; 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci if (timer1->tmr_device > timer->tmr_device) 104162306a36Sopenharmony_ci break; 104262306a36Sopenharmony_ci if (timer1->tmr_device < timer->tmr_device) 104362306a36Sopenharmony_ci continue; 104462306a36Sopenharmony_ci if (timer1->tmr_subdevice > timer->tmr_subdevice) 104562306a36Sopenharmony_ci break; 104662306a36Sopenharmony_ci if (timer1->tmr_subdevice < timer->tmr_subdevice) 104762306a36Sopenharmony_ci continue; 104862306a36Sopenharmony_ci /* conflicts.. */ 104962306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 105062306a36Sopenharmony_ci return -EBUSY; 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci list_add_tail(&timer->device_list, &timer1->device_list); 105362306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 105462306a36Sopenharmony_ci return 0; 105562306a36Sopenharmony_ci} 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_cistatic int snd_timer_dev_disconnect(struct snd_device *device) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci struct snd_timer *timer = device->device_data; 106062306a36Sopenharmony_ci struct snd_timer_instance *ti; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci mutex_lock(®ister_mutex); 106362306a36Sopenharmony_ci list_del_init(&timer->device_list); 106462306a36Sopenharmony_ci /* wake up pending sleepers */ 106562306a36Sopenharmony_ci list_for_each_entry(ti, &timer->open_list_head, open_list) { 106662306a36Sopenharmony_ci if (ti->disconnect) 106762306a36Sopenharmony_ci ti->disconnect(ti); 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 107062306a36Sopenharmony_ci return 0; 107162306a36Sopenharmony_ci} 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_civoid snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tstamp) 107462306a36Sopenharmony_ci{ 107562306a36Sopenharmony_ci unsigned long flags; 107662306a36Sopenharmony_ci unsigned long resolution = 0; 107762306a36Sopenharmony_ci struct snd_timer_instance *ti, *ts; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci if (timer->card && timer->card->shutdown) 108062306a36Sopenharmony_ci return; 108162306a36Sopenharmony_ci if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)) 108262306a36Sopenharmony_ci return; 108362306a36Sopenharmony_ci if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART || 108462306a36Sopenharmony_ci event > SNDRV_TIMER_EVENT_MRESUME)) 108562306a36Sopenharmony_ci return; 108662306a36Sopenharmony_ci spin_lock_irqsave(&timer->lock, flags); 108762306a36Sopenharmony_ci if (event == SNDRV_TIMER_EVENT_MSTART || 108862306a36Sopenharmony_ci event == SNDRV_TIMER_EVENT_MCONTINUE || 108962306a36Sopenharmony_ci event == SNDRV_TIMER_EVENT_MRESUME) 109062306a36Sopenharmony_ci resolution = snd_timer_hw_resolution(timer); 109162306a36Sopenharmony_ci list_for_each_entry(ti, &timer->active_list_head, active_list) { 109262306a36Sopenharmony_ci if (ti->ccallback) 109362306a36Sopenharmony_ci ti->ccallback(ti, event, tstamp, resolution); 109462306a36Sopenharmony_ci list_for_each_entry(ts, &ti->slave_active_head, active_list) 109562306a36Sopenharmony_ci if (ts->ccallback) 109662306a36Sopenharmony_ci ts->ccallback(ts, event, tstamp, resolution); 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci spin_unlock_irqrestore(&timer->lock, flags); 109962306a36Sopenharmony_ci} 110062306a36Sopenharmony_ciEXPORT_SYMBOL(snd_timer_notify); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci/* 110362306a36Sopenharmony_ci * exported functions for global timers 110462306a36Sopenharmony_ci */ 110562306a36Sopenharmony_ciint snd_timer_global_new(char *id, int device, struct snd_timer **rtimer) 110662306a36Sopenharmony_ci{ 110762306a36Sopenharmony_ci struct snd_timer_id tid; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; 111062306a36Sopenharmony_ci tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; 111162306a36Sopenharmony_ci tid.card = -1; 111262306a36Sopenharmony_ci tid.device = device; 111362306a36Sopenharmony_ci tid.subdevice = 0; 111462306a36Sopenharmony_ci return snd_timer_new(NULL, id, &tid, rtimer); 111562306a36Sopenharmony_ci} 111662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_timer_global_new); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ciint snd_timer_global_free(struct snd_timer *timer) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci return snd_timer_free(timer); 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ciEXPORT_SYMBOL(snd_timer_global_free); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ciint snd_timer_global_register(struct snd_timer *timer) 112562306a36Sopenharmony_ci{ 112662306a36Sopenharmony_ci struct snd_device dev; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci memset(&dev, 0, sizeof(dev)); 112962306a36Sopenharmony_ci dev.device_data = timer; 113062306a36Sopenharmony_ci return snd_timer_dev_register(&dev); 113162306a36Sopenharmony_ci} 113262306a36Sopenharmony_ciEXPORT_SYMBOL(snd_timer_global_register); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci/* 113562306a36Sopenharmony_ci * System timer 113662306a36Sopenharmony_ci */ 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_cistruct snd_timer_system_private { 113962306a36Sopenharmony_ci struct timer_list tlist; 114062306a36Sopenharmony_ci struct snd_timer *snd_timer; 114162306a36Sopenharmony_ci unsigned long last_expires; 114262306a36Sopenharmony_ci unsigned long last_jiffies; 114362306a36Sopenharmony_ci unsigned long correction; 114462306a36Sopenharmony_ci}; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_cistatic void snd_timer_s_function(struct timer_list *t) 114762306a36Sopenharmony_ci{ 114862306a36Sopenharmony_ci struct snd_timer_system_private *priv = from_timer(priv, t, 114962306a36Sopenharmony_ci tlist); 115062306a36Sopenharmony_ci struct snd_timer *timer = priv->snd_timer; 115162306a36Sopenharmony_ci unsigned long jiff = jiffies; 115262306a36Sopenharmony_ci if (time_after(jiff, priv->last_expires)) 115362306a36Sopenharmony_ci priv->correction += (long)jiff - (long)priv->last_expires; 115462306a36Sopenharmony_ci snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies); 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_cistatic int snd_timer_s_start(struct snd_timer * timer) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci struct snd_timer_system_private *priv; 116062306a36Sopenharmony_ci unsigned long njiff; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci priv = (struct snd_timer_system_private *) timer->private_data; 116362306a36Sopenharmony_ci njiff = (priv->last_jiffies = jiffies); 116462306a36Sopenharmony_ci if (priv->correction > timer->sticks - 1) { 116562306a36Sopenharmony_ci priv->correction -= timer->sticks - 1; 116662306a36Sopenharmony_ci njiff++; 116762306a36Sopenharmony_ci } else { 116862306a36Sopenharmony_ci njiff += timer->sticks - priv->correction; 116962306a36Sopenharmony_ci priv->correction = 0; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci priv->last_expires = njiff; 117262306a36Sopenharmony_ci mod_timer(&priv->tlist, njiff); 117362306a36Sopenharmony_ci return 0; 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic int snd_timer_s_stop(struct snd_timer * timer) 117762306a36Sopenharmony_ci{ 117862306a36Sopenharmony_ci struct snd_timer_system_private *priv; 117962306a36Sopenharmony_ci unsigned long jiff; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci priv = (struct snd_timer_system_private *) timer->private_data; 118262306a36Sopenharmony_ci del_timer(&priv->tlist); 118362306a36Sopenharmony_ci jiff = jiffies; 118462306a36Sopenharmony_ci if (time_before(jiff, priv->last_expires)) 118562306a36Sopenharmony_ci timer->sticks = priv->last_expires - jiff; 118662306a36Sopenharmony_ci else 118762306a36Sopenharmony_ci timer->sticks = 1; 118862306a36Sopenharmony_ci priv->correction = 0; 118962306a36Sopenharmony_ci return 0; 119062306a36Sopenharmony_ci} 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_cistatic int snd_timer_s_close(struct snd_timer *timer) 119362306a36Sopenharmony_ci{ 119462306a36Sopenharmony_ci struct snd_timer_system_private *priv; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci priv = (struct snd_timer_system_private *)timer->private_data; 119762306a36Sopenharmony_ci del_timer_sync(&priv->tlist); 119862306a36Sopenharmony_ci return 0; 119962306a36Sopenharmony_ci} 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_cistatic const struct snd_timer_hardware snd_timer_system = 120262306a36Sopenharmony_ci{ 120362306a36Sopenharmony_ci .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_WORK, 120462306a36Sopenharmony_ci .resolution = 1000000000L / HZ, 120562306a36Sopenharmony_ci .ticks = 10000000L, 120662306a36Sopenharmony_ci .close = snd_timer_s_close, 120762306a36Sopenharmony_ci .start = snd_timer_s_start, 120862306a36Sopenharmony_ci .stop = snd_timer_s_stop 120962306a36Sopenharmony_ci}; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cistatic void snd_timer_free_system(struct snd_timer *timer) 121262306a36Sopenharmony_ci{ 121362306a36Sopenharmony_ci kfree(timer->private_data); 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic int snd_timer_register_system(void) 121762306a36Sopenharmony_ci{ 121862306a36Sopenharmony_ci struct snd_timer *timer; 121962306a36Sopenharmony_ci struct snd_timer_system_private *priv; 122062306a36Sopenharmony_ci int err; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer); 122362306a36Sopenharmony_ci if (err < 0) 122462306a36Sopenharmony_ci return err; 122562306a36Sopenharmony_ci strcpy(timer->name, "system timer"); 122662306a36Sopenharmony_ci timer->hw = snd_timer_system; 122762306a36Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 122862306a36Sopenharmony_ci if (priv == NULL) { 122962306a36Sopenharmony_ci snd_timer_free(timer); 123062306a36Sopenharmony_ci return -ENOMEM; 123162306a36Sopenharmony_ci } 123262306a36Sopenharmony_ci priv->snd_timer = timer; 123362306a36Sopenharmony_ci timer_setup(&priv->tlist, snd_timer_s_function, 0); 123462306a36Sopenharmony_ci timer->private_data = priv; 123562306a36Sopenharmony_ci timer->private_free = snd_timer_free_system; 123662306a36Sopenharmony_ci return snd_timer_global_register(timer); 123762306a36Sopenharmony_ci} 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci#ifdef CONFIG_SND_PROC_FS 124062306a36Sopenharmony_ci/* 124162306a36Sopenharmony_ci * Info interface 124262306a36Sopenharmony_ci */ 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistatic void snd_timer_proc_read(struct snd_info_entry *entry, 124562306a36Sopenharmony_ci struct snd_info_buffer *buffer) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct snd_timer *timer; 124862306a36Sopenharmony_ci struct snd_timer_instance *ti; 124962306a36Sopenharmony_ci unsigned long resolution; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci mutex_lock(®ister_mutex); 125262306a36Sopenharmony_ci list_for_each_entry(timer, &snd_timer_list, device_list) { 125362306a36Sopenharmony_ci if (timer->card && timer->card->shutdown) 125462306a36Sopenharmony_ci continue; 125562306a36Sopenharmony_ci switch (timer->tmr_class) { 125662306a36Sopenharmony_ci case SNDRV_TIMER_CLASS_GLOBAL: 125762306a36Sopenharmony_ci snd_iprintf(buffer, "G%i: ", timer->tmr_device); 125862306a36Sopenharmony_ci break; 125962306a36Sopenharmony_ci case SNDRV_TIMER_CLASS_CARD: 126062306a36Sopenharmony_ci snd_iprintf(buffer, "C%i-%i: ", 126162306a36Sopenharmony_ci timer->card->number, timer->tmr_device); 126262306a36Sopenharmony_ci break; 126362306a36Sopenharmony_ci case SNDRV_TIMER_CLASS_PCM: 126462306a36Sopenharmony_ci snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number, 126562306a36Sopenharmony_ci timer->tmr_device, timer->tmr_subdevice); 126662306a36Sopenharmony_ci break; 126762306a36Sopenharmony_ci default: 126862306a36Sopenharmony_ci snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class, 126962306a36Sopenharmony_ci timer->card ? timer->card->number : -1, 127062306a36Sopenharmony_ci timer->tmr_device, timer->tmr_subdevice); 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci snd_iprintf(buffer, "%s :", timer->name); 127362306a36Sopenharmony_ci spin_lock_irq(&timer->lock); 127462306a36Sopenharmony_ci resolution = snd_timer_hw_resolution(timer); 127562306a36Sopenharmony_ci spin_unlock_irq(&timer->lock); 127662306a36Sopenharmony_ci if (resolution) 127762306a36Sopenharmony_ci snd_iprintf(buffer, " %lu.%03luus (%lu ticks)", 127862306a36Sopenharmony_ci resolution / 1000, 127962306a36Sopenharmony_ci resolution % 1000, 128062306a36Sopenharmony_ci timer->hw.ticks); 128162306a36Sopenharmony_ci if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 128262306a36Sopenharmony_ci snd_iprintf(buffer, " SLAVE"); 128362306a36Sopenharmony_ci snd_iprintf(buffer, "\n"); 128462306a36Sopenharmony_ci list_for_each_entry(ti, &timer->open_list_head, open_list) 128562306a36Sopenharmony_ci snd_iprintf(buffer, " Client %s : %s\n", 128662306a36Sopenharmony_ci ti->owner ? ti->owner : "unknown", 128762306a36Sopenharmony_ci (ti->flags & (SNDRV_TIMER_IFLG_START | 128862306a36Sopenharmony_ci SNDRV_TIMER_IFLG_RUNNING)) 128962306a36Sopenharmony_ci ? "running" : "stopped"); 129062306a36Sopenharmony_ci } 129162306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 129262306a36Sopenharmony_ci} 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_cistatic struct snd_info_entry *snd_timer_proc_entry; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_cistatic void __init snd_timer_proc_init(void) 129762306a36Sopenharmony_ci{ 129862306a36Sopenharmony_ci struct snd_info_entry *entry; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL); 130162306a36Sopenharmony_ci if (entry != NULL) { 130262306a36Sopenharmony_ci entry->c.text.read = snd_timer_proc_read; 130362306a36Sopenharmony_ci if (snd_info_register(entry) < 0) { 130462306a36Sopenharmony_ci snd_info_free_entry(entry); 130562306a36Sopenharmony_ci entry = NULL; 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci } 130862306a36Sopenharmony_ci snd_timer_proc_entry = entry; 130962306a36Sopenharmony_ci} 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_cistatic void __exit snd_timer_proc_done(void) 131262306a36Sopenharmony_ci{ 131362306a36Sopenharmony_ci snd_info_free_entry(snd_timer_proc_entry); 131462306a36Sopenharmony_ci} 131562306a36Sopenharmony_ci#else /* !CONFIG_SND_PROC_FS */ 131662306a36Sopenharmony_ci#define snd_timer_proc_init() 131762306a36Sopenharmony_ci#define snd_timer_proc_done() 131862306a36Sopenharmony_ci#endif 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci/* 132162306a36Sopenharmony_ci * USER SPACE interface 132262306a36Sopenharmony_ci */ 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_cistatic void snd_timer_user_interrupt(struct snd_timer_instance *timeri, 132562306a36Sopenharmony_ci unsigned long resolution, 132662306a36Sopenharmony_ci unsigned long ticks) 132762306a36Sopenharmony_ci{ 132862306a36Sopenharmony_ci struct snd_timer_user *tu = timeri->callback_data; 132962306a36Sopenharmony_ci struct snd_timer_read *r; 133062306a36Sopenharmony_ci int prev; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci spin_lock(&tu->qlock); 133362306a36Sopenharmony_ci if (tu->qused > 0) { 133462306a36Sopenharmony_ci prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; 133562306a36Sopenharmony_ci r = &tu->queue[prev]; 133662306a36Sopenharmony_ci if (r->resolution == resolution) { 133762306a36Sopenharmony_ci r->ticks += ticks; 133862306a36Sopenharmony_ci goto __wake; 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci if (tu->qused >= tu->queue_size) { 134262306a36Sopenharmony_ci tu->overrun++; 134362306a36Sopenharmony_ci } else { 134462306a36Sopenharmony_ci r = &tu->queue[tu->qtail++]; 134562306a36Sopenharmony_ci tu->qtail %= tu->queue_size; 134662306a36Sopenharmony_ci r->resolution = resolution; 134762306a36Sopenharmony_ci r->ticks = ticks; 134862306a36Sopenharmony_ci tu->qused++; 134962306a36Sopenharmony_ci } 135062306a36Sopenharmony_ci __wake: 135162306a36Sopenharmony_ci spin_unlock(&tu->qlock); 135262306a36Sopenharmony_ci snd_kill_fasync(tu->fasync, SIGIO, POLL_IN); 135362306a36Sopenharmony_ci wake_up(&tu->qchange_sleep); 135462306a36Sopenharmony_ci} 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_cistatic void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu, 135762306a36Sopenharmony_ci struct snd_timer_tread64 *tread) 135862306a36Sopenharmony_ci{ 135962306a36Sopenharmony_ci if (tu->qused >= tu->queue_size) { 136062306a36Sopenharmony_ci tu->overrun++; 136162306a36Sopenharmony_ci } else { 136262306a36Sopenharmony_ci memcpy(&tu->tqueue[tu->qtail++], tread, sizeof(*tread)); 136362306a36Sopenharmony_ci tu->qtail %= tu->queue_size; 136462306a36Sopenharmony_ci tu->qused++; 136562306a36Sopenharmony_ci } 136662306a36Sopenharmony_ci} 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_cistatic void snd_timer_user_ccallback(struct snd_timer_instance *timeri, 136962306a36Sopenharmony_ci int event, 137062306a36Sopenharmony_ci struct timespec64 *tstamp, 137162306a36Sopenharmony_ci unsigned long resolution) 137262306a36Sopenharmony_ci{ 137362306a36Sopenharmony_ci struct snd_timer_user *tu = timeri->callback_data; 137462306a36Sopenharmony_ci struct snd_timer_tread64 r1; 137562306a36Sopenharmony_ci unsigned long flags; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci if (event >= SNDRV_TIMER_EVENT_START && 137862306a36Sopenharmony_ci event <= SNDRV_TIMER_EVENT_PAUSE) 137962306a36Sopenharmony_ci tu->tstamp = *tstamp; 138062306a36Sopenharmony_ci if ((tu->filter & (1 << event)) == 0 || !tu->tread) 138162306a36Sopenharmony_ci return; 138262306a36Sopenharmony_ci memset(&r1, 0, sizeof(r1)); 138362306a36Sopenharmony_ci r1.event = event; 138462306a36Sopenharmony_ci r1.tstamp_sec = tstamp->tv_sec; 138562306a36Sopenharmony_ci r1.tstamp_nsec = tstamp->tv_nsec; 138662306a36Sopenharmony_ci r1.val = resolution; 138762306a36Sopenharmony_ci spin_lock_irqsave(&tu->qlock, flags); 138862306a36Sopenharmony_ci snd_timer_user_append_to_tqueue(tu, &r1); 138962306a36Sopenharmony_ci spin_unlock_irqrestore(&tu->qlock, flags); 139062306a36Sopenharmony_ci snd_kill_fasync(tu->fasync, SIGIO, POLL_IN); 139162306a36Sopenharmony_ci wake_up(&tu->qchange_sleep); 139262306a36Sopenharmony_ci} 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cistatic void snd_timer_user_disconnect(struct snd_timer_instance *timeri) 139562306a36Sopenharmony_ci{ 139662306a36Sopenharmony_ci struct snd_timer_user *tu = timeri->callback_data; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci tu->disconnected = true; 139962306a36Sopenharmony_ci wake_up(&tu->qchange_sleep); 140062306a36Sopenharmony_ci} 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_cistatic void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, 140362306a36Sopenharmony_ci unsigned long resolution, 140462306a36Sopenharmony_ci unsigned long ticks) 140562306a36Sopenharmony_ci{ 140662306a36Sopenharmony_ci struct snd_timer_user *tu = timeri->callback_data; 140762306a36Sopenharmony_ci struct snd_timer_tread64 *r, r1; 140862306a36Sopenharmony_ci struct timespec64 tstamp; 140962306a36Sopenharmony_ci int prev, append = 0; 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci memset(&r1, 0, sizeof(r1)); 141262306a36Sopenharmony_ci memset(&tstamp, 0, sizeof(tstamp)); 141362306a36Sopenharmony_ci spin_lock(&tu->qlock); 141462306a36Sopenharmony_ci if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) | 141562306a36Sopenharmony_ci (1 << SNDRV_TIMER_EVENT_TICK))) == 0) { 141662306a36Sopenharmony_ci spin_unlock(&tu->qlock); 141762306a36Sopenharmony_ci return; 141862306a36Sopenharmony_ci } 141962306a36Sopenharmony_ci if (tu->last_resolution != resolution || ticks > 0) { 142062306a36Sopenharmony_ci if (timer_tstamp_monotonic) 142162306a36Sopenharmony_ci ktime_get_ts64(&tstamp); 142262306a36Sopenharmony_ci else 142362306a36Sopenharmony_ci ktime_get_real_ts64(&tstamp); 142462306a36Sopenharmony_ci } 142562306a36Sopenharmony_ci if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && 142662306a36Sopenharmony_ci tu->last_resolution != resolution) { 142762306a36Sopenharmony_ci r1.event = SNDRV_TIMER_EVENT_RESOLUTION; 142862306a36Sopenharmony_ci r1.tstamp_sec = tstamp.tv_sec; 142962306a36Sopenharmony_ci r1.tstamp_nsec = tstamp.tv_nsec; 143062306a36Sopenharmony_ci r1.val = resolution; 143162306a36Sopenharmony_ci snd_timer_user_append_to_tqueue(tu, &r1); 143262306a36Sopenharmony_ci tu->last_resolution = resolution; 143362306a36Sopenharmony_ci append++; 143462306a36Sopenharmony_ci } 143562306a36Sopenharmony_ci if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0) 143662306a36Sopenharmony_ci goto __wake; 143762306a36Sopenharmony_ci if (ticks == 0) 143862306a36Sopenharmony_ci goto __wake; 143962306a36Sopenharmony_ci if (tu->qused > 0) { 144062306a36Sopenharmony_ci prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; 144162306a36Sopenharmony_ci r = &tu->tqueue[prev]; 144262306a36Sopenharmony_ci if (r->event == SNDRV_TIMER_EVENT_TICK) { 144362306a36Sopenharmony_ci r->tstamp_sec = tstamp.tv_sec; 144462306a36Sopenharmony_ci r->tstamp_nsec = tstamp.tv_nsec; 144562306a36Sopenharmony_ci r->val += ticks; 144662306a36Sopenharmony_ci append++; 144762306a36Sopenharmony_ci goto __wake; 144862306a36Sopenharmony_ci } 144962306a36Sopenharmony_ci } 145062306a36Sopenharmony_ci r1.event = SNDRV_TIMER_EVENT_TICK; 145162306a36Sopenharmony_ci r1.tstamp_sec = tstamp.tv_sec; 145262306a36Sopenharmony_ci r1.tstamp_nsec = tstamp.tv_nsec; 145362306a36Sopenharmony_ci r1.val = ticks; 145462306a36Sopenharmony_ci snd_timer_user_append_to_tqueue(tu, &r1); 145562306a36Sopenharmony_ci append++; 145662306a36Sopenharmony_ci __wake: 145762306a36Sopenharmony_ci spin_unlock(&tu->qlock); 145862306a36Sopenharmony_ci if (append == 0) 145962306a36Sopenharmony_ci return; 146062306a36Sopenharmony_ci snd_kill_fasync(tu->fasync, SIGIO, POLL_IN); 146162306a36Sopenharmony_ci wake_up(&tu->qchange_sleep); 146262306a36Sopenharmony_ci} 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_cistatic int realloc_user_queue(struct snd_timer_user *tu, int size) 146562306a36Sopenharmony_ci{ 146662306a36Sopenharmony_ci struct snd_timer_read *queue = NULL; 146762306a36Sopenharmony_ci struct snd_timer_tread64 *tqueue = NULL; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci if (tu->tread) { 147062306a36Sopenharmony_ci tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL); 147162306a36Sopenharmony_ci if (!tqueue) 147262306a36Sopenharmony_ci return -ENOMEM; 147362306a36Sopenharmony_ci } else { 147462306a36Sopenharmony_ci queue = kcalloc(size, sizeof(*queue), GFP_KERNEL); 147562306a36Sopenharmony_ci if (!queue) 147662306a36Sopenharmony_ci return -ENOMEM; 147762306a36Sopenharmony_ci } 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci spin_lock_irq(&tu->qlock); 148062306a36Sopenharmony_ci kfree(tu->queue); 148162306a36Sopenharmony_ci kfree(tu->tqueue); 148262306a36Sopenharmony_ci tu->queue_size = size; 148362306a36Sopenharmony_ci tu->queue = queue; 148462306a36Sopenharmony_ci tu->tqueue = tqueue; 148562306a36Sopenharmony_ci tu->qhead = tu->qtail = tu->qused = 0; 148662306a36Sopenharmony_ci spin_unlock_irq(&tu->qlock); 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci return 0; 148962306a36Sopenharmony_ci} 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_cistatic int snd_timer_user_open(struct inode *inode, struct file *file) 149262306a36Sopenharmony_ci{ 149362306a36Sopenharmony_ci struct snd_timer_user *tu; 149462306a36Sopenharmony_ci int err; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci err = stream_open(inode, file); 149762306a36Sopenharmony_ci if (err < 0) 149862306a36Sopenharmony_ci return err; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci tu = kzalloc(sizeof(*tu), GFP_KERNEL); 150162306a36Sopenharmony_ci if (tu == NULL) 150262306a36Sopenharmony_ci return -ENOMEM; 150362306a36Sopenharmony_ci spin_lock_init(&tu->qlock); 150462306a36Sopenharmony_ci init_waitqueue_head(&tu->qchange_sleep); 150562306a36Sopenharmony_ci mutex_init(&tu->ioctl_lock); 150662306a36Sopenharmony_ci tu->ticks = 1; 150762306a36Sopenharmony_ci if (realloc_user_queue(tu, 128) < 0) { 150862306a36Sopenharmony_ci kfree(tu); 150962306a36Sopenharmony_ci return -ENOMEM; 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci file->private_data = tu; 151262306a36Sopenharmony_ci return 0; 151362306a36Sopenharmony_ci} 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_cistatic int snd_timer_user_release(struct inode *inode, struct file *file) 151662306a36Sopenharmony_ci{ 151762306a36Sopenharmony_ci struct snd_timer_user *tu; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci if (file->private_data) { 152062306a36Sopenharmony_ci tu = file->private_data; 152162306a36Sopenharmony_ci file->private_data = NULL; 152262306a36Sopenharmony_ci mutex_lock(&tu->ioctl_lock); 152362306a36Sopenharmony_ci if (tu->timeri) { 152462306a36Sopenharmony_ci snd_timer_close(tu->timeri); 152562306a36Sopenharmony_ci snd_timer_instance_free(tu->timeri); 152662306a36Sopenharmony_ci } 152762306a36Sopenharmony_ci mutex_unlock(&tu->ioctl_lock); 152862306a36Sopenharmony_ci snd_fasync_free(tu->fasync); 152962306a36Sopenharmony_ci kfree(tu->queue); 153062306a36Sopenharmony_ci kfree(tu->tqueue); 153162306a36Sopenharmony_ci kfree(tu); 153262306a36Sopenharmony_ci } 153362306a36Sopenharmony_ci return 0; 153462306a36Sopenharmony_ci} 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_cistatic void snd_timer_user_zero_id(struct snd_timer_id *id) 153762306a36Sopenharmony_ci{ 153862306a36Sopenharmony_ci id->dev_class = SNDRV_TIMER_CLASS_NONE; 153962306a36Sopenharmony_ci id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; 154062306a36Sopenharmony_ci id->card = -1; 154162306a36Sopenharmony_ci id->device = -1; 154262306a36Sopenharmony_ci id->subdevice = -1; 154362306a36Sopenharmony_ci} 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_cistatic void snd_timer_user_copy_id(struct snd_timer_id *id, struct snd_timer *timer) 154662306a36Sopenharmony_ci{ 154762306a36Sopenharmony_ci id->dev_class = timer->tmr_class; 154862306a36Sopenharmony_ci id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; 154962306a36Sopenharmony_ci id->card = timer->card ? timer->card->number : -1; 155062306a36Sopenharmony_ci id->device = timer->tmr_device; 155162306a36Sopenharmony_ci id->subdevice = timer->tmr_subdevice; 155262306a36Sopenharmony_ci} 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_cistatic int snd_timer_user_next_device(struct snd_timer_id __user *_tid) 155562306a36Sopenharmony_ci{ 155662306a36Sopenharmony_ci struct snd_timer_id id; 155762306a36Sopenharmony_ci struct snd_timer *timer; 155862306a36Sopenharmony_ci struct list_head *p; 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci if (copy_from_user(&id, _tid, sizeof(id))) 156162306a36Sopenharmony_ci return -EFAULT; 156262306a36Sopenharmony_ci mutex_lock(®ister_mutex); 156362306a36Sopenharmony_ci if (id.dev_class < 0) { /* first item */ 156462306a36Sopenharmony_ci if (list_empty(&snd_timer_list)) 156562306a36Sopenharmony_ci snd_timer_user_zero_id(&id); 156662306a36Sopenharmony_ci else { 156762306a36Sopenharmony_ci timer = list_entry(snd_timer_list.next, 156862306a36Sopenharmony_ci struct snd_timer, device_list); 156962306a36Sopenharmony_ci snd_timer_user_copy_id(&id, timer); 157062306a36Sopenharmony_ci } 157162306a36Sopenharmony_ci } else { 157262306a36Sopenharmony_ci switch (id.dev_class) { 157362306a36Sopenharmony_ci case SNDRV_TIMER_CLASS_GLOBAL: 157462306a36Sopenharmony_ci id.device = id.device < 0 ? 0 : id.device + 1; 157562306a36Sopenharmony_ci list_for_each(p, &snd_timer_list) { 157662306a36Sopenharmony_ci timer = list_entry(p, struct snd_timer, device_list); 157762306a36Sopenharmony_ci if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) { 157862306a36Sopenharmony_ci snd_timer_user_copy_id(&id, timer); 157962306a36Sopenharmony_ci break; 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci if (timer->tmr_device >= id.device) { 158262306a36Sopenharmony_ci snd_timer_user_copy_id(&id, timer); 158362306a36Sopenharmony_ci break; 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci } 158662306a36Sopenharmony_ci if (p == &snd_timer_list) 158762306a36Sopenharmony_ci snd_timer_user_zero_id(&id); 158862306a36Sopenharmony_ci break; 158962306a36Sopenharmony_ci case SNDRV_TIMER_CLASS_CARD: 159062306a36Sopenharmony_ci case SNDRV_TIMER_CLASS_PCM: 159162306a36Sopenharmony_ci if (id.card < 0) { 159262306a36Sopenharmony_ci id.card = 0; 159362306a36Sopenharmony_ci } else { 159462306a36Sopenharmony_ci if (id.device < 0) { 159562306a36Sopenharmony_ci id.device = 0; 159662306a36Sopenharmony_ci } else { 159762306a36Sopenharmony_ci if (id.subdevice < 0) 159862306a36Sopenharmony_ci id.subdevice = 0; 159962306a36Sopenharmony_ci else if (id.subdevice < INT_MAX) 160062306a36Sopenharmony_ci id.subdevice++; 160162306a36Sopenharmony_ci } 160262306a36Sopenharmony_ci } 160362306a36Sopenharmony_ci list_for_each(p, &snd_timer_list) { 160462306a36Sopenharmony_ci timer = list_entry(p, struct snd_timer, device_list); 160562306a36Sopenharmony_ci if (timer->tmr_class > id.dev_class) { 160662306a36Sopenharmony_ci snd_timer_user_copy_id(&id, timer); 160762306a36Sopenharmony_ci break; 160862306a36Sopenharmony_ci } 160962306a36Sopenharmony_ci if (timer->tmr_class < id.dev_class) 161062306a36Sopenharmony_ci continue; 161162306a36Sopenharmony_ci if (timer->card->number > id.card) { 161262306a36Sopenharmony_ci snd_timer_user_copy_id(&id, timer); 161362306a36Sopenharmony_ci break; 161462306a36Sopenharmony_ci } 161562306a36Sopenharmony_ci if (timer->card->number < id.card) 161662306a36Sopenharmony_ci continue; 161762306a36Sopenharmony_ci if (timer->tmr_device > id.device) { 161862306a36Sopenharmony_ci snd_timer_user_copy_id(&id, timer); 161962306a36Sopenharmony_ci break; 162062306a36Sopenharmony_ci } 162162306a36Sopenharmony_ci if (timer->tmr_device < id.device) 162262306a36Sopenharmony_ci continue; 162362306a36Sopenharmony_ci if (timer->tmr_subdevice > id.subdevice) { 162462306a36Sopenharmony_ci snd_timer_user_copy_id(&id, timer); 162562306a36Sopenharmony_ci break; 162662306a36Sopenharmony_ci } 162762306a36Sopenharmony_ci if (timer->tmr_subdevice < id.subdevice) 162862306a36Sopenharmony_ci continue; 162962306a36Sopenharmony_ci snd_timer_user_copy_id(&id, timer); 163062306a36Sopenharmony_ci break; 163162306a36Sopenharmony_ci } 163262306a36Sopenharmony_ci if (p == &snd_timer_list) 163362306a36Sopenharmony_ci snd_timer_user_zero_id(&id); 163462306a36Sopenharmony_ci break; 163562306a36Sopenharmony_ci default: 163662306a36Sopenharmony_ci snd_timer_user_zero_id(&id); 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci } 163962306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 164062306a36Sopenharmony_ci if (copy_to_user(_tid, &id, sizeof(*_tid))) 164162306a36Sopenharmony_ci return -EFAULT; 164262306a36Sopenharmony_ci return 0; 164362306a36Sopenharmony_ci} 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_cistatic int snd_timer_user_ginfo(struct file *file, 164662306a36Sopenharmony_ci struct snd_timer_ginfo __user *_ginfo) 164762306a36Sopenharmony_ci{ 164862306a36Sopenharmony_ci struct snd_timer_ginfo *ginfo; 164962306a36Sopenharmony_ci struct snd_timer_id tid; 165062306a36Sopenharmony_ci struct snd_timer *t; 165162306a36Sopenharmony_ci struct list_head *p; 165262306a36Sopenharmony_ci int err = 0; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci ginfo = memdup_user(_ginfo, sizeof(*ginfo)); 165562306a36Sopenharmony_ci if (IS_ERR(ginfo)) 165662306a36Sopenharmony_ci return PTR_ERR(ginfo); 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci tid = ginfo->tid; 165962306a36Sopenharmony_ci memset(ginfo, 0, sizeof(*ginfo)); 166062306a36Sopenharmony_ci ginfo->tid = tid; 166162306a36Sopenharmony_ci mutex_lock(®ister_mutex); 166262306a36Sopenharmony_ci t = snd_timer_find(&tid); 166362306a36Sopenharmony_ci if (t != NULL) { 166462306a36Sopenharmony_ci ginfo->card = t->card ? t->card->number : -1; 166562306a36Sopenharmony_ci if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) 166662306a36Sopenharmony_ci ginfo->flags |= SNDRV_TIMER_FLG_SLAVE; 166762306a36Sopenharmony_ci strscpy(ginfo->id, t->id, sizeof(ginfo->id)); 166862306a36Sopenharmony_ci strscpy(ginfo->name, t->name, sizeof(ginfo->name)); 166962306a36Sopenharmony_ci spin_lock_irq(&t->lock); 167062306a36Sopenharmony_ci ginfo->resolution = snd_timer_hw_resolution(t); 167162306a36Sopenharmony_ci spin_unlock_irq(&t->lock); 167262306a36Sopenharmony_ci if (t->hw.resolution_min > 0) { 167362306a36Sopenharmony_ci ginfo->resolution_min = t->hw.resolution_min; 167462306a36Sopenharmony_ci ginfo->resolution_max = t->hw.resolution_max; 167562306a36Sopenharmony_ci } 167662306a36Sopenharmony_ci list_for_each(p, &t->open_list_head) { 167762306a36Sopenharmony_ci ginfo->clients++; 167862306a36Sopenharmony_ci } 167962306a36Sopenharmony_ci } else { 168062306a36Sopenharmony_ci err = -ENODEV; 168162306a36Sopenharmony_ci } 168262306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 168362306a36Sopenharmony_ci if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo))) 168462306a36Sopenharmony_ci err = -EFAULT; 168562306a36Sopenharmony_ci kfree(ginfo); 168662306a36Sopenharmony_ci return err; 168762306a36Sopenharmony_ci} 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_cistatic int timer_set_gparams(struct snd_timer_gparams *gparams) 169062306a36Sopenharmony_ci{ 169162306a36Sopenharmony_ci struct snd_timer *t; 169262306a36Sopenharmony_ci int err; 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci mutex_lock(®ister_mutex); 169562306a36Sopenharmony_ci t = snd_timer_find(&gparams->tid); 169662306a36Sopenharmony_ci if (!t) { 169762306a36Sopenharmony_ci err = -ENODEV; 169862306a36Sopenharmony_ci goto _error; 169962306a36Sopenharmony_ci } 170062306a36Sopenharmony_ci if (!list_empty(&t->open_list_head)) { 170162306a36Sopenharmony_ci err = -EBUSY; 170262306a36Sopenharmony_ci goto _error; 170362306a36Sopenharmony_ci } 170462306a36Sopenharmony_ci if (!t->hw.set_period) { 170562306a36Sopenharmony_ci err = -ENOSYS; 170662306a36Sopenharmony_ci goto _error; 170762306a36Sopenharmony_ci } 170862306a36Sopenharmony_ci err = t->hw.set_period(t, gparams->period_num, gparams->period_den); 170962306a36Sopenharmony_ci_error: 171062306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 171162306a36Sopenharmony_ci return err; 171262306a36Sopenharmony_ci} 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_cistatic int snd_timer_user_gparams(struct file *file, 171562306a36Sopenharmony_ci struct snd_timer_gparams __user *_gparams) 171662306a36Sopenharmony_ci{ 171762306a36Sopenharmony_ci struct snd_timer_gparams gparams; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci if (copy_from_user(&gparams, _gparams, sizeof(gparams))) 172062306a36Sopenharmony_ci return -EFAULT; 172162306a36Sopenharmony_ci return timer_set_gparams(&gparams); 172262306a36Sopenharmony_ci} 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_cistatic int snd_timer_user_gstatus(struct file *file, 172562306a36Sopenharmony_ci struct snd_timer_gstatus __user *_gstatus) 172662306a36Sopenharmony_ci{ 172762306a36Sopenharmony_ci struct snd_timer_gstatus gstatus; 172862306a36Sopenharmony_ci struct snd_timer_id tid; 172962306a36Sopenharmony_ci struct snd_timer *t; 173062306a36Sopenharmony_ci int err = 0; 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus))) 173362306a36Sopenharmony_ci return -EFAULT; 173462306a36Sopenharmony_ci tid = gstatus.tid; 173562306a36Sopenharmony_ci memset(&gstatus, 0, sizeof(gstatus)); 173662306a36Sopenharmony_ci gstatus.tid = tid; 173762306a36Sopenharmony_ci mutex_lock(®ister_mutex); 173862306a36Sopenharmony_ci t = snd_timer_find(&tid); 173962306a36Sopenharmony_ci if (t != NULL) { 174062306a36Sopenharmony_ci spin_lock_irq(&t->lock); 174162306a36Sopenharmony_ci gstatus.resolution = snd_timer_hw_resolution(t); 174262306a36Sopenharmony_ci if (t->hw.precise_resolution) { 174362306a36Sopenharmony_ci t->hw.precise_resolution(t, &gstatus.resolution_num, 174462306a36Sopenharmony_ci &gstatus.resolution_den); 174562306a36Sopenharmony_ci } else { 174662306a36Sopenharmony_ci gstatus.resolution_num = gstatus.resolution; 174762306a36Sopenharmony_ci gstatus.resolution_den = 1000000000uL; 174862306a36Sopenharmony_ci } 174962306a36Sopenharmony_ci spin_unlock_irq(&t->lock); 175062306a36Sopenharmony_ci } else { 175162306a36Sopenharmony_ci err = -ENODEV; 175262306a36Sopenharmony_ci } 175362306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 175462306a36Sopenharmony_ci if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus))) 175562306a36Sopenharmony_ci err = -EFAULT; 175662306a36Sopenharmony_ci return err; 175762306a36Sopenharmony_ci} 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_cistatic int snd_timer_user_tselect(struct file *file, 176062306a36Sopenharmony_ci struct snd_timer_select __user *_tselect) 176162306a36Sopenharmony_ci{ 176262306a36Sopenharmony_ci struct snd_timer_user *tu; 176362306a36Sopenharmony_ci struct snd_timer_select tselect; 176462306a36Sopenharmony_ci char str[32]; 176562306a36Sopenharmony_ci int err = 0; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci tu = file->private_data; 176862306a36Sopenharmony_ci if (tu->timeri) { 176962306a36Sopenharmony_ci snd_timer_close(tu->timeri); 177062306a36Sopenharmony_ci snd_timer_instance_free(tu->timeri); 177162306a36Sopenharmony_ci tu->timeri = NULL; 177262306a36Sopenharmony_ci } 177362306a36Sopenharmony_ci if (copy_from_user(&tselect, _tselect, sizeof(tselect))) { 177462306a36Sopenharmony_ci err = -EFAULT; 177562306a36Sopenharmony_ci goto __err; 177662306a36Sopenharmony_ci } 177762306a36Sopenharmony_ci sprintf(str, "application %i", current->pid); 177862306a36Sopenharmony_ci if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE) 177962306a36Sopenharmony_ci tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION; 178062306a36Sopenharmony_ci tu->timeri = snd_timer_instance_new(str); 178162306a36Sopenharmony_ci if (!tu->timeri) { 178262306a36Sopenharmony_ci err = -ENOMEM; 178362306a36Sopenharmony_ci goto __err; 178462306a36Sopenharmony_ci } 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST; 178762306a36Sopenharmony_ci tu->timeri->callback = tu->tread 178862306a36Sopenharmony_ci ? snd_timer_user_tinterrupt : snd_timer_user_interrupt; 178962306a36Sopenharmony_ci tu->timeri->ccallback = snd_timer_user_ccallback; 179062306a36Sopenharmony_ci tu->timeri->callback_data = (void *)tu; 179162306a36Sopenharmony_ci tu->timeri->disconnect = snd_timer_user_disconnect; 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci err = snd_timer_open(tu->timeri, &tselect.id, current->pid); 179462306a36Sopenharmony_ci if (err < 0) { 179562306a36Sopenharmony_ci snd_timer_instance_free(tu->timeri); 179662306a36Sopenharmony_ci tu->timeri = NULL; 179762306a36Sopenharmony_ci } 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci __err: 180062306a36Sopenharmony_ci return err; 180162306a36Sopenharmony_ci} 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_cistatic int snd_timer_user_info(struct file *file, 180462306a36Sopenharmony_ci struct snd_timer_info __user *_info) 180562306a36Sopenharmony_ci{ 180662306a36Sopenharmony_ci struct snd_timer_user *tu; 180762306a36Sopenharmony_ci struct snd_timer_info *info; 180862306a36Sopenharmony_ci struct snd_timer *t; 180962306a36Sopenharmony_ci int err = 0; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci tu = file->private_data; 181262306a36Sopenharmony_ci if (!tu->timeri) 181362306a36Sopenharmony_ci return -EBADFD; 181462306a36Sopenharmony_ci t = tu->timeri->timer; 181562306a36Sopenharmony_ci if (!t) 181662306a36Sopenharmony_ci return -EBADFD; 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci info = kzalloc(sizeof(*info), GFP_KERNEL); 181962306a36Sopenharmony_ci if (! info) 182062306a36Sopenharmony_ci return -ENOMEM; 182162306a36Sopenharmony_ci info->card = t->card ? t->card->number : -1; 182262306a36Sopenharmony_ci if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) 182362306a36Sopenharmony_ci info->flags |= SNDRV_TIMER_FLG_SLAVE; 182462306a36Sopenharmony_ci strscpy(info->id, t->id, sizeof(info->id)); 182562306a36Sopenharmony_ci strscpy(info->name, t->name, sizeof(info->name)); 182662306a36Sopenharmony_ci spin_lock_irq(&t->lock); 182762306a36Sopenharmony_ci info->resolution = snd_timer_hw_resolution(t); 182862306a36Sopenharmony_ci spin_unlock_irq(&t->lock); 182962306a36Sopenharmony_ci if (copy_to_user(_info, info, sizeof(*_info))) 183062306a36Sopenharmony_ci err = -EFAULT; 183162306a36Sopenharmony_ci kfree(info); 183262306a36Sopenharmony_ci return err; 183362306a36Sopenharmony_ci} 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_cistatic int snd_timer_user_params(struct file *file, 183662306a36Sopenharmony_ci struct snd_timer_params __user *_params) 183762306a36Sopenharmony_ci{ 183862306a36Sopenharmony_ci struct snd_timer_user *tu; 183962306a36Sopenharmony_ci struct snd_timer_params params; 184062306a36Sopenharmony_ci struct snd_timer *t; 184162306a36Sopenharmony_ci int err; 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci tu = file->private_data; 184462306a36Sopenharmony_ci if (!tu->timeri) 184562306a36Sopenharmony_ci return -EBADFD; 184662306a36Sopenharmony_ci t = tu->timeri->timer; 184762306a36Sopenharmony_ci if (!t) 184862306a36Sopenharmony_ci return -EBADFD; 184962306a36Sopenharmony_ci if (copy_from_user(¶ms, _params, sizeof(params))) 185062306a36Sopenharmony_ci return -EFAULT; 185162306a36Sopenharmony_ci if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) { 185262306a36Sopenharmony_ci u64 resolution; 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci if (params.ticks < 1) { 185562306a36Sopenharmony_ci err = -EINVAL; 185662306a36Sopenharmony_ci goto _end; 185762306a36Sopenharmony_ci } 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci /* Don't allow resolution less than 1ms */ 186062306a36Sopenharmony_ci resolution = snd_timer_resolution(tu->timeri); 186162306a36Sopenharmony_ci resolution *= params.ticks; 186262306a36Sopenharmony_ci if (resolution < 1000000) { 186362306a36Sopenharmony_ci err = -EINVAL; 186462306a36Sopenharmony_ci goto _end; 186562306a36Sopenharmony_ci } 186662306a36Sopenharmony_ci } 186762306a36Sopenharmony_ci if (params.queue_size > 0 && 186862306a36Sopenharmony_ci (params.queue_size < 32 || params.queue_size > 1024)) { 186962306a36Sopenharmony_ci err = -EINVAL; 187062306a36Sopenharmony_ci goto _end; 187162306a36Sopenharmony_ci } 187262306a36Sopenharmony_ci if (params.filter & ~((1<<SNDRV_TIMER_EVENT_RESOLUTION)| 187362306a36Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_TICK)| 187462306a36Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_START)| 187562306a36Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_STOP)| 187662306a36Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_CONTINUE)| 187762306a36Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_PAUSE)| 187862306a36Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_SUSPEND)| 187962306a36Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_RESUME)| 188062306a36Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_MSTART)| 188162306a36Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_MSTOP)| 188262306a36Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_MCONTINUE)| 188362306a36Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_MPAUSE)| 188462306a36Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_MSUSPEND)| 188562306a36Sopenharmony_ci (1<<SNDRV_TIMER_EVENT_MRESUME))) { 188662306a36Sopenharmony_ci err = -EINVAL; 188762306a36Sopenharmony_ci goto _end; 188862306a36Sopenharmony_ci } 188962306a36Sopenharmony_ci snd_timer_stop(tu->timeri); 189062306a36Sopenharmony_ci spin_lock_irq(&t->lock); 189162306a36Sopenharmony_ci tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO| 189262306a36Sopenharmony_ci SNDRV_TIMER_IFLG_EXCLUSIVE| 189362306a36Sopenharmony_ci SNDRV_TIMER_IFLG_EARLY_EVENT); 189462306a36Sopenharmony_ci if (params.flags & SNDRV_TIMER_PSFLG_AUTO) 189562306a36Sopenharmony_ci tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO; 189662306a36Sopenharmony_ci if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE) 189762306a36Sopenharmony_ci tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE; 189862306a36Sopenharmony_ci if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT) 189962306a36Sopenharmony_ci tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT; 190062306a36Sopenharmony_ci spin_unlock_irq(&t->lock); 190162306a36Sopenharmony_ci if (params.queue_size > 0 && 190262306a36Sopenharmony_ci (unsigned int)tu->queue_size != params.queue_size) { 190362306a36Sopenharmony_ci err = realloc_user_queue(tu, params.queue_size); 190462306a36Sopenharmony_ci if (err < 0) 190562306a36Sopenharmony_ci goto _end; 190662306a36Sopenharmony_ci } 190762306a36Sopenharmony_ci spin_lock_irq(&tu->qlock); 190862306a36Sopenharmony_ci tu->qhead = tu->qtail = tu->qused = 0; 190962306a36Sopenharmony_ci if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { 191062306a36Sopenharmony_ci if (tu->tread) { 191162306a36Sopenharmony_ci struct snd_timer_tread64 tread; 191262306a36Sopenharmony_ci memset(&tread, 0, sizeof(tread)); 191362306a36Sopenharmony_ci tread.event = SNDRV_TIMER_EVENT_EARLY; 191462306a36Sopenharmony_ci tread.tstamp_sec = 0; 191562306a36Sopenharmony_ci tread.tstamp_nsec = 0; 191662306a36Sopenharmony_ci tread.val = 0; 191762306a36Sopenharmony_ci snd_timer_user_append_to_tqueue(tu, &tread); 191862306a36Sopenharmony_ci } else { 191962306a36Sopenharmony_ci struct snd_timer_read *r = &tu->queue[0]; 192062306a36Sopenharmony_ci r->resolution = 0; 192162306a36Sopenharmony_ci r->ticks = 0; 192262306a36Sopenharmony_ci tu->qused++; 192362306a36Sopenharmony_ci tu->qtail++; 192462306a36Sopenharmony_ci } 192562306a36Sopenharmony_ci } 192662306a36Sopenharmony_ci tu->filter = params.filter; 192762306a36Sopenharmony_ci tu->ticks = params.ticks; 192862306a36Sopenharmony_ci spin_unlock_irq(&tu->qlock); 192962306a36Sopenharmony_ci err = 0; 193062306a36Sopenharmony_ci _end: 193162306a36Sopenharmony_ci if (copy_to_user(_params, ¶ms, sizeof(params))) 193262306a36Sopenharmony_ci return -EFAULT; 193362306a36Sopenharmony_ci return err; 193462306a36Sopenharmony_ci} 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_cistatic int snd_timer_user_status32(struct file *file, 193762306a36Sopenharmony_ci struct snd_timer_status32 __user *_status) 193862306a36Sopenharmony_ci { 193962306a36Sopenharmony_ci struct snd_timer_user *tu; 194062306a36Sopenharmony_ci struct snd_timer_status32 status; 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci tu = file->private_data; 194362306a36Sopenharmony_ci if (!tu->timeri) 194462306a36Sopenharmony_ci return -EBADFD; 194562306a36Sopenharmony_ci memset(&status, 0, sizeof(status)); 194662306a36Sopenharmony_ci status.tstamp_sec = tu->tstamp.tv_sec; 194762306a36Sopenharmony_ci status.tstamp_nsec = tu->tstamp.tv_nsec; 194862306a36Sopenharmony_ci status.resolution = snd_timer_resolution(tu->timeri); 194962306a36Sopenharmony_ci status.lost = tu->timeri->lost; 195062306a36Sopenharmony_ci status.overrun = tu->overrun; 195162306a36Sopenharmony_ci spin_lock_irq(&tu->qlock); 195262306a36Sopenharmony_ci status.queue = tu->qused; 195362306a36Sopenharmony_ci spin_unlock_irq(&tu->qlock); 195462306a36Sopenharmony_ci if (copy_to_user(_status, &status, sizeof(status))) 195562306a36Sopenharmony_ci return -EFAULT; 195662306a36Sopenharmony_ci return 0; 195762306a36Sopenharmony_ci} 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_cistatic int snd_timer_user_status64(struct file *file, 196062306a36Sopenharmony_ci struct snd_timer_status64 __user *_status) 196162306a36Sopenharmony_ci{ 196262306a36Sopenharmony_ci struct snd_timer_user *tu; 196362306a36Sopenharmony_ci struct snd_timer_status64 status; 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci tu = file->private_data; 196662306a36Sopenharmony_ci if (!tu->timeri) 196762306a36Sopenharmony_ci return -EBADFD; 196862306a36Sopenharmony_ci memset(&status, 0, sizeof(status)); 196962306a36Sopenharmony_ci status.tstamp_sec = tu->tstamp.tv_sec; 197062306a36Sopenharmony_ci status.tstamp_nsec = tu->tstamp.tv_nsec; 197162306a36Sopenharmony_ci status.resolution = snd_timer_resolution(tu->timeri); 197262306a36Sopenharmony_ci status.lost = tu->timeri->lost; 197362306a36Sopenharmony_ci status.overrun = tu->overrun; 197462306a36Sopenharmony_ci spin_lock_irq(&tu->qlock); 197562306a36Sopenharmony_ci status.queue = tu->qused; 197662306a36Sopenharmony_ci spin_unlock_irq(&tu->qlock); 197762306a36Sopenharmony_ci if (copy_to_user(_status, &status, sizeof(status))) 197862306a36Sopenharmony_ci return -EFAULT; 197962306a36Sopenharmony_ci return 0; 198062306a36Sopenharmony_ci} 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_cistatic int snd_timer_user_start(struct file *file) 198362306a36Sopenharmony_ci{ 198462306a36Sopenharmony_ci int err; 198562306a36Sopenharmony_ci struct snd_timer_user *tu; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci tu = file->private_data; 198862306a36Sopenharmony_ci if (!tu->timeri) 198962306a36Sopenharmony_ci return -EBADFD; 199062306a36Sopenharmony_ci snd_timer_stop(tu->timeri); 199162306a36Sopenharmony_ci tu->timeri->lost = 0; 199262306a36Sopenharmony_ci tu->last_resolution = 0; 199362306a36Sopenharmony_ci err = snd_timer_start(tu->timeri, tu->ticks); 199462306a36Sopenharmony_ci if (err < 0) 199562306a36Sopenharmony_ci return err; 199662306a36Sopenharmony_ci return 0; 199762306a36Sopenharmony_ci} 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_cistatic int snd_timer_user_stop(struct file *file) 200062306a36Sopenharmony_ci{ 200162306a36Sopenharmony_ci int err; 200262306a36Sopenharmony_ci struct snd_timer_user *tu; 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci tu = file->private_data; 200562306a36Sopenharmony_ci if (!tu->timeri) 200662306a36Sopenharmony_ci return -EBADFD; 200762306a36Sopenharmony_ci err = snd_timer_stop(tu->timeri); 200862306a36Sopenharmony_ci if (err < 0) 200962306a36Sopenharmony_ci return err; 201062306a36Sopenharmony_ci return 0; 201162306a36Sopenharmony_ci} 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_cistatic int snd_timer_user_continue(struct file *file) 201462306a36Sopenharmony_ci{ 201562306a36Sopenharmony_ci int err; 201662306a36Sopenharmony_ci struct snd_timer_user *tu; 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci tu = file->private_data; 201962306a36Sopenharmony_ci if (!tu->timeri) 202062306a36Sopenharmony_ci return -EBADFD; 202162306a36Sopenharmony_ci /* start timer instead of continue if it's not used before */ 202262306a36Sopenharmony_ci if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED)) 202362306a36Sopenharmony_ci return snd_timer_user_start(file); 202462306a36Sopenharmony_ci tu->timeri->lost = 0; 202562306a36Sopenharmony_ci err = snd_timer_continue(tu->timeri); 202662306a36Sopenharmony_ci if (err < 0) 202762306a36Sopenharmony_ci return err; 202862306a36Sopenharmony_ci return 0; 202962306a36Sopenharmony_ci} 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_cistatic int snd_timer_user_pause(struct file *file) 203262306a36Sopenharmony_ci{ 203362306a36Sopenharmony_ci int err; 203462306a36Sopenharmony_ci struct snd_timer_user *tu; 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci tu = file->private_data; 203762306a36Sopenharmony_ci if (!tu->timeri) 203862306a36Sopenharmony_ci return -EBADFD; 203962306a36Sopenharmony_ci err = snd_timer_pause(tu->timeri); 204062306a36Sopenharmony_ci if (err < 0) 204162306a36Sopenharmony_ci return err; 204262306a36Sopenharmony_ci return 0; 204362306a36Sopenharmony_ci} 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_cistatic int snd_timer_user_tread(void __user *argp, struct snd_timer_user *tu, 204662306a36Sopenharmony_ci unsigned int cmd, bool compat) 204762306a36Sopenharmony_ci{ 204862306a36Sopenharmony_ci int __user *p = argp; 204962306a36Sopenharmony_ci int xarg, old_tread; 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci if (tu->timeri) /* too late */ 205262306a36Sopenharmony_ci return -EBUSY; 205362306a36Sopenharmony_ci if (get_user(xarg, p)) 205462306a36Sopenharmony_ci return -EFAULT; 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci old_tread = tu->tread; 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci if (!xarg) 205962306a36Sopenharmony_ci tu->tread = TREAD_FORMAT_NONE; 206062306a36Sopenharmony_ci else if (cmd == SNDRV_TIMER_IOCTL_TREAD64 || 206162306a36Sopenharmony_ci (IS_ENABLED(CONFIG_64BIT) && !compat)) 206262306a36Sopenharmony_ci tu->tread = TREAD_FORMAT_TIME64; 206362306a36Sopenharmony_ci else 206462306a36Sopenharmony_ci tu->tread = TREAD_FORMAT_TIME32; 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci if (tu->tread != old_tread && 206762306a36Sopenharmony_ci realloc_user_queue(tu, tu->queue_size) < 0) { 206862306a36Sopenharmony_ci tu->tread = old_tread; 206962306a36Sopenharmony_ci return -ENOMEM; 207062306a36Sopenharmony_ci } 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci return 0; 207362306a36Sopenharmony_ci} 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_cienum { 207662306a36Sopenharmony_ci SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20), 207762306a36Sopenharmony_ci SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21), 207862306a36Sopenharmony_ci SNDRV_TIMER_IOCTL_CONTINUE_OLD = _IO('T', 0x22), 207962306a36Sopenharmony_ci SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23), 208062306a36Sopenharmony_ci}; 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_cistatic long __snd_timer_user_ioctl(struct file *file, unsigned int cmd, 208362306a36Sopenharmony_ci unsigned long arg, bool compat) 208462306a36Sopenharmony_ci{ 208562306a36Sopenharmony_ci struct snd_timer_user *tu; 208662306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 208762306a36Sopenharmony_ci int __user *p = argp; 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci tu = file->private_data; 209062306a36Sopenharmony_ci switch (cmd) { 209162306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_PVERSION: 209262306a36Sopenharmony_ci return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0; 209362306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_NEXT_DEVICE: 209462306a36Sopenharmony_ci return snd_timer_user_next_device(argp); 209562306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_TREAD_OLD: 209662306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_TREAD64: 209762306a36Sopenharmony_ci return snd_timer_user_tread(argp, tu, cmd, compat); 209862306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_GINFO: 209962306a36Sopenharmony_ci return snd_timer_user_ginfo(file, argp); 210062306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_GPARAMS: 210162306a36Sopenharmony_ci return snd_timer_user_gparams(file, argp); 210262306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_GSTATUS: 210362306a36Sopenharmony_ci return snd_timer_user_gstatus(file, argp); 210462306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_SELECT: 210562306a36Sopenharmony_ci return snd_timer_user_tselect(file, argp); 210662306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_INFO: 210762306a36Sopenharmony_ci return snd_timer_user_info(file, argp); 210862306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_PARAMS: 210962306a36Sopenharmony_ci return snd_timer_user_params(file, argp); 211062306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_STATUS32: 211162306a36Sopenharmony_ci return snd_timer_user_status32(file, argp); 211262306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_STATUS64: 211362306a36Sopenharmony_ci return snd_timer_user_status64(file, argp); 211462306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_START: 211562306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_START_OLD: 211662306a36Sopenharmony_ci return snd_timer_user_start(file); 211762306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_STOP: 211862306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_STOP_OLD: 211962306a36Sopenharmony_ci return snd_timer_user_stop(file); 212062306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_CONTINUE: 212162306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_CONTINUE_OLD: 212262306a36Sopenharmony_ci return snd_timer_user_continue(file); 212362306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_PAUSE: 212462306a36Sopenharmony_ci case SNDRV_TIMER_IOCTL_PAUSE_OLD: 212562306a36Sopenharmony_ci return snd_timer_user_pause(file); 212662306a36Sopenharmony_ci } 212762306a36Sopenharmony_ci return -ENOTTY; 212862306a36Sopenharmony_ci} 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_cistatic long snd_timer_user_ioctl(struct file *file, unsigned int cmd, 213162306a36Sopenharmony_ci unsigned long arg) 213262306a36Sopenharmony_ci{ 213362306a36Sopenharmony_ci struct snd_timer_user *tu = file->private_data; 213462306a36Sopenharmony_ci long ret; 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci mutex_lock(&tu->ioctl_lock); 213762306a36Sopenharmony_ci ret = __snd_timer_user_ioctl(file, cmd, arg, false); 213862306a36Sopenharmony_ci mutex_unlock(&tu->ioctl_lock); 213962306a36Sopenharmony_ci return ret; 214062306a36Sopenharmony_ci} 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_cistatic int snd_timer_user_fasync(int fd, struct file * file, int on) 214362306a36Sopenharmony_ci{ 214462306a36Sopenharmony_ci struct snd_timer_user *tu; 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci tu = file->private_data; 214762306a36Sopenharmony_ci return snd_fasync_helper(fd, file, on, &tu->fasync); 214862306a36Sopenharmony_ci} 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_cistatic ssize_t snd_timer_user_read(struct file *file, char __user *buffer, 215162306a36Sopenharmony_ci size_t count, loff_t *offset) 215262306a36Sopenharmony_ci{ 215362306a36Sopenharmony_ci struct snd_timer_tread64 *tread; 215462306a36Sopenharmony_ci struct snd_timer_tread32 tread32; 215562306a36Sopenharmony_ci struct snd_timer_user *tu; 215662306a36Sopenharmony_ci long result = 0, unit; 215762306a36Sopenharmony_ci int qhead; 215862306a36Sopenharmony_ci int err = 0; 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci tu = file->private_data; 216162306a36Sopenharmony_ci switch (tu->tread) { 216262306a36Sopenharmony_ci case TREAD_FORMAT_TIME64: 216362306a36Sopenharmony_ci unit = sizeof(struct snd_timer_tread64); 216462306a36Sopenharmony_ci break; 216562306a36Sopenharmony_ci case TREAD_FORMAT_TIME32: 216662306a36Sopenharmony_ci unit = sizeof(struct snd_timer_tread32); 216762306a36Sopenharmony_ci break; 216862306a36Sopenharmony_ci case TREAD_FORMAT_NONE: 216962306a36Sopenharmony_ci unit = sizeof(struct snd_timer_read); 217062306a36Sopenharmony_ci break; 217162306a36Sopenharmony_ci default: 217262306a36Sopenharmony_ci WARN_ONCE(1, "Corrupt snd_timer_user\n"); 217362306a36Sopenharmony_ci return -ENOTSUPP; 217462306a36Sopenharmony_ci } 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci mutex_lock(&tu->ioctl_lock); 217762306a36Sopenharmony_ci spin_lock_irq(&tu->qlock); 217862306a36Sopenharmony_ci while ((long)count - result >= unit) { 217962306a36Sopenharmony_ci while (!tu->qused) { 218062306a36Sopenharmony_ci wait_queue_entry_t wait; 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { 218362306a36Sopenharmony_ci err = -EAGAIN; 218462306a36Sopenharmony_ci goto _error; 218562306a36Sopenharmony_ci } 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 218862306a36Sopenharmony_ci init_waitqueue_entry(&wait, current); 218962306a36Sopenharmony_ci add_wait_queue(&tu->qchange_sleep, &wait); 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci spin_unlock_irq(&tu->qlock); 219262306a36Sopenharmony_ci mutex_unlock(&tu->ioctl_lock); 219362306a36Sopenharmony_ci schedule(); 219462306a36Sopenharmony_ci mutex_lock(&tu->ioctl_lock); 219562306a36Sopenharmony_ci spin_lock_irq(&tu->qlock); 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci remove_wait_queue(&tu->qchange_sleep, &wait); 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci if (tu->disconnected) { 220062306a36Sopenharmony_ci err = -ENODEV; 220162306a36Sopenharmony_ci goto _error; 220262306a36Sopenharmony_ci } 220362306a36Sopenharmony_ci if (signal_pending(current)) { 220462306a36Sopenharmony_ci err = -ERESTARTSYS; 220562306a36Sopenharmony_ci goto _error; 220662306a36Sopenharmony_ci } 220762306a36Sopenharmony_ci } 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci qhead = tu->qhead++; 221062306a36Sopenharmony_ci tu->qhead %= tu->queue_size; 221162306a36Sopenharmony_ci tu->qused--; 221262306a36Sopenharmony_ci spin_unlock_irq(&tu->qlock); 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci tread = &tu->tqueue[qhead]; 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci switch (tu->tread) { 221762306a36Sopenharmony_ci case TREAD_FORMAT_TIME64: 221862306a36Sopenharmony_ci if (copy_to_user(buffer, tread, 221962306a36Sopenharmony_ci sizeof(struct snd_timer_tread64))) 222062306a36Sopenharmony_ci err = -EFAULT; 222162306a36Sopenharmony_ci break; 222262306a36Sopenharmony_ci case TREAD_FORMAT_TIME32: 222362306a36Sopenharmony_ci memset(&tread32, 0, sizeof(tread32)); 222462306a36Sopenharmony_ci tread32 = (struct snd_timer_tread32) { 222562306a36Sopenharmony_ci .event = tread->event, 222662306a36Sopenharmony_ci .tstamp_sec = tread->tstamp_sec, 222762306a36Sopenharmony_ci .tstamp_nsec = tread->tstamp_nsec, 222862306a36Sopenharmony_ci .val = tread->val, 222962306a36Sopenharmony_ci }; 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci if (copy_to_user(buffer, &tread32, sizeof(tread32))) 223262306a36Sopenharmony_ci err = -EFAULT; 223362306a36Sopenharmony_ci break; 223462306a36Sopenharmony_ci case TREAD_FORMAT_NONE: 223562306a36Sopenharmony_ci if (copy_to_user(buffer, &tu->queue[qhead], 223662306a36Sopenharmony_ci sizeof(struct snd_timer_read))) 223762306a36Sopenharmony_ci err = -EFAULT; 223862306a36Sopenharmony_ci break; 223962306a36Sopenharmony_ci default: 224062306a36Sopenharmony_ci err = -ENOTSUPP; 224162306a36Sopenharmony_ci break; 224262306a36Sopenharmony_ci } 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci spin_lock_irq(&tu->qlock); 224562306a36Sopenharmony_ci if (err < 0) 224662306a36Sopenharmony_ci goto _error; 224762306a36Sopenharmony_ci result += unit; 224862306a36Sopenharmony_ci buffer += unit; 224962306a36Sopenharmony_ci } 225062306a36Sopenharmony_ci _error: 225162306a36Sopenharmony_ci spin_unlock_irq(&tu->qlock); 225262306a36Sopenharmony_ci mutex_unlock(&tu->ioctl_lock); 225362306a36Sopenharmony_ci return result > 0 ? result : err; 225462306a36Sopenharmony_ci} 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_cistatic __poll_t snd_timer_user_poll(struct file *file, poll_table * wait) 225762306a36Sopenharmony_ci{ 225862306a36Sopenharmony_ci __poll_t mask; 225962306a36Sopenharmony_ci struct snd_timer_user *tu; 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci tu = file->private_data; 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci poll_wait(file, &tu->qchange_sleep, wait); 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci mask = 0; 226662306a36Sopenharmony_ci spin_lock_irq(&tu->qlock); 226762306a36Sopenharmony_ci if (tu->qused) 226862306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 226962306a36Sopenharmony_ci if (tu->disconnected) 227062306a36Sopenharmony_ci mask |= EPOLLERR; 227162306a36Sopenharmony_ci spin_unlock_irq(&tu->qlock); 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci return mask; 227462306a36Sopenharmony_ci} 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 227762306a36Sopenharmony_ci#include "timer_compat.c" 227862306a36Sopenharmony_ci#else 227962306a36Sopenharmony_ci#define snd_timer_user_ioctl_compat NULL 228062306a36Sopenharmony_ci#endif 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_cistatic const struct file_operations snd_timer_f_ops = 228362306a36Sopenharmony_ci{ 228462306a36Sopenharmony_ci .owner = THIS_MODULE, 228562306a36Sopenharmony_ci .read = snd_timer_user_read, 228662306a36Sopenharmony_ci .open = snd_timer_user_open, 228762306a36Sopenharmony_ci .release = snd_timer_user_release, 228862306a36Sopenharmony_ci .llseek = no_llseek, 228962306a36Sopenharmony_ci .poll = snd_timer_user_poll, 229062306a36Sopenharmony_ci .unlocked_ioctl = snd_timer_user_ioctl, 229162306a36Sopenharmony_ci .compat_ioctl = snd_timer_user_ioctl_compat, 229262306a36Sopenharmony_ci .fasync = snd_timer_user_fasync, 229362306a36Sopenharmony_ci}; 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci/* unregister the system timer */ 229662306a36Sopenharmony_cistatic void snd_timer_free_all(void) 229762306a36Sopenharmony_ci{ 229862306a36Sopenharmony_ci struct snd_timer *timer, *n; 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci list_for_each_entry_safe(timer, n, &snd_timer_list, device_list) 230162306a36Sopenharmony_ci snd_timer_free(timer); 230262306a36Sopenharmony_ci} 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_cistatic struct device *timer_dev; 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci/* 230762306a36Sopenharmony_ci * ENTRY functions 230862306a36Sopenharmony_ci */ 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_cistatic int __init alsa_timer_init(void) 231162306a36Sopenharmony_ci{ 231262306a36Sopenharmony_ci int err; 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci err = snd_device_alloc(&timer_dev, NULL); 231562306a36Sopenharmony_ci if (err < 0) 231662306a36Sopenharmony_ci return err; 231762306a36Sopenharmony_ci dev_set_name(timer_dev, "timer"); 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci#ifdef SNDRV_OSS_INFO_DEV_TIMERS 232062306a36Sopenharmony_ci snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1, 232162306a36Sopenharmony_ci "system timer"); 232262306a36Sopenharmony_ci#endif 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci err = snd_timer_register_system(); 232562306a36Sopenharmony_ci if (err < 0) { 232662306a36Sopenharmony_ci pr_err("ALSA: unable to register system timer (%i)\n", err); 232762306a36Sopenharmony_ci goto put_timer; 232862306a36Sopenharmony_ci } 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0, 233162306a36Sopenharmony_ci &snd_timer_f_ops, NULL, timer_dev); 233262306a36Sopenharmony_ci if (err < 0) { 233362306a36Sopenharmony_ci pr_err("ALSA: unable to register timer device (%i)\n", err); 233462306a36Sopenharmony_ci snd_timer_free_all(); 233562306a36Sopenharmony_ci goto put_timer; 233662306a36Sopenharmony_ci } 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci snd_timer_proc_init(); 233962306a36Sopenharmony_ci return 0; 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ciput_timer: 234262306a36Sopenharmony_ci put_device(timer_dev); 234362306a36Sopenharmony_ci return err; 234462306a36Sopenharmony_ci} 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_cistatic void __exit alsa_timer_exit(void) 234762306a36Sopenharmony_ci{ 234862306a36Sopenharmony_ci snd_unregister_device(timer_dev); 234962306a36Sopenharmony_ci snd_timer_free_all(); 235062306a36Sopenharmony_ci put_device(timer_dev); 235162306a36Sopenharmony_ci snd_timer_proc_done(); 235262306a36Sopenharmony_ci#ifdef SNDRV_OSS_INFO_DEV_TIMERS 235362306a36Sopenharmony_ci snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1); 235462306a36Sopenharmony_ci#endif 235562306a36Sopenharmony_ci} 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_cimodule_init(alsa_timer_init) 235862306a36Sopenharmony_cimodule_exit(alsa_timer_exit) 2359