struct snd_timer_status64 {
s64 tstamp_sec; /* Timestamp - last update */
s64 tstamp_nsec; unsignedint resolution; /* current period resolution in ns */ unsignedint lost; /* counter of master tick lost */ unsignedint overrun; /* count of read queue overruns */ unsignedint queue; /* used queue size */ unsignedchar reserved[64]; /* reserved */
};
#ifdef CONFIG_SND_UTIMER #define SNDRV_UTIMERS_MAX_COUNT 128 /* Internal data structure for keeping the state of the userspace-driven timer */ struct snd_utimer { char *name; struct snd_timer *timer; unsignedint id;
}; #endif
/* * create a timer instance with the given owner string.
*/ struct snd_timer_instance *snd_timer_instance_new(constchar *owner)
{ struct snd_timer_instance *timeri;
staticvoid snd_timer_request(struct snd_timer_id *tid)
{ switch (tid->dev_class) { case SNDRV_TIMER_CLASS_GLOBAL: if (tid->device < timer_limit)
request_module("snd-timer-%i", tid->device); break; case SNDRV_TIMER_CLASS_CARD: case SNDRV_TIMER_CLASS_PCM: if (tid->card < snd_ecards_limit)
request_module("snd-card-%i", tid->card); break; default: break;
}
}
#endif
/* move the slave if it belongs to the master; return 1 if match */ staticint check_matching_master_slave(struct snd_timer_instance *master, struct snd_timer_instance *slave)
{ if (slave->slave_class != master->slave_class ||
slave->slave_id != master->slave_id) return 0; if (master->timer->num_instances >= master->timer->max_instances) return -EBUSY;
list_move_tail(&slave->open_list, &master->slave_list_head);
master->timer->num_instances++;
guard(spinlock_irq)(&slave_active_lock);
guard(spinlock)(&master->timer->lock);
slave->master = master;
slave->timer = master->timer; if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
list_add_tail(&slave->active_list, &master->slave_active_head); return 1;
}
/* * look for a master instance matching with the slave id of the given slave. * when found, relink the open_link of the slave. * * call this with register_mutex down.
*/ staticint snd_timer_check_slave(struct snd_timer_instance *slave)
{ struct snd_timer *timer; struct snd_timer_instance *master; int err = 0;
/* FIXME: it's really dumb to look up all entries.. */
list_for_each_entry(timer, &snd_timer_list, device_list) {
list_for_each_entry(master, &timer->open_list_head, open_list) {
err = check_matching_master_slave(master, slave); if (err != 0) /* match found or error */ goto out;
}
}
out: return err < 0 ? err : 0;
}
/* * look for slave instances matching with the slave id of the given master. * when found, relink the open_link of slaves. * * call this with register_mutex down.
*/ staticint snd_timer_check_master(struct snd_timer_instance *master)
{ struct snd_timer_instance *slave, *tmp; int err = 0;
/* * open a timer instance * when opening a master, the slave id must be here given.
*/ int snd_timer_open(struct snd_timer_instance *timeri, struct snd_timer_id *tid, unsignedint slave_id)
{ struct snd_timer *timer; struct device *card_dev_to_put = NULL; int err;
unlock:
mutex_unlock(®ister_mutex); /* put_device() is called after unlock for avoiding deadlock */ if (err < 0 && card_dev_to_put)
put_device(card_dev_to_put); return err;
}
EXPORT_SYMBOL(snd_timer_open);
/* * close a timer instance * call this with register_mutex down.
*/ staticvoid snd_timer_close_locked(struct snd_timer_instance *timeri, struct device **card_devp_to_put)
{ struct snd_timer *timer = timeri->timer;
if (timer) {
guard(spinlock_irq)(&timer->lock);
timeri->flags |= SNDRV_TIMER_IFLG_DEAD;
}
if (!list_empty(&timeri->open_list)) {
list_del_init(&timeri->open_list); if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
num_slaves--;
}
/* force to stop the timer */
snd_timer_stop(timeri);
if (timer) {
timer->num_instances--; /* wait, until the active callback is finished */
spin_lock_irq(&timer->lock); while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
spin_unlock_irq(&timer->lock);
udelay(10);
spin_lock_irq(&timer->lock);
}
spin_unlock_irq(&timer->lock);
remove_slave_links(timeri, timer);
/* slave doesn't need to release timer resources below */ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
timer = NULL;
}
if (timer) { if (list_empty(&timer->open_list_head) && timer->hw.close)
timer->hw.close(timer); /* release a card refcount for safe disconnection */ if (timer->card)
*card_devp_to_put = &timer->card->card_dev;
module_put(timer->module);
}
}
/* * close a timer instance
*/ void snd_timer_close(struct snd_timer_instance *timeri)
{ struct device *card_dev_to_put = NULL;
if (snd_BUG_ON(!timeri)) return;
scoped_guard(mutex, ®ister_mutex)
snd_timer_close_locked(timeri, &card_dev_to_put); /* put_device() is called after unlock for avoiding deadlock */ if (card_dev_to_put)
put_device(card_dev_to_put);
}
EXPORT_SYMBOL(snd_timer_close);
if (timer_tstamp_monotonic)
ktime_get_ts64(&tstamp); else
ktime_get_real_ts64(&tstamp); if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START ||
event > SNDRV_TIMER_EVENT_PAUSE)) return; if (timer &&
(event == SNDRV_TIMER_EVENT_START ||
event == SNDRV_TIMER_EVENT_CONTINUE))
resolution = snd_timer_hw_resolution(timer); if (ti->ccallback)
ti->ccallback(ti, event, &tstamp, resolution); if (ti->flags & SNDRV_TIMER_IFLG_SLAVE) return; if (timer == NULL) return; if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) return;
event += 10; /* convert to SNDRV_TIMER_EVENT_MXXX */
list_for_each_entry(ts, &ti->slave_active_head, active_list) if (ts->ccallback)
ts->ccallback(ts, event, &tstamp, resolution);
}
/* start/continue a master timer */ staticint snd_timer_start1(struct snd_timer_instance *timeri, bool start, unsignedlong ticks)
{ struct snd_timer *timer; int result;
timer = timeri->timer; if (!timer) return -EINVAL;
guard(spinlock_irqsave)(&timer->lock); if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) return -EINVAL; if (timer->card && timer->card->shutdown) return -ENODEV; if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
SNDRV_TIMER_IFLG_START)) return -EBUSY;
/* check the actual time for the start tick; * bail out as error if it's way too low (< 100us)
*/ if (start && !(timer->hw.flags & SNDRV_TIMER_HW_SLAVE)) { if ((u64)snd_timer_hw_resolution(timer) * ticks < 100000) return -EINVAL;
}
/* * start the timer instance
*/ int snd_timer_start(struct snd_timer_instance *timeri, unsignedint ticks)
{ if (timeri == NULL || ticks < 1) return -EINVAL; if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) return snd_timer_start_slave(timeri, true); else return snd_timer_start1(timeri, true, ticks);
}
EXPORT_SYMBOL(snd_timer_start);
/* * stop the timer instance. * * do not call this from the timer callback!
*/ int snd_timer_stop(struct snd_timer_instance *timeri)
{ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) return snd_timer_stop_slave(timeri, true); else return snd_timer_stop1(timeri, true);
}
EXPORT_SYMBOL(snd_timer_stop);
/* * start again.. the tick is kept.
*/ int snd_timer_continue(struct snd_timer_instance *timeri)
{ /* timer can continue only after pause */ if (!(timeri->flags & SNDRV_TIMER_IFLG_PAUSED)) return -EINVAL;
/* * pause.. remember the ticks left
*/ int snd_timer_pause(struct snd_timer_instance * timeri)
{ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) return snd_timer_stop_slave(timeri, false); else return snd_timer_stop1(timeri, false);
}
EXPORT_SYMBOL(snd_timer_pause);
/* * reschedule the timer * * start pending instances and check the scheduling ticks. * when the scheduling ticks is changed set CHANGE flag to reprogram the timer.
*/ staticvoid snd_timer_reschedule(struct snd_timer * timer, unsignedlong ticks_left)
{ struct snd_timer_instance *ti; unsignedlong ticks = ~0UL;
list_for_each_entry(ti, &timer->active_list_head, active_list) { if (ti->flags & SNDRV_TIMER_IFLG_START) {
ti->flags &= ~SNDRV_TIMER_IFLG_START;
ti->flags |= SNDRV_TIMER_IFLG_RUNNING;
timer->running++;
} if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) { if (ticks > ti->cticks)
ticks = ti->cticks;
}
} if (ticks == ~0UL) {
timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; return;
} if (ticks > timer->hw.ticks)
ticks = timer->hw.ticks; if (ticks_left != ticks)
timer->flags |= SNDRV_TIMER_FLG_CHANGE;
timer->sticks = ticks;
}
if (timer->card && timer->card->shutdown) {
snd_timer_clear_callbacks(timer, &timer->ack_list_head); return;
}
guard(spinlock_irqsave)(&timer->lock);
/* remember the current resolution */
resolution = snd_timer_hw_resolution(timer);
/* loop for all active instances * Here we cannot use list_for_each_entry because the active_list of a * processed instance is relinked to done_list_head before the callback * is called.
*/
list_for_each_entry_safe(ti, tmp, &timer->active_list_head,
active_list) { if (ti->flags & SNDRV_TIMER_IFLG_DEAD) continue; if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING)) continue;
ti->pticks += ticks_left;
ti->resolution = resolution; if (ti->cticks < ticks_left)
ti->cticks = 0; else
ti->cticks -= ticks_left; if (ti->cticks) /* not expired */ continue; if (ti->flags & SNDRV_TIMER_IFLG_AUTO) {
ti->cticks = ti->ticks;
} else {
ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
--timer->running;
list_del_init(&ti->active_list);
} if ((timer->hw.flags & SNDRV_TIMER_HW_WORK) ||
(ti->flags & SNDRV_TIMER_IFLG_FAST))
ack_list_head = &timer->ack_list_head; else
ack_list_head = &timer->sack_list_head; if (list_empty(&ti->ack_list))
list_add_tail(&ti->ack_list, ack_list_head);
list_for_each_entry(ts, &ti->slave_active_head, active_list) {
ts->pticks = ti->pticks;
ts->resolution = resolution; if (list_empty(&ts->ack_list))
list_add_tail(&ts->ack_list, ack_list_head);
}
} if (timer->flags & SNDRV_TIMER_FLG_RESCHED)
snd_timer_reschedule(timer, timer->sticks); if (timer->running) { if (timer->hw.flags & SNDRV_TIMER_HW_STOP) {
timer->hw.stop(timer);
timer->flags |= SNDRV_TIMER_FLG_CHANGE;
} if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) ||
(timer->flags & SNDRV_TIMER_FLG_CHANGE)) { /* restart timer */
timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;
timer->hw.start(timer);
}
} else {
timer->hw.stop(timer);
}
/* now process all fast callbacks */
snd_timer_process_callbacks(timer, &timer->ack_list_head);
/* do we have any slow callbacks? */ if (!list_empty(&timer->sack_list_head))
queue_work(system_highpri_wq, &timer->task_work);
}
EXPORT_SYMBOL(snd_timer_interrupt);
tu = file->private_data; if (!tu->timeri) return -EBADFD;
t = tu->timeri->timer; if (!t) return -EBADFD; if (copy_from_user(¶ms, _params, sizeof(params))) return -EFAULT; if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
u64 resolution;
staticint snd_timer_user_start(struct file *file)
{ int err; struct snd_timer_user *tu;
tu = file->private_data; if (!tu->timeri) return -EBADFD;
snd_timer_stop(tu->timeri);
tu->timeri->lost = 0;
tu->last_resolution = 0;
err = snd_timer_start(tu->timeri, tu->ticks); if (err < 0) return err; return 0;
}
staticint snd_timer_user_stop(struct file *file)
{ int err; struct snd_timer_user *tu;
tu = file->private_data; if (!tu->timeri) return -EBADFD;
err = snd_timer_stop(tu->timeri); if (err < 0) return err; return 0;
}
staticint snd_timer_user_continue(struct file *file)
{ int err; struct snd_timer_user *tu;
tu = file->private_data; if (!tu->timeri) return -EBADFD; /* start timer instead of continue if it's not used before */ if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED)) return snd_timer_user_start(file);
tu->timeri->lost = 0;
err = snd_timer_continue(tu->timeri); if (err < 0) return err; return 0;
}
staticint snd_timer_user_pause(struct file *file)
{ int err; struct snd_timer_user *tu;
tu = file->private_data; if (!tu->timeri) return -EBADFD;
err = snd_timer_pause(tu->timeri); if (err < 0) return err; return 0;
}
staticint snd_timer_user_tread(void __user *argp, struct snd_timer_user *tu, unsignedint cmd, bool compat)
{ int __user *p = argp; int xarg, old_tread;
if (tu->timeri) /* too late */ return -EBUSY; if (get_user(xarg, p)) return -EFAULT;
#ifdef CONFIG_SND_UTIMER /* * Since userspace-driven timers are passed to userspace, we need to have an identifier * which will allow us to use them (basically, the subdevice number of udriven timer).
*/ static DEFINE_IDA(snd_utimer_ids);
staticvoid snd_utimer_put_id(struct snd_utimer *utimer)
{ int timer_id = utimer->id;
if (!utimer_info || utimer_info->resolution == 0) return -EINVAL;
utimer = kzalloc(sizeof(*utimer), GFP_KERNEL); if (!utimer) return -ENOMEM;
/* We hold the ioctl lock here so we won't get a race condition when allocating id */
utimer_id = snd_utimer_take_id(); if (utimer_id < 0) {
err = utimer_id; goto err_take_id;
}
err = copy_to_user(_utimer_info, utimer_info, sizeof(*utimer_info)); if (err) { /* * "Leak" the fd, as there is nothing we can do about it. * It might have been closed already since anon_inode_getfd * makes it available for userspace. * * We have to rely on the process exit path to do any * necessary cleanup (e.g. releasing the file).
*/ return -EFAULT;
}
tu = file->private_data; switch (cmd) { case SNDRV_TIMER_IOCTL_PVERSION: return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0; case SNDRV_TIMER_IOCTL_NEXT_DEVICE: return snd_timer_user_next_device(argp); case SNDRV_TIMER_IOCTL_TREAD_OLD: case SNDRV_TIMER_IOCTL_TREAD64: return snd_timer_user_tread(argp, tu, cmd, compat); case SNDRV_TIMER_IOCTL_GINFO: return snd_timer_user_ginfo(file, argp); case SNDRV_TIMER_IOCTL_GPARAMS: return snd_timer_user_gparams(file, argp); case SNDRV_TIMER_IOCTL_GSTATUS: return snd_timer_user_gstatus(file, argp); case SNDRV_TIMER_IOCTL_SELECT: return snd_timer_user_tselect(file, argp); case SNDRV_TIMER_IOCTL_INFO: return snd_timer_user_info(file, argp); case SNDRV_TIMER_IOCTL_PARAMS: return snd_timer_user_params(file, argp); case SNDRV_TIMER_IOCTL_STATUS32: return snd_timer_user_status32(file, argp); case SNDRV_TIMER_IOCTL_STATUS64: return snd_timer_user_status64(file, argp); case SNDRV_TIMER_IOCTL_START: case SNDRV_TIMER_IOCTL_START_OLD: return snd_timer_user_start(file); case SNDRV_TIMER_IOCTL_STOP: case SNDRV_TIMER_IOCTL_STOP_OLD: return snd_timer_user_stop(file); case SNDRV_TIMER_IOCTL_CONTINUE: case SNDRV_TIMER_IOCTL_CONTINUE_OLD: return snd_timer_user_continue(file); case SNDRV_TIMER_IOCTL_PAUSE: case SNDRV_TIMER_IOCTL_PAUSE_OLD: return snd_timer_user_pause(file); case SNDRV_TIMER_IOCTL_CREATE: return snd_utimer_ioctl_create(file, argp);
} return -ENOTTY;
}
tu = file->private_data; return snd_fasync_helper(fd, file, on, &tu->fasync);
}
static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
size_t count, loff_t *offset)
{ struct snd_timer_tread64 *tread; struct snd_timer_tread32 tread32; struct snd_timer_user *tu; long result = 0, unit; int qhead; int err = 0;
tu = file->private_data; switch (tu->tread) { case TREAD_FORMAT_TIME64:
unit = sizeof(struct snd_timer_tread64); break; case TREAD_FORMAT_TIME32:
unit = sizeof(struct snd_timer_tread32); break; case TREAD_FORMAT_NONE:
unit = sizeof(struct snd_timer_read); break; default:
WARN_ONCE(1, "Corrupt snd_timer_user\n"); return -ENOTSUPP;
}
mutex_lock(&tu->ioctl_lock);
spin_lock_irq(&tu->qlock); while ((long)count - result >= unit) { while (!tu->qused) {
wait_queue_entry_t wait;
if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
err = -EAGAIN; goto _error;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.