Index: alsa-driver-1.0.22.1+dfsg/sound/core/pcm_lib.c =================================================================== --- alsa-driver-1.0.22.1+dfsg.orig/sound/core/pcm_lib.c 2010-01-07 18:39:49.000000000 -0500 +++ alsa-driver-1.0.22.1+dfsg/sound/core/pcm_lib.c 2010-01-07 19:02:44.000000000 -0500 @@ -126,17 +126,6 @@ } } -#ifdef CONFIG_SND_PCM_XRUN_DEBUG -#define xrun_debug(substream, mask) ((substream)->pstr->xrun_debug & (mask)) -#else -#define xrun_debug(substream, mask) 0 -#endif - -#define dump_stack_on_xrun(substream) do { \ - if (xrun_debug(substream, 2)) \ - dump_stack(); \ - } while (0) - static void pcm_debug_name(struct snd_pcm_substream *substream, char *name, size_t len) { @@ -147,6 +136,24 @@ substream->number); } +#define XRUN_DEBUG_BASIC (1<<0) +#define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ +#define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ +#define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */ +#define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */ +#define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */ +#define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */ + +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + +#define xrun_debug(substream, mask) \ + ((substream)->pstr->xrun_debug & (mask)) + +#define dump_stack_on_xrun(substream) do { \ + if (xrun_debug(substream, XRUN_DEBUG_STACK)) \ + dump_stack(); \ + } while (0) + static void xrun(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -154,7 +161,7 @@ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - if (xrun_debug(substream, 1)) { + if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { char name[16]; pcm_debug_name(substream, name, sizeof(name)); snd_printd(KERN_DEBUG "XRUN: %s\n", name); @@ -162,32 +169,102 @@ } } -static snd_pcm_uframes_t -snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, - struct snd_pcm_runtime *runtime) -{ +#define hw_ptr_error(substream, fmt, args...) \ + do { \ + if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ + xrun_log_show(substream); \ + if (printk_ratelimit()) { \ + snd_printd("PCM: " fmt, ##args); \ + } \ + dump_stack_on_xrun(substream); \ + } \ + } while (0) + +#define XRUN_LOG_CNT 10 + +struct hwptr_log_entry { + unsigned long jiffies; snd_pcm_uframes_t pos; + snd_pcm_uframes_t period_size; + snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t old_hw_ptr; + snd_pcm_uframes_t hw_ptr_base; +}; + +struct snd_pcm_hwptr_log { + unsigned int idx; + unsigned int hit: 1; + struct hwptr_log_entry entries[XRUN_LOG_CNT]; +}; - pos = substream->ops->pointer(substream); - if (pos == SNDRV_PCM_POS_XRUN) - return pos; /* XRUN */ - if (pos >= runtime->buffer_size) { - if (printk_ratelimit()) { - char name[16]; - pcm_debug_name(substream, name, sizeof(name)); - snd_printd(KERN_ERR "BUG: %s, pos = 0x%lx, " - "buffer size = 0x%lx, period size = 0x%lx\n", - name, pos, runtime->buffer_size, - runtime->period_size); - } - pos = 0; +static void xrun_log(struct snd_pcm_substream *substream, + snd_pcm_uframes_t pos) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_hwptr_log *log = runtime->hwptr_log; + struct hwptr_log_entry *entry; + + if (log == NULL) { + log = kzalloc(sizeof(*log), GFP_ATOMIC); + if (log == NULL) + return; + runtime->hwptr_log = log; + } else { + if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) + return; } - pos -= pos % runtime->min_align; - return pos; + entry = &log->entries[log->idx]; + entry->jiffies = jiffies; + entry->pos = pos; + entry->period_size = runtime->period_size; + entry->buffer_size = runtime->buffer_size;; + entry->old_hw_ptr = runtime->status->hw_ptr; + entry->hw_ptr_base = runtime->hw_ptr_base; + log->idx = (log->idx + 1) % XRUN_LOG_CNT; +} + +static void xrun_log_show(struct snd_pcm_substream *substream) +{ + struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log; + struct hwptr_log_entry *entry; + char name[16]; + unsigned int idx; + int cnt; + + if (log == NULL) + return; + if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) + return; + pcm_debug_name(substream, name, sizeof(name)); + for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) { + entry = &log->entries[idx]; + if (entry->period_size == 0) + break; + snd_printd("hwptr log: %s: j=%lu, pos=%ld/%ld/%ld, " + "hwptr=%ld/%ld\n", + name, entry->jiffies, (unsigned long)entry->pos, + (unsigned long)entry->period_size, + (unsigned long)entry->buffer_size, + (unsigned long)entry->old_hw_ptr, + (unsigned long)entry->hw_ptr_base); + idx++; + idx %= XRUN_LOG_CNT; + } + log->hit = 1; } -static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, - struct snd_pcm_runtime *runtime) +#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */ + +#define xrun_debug(substream, mask) 0 +#define xrun(substream) do { } while (0) +#define hw_ptr_error(substream, fmt, args...) do { } while (0) +#define xrun_log(substream, pos) do { } while (0) +#define xrun_log_show(substream) do { } while (0) + +#endif + +int snd_pcm_update_state(struct snd_pcm_substream *substream, + struct snd_pcm_runtime *runtime) { snd_pcm_uframes_t avail; @@ -208,89 +285,96 @@ return -EPIPE; } } - if (avail >= runtime->control->avail_min) + if (!runtime->nowake && avail >= runtime->control->avail_min) wake_up(&runtime->sleep); return 0; } -#define hw_ptr_error(substream, fmt, args...) \ - do { \ - if (xrun_debug(substream, 1)) { \ - if (printk_ratelimit()) { \ - snd_printd("PCM: " fmt, ##args); \ - } \ - dump_stack_on_xrun(substream); \ - } \ - } while (0) - -static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) +static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, + unsigned int in_interrupt) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t pos; - snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt, hw_base; + snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; snd_pcm_sframes_t hdelta, delta; unsigned long jdelta; old_hw_ptr = runtime->status->hw_ptr; - pos = snd_pcm_update_hw_ptr_pos(substream, runtime); + pos = substream->ops->pointer(substream); if (pos == SNDRV_PCM_POS_XRUN) { xrun(substream); return -EPIPE; } - if (xrun_debug(substream, 8)) { - char name[16]; - pcm_debug_name(substream, name, sizeof(name)); - snd_printd("period_update: %s: pos=0x%x/0x%x/0x%x, " - "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n", - name, (unsigned int)pos, - (unsigned int)runtime->period_size, - (unsigned int)runtime->buffer_size, - (unsigned long)old_hw_ptr, - (unsigned long)runtime->hw_ptr_base, - (unsigned long)runtime->hw_ptr_interrupt); + if (pos >= runtime->buffer_size) { + if (printk_ratelimit()) { + char name[16]; + pcm_debug_name(substream, name, sizeof(name)); + xrun_log_show(substream); + snd_printd(KERN_ERR "BUG: %s, pos = %ld, " + "buffer size = %ld, period size = %ld\n", + name, pos, runtime->buffer_size, + runtime->period_size); + } + pos = 0; } + pos -= pos % runtime->min_align; + if (xrun_debug(substream, XRUN_DEBUG_LOG)) + xrun_log(substream, pos); hw_base = runtime->hw_ptr_base; new_hw_ptr = hw_base + pos; - hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; - delta = new_hw_ptr - hw_ptr_interrupt; - if (hw_ptr_interrupt >= runtime->boundary) { - hw_ptr_interrupt -= runtime->boundary; - if (hw_base < runtime->boundary / 2) - /* hw_base was already lapped; recalc delta */ - delta = new_hw_ptr - hw_ptr_interrupt; - } - if (delta < 0) { - if (runtime->periods == 1 || new_hw_ptr < old_hw_ptr) - delta += runtime->buffer_size; - if (delta < 0) { - hw_ptr_error(substream, - "Unexpected hw_pointer value " - "(stream=%i, pos=%ld, intr_ptr=%ld)\n", - substream->stream, (long)pos, - (long)hw_ptr_interrupt); -#if 1 - /* simply skipping the hwptr update seems more - * robust in some cases, e.g. on VMware with - * inaccurate timer source - */ - return 0; /* skip this update */ -#else - /* rebase to interrupt position */ - hw_base = new_hw_ptr = hw_ptr_interrupt; - /* align hw_base to buffer_size */ - hw_base -= hw_base % runtime->buffer_size; - delta = 0; -#endif - } else { + if (in_interrupt) { + /* we know that one period was processed */ + /* delta = "expected next hw_ptr" for in_interrupt != 0 */ + delta = old_hw_ptr - (old_hw_ptr % runtime->period_size) + + runtime->period_size; + if (delta > new_hw_ptr) { hw_base += runtime->buffer_size; if (hw_base >= runtime->boundary) hw_base = 0; new_hw_ptr = hw_base + pos; + goto __delta; } } + /* new_hw_ptr might be lower than old_hw_ptr in case when */ + /* pointer crosses the end of the ring buffer */ + if (new_hw_ptr < old_hw_ptr) { + hw_base += runtime->buffer_size; + if (hw_base >= runtime->boundary) + hw_base = 0; + new_hw_ptr = hw_base + pos; + } + __delta: + delta = (new_hw_ptr - old_hw_ptr) % runtime->boundary; + if (xrun_debug(substream, in_interrupt ? + XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) { + char name[16]; + pcm_debug_name(substream, name, sizeof(name)); + snd_printd("%s_update: %s: pos=%u/%u/%u, " + "hwptr=%ld/%ld/%ld/%ld\n", + in_interrupt ? "period" : "hwptr", + name, + (unsigned int)pos, + (unsigned int)runtime->period_size, + (unsigned int)runtime->buffer_size, + (unsigned long)delta, + (unsigned long)old_hw_ptr, + (unsigned long)new_hw_ptr, + (unsigned long)runtime->hw_ptr_base); + } + /* something must be really wrong */ + if (delta >= runtime->buffer_size) { + hw_ptr_error(substream, + "Unexpected hw_pointer value %s" + "(stream=%i, pos=%ld, new_hw_ptr=%ld, " + "old_hw_ptr=%ld)\n", + in_interrupt ? "[Q] " : "[P]", + substream->stream, (long)pos, + (long)new_hw_ptr, (long)old_hw_ptr); + return 0; + } /* Do jiffies check only in xrun_debug mode */ - if (!xrun_debug(substream, 4)) + if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK)) goto no_jiffies_check; /* Skip the jiffies check for hardwares with BATCH flag. @@ -299,7 +383,7 @@ */ if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) goto no_jiffies_check; - hdelta = new_hw_ptr - old_hw_ptr; + hdelta = delta; if (hdelta < runtime->delay) goto no_jiffies_check; hdelta -= runtime->delay; @@ -308,130 +392,62 @@ delta = jdelta / (((runtime->period_size * HZ) / runtime->rate) + HZ/100); + /* move new_hw_ptr according jiffies not pos variable */ + new_hw_ptr = old_hw_ptr; + /* use loop to avoid checks for delta overflows */ + /* the delta value is small or zero in most cases */ + while (delta > 0) { + new_hw_ptr += runtime->period_size; + if (new_hw_ptr >= runtime->boundary) + new_hw_ptr -= runtime->boundary; + delta--; + } + /* align hw_base to buffer_size */ + hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size); + delta = 0; hw_ptr_error(substream, - "hw_ptr skipping! [Q] " + "hw_ptr skipping! %s" "(pos=%ld, delta=%ld, period=%ld, " - "jdelta=%lu/%lu/%lu)\n", + "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n", + in_interrupt ? "[Q] " : "", (long)pos, (long)hdelta, (long)runtime->period_size, jdelta, - ((hdelta * HZ) / runtime->rate), delta); - hw_ptr_interrupt = runtime->hw_ptr_interrupt + - runtime->period_size * delta; - if (hw_ptr_interrupt >= runtime->boundary) - hw_ptr_interrupt -= runtime->boundary; - /* rebase to interrupt position */ - hw_base = new_hw_ptr = hw_ptr_interrupt; - /* align hw_base to buffer_size */ - hw_base -= hw_base % runtime->buffer_size; - delta = 0; + ((hdelta * HZ) / runtime->rate), delta, + (unsigned long)old_hw_ptr, + (unsigned long)new_hw_ptr); } no_jiffies_check: if (delta > runtime->period_size + runtime->period_size / 2) { hw_ptr_error(substream, - "Lost interrupts? " - "(stream=%i, delta=%ld, intr_ptr=%ld)\n", + "Lost interrupts? %s" + "(stream=%i, delta=%ld, new_hw_ptr=%ld, " + "old_hw_ptr=%ld)\n", + in_interrupt ? "[Q] " : "", substream->stream, (long)delta, - (long)hw_ptr_interrupt); - /* rebase hw_ptr_interrupt */ - hw_ptr_interrupt = - new_hw_ptr - new_hw_ptr % runtime->period_size; + (long)new_hw_ptr, + (long)old_hw_ptr); } - runtime->hw_ptr_interrupt = hw_ptr_interrupt; + + if (runtime->status->hw_ptr == new_hw_ptr) + return 0; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, new_hw_ptr); - if (runtime->status->hw_ptr == new_hw_ptr) - return 0; - runtime->hw_ptr_base = hw_base; runtime->status->hw_ptr = new_hw_ptr; runtime->hw_ptr_jiffies = jiffies; if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); - return snd_pcm_update_hw_ptr_post(substream, runtime); + return snd_pcm_update_state(substream, runtime); } /* CAUTION: call it with irq disabled */ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t pos; - snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; - snd_pcm_sframes_t delta; - unsigned long jdelta; - - old_hw_ptr = runtime->status->hw_ptr; - pos = snd_pcm_update_hw_ptr_pos(substream, runtime); - if (pos == SNDRV_PCM_POS_XRUN) { - xrun(substream); - return -EPIPE; - } - if (xrun_debug(substream, 16)) { - char name[16]; - pcm_debug_name(substream, name, sizeof(name)); - snd_printd("hw_update: %s: pos=0x%x/0x%x/0x%x, " - "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n", - name, (unsigned int)pos, - (unsigned int)runtime->period_size, - (unsigned int)runtime->buffer_size, - (unsigned long)old_hw_ptr, - (unsigned long)runtime->hw_ptr_base, - (unsigned long)runtime->hw_ptr_interrupt); - } - - hw_base = runtime->hw_ptr_base; - new_hw_ptr = hw_base + pos; - - delta = new_hw_ptr - old_hw_ptr; - jdelta = jiffies - runtime->hw_ptr_jiffies; - if (delta < 0) { - delta += runtime->buffer_size; - if (delta < 0) { - hw_ptr_error(substream, - "Unexpected hw_pointer value [2] " - "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n", - substream->stream, (long)pos, - (long)old_hw_ptr, jdelta); - return 0; - } - hw_base += runtime->buffer_size; - if (hw_base >= runtime->boundary) - hw_base = 0; - new_hw_ptr = hw_base + pos; - } - /* Do jiffies check only in xrun_debug mode */ - if (!xrun_debug(substream, 4)) - goto no_jiffies_check; - if (delta < runtime->delay) - goto no_jiffies_check; - delta -= runtime->delay; - if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) { - hw_ptr_error(substream, - "hw_ptr skipping! " - "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", - (long)pos, (long)delta, - (long)runtime->period_size, jdelta, - ((delta * HZ) / runtime->rate)); - return 0; - } - no_jiffies_check: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && - runtime->silence_size > 0) - snd_pcm_playback_silence(substream, new_hw_ptr); - - if (runtime->status->hw_ptr == new_hw_ptr) - return 0; - - runtime->hw_ptr_base = hw_base; - runtime->status->hw_ptr = new_hw_ptr; - runtime->hw_ptr_jiffies = jiffies; - if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) - snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); - - return snd_pcm_update_hw_ptr_post(substream, runtime); + return snd_pcm_update_hw_ptr0(substream, 0); } /** @@ -1657,7 +1673,7 @@ snd_pcm_stream_lock_irqsave(substream, flags); if (!snd_pcm_running(substream) || - snd_pcm_update_hw_ptr_interrupt(substream) < 0) + snd_pcm_update_hw_ptr0(substream, 1) < 0) goto _end; if (substream->timer_running) @@ -1790,6 +1806,7 @@ goto _end_unlock; } + runtime->nowake = 1; while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t avail; @@ -1811,15 +1828,17 @@ if (frames > cont) frames = cont; if (snd_BUG_ON(!frames)) { + runtime->nowake = 0; snd_pcm_stream_unlock_irq(substream); return -EINVAL; } appl_ptr = runtime->control->appl_ptr; appl_ofs = appl_ptr % runtime->buffer_size; snd_pcm_stream_unlock_irq(substream); - if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) - goto _end; + err = transfer(substream, appl_ofs, data, offset, frames); snd_pcm_stream_lock_irq(substream); + if (err < 0) + goto _end_unlock; switch (runtime->status->state) { case SNDRV_PCM_STATE_XRUN: err = -EPIPE; @@ -1848,8 +1867,10 @@ } } _end_unlock: + runtime->nowake = 0; + if (xfer > 0 && err >= 0) + snd_pcm_update_state(substream, runtime); snd_pcm_stream_unlock_irq(substream); - _end: return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } @@ -2007,6 +2028,7 @@ goto _end_unlock; } + runtime->nowake = 1; while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t avail; @@ -2035,15 +2057,17 @@ if (frames > cont) frames = cont; if (snd_BUG_ON(!frames)) { + runtime->nowake = 0; snd_pcm_stream_unlock_irq(substream); return -EINVAL; } appl_ptr = runtime->control->appl_ptr; appl_ofs = appl_ptr % runtime->buffer_size; snd_pcm_stream_unlock_irq(substream); - if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) - goto _end; + err = transfer(substream, appl_ofs, data, offset, frames); snd_pcm_stream_lock_irq(substream); + if (err < 0) + goto _end_unlock; switch (runtime->status->state) { case SNDRV_PCM_STATE_XRUN: err = -EPIPE; @@ -2066,8 +2090,10 @@ xfer += frames; } _end_unlock: + runtime->nowake = 0; + if (xfer > 0 && err >= 0) + snd_pcm_update_state(substream, runtime); snd_pcm_stream_unlock_irq(substream); - _end: return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } Index: alsa-driver-1.0.22.1+dfsg/sound/core/pcm.c =================================================================== --- alsa-driver-1.0.22.1+dfsg.orig/sound/core/pcm.c 2010-01-07 18:39:49.000000000 -0500 +++ alsa-driver-1.0.22.1+dfsg/sound/core/pcm.c 2010-01-07 19:02:44.000000000 -0500 @@ -921,6 +921,10 @@ snd_free_pages((void*)runtime->control, PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); kfree(runtime->hw_constraints.rules); +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + if (runtime->hwptr_log) + kfree(runtime->hwptr_log); +#endif kfree(runtime); substream->runtime = NULL; put_pid(substream->pid); Index: alsa-driver-1.0.22.1+dfsg/sound/include/pcm.h =================================================================== --- alsa-driver-1.0.22.1+dfsg.orig/sound/include/pcm.h 2010-01-07 18:39:49.000000000 -0500 +++ alsa-driver-1.0.22.1+dfsg/sound/include/pcm.h 2010-01-07 19:02:44.000000000 -0500 @@ -262,6 +262,8 @@ unsigned int mask; }; +struct snd_pcm_hwptr_log; + struct snd_pcm_runtime { /* -- Status -- */ struct snd_pcm_substream *trigger_master; @@ -269,7 +271,6 @@ int overrange; snd_pcm_uframes_t avail_max; snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ - snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */ unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */ snd_pcm_sframes_t delay; /* extra delay; typically FIFO size */ @@ -310,6 +311,7 @@ struct snd_pcm_mmap_control *control; /* -- locking / scheduling -- */ + unsigned int nowake: 1; /* no wakeup (data-copy in progress) */ wait_queue_head_t sleep; struct fasync_struct *fasync; @@ -340,6 +342,10 @@ /* -- OSS things -- */ struct snd_pcm_oss_runtime oss; #endif + +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + struct snd_pcm_hwptr_log *hwptr_log; +#endif }; struct snd_pcm_group { /* keep linked substreams */ @@ -834,6 +840,8 @@ int snd_pcm_lib_interleave_len(struct snd_pcm_substream *substream); int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg); +int snd_pcm_update_state(struct snd_pcm_substream *substream, + struct snd_pcm_runtime *runtime); int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream); int snd_pcm_playback_xrun_check(struct snd_pcm_substream *substream); int snd_pcm_capture_xrun_check(struct snd_pcm_substream *substream); Index: alsa-driver-1.0.22.1+dfsg/sound/core/oss/pcm_oss.c =================================================================== --- alsa-driver-1.0.22.1+dfsg.orig/sound/core/oss/pcm_oss.c 2010-01-07 18:39:49.000000000 -0500 +++ alsa-driver-1.0.22.1+dfsg/sound/core/oss/pcm_oss.c 2010-01-07 19:02:44.000000000 -0500 @@ -632,6 +632,13 @@ return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes); } +static inline +snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime) +{ + snd_pcm_uframes_t ptr = runtime->status->hw_ptr; + return ptr - (ptr % runtime->period_size); +} + /* define extended formats in the recent OSS versions (if any) */ /* linear formats */ #define AFMT_S32_LE 0x00001000 @@ -1102,7 +1109,7 @@ return err; } runtime->oss.prepare = 0; - runtime->oss.prev_hw_ptr_interrupt = 0; + runtime->oss.prev_hw_ptr_period = 0; runtime->oss.period_ptr = 0; runtime->oss.buffer_used = 0; @@ -1950,7 +1957,8 @@ return result; } -static void snd_pcm_oss_simulate_fill(struct snd_pcm_substream *substream, snd_pcm_uframes_t hw_ptr) +static void snd_pcm_oss_simulate_fill(struct snd_pcm_substream *substream, + snd_pcm_uframes_t hw_ptr) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t appl_ptr; @@ -1986,7 +1994,8 @@ if (runtime->oss.trigger) goto _skip1; if (atomic_read(&psubstream->mmap_count)) - snd_pcm_oss_simulate_fill(psubstream, runtime->hw_ptr_interrupt); + snd_pcm_oss_simulate_fill(psubstream, + get_hw_ptr_period(runtime)); runtime->oss.trigger = 1; runtime->start_threshold = 1; cmd = SNDRV_PCM_IOCTL_START; @@ -2105,11 +2114,12 @@ info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size); if (atomic_read(&substream->mmap_count)) { snd_pcm_sframes_t n; - n = (delay = runtime->hw_ptr_interrupt) - runtime->oss.prev_hw_ptr_interrupt; + delay = get_hw_ptr_period(runtime); + n = delay - runtime->oss.prev_hw_ptr_period; if (n < 0) n += runtime->boundary; info.blocks = n / runtime->period_size; - runtime->oss.prev_hw_ptr_interrupt = delay; + runtime->oss.prev_hw_ptr_period = delay; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) snd_pcm_oss_simulate_fill(substream, delay); info.bytes = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr) & INT_MAX; @@ -2673,18 +2683,22 @@ { struct snd_pcm_runtime *runtime = substream->runtime; if (atomic_read(&substream->mmap_count)) - return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; + return runtime->oss.prev_hw_ptr_period != + get_hw_ptr_period(runtime); else - return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames; + return snd_pcm_playback_avail(runtime) >= + runtime->oss.period_frames; } static int snd_pcm_oss_capture_ready(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; if (atomic_read(&substream->mmap_count)) - return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; + return runtime->oss.prev_hw_ptr_period != + get_hw_ptr_period(runtime); else - return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames; + return snd_pcm_capture_avail(runtime) >= + runtime->oss.period_frames; } static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait) Index: alsa-driver-1.0.22.1+dfsg/sound/core/pcm_native.c =================================================================== --- alsa-driver-1.0.22.1+dfsg.orig/sound/core/pcm_native.c 2010-01-07 18:39:49.000000000 -0500 +++ alsa-driver-1.0.22.1+dfsg/sound/core/pcm_native.c 2010-01-07 19:02:44.000000000 -0500 @@ -516,6 +516,7 @@ struct snd_pcm_sw_params *params) { struct snd_pcm_runtime *runtime; + int err; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; @@ -540,6 +541,7 @@ if (params->silence_threshold > runtime->buffer_size) return -EINVAL; } + err = 0; snd_pcm_stream_lock_irq(substream); runtime->tstamp_mode = params->tstamp_mode; runtime->period_step = params->period_step; @@ -553,10 +555,10 @@ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, ULONG_MAX); - wake_up(&runtime->sleep); + err = snd_pcm_update_state(substream, runtime); } snd_pcm_stream_unlock_irq(substream); - return 0; + return err; } static int snd_pcm_sw_params_user(struct snd_pcm_substream *substream, @@ -1247,8 +1249,6 @@ if (err < 0) return err; runtime->hw_ptr_base = 0; - runtime->hw_ptr_interrupt = runtime->status->hw_ptr - - runtime->status->hw_ptr % runtime->period_size; runtime->silence_start = runtime->status->hw_ptr; runtime->silence_filled = 0; return 0; Index: alsa-driver-1.0.22.1+dfsg/sound/include/pcm_oss.h =================================================================== --- alsa-driver-1.0.22.1+dfsg.orig/sound/include/pcm_oss.h 2010-01-07 18:39:49.000000000 -0500 +++ alsa-driver-1.0.22.1+dfsg/sound/include/pcm_oss.h 2010-01-07 19:02:44.000000000 -0500 @@ -61,7 +61,7 @@ struct snd_pcm_plugin *plugin_first; struct snd_pcm_plugin *plugin_last; #endif - unsigned int prev_hw_ptr_interrupt; + unsigned int prev_hw_ptr_period; }; struct snd_pcm_oss_file { Index: alsa-driver-1.0.22.1+dfsg/include/sound/Makefile =================================================================== --- alsa-driver-1.0.22.1+dfsg.orig/include/sound/Makefile 2010-01-07 19:03:22.000000000 -0500 +++ alsa-driver-1.0.22.1+dfsg/include/sound/Makefile 2010-01-07 19:03:32.000000000 -0500 @@ -1,5 +1,9 @@ INCSRC=../../alsa-kernel/include +.PHONY: all +all: + @echo "Nothing to do.." + ifeq (.includes,$(wildcard .includes)) include .includes endif @@ -17,14 +21,26 @@ .includes.tmp: @echo -n "HEADERS=" > $@ @echo $(INCSRC)/*.h | sed -es@$(INCSRC)/@@g >> $@ + @echo >> $@ + @for i in $(INCSRC)/*.h ; do \ + s=$$(echo $$i | sed -es@$(INCSRC)/@@g); \ + b=$$(basename $$s .h); \ + if test -f $$b.patch; then \ + echo "$$s: $$i $$b.patch" >> $@; \ + echo " cp \$$(INCSRC)/\$$@ ." >> $@; \ + echo " patch -p0 -i $$b.patch \$$@" >> $@; \ + echo >> $@; \ + fi; \ + done -.includes: .includes.tmp +.includes: + $(MAKE) .includes.tmp @if [ -f $@ ]; then \ - cmp -s $@ $< || mv -f $< $@; \ + cmp -s $@ .includes.tmp || mv -f .includes.tmp $@; \ else \ - mv -f $< $@; \ + mv -f .includes.tmp $@; \ fi; \ - rm -f $< + rm -f .includes.tmp .PHONY: prepare prepare2 prepare2: $(HEADERS) Index: alsa-driver-1.0.22.1+dfsg/include/sound/pcm.patch =================================================================== --- alsa-driver-1.0.22.1+dfsg.orig/include/sound/pcm.patch 2010-01-07 19:03:22.000000000 -0500 +++ alsa-driver-1.0.22.1+dfsg/include/sound/pcm.patch 2010-01-07 19:03:32.000000000 -0500 @@ -1,9 +1,12 @@ ---- ../../alsa-kernel/include/pcm.h 2009-08-13 14:32:51.000000000 +0200 -+++ pcm.h 2009-09-07 16:11:53.000000000 +0200 -@@ -270,9 +270,12 @@ +--- ../../alsa-kernel/include/pcm.h 2010-01-07 13:57:18.000000000 +0100 ++++ pcm.h 2010-01-07 13:57:01.000000000 +0100 +@@ -271,9 +271,15 @@ + int overrange; snd_pcm_uframes_t avail_max; snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ - snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) ++ unsigned long old_hw_ptr_interrupt; /* for KABI compatibility */ ++#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */ +#endif @@ -14,7 +17,7 @@ /* -- HW params -- */ snd_pcm_access_t access; /* access mode */ snd_pcm_format_t format; /* SNDRV_PCM_FORMAT_* */ -@@ -282,6 +285,9 @@ +@@ -283,6 +289,9 @@ snd_pcm_uframes_t period_size; /* period size */ unsigned int periods; /* periods */ snd_pcm_uframes_t buffer_size; /* buffer size */ @@ -24,7 +27,7 @@ snd_pcm_uframes_t min_align; /* Min alignment for the format */ size_t byte_align; unsigned int frame_bits; -@@ -293,6 +299,10 @@ +@@ -294,6 +303,10 @@ /* -- SW params -- */ int tstamp_mode; /* mmap timestamp is updated */ unsigned int period_step; @@ -35,9 +38,13 @@ snd_pcm_uframes_t start_threshold; snd_pcm_uframes_t stop_threshold; snd_pcm_uframes_t silence_threshold; /* Silence filling happens when -@@ -311,6 +321,9 @@ +@@ -311,8 +324,13 @@ + struct snd_pcm_mmap_control *control; /* -- locking / scheduling -- */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) + unsigned int nowake: 1; /* no wakeup (data-copy in progress) */ ++#endif wait_queue_head_t sleep; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) + struct timer_list old_tick_timer; /* for ABI compatibility */ @@ -45,7 +52,7 @@ struct fasync_struct *fasync; /* -- private section -- */ -@@ -327,8 +340,9 @@ +@@ -329,8 +347,9 @@ /* -- timer -- */ unsigned int timer_resolution; /* timer resolution */ @@ -56,9 +63,9 @@ /* -- DMA -- */ unsigned char *dma_area; /* DMA area */ dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */ -@@ -340,6 +354,17 @@ - /* -- OSS things -- */ - struct snd_pcm_oss_runtime oss; +@@ -346,6 +365,20 @@ + #ifdef CONFIG_SND_PCM_XRUN_DEBUG + struct snd_pcm_hwptr_log *hwptr_log; #endif + + /* appended below for ABI compatibility */ @@ -71,10 +78,13 @@ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) + int tstamp_type; /* timestamp type */ +#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) ++ unsigned int nowake: 1; /* no wakeup (data-copy in progress) */ ++#endif }; struct snd_pcm_group { /* keep linked substreams */ -@@ -367,6 +392,9 @@ +@@ -375,6 +408,9 @@ /* -- timer section -- */ struct snd_timer *timer; /* timer */ unsigned timer_running: 1; /* time is running */