[alsa-devel] How to report error in _pointer function?
In my driver, I can detect in the _pointer callback function whether or not the DMA engine has actually started. Sometimes, when I have a programming error, the DMA will not start, so my _pointer function calculates a crazy value for the current position. The number it returns causes ALSA to go haywire (see my post titled, "underrun!!! (at least 1786051083.613 ms long)".
What is the best way to handle this? Is there a way I can tell ALSA, "hey, this is really screwed up, just abort playback and return an error to the app"?
At Wed, 30 Jul 2008 10:56:31 -0500, Timur Tabi wrote:
In my driver, I can detect in the _pointer callback function whether or not the DMA engine has actually started. Sometimes, when I have a programming error, the DMA will not start, so my _pointer function calculates a crazy value for the current position. The number it returns causes ALSA to go haywire (see my post titled, "underrun!!! (at least 1786051083.613 ms long)".
What is the best way to handle this? Is there a way I can tell ALSA, "hey, this is really screwed up, just abort playback and return an error to the app"?
You can return SNDRV_PCM_POS_XRUN from the pointer callback (with kernel messages if you like). Then the PCM stream is stopped and the status is changed to SND_PCM_STATUS_XRUN. Further access will result in -EPIPE.
I guess this doesn't fully satisfy your demand, but maybe better than now :) We'd need a mechanism to pass a proper error code if other critical error code must be passed.
Takashi
Takashi Iwai wrote:
You can return SNDRV_PCM_POS_XRUN from the pointer callback (with kernel messages if you like). Then the PCM stream is stopped and the status is changed to SND_PCM_STATUS_XRUN. Further access will result in -EPIPE.
This doesn't seem to work. I don't get any more underrun/overrun messages, but the application doesn't know that something's failed.
With mplayer, I see this message:
alsa-lib: pcm_hw.c:405:(snd_pcm_hw_hwsync) SNDRV_PCM_IOCTL_HWSYNC failed: Broken pipe
And then mplayer hangs with the status bar here:
A: 0.0 (00.0) of 249.0 (04:09.0) ??,?% $<50>
aplay just hangs.
I also tried adding this code to my _pointer function:
snd_pcm_stream_lock_irqsave(substream, flags);
if (snd_pcm_running(substream)) snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
snd_pcm_stream_unlock_irqrestore(substream, flags);
but it didn't make any difference.
At Wed, 06 Aug 2008 16:54:38 -0500, Timur Tabi wrote:
Takashi Iwai wrote:
You can return SNDRV_PCM_POS_XRUN from the pointer callback (with kernel messages if you like). Then the PCM stream is stopped and the status is changed to SND_PCM_STATUS_XRUN. Further access will result in -EPIPE.
This doesn't seem to work. I don't get any more underrun/overrun messages, but the application doesn't know that something's failed.
With mplayer, I see this message:
alsa-lib: pcm_hw.c:405:(snd_pcm_hw_hwsync) SNDRV_PCM_IOCTL_HWSYNC failed: Broken pipe
And then mplayer hangs with the status bar here:
A: 0.0 (00.0) of 249.0 (04:09.0) ??,?% $<50>
aplay just hangs.
Hm... The behavior with returning SNDRV_PCM_POS_XRUN is basically identical with the real XRUN detection. I guess the XRUN-stop mechanism doesn't work well with the case of hwsync.
How is the PCM status during hang? Check /proc/asound/card0/pcm*/sub*/* files. Also, could you run with gdb and check at which code-path is it stopped?
thanks,
Takashi
On Thu, Aug 7, 2008 at 11:29 AM, Takashi Iwai tiwai@suse.de wrote:
How is the PCM status during hang? Check /proc/asound/card0/pcm*/sub*/* files.
I wrote a script that repeatedly dumps the contents of /proc/asound/card0/pcm0p/sub0/status. I started playback, and it shows this:
... closed closed closed state: OPEN trigger_time: 0.000000000 tstamp : 0.000000000 delay : 0 avail : 0 avail_max : 0 ----- hw_ptr : 0 appl_ptr : 0 state: OPEN trigger_time: 0.000000000 tstamp : 0.000000000 delay : 0 avail : 0 avail_max : 0 ----- hw_ptr : 0 appl_ptr : 0 state: XRUN trigger_time: 165.300191122 tstamp : 165.302136461 delay : 0 avail : 0 avail_max : 0 ----- hw_ptr : 0 appl_ptr : 17840 state: XRUN trigger_time: 165.300191122 tstamp : 165.304661787 delay : 0 avail : 0 avail_max : 0 ----- hw_ptr : 0 appl_ptr : 17840 state: XRUN trigger_time: 165.300191122 tstamp : 165.306860459 delay : 0 avail : 0 avail_max : 0 ----- hw_ptr : 0 appl_ptr : 17840
and so on.
Also, could you run with gdb and check at which code-path is it stopped?
Sorry, I don't use gdb that much. How can I do this? I don't think I have debug versions of alsa-lib or mplayer.
At Thu, 7 Aug 2008 13:59:56 -0500, Timur Tabi wrote:
On Thu, Aug 7, 2008 at 11:29 AM, Takashi Iwai tiwai@suse.de wrote:
How is the PCM status during hang? Check /proc/asound/card0/pcm*/sub*/* files.
I wrote a script that repeatedly dumps the contents of /proc/asound/card0/pcm0p/sub0/status. I started playback, and it shows this:
...
hw_ptr : 0 appl_ptr : 17840 state: XRUN trigger_time: 165.300191122 tstamp : 165.304661787 delay : 0 avail : 0 avail_max : 0
So, the PCM status is properly changed to XRUN as expected. From the driver perspective, everything must be fine.
Also, could you run with gdb and check at which code-path is it stopped?
Sorry, I don't use gdb that much. How can I do this? I don't think I have debug versions of alsa-lib or mplayer.
What about ltrace? I'd like to know which functions are called.
Takashi
Takashi Iwai wrote:
What about ltrace? I'd like to know which functions are called.
This is an embedded system. I don't have ltrace or strace installed.
I suspect that this problem can easily be reproduced on any system. Just modify the _pointer function to always return XRUN, and see what aplay does.
Anyway, I tried debugging aplay. I'm not familiar with ALSA application programming (just drivers), so I don't know what to look for, but I did find a pattern.
I set a breakpoint on function snd_pcm_hw_writei(), which I presume is an alsa-lib function. When I run aplay, the breakpoint is hit:
Breakpoint 1, snd_pcm_hw_writei (pcm=0x10024fd8, buffer=0x100250e8, size=6000) at pcm_hw.c:627 627 err = ioctl(fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &xferi); (gdb)
I hit continue, and then it immediately stops at this function again. I can do continue four more times and it does the same thing. On the sixth "continue", there is an 8-second delay. Then my driver prints a message saying that it's returning XRUN, and GDB stops again at snd_pcm_hw_writei(). I don't know what's happening during that 8-second delay.
This whole process repeats forever.
Takashi Iwai wrote:
So, the PCM status is properly changed to XRUN as expected. From the driver perspective, everything must be fine.
I've been debugging aplay. The main loop is function pcm_write(). It calls writei_func() which returns -EPIPE. This causes aplay to jump to xrun(), which attempts to recover.
This is not what I want. I don't want to return overrun/underrun. I want to return failure. Returning XRUN is wrong.
Instead, I need to take a look at this code I have:
snd_pcm_stream_lock_irqsave(substream, flags);
if (snd_pcm_running(substream)) snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
snd_pcm_stream_unlock_irqrestore(substream, flags);
It's the call to snd_pcm_stop() that I don't like. There doesn't appear to be a SNDRV_PCM_STATE_xxx state that makes sense. This function should eventually bring me to snd_pcm_pre_stop(). But this function returns -EBADFD because the current state is RUNNING and not OPEN. Because it returns an error, snd_pcm_do_stop() is never called.
This doesn't make any sense to me. Why should I be able to stop a stream only if it's open but not running? If it's open, but not running, then there's nothing to stop! Can you explain the restriction in snd_pcm_pre_stop()?
At Fri, 08 Aug 2008 17:26:11 -0500, Timur Tabi wrote:
Takashi Iwai wrote:
So, the PCM status is properly changed to XRUN as expected. From the driver perspective, everything must be fine.
I've been debugging aplay. The main loop is function pcm_write(). It calls writei_func() which returns -EPIPE. This causes aplay to jump to xrun(), which attempts to recover.
This is not what I want. I don't want to return overrun/underrun. I want to return failure. Returning XRUN is wrong.
Instead, I need to take a look at this code I have:
snd_pcm_stream_lock_irqsave(substream, flags);
if (snd_pcm_running(substream)) snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
snd_pcm_stream_unlock_irqrestore(substream, flags);
It's the call to snd_pcm_stop() that I don't like. There doesn't appear to be a SNDRV_PCM_STATE_xxx state that makes sense. This function should eventually bring me to snd_pcm_pre_stop(). But this function returns -EBADFD because the current state is RUNNING and not OPEN. Because it returns an error, snd_pcm_do_stop() is never called.
Hm? snd_pcm_pre_stop() returns -EBADFD if the stream *IS* OPEN. If it's RUNNING, it will stop the stream and change the state.
This doesn't make any sense to me. Why should I be able to stop a stream only if it's open but not running? If it's open, but not running, then there's nothing to stop! Can you explain the restriction in snd_pcm_pre_stop()?
If the state is OPEN, it means that the stream is already stopped.
Not sure which version you are referring, but I see no problem in the current code, at least...
Takashi
Takashi Iwai wrote:
Hm? snd_pcm_pre_stop() returns -EBADFD if the stream *IS* OPEN. If it's RUNNING, it will stop the stream and change the state.
Hmmm.... you're right. I don't know what I was thinking.
Tracing the code, I see that ALSA does call my driver's trigger function to stop the stream. So why doesn't the application also stop?
participants (2)
-
Takashi Iwai
-
Timur Tabi