Merge branch 'topic/core-cleanup' into for-linus
[safe/jmp/linux-2.6] / sound / core / timer.c
index 74637ce..13afb60 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Timers abstract layer
- *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
  *
  *
  *   This program is free software; you can redistribute it and/or modify
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
-#include <linux/smp_lock.h>
 #include <linux/slab.h>
 #include <linux/time.h>
+#include <linux/mutex.h>
 #include <linux/moduleparam.h>
 #include <linux/string.h>
 #include <sound/core.h>
@@ -34,9 +33,6 @@
 #include <sound/minors.h>
 #include <sound/initval.h>
 #include <linux/kmod.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#endif
 
 #if defined(CONFIG_SND_HPET) || defined(CONFIG_SND_HPET_MODULE)
 #define DEFAULT_TIMER_LIMIT 3
 #endif
 
 static int timer_limit = DEFAULT_TIMER_LIMIT;
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de>");
+static int timer_tstamp_monotonic = 1;
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>");
 MODULE_DESCRIPTION("ALSA timer interface");
 MODULE_LICENSE("GPL");
 module_param(timer_limit, int, 0444);
 MODULE_PARM_DESC(timer_limit, "Maximum global timers in system.");
+module_param(timer_tstamp_monotonic, int, 0444);
+MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default).");
 
 struct snd_timer_user {
        struct snd_timer_instance *timeri;
@@ -70,7 +69,7 @@ struct snd_timer_user {
        struct timespec tstamp;         /* trigger tstamp */
        wait_queue_head_t qchange_sleep;
        struct fasync_struct *fasync;
-       struct semaphore tread_sem;
+       struct mutex tread_sem;
 };
 
 /* list of timers */
@@ -82,12 +81,12 @@ static LIST_HEAD(snd_timer_slave_list);
 /* lock for slave active lists */
 static DEFINE_SPINLOCK(slave_active_lock);
 
-static DECLARE_MUTEX(register_mutex);
+static DEFINE_MUTEX(register_mutex);
 
 static int snd_timer_free(struct snd_timer *timer);
 static int snd_timer_dev_free(struct snd_device *device);
 static int snd_timer_dev_register(struct snd_device *device);
-static int snd_timer_dev_unregister(struct snd_device *device);
+static int snd_timer_dev_disconnect(struct snd_device *device);
 
 static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left);
 
@@ -129,11 +128,8 @@ static struct snd_timer_instance *snd_timer_instance_new(char *owner,
 static struct snd_timer *snd_timer_find(struct snd_timer_id *tid)
 {
        struct snd_timer *timer = NULL;
-       struct list_head *p;
-
-       list_for_each(p, &snd_timer_list) {
-               timer = list_entry(p, struct snd_timer, device_list);
 
+       list_for_each_entry(timer, &snd_timer_list, device_list) {
                if (timer->tmr_class != tid->dev_class)
                        continue;
                if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD ||
@@ -150,12 +146,10 @@ static struct snd_timer *snd_timer_find(struct snd_timer_id *tid)
        return NULL;
 }
 
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
 
 static void snd_timer_request(struct snd_timer_id *tid)
 {
-       if (! current->fs->root)
-               return;
        switch (tid->dev_class) {
        case SNDRV_TIMER_CLASS_GLOBAL:
                if (tid->device < timer_limit)
@@ -183,13 +177,10 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave)
 {
        struct snd_timer *timer;
        struct snd_timer_instance *master;
-       struct list_head *p, *q;
 
        /* FIXME: it's really dumb to look up all entries.. */
-       list_for_each(p, &snd_timer_list) {
-               timer = list_entry(p, struct snd_timer, device_list);
-               list_for_each(q, &timer->open_list_head) {
-                       master = list_entry(q, struct snd_timer_instance, open_list);
+       list_for_each_entry(timer, &snd_timer_list, device_list) {
+               list_for_each_entry(master, &timer->open_list_head, open_list) {
                        if (slave->slave_class == master->slave_class &&
                            slave->slave_id == master->slave_id) {
                                list_del(&slave->open_list);
@@ -213,16 +204,13 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave)
  */
 static void snd_timer_check_master(struct snd_timer_instance *master)
 {
-       struct snd_timer_instance *slave;
-       struct list_head *p, *n;
+       struct snd_timer_instance *slave, *tmp;
 
        /* check all pending slaves */
-       list_for_each_safe(p, n, &snd_timer_slave_list) {
-               slave = list_entry(p, struct snd_timer_instance, open_list);
+       list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) {
                if (slave->slave_class == master->slave_class &&
                    slave->slave_id == master->slave_id) {
-                       list_del(p);
-                       list_add_tail(p, &master->slave_list_head);
+                       list_move_tail(&slave->open_list, &master->slave_list_head);
                        spin_lock_irq(&slave_active_lock);
                        slave->master = master;
                        slave->timer = master->timer;
@@ -252,10 +240,10 @@ int snd_timer_open(struct snd_timer_instance **ti,
                        snd_printd("invalid slave class %i\n", tid->dev_sclass);
                        return -EINVAL;
                }
-               down(&register_mutex);
+               mutex_lock(&register_mutex);
                timeri = snd_timer_instance_new(owner, NULL);
                if (!timeri) {
-                       up(&register_mutex);
+                       mutex_unlock(&register_mutex);
                        return -ENOMEM;
                }
                timeri->slave_class = tid->dev_sclass;
@@ -263,37 +251,37 @@ int snd_timer_open(struct snd_timer_instance **ti,
                timeri->flags |= SNDRV_TIMER_IFLG_SLAVE;
                list_add_tail(&timeri->open_list, &snd_timer_slave_list);
                snd_timer_check_slave(timeri);
-               up(&register_mutex);
+               mutex_unlock(&register_mutex);
                *ti = timeri;
                return 0;
        }
 
        /* open a master instance */
-       down(&register_mutex);
+       mutex_lock(&register_mutex);
        timer = snd_timer_find(tid);
-#ifdef CONFIG_KMOD
-       if (timer == NULL) {
-               up(&register_mutex);
+#ifdef CONFIG_MODULES
+       if (!timer) {
+               mutex_unlock(&register_mutex);
                snd_timer_request(tid);
-               down(&register_mutex);
+               mutex_lock(&register_mutex);
                timer = snd_timer_find(tid);
        }
 #endif
        if (!timer) {
-               up(&register_mutex);
+               mutex_unlock(&register_mutex);
                return -ENODEV;
        }
        if (!list_empty(&timer->open_list_head)) {
                timeri = list_entry(timer->open_list_head.next,
                                    struct snd_timer_instance, open_list);
                if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
-                       up(&register_mutex);
+                       mutex_unlock(&register_mutex);
                        return -EBUSY;
                }
        }
        timeri = snd_timer_instance_new(owner, timer);
        if (!timeri) {
-               up(&register_mutex);
+               mutex_unlock(&register_mutex);
                return -ENOMEM;
        }
        timeri->slave_class = tid->dev_sclass;
@@ -302,7 +290,7 @@ int snd_timer_open(struct snd_timer_instance **ti,
                timer->hw.open(timer);
        list_add_tail(&timeri->open_list, &timer->open_list_head);
        snd_timer_check_master(timeri);
-       up(&register_mutex);
+       mutex_unlock(&register_mutex);
        *ti = timeri;
        return 0;
 }
@@ -316,10 +304,10 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri,
 int snd_timer_close(struct snd_timer_instance *timeri)
 {
        struct snd_timer *timer = NULL;
-       struct list_head *p, *n;
-       struct snd_timer_instance *slave;
+       struct snd_timer_instance *slave, *tmp;
 
-       snd_assert(timeri != NULL, return -ENXIO);
+       if (snd_BUG_ON(!timeri))
+               return -ENXIO;
 
        /* force to stop the timer */
        snd_timer_stop(timeri);
@@ -333,9 +321,9 @@ int snd_timer_close(struct snd_timer_instance *timeri)
                        spin_lock_irq(&slave_active_lock);
                }
                spin_unlock_irq(&slave_active_lock);
-               down(&register_mutex);
+               mutex_lock(&register_mutex);
                list_del(&timeri->open_list);
-               up(&register_mutex);
+               mutex_unlock(&register_mutex);
        } else {
                timer = timeri->timer;
                /* wait, until the active callback is finished */
@@ -346,23 +334,22 @@ int snd_timer_close(struct snd_timer_instance *timeri)
                        spin_lock_irq(&timer->lock);
                }
                spin_unlock_irq(&timer->lock);
-               down(&register_mutex);
+               mutex_lock(&register_mutex);
                list_del(&timeri->open_list);
                if (timer && list_empty(&timer->open_list_head) &&
                    timer->hw.close)
                        timer->hw.close(timer);
                /* remove slave links */
-               list_for_each_safe(p, n, &timeri->slave_list_head) {
-                       slave = list_entry(p, struct snd_timer_instance, open_list);
+               list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
+                                        open_list) {
                        spin_lock_irq(&slave_active_lock);
                        _snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION);
-                       list_del(p);
-                       list_add_tail(p, &snd_timer_slave_list);
+                       list_move_tail(&slave->open_list, &snd_timer_slave_list);
                        slave->master = NULL;
                        slave->timer = NULL;
                        spin_unlock_irq(&slave_active_lock);
                }
-               up(&register_mutex);
+               mutex_unlock(&register_mutex);
        }
        if (timeri->private_free)
                timeri->private_free(timeri);
@@ -393,17 +380,20 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
        unsigned long flags;
        unsigned long resolution = 0;
        struct snd_timer_instance *ts;
-       struct list_head *n;
        struct timespec tstamp;
 
-       getnstimeofday(&tstamp);
-       snd_assert(event >= SNDRV_TIMER_EVENT_START &&
-                  event <= SNDRV_TIMER_EVENT_PAUSE, return);
+       if (timer_tstamp_monotonic)
+               do_posix_clock_monotonic_gettime(&tstamp);
+       else
+               getnstimeofday(&tstamp);
+       if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START ||
+                      event > SNDRV_TIMER_EVENT_PAUSE))
+               return;
        if (event == SNDRV_TIMER_EVENT_START ||
            event == SNDRV_TIMER_EVENT_CONTINUE)
                resolution = snd_timer_resolution(ti);
        if (ti->ccallback)
-               ti->ccallback(ti, SNDRV_TIMER_EVENT_START, &tstamp, resolution);
+               ti->ccallback(ti, event, &tstamp, resolution);
        if (ti->flags & SNDRV_TIMER_IFLG_SLAVE)
                return;
        timer = ti->timer;
@@ -412,11 +402,9 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
        if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
                return;
        spin_lock_irqsave(&timer->lock, flags);
-       list_for_each(n, &ti->slave_active_head) {
-               ts = list_entry(n, struct snd_timer_instance, active_list);
+       list_for_each_entry(ts, &ti->slave_active_head, active_list)
                if (ts->ccallback)
                        ts->ccallback(ti, event + 100, &tstamp, resolution);
-       }
        spin_unlock_irqrestore(&timer->lock, flags);
 }
 
@@ -488,7 +476,8 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
        struct snd_timer *timer;
        unsigned long flags;
 
-       snd_assert(timeri != NULL, return -ENXIO);
+       if (snd_BUG_ON(!timeri))
+               return -ENXIO;
 
        if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
                if (!keep_flag) {
@@ -592,10 +581,8 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l
 {
        struct snd_timer_instance *ti;
        unsigned long ticks = ~0UL;
-       struct list_head *p;
 
-       list_for_each(p, &timer->active_list_head) {
-               ti = list_entry(p, struct snd_timer_instance, active_list);
+       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;
@@ -627,8 +614,9 @@ static void snd_timer_tasklet(unsigned long arg)
        struct snd_timer_instance *ti;
        struct list_head *p;
        unsigned long resolution, ticks;
+       unsigned long flags;
 
-       spin_lock(&timer->lock);
+       spin_lock_irqsave(&timer->lock, flags);
        /* now process all callbacks */
        while (!list_empty(&timer->sack_list_head)) {
                p = timer->sack_list_head.next;         /* get first item */
@@ -648,7 +636,7 @@ static void snd_timer_tasklet(unsigned long arg)
                spin_lock(&timer->lock);
                ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
        }
-       spin_unlock(&timer->lock);
+       spin_unlock_irqrestore(&timer->lock, flags);
 }
 
 /*
@@ -659,15 +647,16 @@ static void snd_timer_tasklet(unsigned long arg)
  */
 void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
 {
-       struct snd_timer_instance *ti, *ts;
+       struct snd_timer_instance *ti, *ts, *tmp;
        unsigned long resolution, ticks;
-       struct list_head *p, *q, *n, *ack_list_head;
+       struct list_head *p, *ack_list_head;
+       unsigned long flags;
        int use_tasklet = 0;
 
        if (timer == NULL)
                return;
 
-       spin_lock(&timer->lock);
+       spin_lock_irqsave(&timer->lock, flags);
 
        /* remember the current resolution */
        if (timer->hw.c_resolution)
@@ -676,12 +665,12 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
                resolution = timer->hw.resolution;
 
        /* loop for all active instances
-        * Here we cannot use list_for_each because the active_list of a
+        * 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_safe(p, n, &timer->active_list_head) {
-               ti = list_entry(p, struct snd_timer_instance, active_list);
+       list_for_each_entry_safe(ti, tmp, &timer->active_list_head,
+                                active_list) {
                if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING))
                        continue;
                ti->pticks += ticks_left;
@@ -697,7 +686,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
                } else {
                        ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
                        if (--timer->running)
-                               list_del(p);
+                               list_del(&ti->active_list);
                }
                if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) ||
                    (ti->flags & SNDRV_TIMER_IFLG_FAST))
@@ -706,8 +695,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
                        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(q, &ti->slave_active_head) {
-                       ts = list_entry(q, struct snd_timer_instance, active_list);
+               list_for_each_entry(ts, &ti->slave_active_head, active_list) {
                        ts->pticks = ti->pticks;
                        ts->resolution = resolution;
                        if (list_empty(&ts->ack_list))
@@ -715,7 +703,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
                }
        }
        if (timer->flags & SNDRV_TIMER_FLG_RESCHED)
-               snd_timer_reschedule(timer, ticks_left);
+               snd_timer_reschedule(timer, timer->sticks);
        if (timer->running) {
                if (timer->hw.flags & SNDRV_TIMER_HW_STOP) {
                        timer->hw.stop(timer);
@@ -752,10 +740,10 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
 
        /* do we have any slow callbacks? */
        use_tasklet = !list_empty(&timer->sack_list_head);
-       spin_unlock(&timer->lock);
+       spin_unlock_irqrestore(&timer->lock, flags);
 
        if (use_tasklet)
-               tasklet_hi_schedule(&timer->task_queue);
+               tasklet_schedule(&timer->task_queue);
 }
 
 /*
@@ -770,12 +758,13 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
        static struct snd_device_ops ops = {
                .dev_free = snd_timer_dev_free,
                .dev_register = snd_timer_dev_register,
-               .dev_unregister = snd_timer_dev_unregister
+               .dev_disconnect = snd_timer_dev_disconnect,
        };
 
-       snd_assert(tid != NULL, return -EINVAL);
-       snd_assert(rtimer != NULL, return -EINVAL);
-       *rtimer = NULL;
+       if (snd_BUG_ON(!tid))
+               return -EINVAL;
+       if (rtimer)
+               *rtimer = NULL;
        timer = kzalloc(sizeof(*timer), GFP_KERNEL);
        if (timer == NULL) {
                snd_printk(KERN_ERR "timer: cannot allocate\n");
@@ -803,13 +792,30 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
                        return err;
                }
        }
-       *rtimer = timer;
+       if (rtimer)
+               *rtimer = timer;
        return 0;
 }
 
 static int snd_timer_free(struct snd_timer *timer)
 {
-       snd_assert(timer != NULL, return -ENXIO);
+       if (!timer)
+               return 0;
+
+       mutex_lock(&register_mutex);
+       if (! list_empty(&timer->open_list_head)) {
+               struct list_head *p, *n;
+               struct snd_timer_instance *ti;
+               snd_printk(KERN_WARNING "timer %p is busy?\n", timer);
+               list_for_each_safe(p, n, &timer->open_list_head) {
+                       list_del_init(p);
+                       ti = list_entry(p, struct snd_timer_instance, open_list);
+                       ti->timer = NULL;
+               }
+       }
+       list_del(&timer->device_list);
+       mutex_unlock(&register_mutex);
+
        if (timer->private_free)
                timer->private_free(timer);
        kfree(timer);
@@ -826,17 +832,15 @@ static int snd_timer_dev_register(struct snd_device *dev)
 {
        struct snd_timer *timer = dev->device_data;
        struct snd_timer *timer1;
-       struct list_head *p;
 
-       snd_assert(timer != NULL && timer->hw.start != NULL &&
-                  timer->hw.stop != NULL, return -ENXIO);
+       if (snd_BUG_ON(!timer || !timer->hw.start || !timer->hw.stop))
+               return -ENXIO;
        if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) &&
            !timer->hw.resolution && timer->hw.c_resolution == NULL)
                return -EINVAL;
 
-       down(&register_mutex);
-       list_for_each(p, &snd_timer_list) {
-               timer1 = list_entry(p, struct snd_timer, device_list);
+       mutex_lock(&register_mutex);
+       list_for_each_entry(timer1, &snd_timer_list, device_list) {
                if (timer1->tmr_class > timer->tmr_class)
                        break;
                if (timer1->tmr_class < timer->tmr_class)
@@ -856,38 +860,21 @@ static int snd_timer_dev_register(struct snd_device *dev)
                if (timer1->tmr_subdevice < timer->tmr_subdevice)
                        continue;
                /* conflicts.. */
-               up(&register_mutex);
+               mutex_unlock(&register_mutex);
                return -EBUSY;
        }
-       list_add_tail(&timer->device_list, p);
-       up(&register_mutex);
+       list_add_tail(&timer->device_list, &timer1->device_list);
+       mutex_unlock(&register_mutex);
        return 0;
 }
 
-static int snd_timer_unregister(struct snd_timer *timer)
-{
-       struct list_head *p, *n;
-       struct snd_timer_instance *ti;
-
-       snd_assert(timer != NULL, return -ENXIO);
-       down(&register_mutex);
-       if (! list_empty(&timer->open_list_head)) {
-               snd_printk(KERN_WARNING "timer 0x%lx is busy?\n", (long)timer);
-               list_for_each_safe(p, n, &timer->open_list_head) {
-                       list_del_init(p);
-                       ti = list_entry(p, struct snd_timer_instance, open_list);
-                       ti->timer = NULL;
-               }
-       }
-       list_del(&timer->device_list);
-       up(&register_mutex);
-       return snd_timer_free(timer);
-}
-
-static int snd_timer_dev_unregister(struct snd_device *device)
+static int snd_timer_dev_disconnect(struct snd_device *device)
 {
        struct snd_timer *timer = device->device_data;
-       return snd_timer_unregister(timer);
+       mutex_lock(&register_mutex);
+       list_del_init(&timer->device_list);
+       mutex_unlock(&register_mutex);
+       return 0;
 }
 
 void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp)
@@ -895,12 +882,12 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
        unsigned long flags;
        unsigned long resolution = 0;
        struct snd_timer_instance *ti, *ts;
-       struct list_head *p, *n;
 
        if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
                return;
-       snd_assert(event >= SNDRV_TIMER_EVENT_MSTART &&
-                  event <= SNDRV_TIMER_EVENT_MRESUME, return);
+       if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
+                      event > SNDRV_TIMER_EVENT_MRESUME))
+               return;
        spin_lock_irqsave(&timer->lock, flags);
        if (event == SNDRV_TIMER_EVENT_MSTART ||
            event == SNDRV_TIMER_EVENT_MCONTINUE ||
@@ -910,15 +897,12 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
                else
                        resolution = timer->hw.resolution;
        }
-       list_for_each(p, &timer->active_list_head) {
-               ti = list_entry(p, struct snd_timer_instance, active_list);
+       list_for_each_entry(ti, &timer->active_list_head, active_list) {
                if (ti->ccallback)
                        ti->ccallback(ti, event, tstamp, resolution);
-               list_for_each(n, &ti->slave_active_head) {
-                       ts = list_entry(n, struct snd_timer_instance, active_list);
+               list_for_each_entry(ts, &ti->slave_active_head, active_list)
                        if (ts->ccallback)
                                ts->ccallback(ts, event, tstamp, resolution);
-               }
        }
        spin_unlock_irqrestore(&timer->lock, flags);
 }
@@ -952,18 +936,12 @@ int snd_timer_global_register(struct snd_timer *timer)
        return snd_timer_dev_register(&dev);
 }
 
-int snd_timer_global_unregister(struct snd_timer *timer)
-{
-       return snd_timer_unregister(timer);
-}
-
 /*
  *  System timer
  */
 
 struct snd_timer_system_private {
        struct timer_list tlist;
-       struct timer * timer;
        unsigned long last_expires;
        unsigned long last_jiffies;
        unsigned long correction;
@@ -975,7 +953,7 @@ static void snd_timer_s_function(unsigned long data)
        struct snd_timer_system_private *priv = timer->private_data;
        unsigned long jiff = jiffies;
        if (time_after(jiff, priv->last_expires))
-               priv->correction = (long)jiff - (long)priv->last_expires;
+               priv->correction += (long)jiff - (long)priv->last_expires;
        snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies);
 }
 
@@ -991,7 +969,7 @@ static int snd_timer_s_start(struct snd_timer * timer)
                njiff++;
        } else {
                njiff += timer->sticks - priv->correction;
-               priv->correction -= timer->sticks;
+               priv->correction = 0;
        }
        priv->last_expires = priv->tlist.expires = njiff;
        add_timer(&priv->tlist);
@@ -1010,6 +988,7 @@ static int snd_timer_s_stop(struct snd_timer * timer)
                timer->sticks = priv->last_expires - jiff;
        else
                timer->sticks = 1;
+       priv->correction = 0;
        return 0;
 }
 
@@ -1051,6 +1030,7 @@ static int snd_timer_register_system(void)
        return snd_timer_global_register(timer);
 }
 
+#ifdef CONFIG_PROC_FS
 /*
  *  Info interface
  */
@@ -1058,14 +1038,11 @@ static int snd_timer_register_system(void)
 static void snd_timer_proc_read(struct snd_info_entry *entry,
                                struct snd_info_buffer *buffer)
 {
-       unsigned long flags;
        struct snd_timer *timer;
        struct snd_timer_instance *ti;
-       struct list_head *p, *q;
 
-       down(&register_mutex);
-       list_for_each(p, &snd_timer_list) {
-               timer = list_entry(p, struct snd_timer, device_list);
+       mutex_lock(&register_mutex);
+       list_for_each_entry(timer, &snd_timer_list, device_list) {
                switch (timer->tmr_class) {
                case SNDRV_TIMER_CLASS_GLOBAL:
                        snd_iprintf(buffer, "G%i: ", timer->tmr_device);
@@ -1092,20 +1069,42 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
                if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
                        snd_iprintf(buffer, " SLAVE");
                snd_iprintf(buffer, "\n");
-               spin_lock_irqsave(&timer->lock, flags);
-               list_for_each(q, &timer->open_list_head) {
-                       ti = list_entry(q, struct snd_timer_instance, open_list);
+               list_for_each_entry(ti, &timer->open_list_head, open_list)
                        snd_iprintf(buffer, "  Client %s : %s\n",
                                    ti->owner ? ti->owner : "unknown",
                                    ti->flags & (SNDRV_TIMER_IFLG_START |
                                                 SNDRV_TIMER_IFLG_RUNNING)
                                    ? "running" : "stopped");
+       }
+       mutex_unlock(&register_mutex);
+}
+
+static struct snd_info_entry *snd_timer_proc_entry;
+
+static void __init snd_timer_proc_init(void)
+{
+       struct snd_info_entry *entry;
+
+       entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL);
+       if (entry != NULL) {
+               entry->c.text.read = snd_timer_proc_read;
+               if (snd_info_register(entry) < 0) {
+                       snd_info_free_entry(entry);
+                       entry = NULL;
                }
-               spin_unlock_irqrestore(&timer->lock, flags);
        }
-       up(&register_mutex);
+       snd_timer_proc_entry = entry;
 }
 
+static void __exit snd_timer_proc_done(void)
+{
+       snd_info_free_entry(snd_timer_proc_entry);
+}
+#else /* !CONFIG_PROC_FS */
+#define snd_timer_proc_init()
+#define snd_timer_proc_done()
+#endif
+
 /*
  *  USER SPACE interface
  */
@@ -1161,6 +1160,7 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
 {
        struct snd_timer_user *tu = timeri->callback_data;
        struct snd_timer_tread r1;
+       unsigned long flags;
 
        if (event >= SNDRV_TIMER_EVENT_START &&
            event <= SNDRV_TIMER_EVENT_PAUSE)
@@ -1170,9 +1170,9 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
        r1.event = event;
        r1.tstamp = *tstamp;
        r1.val = resolution;
-       spin_lock(&tu->qlock);
+       spin_lock_irqsave(&tu->qlock, flags);
        snd_timer_user_append_to_tqueue(tu, &r1);
-       spin_unlock(&tu->qlock);
+       spin_unlock_irqrestore(&tu->qlock, flags);
        kill_fasync(&tu->fasync, SIGIO, POLL_IN);
        wake_up(&tu->qchange_sleep);
 }
@@ -1193,8 +1193,12 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
                spin_unlock(&tu->qlock);
                return;
        }
-       if (tu->last_resolution != resolution || ticks > 0)
-               getnstimeofday(&tstamp);
+       if (tu->last_resolution != resolution || ticks > 0) {
+               if (timer_tstamp_monotonic)
+                       do_posix_clock_monotonic_gettime(&tstamp);
+               else
+                       getnstimeofday(&tstamp);
+       }
        if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
            tu->last_resolution != resolution) {
                r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
@@ -1234,13 +1238,18 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
 static int snd_timer_user_open(struct inode *inode, struct file *file)
 {
        struct snd_timer_user *tu;
+       int err;
+
+       err = nonseekable_open(inode, file);
+       if (err < 0)
+               return err;
 
        tu = kzalloc(sizeof(*tu), GFP_KERNEL);
        if (tu == NULL)
                return -ENOMEM;
        spin_lock_init(&tu->qlock);
        init_waitqueue_head(&tu->qchange_sleep);
-       init_MUTEX(&tu->tread_sem);
+       mutex_init(&tu->tread_sem);
        tu->ticks = 1;
        tu->queue_size = 128;
        tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read),
@@ -1260,7 +1269,6 @@ static int snd_timer_user_release(struct inode *inode, struct file *file)
        if (file->private_data) {
                tu = file->private_data;
                file->private_data = NULL;
-               fasync_helper(-1, file, 0, &tu->fasync);
                if (tu->timeri)
                        snd_timer_close(tu->timeri);
                kfree(tu->queue);
@@ -1296,7 +1304,7 @@ static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
 
        if (copy_from_user(&id, _tid, sizeof(id)))
                return -EFAULT;
-       down(&register_mutex);
+       mutex_lock(&register_mutex);
        if (id.dev_class < 0) {         /* first item */
                if (list_empty(&snd_timer_list))
                        snd_timer_user_zero_id(&id);
@@ -1378,7 +1386,7 @@ static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
                        snd_timer_user_zero_id(&id);
                }
        }
-       up(&register_mutex);
+       mutex_unlock(&register_mutex);
        if (copy_to_user(_tid, &id, sizeof(*_tid)))
                return -EFAULT;
        return 0;
@@ -1393,17 +1401,14 @@ static int snd_timer_user_ginfo(struct file *file,
        struct list_head *p;
        int err = 0;
 
-       ginfo = kmalloc(sizeof(*ginfo), GFP_KERNEL);
-       if (! ginfo)
-               return -ENOMEM;
-       if (copy_from_user(ginfo, _ginfo, sizeof(*ginfo))) {
-               kfree(ginfo);
-               return -EFAULT;
-       }
+       ginfo = memdup_user(_ginfo, sizeof(*ginfo));
+       if (IS_ERR(ginfo))
+               return PTR_ERR(ginfo);
+
        tid = ginfo->tid;
        memset(ginfo, 0, sizeof(*ginfo));
        ginfo->tid = tid;
-       down(&register_mutex);
+       mutex_lock(&register_mutex);
        t = snd_timer_find(&tid);
        if (t != NULL) {
                ginfo->card = t->card ? t->card->number : -1;
@@ -1422,7 +1427,7 @@ static int snd_timer_user_ginfo(struct file *file,
        } else {
                err = -ENODEV;
        }
-       up(&register_mutex);
+       mutex_unlock(&register_mutex);
        if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo)))
                err = -EFAULT;
        kfree(ginfo);
@@ -1438,7 +1443,7 @@ static int snd_timer_user_gparams(struct file *file,
 
        if (copy_from_user(&gparams, _gparams, sizeof(gparams)))
                return -EFAULT;
-       down(&register_mutex);
+       mutex_lock(&register_mutex);
        t = snd_timer_find(&gparams.tid);
        if (!t) {
                err = -ENODEV;
@@ -1454,7 +1459,7 @@ static int snd_timer_user_gparams(struct file *file,
        }
        err = t->hw.set_period(t, gparams.period_num, gparams.period_den);
 _error:
-       up(&register_mutex);
+       mutex_unlock(&register_mutex);
        return err;
 }
 
@@ -1471,7 +1476,7 @@ static int snd_timer_user_gstatus(struct file *file,
        tid = gstatus.tid;
        memset(&gstatus, 0, sizeof(gstatus));
        gstatus.tid = tid;
-       down(&register_mutex);
+       mutex_lock(&register_mutex);
        t = snd_timer_find(&tid);
        if (t != NULL) {
                if (t->hw.c_resolution)
@@ -1488,7 +1493,7 @@ static int snd_timer_user_gstatus(struct file *file,
        } else {
                err = -ENODEV;
        }
-       up(&register_mutex);
+       mutex_unlock(&register_mutex);
        if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus)))
                err = -EFAULT;
        return err;
@@ -1503,7 +1508,7 @@ static int snd_timer_user_tselect(struct file *file,
        int err = 0;
 
        tu = file->private_data;
-       down(&tu->tread_sem);
+       mutex_lock(&tu->tread_sem);
        if (tu->timeri) {
                snd_timer_close(tu->timeri);
                tu->timeri = NULL;
@@ -1547,7 +1552,7 @@ static int snd_timer_user_tselect(struct file *file,
        }
 
       __err:
-       up(&tu->tread_sem);
+       mutex_unlock(&tu->tread_sem);
        return err;
 }
 
@@ -1560,9 +1565,11 @@ static int snd_timer_user_info(struct file *file,
        int err = 0;
 
        tu = file->private_data;
-       snd_assert(tu->timeri != NULL, return -ENXIO);
+       if (!tu->timeri)
+               return -EBADFD;
        t = tu->timeri->timer;
-       snd_assert(t != NULL, return -ENXIO);
+       if (!t)
+               return -EBADFD;
 
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (! info)
@@ -1590,9 +1597,11 @@ static int snd_timer_user_params(struct file *file,
        int err;
 
        tu = file->private_data;
-       snd_assert(tu->timeri != NULL, return -ENXIO);
+       if (!tu->timeri)
+               return -EBADFD;
        t = tu->timeri->timer;
-       snd_assert(t != NULL, return -ENXIO);
+       if (!t)
+               return -EBADFD;
        if (copy_from_user(&params, _params, sizeof(params)))
                return -EFAULT;
        if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) {
@@ -1686,7 +1695,8 @@ static int snd_timer_user_status(struct file *file,
        struct snd_timer_status status;
 
        tu = file->private_data;
-       snd_assert(tu->timeri != NULL, return -ENXIO);
+       if (!tu->timeri)
+               return -EBADFD;
        memset(&status, 0, sizeof(status));
        status.tstamp = tu->tstamp;
        status.resolution = snd_timer_resolution(tu->timeri);
@@ -1706,7 +1716,8 @@ static int snd_timer_user_start(struct file *file)
        struct snd_timer_user *tu;
 
        tu = file->private_data;
-       snd_assert(tu->timeri != NULL, return -ENXIO);
+       if (!tu->timeri)
+               return -EBADFD;
        snd_timer_stop(tu->timeri);
        tu->timeri->lost = 0;
        tu->last_resolution = 0;
@@ -1719,7 +1730,8 @@ static int snd_timer_user_stop(struct file *file)
        struct snd_timer_user *tu;
 
        tu = file->private_data;
-       snd_assert(tu->timeri != NULL, return -ENXIO);
+       if (!tu->timeri)
+               return -EBADFD;
        return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
 }
 
@@ -1729,7 +1741,8 @@ static int snd_timer_user_continue(struct file *file)
        struct snd_timer_user *tu;
 
        tu = file->private_data;
-       snd_assert(tu->timeri != NULL, return -ENXIO);
+       if (!tu->timeri)
+               return -EBADFD;
        tu->timeri->lost = 0;
        return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
 }
@@ -1740,7 +1753,8 @@ static int snd_timer_user_pause(struct file *file)
        struct snd_timer_user *tu;
 
        tu = file->private_data;
-       snd_assert(tu->timeri != NULL, return -ENXIO);
+       if (!tu->timeri)
+               return -EBADFD;
        return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
 }
 
@@ -1768,17 +1782,17 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
        {
                int xarg;
 
-               down(&tu->tread_sem);
+               mutex_lock(&tu->tread_sem);
                if (tu->timeri) {       /* too late */
-                       up(&tu->tread_sem);
+                       mutex_unlock(&tu->tread_sem);
                        return -EBUSY;
                }
                if (get_user(xarg, p)) {
-                       up(&tu->tread_sem);
+                       mutex_unlock(&tu->tread_sem);
                        return -EFAULT;
                }
                tu->tread = xarg ? 1 : 0;
-               up(&tu->tread_sem);
+               mutex_unlock(&tu->tread_sem);
                return 0;
        }
        case SNDRV_TIMER_IOCTL_GINFO:
@@ -1814,13 +1828,9 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
 static int snd_timer_user_fasync(int fd, struct file * file, int on)
 {
        struct snd_timer_user *tu;
-       int err;
 
        tu = file->private_data;
-       err = fasync_helper(fd, file, on, &tu->fasync);
-        if (err < 0)
-               return err;
-       return 0;
+       return fasync_helper(fd, file, on, &tu->fasync);
 }
 
 static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
@@ -1911,56 +1921,40 @@ static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
 #define snd_timer_user_ioctl_compat    NULL
 #endif
 
-static struct file_operations snd_timer_f_ops =
+static const struct file_operations snd_timer_f_ops =
 {
        .owner =        THIS_MODULE,
        .read =         snd_timer_user_read,
        .open =         snd_timer_user_open,
        .release =      snd_timer_user_release,
+       .llseek =       no_llseek,
        .poll =         snd_timer_user_poll,
        .unlocked_ioctl =       snd_timer_user_ioctl,
        .compat_ioctl = snd_timer_user_ioctl_compat,
        .fasync =       snd_timer_user_fasync,
 };
 
-static struct snd_minor snd_timer_reg =
-{
-       .comment =      "timer",
-       .f_ops =        &snd_timer_f_ops,
-};
-
 /*
  *  ENTRY functions
  */
 
-static struct snd_info_entry *snd_timer_proc_entry = NULL;
-
 static int __init alsa_timer_init(void)
 {
        int err;
-       struct snd_info_entry *entry;
 
 #ifdef SNDRV_OSS_INFO_DEV_TIMERS
        snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1,
                              "system timer");
 #endif
-       entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL);
-       if (entry != NULL) {
-               entry->c.text.read_size = SNDRV_TIMER_DEVICES * 128;
-               entry->c.text.read = snd_timer_proc_read;
-               if (snd_info_register(entry) < 0) {
-                       snd_info_free_entry(entry);
-                       entry = NULL;
-               }
-       }
-       snd_timer_proc_entry = entry;
+
        if ((err = snd_timer_register_system()) < 0)
                snd_printk(KERN_ERR "unable to register system timer (%i)\n",
                           err);
-       if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER,
-                                       NULL, 0, &snd_timer_reg, "timer"))<0)
+       if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0,
+                                      &snd_timer_f_ops, NULL, "timer")) < 0)
                snd_printk(KERN_ERR "unable to register timer device (%i)\n",
                           err);
+       snd_timer_proc_init();
        return 0;
 }
 
@@ -1972,12 +1966,9 @@ static void __exit alsa_timer_exit(void)
        /* unregister the system timer */
        list_for_each_safe(p, n, &snd_timer_list) {
                struct snd_timer *timer = list_entry(p, struct snd_timer, device_list);
-               snd_timer_unregister(timer);
-       }
-       if (snd_timer_proc_entry) {
-               snd_info_unregister(snd_timer_proc_entry);
-               snd_timer_proc_entry = NULL;
+               snd_timer_free(timer);
        }
+       snd_timer_proc_done();
 #ifdef SNDRV_OSS_INFO_DEV_TIMERS
        snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1);
 #endif
@@ -1998,5 +1989,4 @@ EXPORT_SYMBOL(snd_timer_notify);
 EXPORT_SYMBOL(snd_timer_global_new);
 EXPORT_SYMBOL(snd_timer_global_free);
 EXPORT_SYMBOL(snd_timer_global_register);
-EXPORT_SYMBOL(snd_timer_global_unregister);
 EXPORT_SYMBOL(snd_timer_interrupt);