Index: alsa-kernel/core/timer.c =================================================================== RCS file: /home/iwai/cvs/alsa/alsa-kernel/core/timer.c,v retrieving revision 1.67 diff -u -r1.67 timer.c --- alsa-kernel/core/timer.c 12 Oct 2005 16:36:55 -0000 1.67 +++ alsa-kernel/core/timer.c 31 Oct 2005 17:22:04 -0000 @@ -424,6 +424,9 @@ static int snd_timer_start1(snd_timer_t *timer, snd_timer_instance_t *timeri, unsigned long sticks) { + unsigned long flags; + + spin_lock_irqsave(&timer->lock, flags); list_del(&timeri->active_list); list_add_tail(&timeri->active_list, &timer->active_list_head); if (timer->running) { @@ -431,13 +434,19 @@ goto __start_now; timer->flags |= SNDRV_TIMER_FLG_RESCHED; timeri->flags |= SNDRV_TIMER_IFLG_START; + spin_unlock_irqrestore(&timer->lock, flags); return 1; /* delayed start */ } else { timer->sticks = sticks; + if (!(timer->hw.flags & SNDRV_TIMER_HW_ATOMIC)) + spin_unlock_irqrestore(&timer->lock, flags); timer->hw.start(timer); + if (!(timer->hw.flags & SNDRV_TIMER_HW_ATOMIC)) + spin_lock_irqsave(&timer->lock, flags); __start_now: timer->running++; timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; + spin_unlock_irqrestore(&timer->lock, flags); return 0; } } @@ -477,8 +486,8 @@ spin_lock_irqsave(&timer->lock, flags); timeri->ticks = timeri->cticks = ticks; timeri->pticks = 0; - result = snd_timer_start1(timer, timeri, ticks); spin_unlock_irqrestore(&timer->lock, flags); + result = snd_timer_start1(timer, timeri, ticks); snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); return result; } @@ -488,6 +497,7 @@ { snd_timer_t *timer; unsigned long flags; + int running = 1; snd_assert(timeri != NULL, return -ENXIO); @@ -505,22 +515,32 @@ spin_lock_irqsave(&timer->lock, flags); list_del_init(&timeri->ack_list); list_del_init(&timeri->active_list); - if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && - !(--timer->running)) { + if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) + running = --timer->running; + spin_unlock_irqrestore(&timer->lock, flags); + if (! running) { timer->hw.stop(timer); + spin_lock_irqsave(&timer->lock, flags); if (timer->flags & SNDRV_TIMER_FLG_RESCHED) { timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; snd_timer_reschedule(timer, 0); if (timer->flags & SNDRV_TIMER_FLG_CHANGE) { timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; + if (! (timer->hw.flags & SNDRV_TIMER_HW_ATOMIC)) + spin_unlock_irqrestore(&timer->lock, flags); timer->hw.start(timer); + if (! (timer->hw.flags & SNDRV_TIMER_HW_ATOMIC)) + spin_lock_irqsave(&timer->lock, flags); } } + spin_unlock_irqrestore(&timer->lock, flags); } - if (!keep_flag) + if (!keep_flag) { + spin_lock_irqsave(&slave_active_lock, flags); timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START); - spin_unlock_irqrestore(&timer->lock, flags); + spin_unlock_irqrestore(&slave_active_lock, flags); + } __end: if (event != SNDRV_TIMER_EVENT_RESOLUTION) snd_timer_notify1(timeri, event); @@ -569,8 +589,8 @@ if (!timeri->cticks) timeri->cticks = 1; timeri->pticks = 0; - result = snd_timer_start1(timer, timeri, timer->sticks); spin_unlock_irqrestore(&timer->lock, flags); + result = snd_timer_start1(timer, timeri, timer->sticks); snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE); return result; } @@ -718,17 +738,19 @@ if (timer->flags & SNDRV_TIMER_FLG_RESCHED) snd_timer_reschedule(timer, ticks_left); if (timer->running) { - if (timer->hw.flags & SNDRV_TIMER_HW_STOP) { + if ((timer->hw.flags & SNDRV_TIMER_HW_STOP) && + (timer->hw.flags & SNDRV_TIMER_HW_ATOMIC)) { timer->hw.stop(timer); timer->flags |= SNDRV_TIMER_FLG_CHANGE; } - if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) || - (timer->flags & SNDRV_TIMER_FLG_CHANGE)) { + if ((!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) || + (timer->flags & SNDRV_TIMER_FLG_CHANGE)) && + (timer->hw.flags & SNDRV_TIMER_HW_ATOMIC)){ /* restart timer */ timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; timer->hw.start(timer); } - } else { + } else if (timer->hw.flags & SNDRV_TIMER_HW_ATOMIC) { timer->hw.stop(timer); } @@ -1015,7 +1037,7 @@ static struct _snd_timer_hardware snd_timer_system = { - .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET, + .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET | SNDRV_TIMER_HW_ATOMIC, .resolution = 1000000000L / HZ, .ticks = 10000000L, .start = snd_timer_s_start, Index: alsa-kernel/include/timer.h =================================================================== RCS file: /home/iwai/cvs/alsa/alsa-kernel/include/timer.h,v retrieving revision 1.14 diff -u -r1.14 timer.h --- alsa-kernel/include/timer.h 12 Oct 2005 16:36:55 -0000 1.14 +++ alsa-kernel/include/timer.h 31 Oct 2005 17:22:04 -0000 @@ -51,6 +51,7 @@ #define SNDRV_TIMER_HW_SLAVE 0x00000004 /* only slave timer (variable resolution) */ #define SNDRV_TIMER_HW_FIRST 0x00000008 /* first tick can be incomplete */ #define SNDRV_TIMER_HW_TASKLET 0x00000010 /* timer is called from tasklet */ +#define SNDRV_TIMER_HW_ATOMIC 0x00000020 /* start/stop are atomoic */ #define SNDRV_TIMER_IFLG_SLAVE 0x00000001 #define SNDRV_TIMER_IFLG_RUNNING 0x00000002