ALSA: timer: Use mod_timer() for rearming the system timer
[pandora-kernel.git] / sound / core / timer.c
index 8e7561d..bc83a96 100644 (file)
@@ -72,7 +72,7 @@ struct snd_timer_user {
        struct timespec tstamp;         /* trigger tstamp */
        wait_queue_head_t qchange_sleep;
        struct fasync_struct *fasync;
-       struct mutex tread_sem;
+       struct mutex ioctl_lock;
 };
 
 /* list of timers */
@@ -214,11 +214,13 @@ static void snd_timer_check_master(struct snd_timer_instance *master)
                    slave->slave_id == master->slave_id) {
                        list_move_tail(&slave->open_list, &master->slave_list_head);
                        spin_lock_irq(&slave_active_lock);
+                       spin_lock(&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);
+                       spin_unlock(&master->timer->lock);
                        spin_unlock_irq(&slave_active_lock);
                }
        }
@@ -344,15 +346,18 @@ int snd_timer_close(struct snd_timer_instance *timeri)
                    timer->hw.close)
                        timer->hw.close(timer);
                /* remove slave links */
+               spin_lock_irq(&slave_active_lock);
+               spin_lock(&timer->lock);
                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_move_tail(&slave->open_list, &snd_timer_slave_list);
                        slave->master = NULL;
                        slave->timer = NULL;
-                       spin_unlock_irq(&slave_active_lock);
+                       list_del_init(&slave->ack_list);
+                       list_del_init(&slave->active_list);
                }
+               spin_unlock(&timer->lock);
+               spin_unlock_irq(&slave_active_lock);
                mutex_unlock(&register_mutex);
        }
  out:
@@ -409,7 +414,7 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
        spin_lock_irqsave(&timer->lock, flags);
        list_for_each_entry(ts, &ti->slave_active_head, active_list)
                if (ts->ccallback)
-                       ts->ccallback(ti, event + 100, &tstamp, resolution);
+                       ts->ccallback(ts, event + 100, &tstamp, resolution);
        spin_unlock_irqrestore(&timer->lock, flags);
 }
 
@@ -438,10 +443,17 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri)
        unsigned long flags;
 
        spin_lock_irqsave(&slave_active_lock, flags);
+       if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
+               spin_unlock_irqrestore(&slave_active_lock, flags);
+               return -EBUSY;
+       }
        timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
-       if (timeri->master)
+       if (timeri->master && timeri->timer) {
+               spin_lock(&timeri->timer->lock);
                list_add_tail(&timeri->active_list,
                              &timeri->master->slave_active_head);
+               spin_unlock(&timeri->timer->lock);
+       }
        spin_unlock_irqrestore(&slave_active_lock, flags);
        return 1; /* delayed start */
 }
@@ -459,18 +471,26 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
                return -EINVAL;
        if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
                result = snd_timer_start_slave(timeri);
-               snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
+               if (result >= 0)
+                       snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
                return result;
        }
        timer = timeri->timer;
        if (timer == NULL)
                return -EINVAL;
        spin_lock_irqsave(&timer->lock, flags);
+       if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
+                            SNDRV_TIMER_IFLG_START)) {
+               result = -EBUSY;
+               goto unlock;
+       }
        timeri->ticks = timeri->cticks = ticks;
        timeri->pticks = 0;
        result = snd_timer_start1(timer, timeri, ticks);
+ unlock:
        spin_unlock_irqrestore(&timer->lock, flags);
-       snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
+       if (result >= 0)
+               snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
        return result;
 }
 
@@ -486,7 +506,17 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
        if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
                if (!keep_flag) {
                        spin_lock_irqsave(&slave_active_lock, flags);
+                       if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
+                               spin_unlock_irqrestore(&slave_active_lock, flags);
+                               return -EBUSY;
+                       }
+                       if (timeri->timer)
+                               spin_lock(&timeri->timer->lock);
                        timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
+                       list_del_init(&timeri->ack_list);
+                       list_del_init(&timeri->active_list);
+                       if (timeri->timer)
+                               spin_unlock(&timeri->timer->lock);
                        spin_unlock_irqrestore(&slave_active_lock, flags);
                }
                goto __end;
@@ -495,6 +525,11 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
        if (!timer)
                return -EINVAL;
        spin_lock_irqsave(&timer->lock, flags);
+       if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
+                              SNDRV_TIMER_IFLG_START))) {
+               spin_unlock_irqrestore(&timer->lock, flags);
+               return -EBUSY;
+       }
        list_del_init(&timeri->ack_list);
        list_del_init(&timeri->active_list);
        if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
@@ -560,10 +595,15 @@ int snd_timer_continue(struct snd_timer_instance *timeri)
        if (! timer)
                return -EINVAL;
        spin_lock_irqsave(&timer->lock, flags);
+       if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
+               result = -EBUSY;
+               goto unlock;
+       }
        if (!timeri->cticks)
                timeri->cticks = 1;
        timeri->pticks = 0;
        result = snd_timer_start1(timer, timeri, timer->sticks);
+ unlock:
        spin_unlock_irqrestore(&timer->lock, flags);
        snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE);
        return result;
@@ -691,8 +731,8 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
                        ti->cticks = ti->ticks;
                } else {
                        ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
-                       if (--timer->running)
-                               list_del(&ti->active_list);
+                       --timer->running;
+                       list_del_init(&ti->active_list);
                }
                if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) ||
                    (ti->flags & SNDRV_TIMER_IFLG_FAST))
@@ -977,8 +1017,8 @@ static int snd_timer_s_start(struct snd_timer * timer)
                njiff += timer->sticks - priv->correction;
                priv->correction = 0;
        }
-       priv->last_expires = priv->tlist.expires = njiff;
-       add_timer(&priv->tlist);
+       priv->last_expires = njiff;
+       mod_timer(&priv->tlist, njiff);
        return 0;
 }
 
@@ -1255,7 +1295,7 @@ static int snd_timer_user_open(struct inode *inode, struct file *file)
                return -ENOMEM;
        spin_lock_init(&tu->qlock);
        init_waitqueue_head(&tu->qchange_sleep);
-       mutex_init(&tu->tread_sem);
+       mutex_init(&tu->ioctl_lock);
        tu->ticks = 1;
        tu->queue_size = 128;
        tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read),
@@ -1275,8 +1315,10 @@ static int snd_timer_user_release(struct inode *inode, struct file *file)
        if (file->private_data) {
                tu = file->private_data;
                file->private_data = NULL;
+               mutex_lock(&tu->ioctl_lock);
                if (tu->timeri)
                        snd_timer_close(tu->timeri);
+               mutex_unlock(&tu->ioctl_lock);
                kfree(tu->queue);
                kfree(tu->tqueue);
                kfree(tu);
@@ -1514,7 +1556,6 @@ static int snd_timer_user_tselect(struct file *file,
        int err = 0;
 
        tu = file->private_data;
-       mutex_lock(&tu->tread_sem);
        if (tu->timeri) {
                snd_timer_close(tu->timeri);
                tu->timeri = NULL;
@@ -1558,7 +1599,6 @@ static int snd_timer_user_tselect(struct file *file,
        }
 
       __err:
-       mutex_unlock(&tu->tread_sem);
        return err;
 }
 
@@ -1771,7 +1811,7 @@ enum {
        SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
 };
 
-static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
+static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
                                 unsigned long arg)
 {
        struct snd_timer_user *tu;
@@ -1788,17 +1828,11 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
        {
                int xarg;
 
-               mutex_lock(&tu->tread_sem);
-               if (tu->timeri) {       /* too late */
-                       mutex_unlock(&tu->tread_sem);
+               if (tu->timeri) /* too late */
                        return -EBUSY;
-               }
-               if (get_user(xarg, p)) {
-                       mutex_unlock(&tu->tread_sem);
+               if (get_user(xarg, p))
                        return -EFAULT;
-               }
                tu->tread = xarg ? 1 : 0;
-               mutex_unlock(&tu->tread_sem);
                return 0;
        }
        case SNDRV_TIMER_IOCTL_GINFO:
@@ -1831,6 +1865,18 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
        return -ENOTTY;
 }
 
+static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
+                                unsigned long arg)
+{
+       struct snd_timer_user *tu = file->private_data;
+       long ret;
+
+       mutex_lock(&tu->ioctl_lock);
+       ret = __snd_timer_user_ioctl(file, cmd, arg);
+       mutex_unlock(&tu->ioctl_lock);
+       return ret;
+}
+
 static int snd_timer_user_fasync(int fd, struct file * file, int on)
 {
        struct snd_timer_user *tu;
@@ -1844,6 +1890,7 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
 {
        struct snd_timer_user *tu;
        long result = 0, unit;
+       int qhead;
        int err = 0;
 
        tu = file->private_data;
@@ -1855,7 +1902,7 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
 
                        if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
                                err = -EAGAIN;
-                               break;
+                               goto _error;
                        }
 
                        set_current_state(TASK_INTERRUPTIBLE);
@@ -1870,38 +1917,33 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
 
                        if (signal_pending(current)) {
                                err = -ERESTARTSYS;
-                               break;
+                               goto _error;
                        }
                }
 
+               qhead = tu->qhead++;
+               tu->qhead %= tu->queue_size;
                spin_unlock_irq(&tu->qlock);
-               if (err < 0)
-                       goto _error;
 
                if (tu->tread) {
-                       if (copy_to_user(buffer, &tu->tqueue[tu->qhead++],
-                                        sizeof(struct snd_timer_tread))) {
+                       if (copy_to_user(buffer, &tu->tqueue[qhead],
+                                        sizeof(struct snd_timer_tread)))
                                err = -EFAULT;
-                               goto _error;
-                       }
                } else {
-                       if (copy_to_user(buffer, &tu->queue[tu->qhead++],
-                                        sizeof(struct snd_timer_read))) {
+                       if (copy_to_user(buffer, &tu->queue[qhead],
+                                        sizeof(struct snd_timer_read)))
                                err = -EFAULT;
-                               goto _error;
-                       }
                }
 
-               tu->qhead %= tu->queue_size;
-
-               result += unit;
-               buffer += unit;
-
                spin_lock_irq(&tu->qlock);
                tu->qused--;
+               if (err < 0)
+                       goto _error;
+               result += unit;
+               buffer += unit;
        }
-       spin_unlock_irq(&tu->qlock);
  _error:
+       spin_unlock_irq(&tu->qlock);
        return result > 0 ? result : err;
 }