在alsa驱动中有如下状态:
#define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) /* stream is open */
#define SNDRV_PCM_STATE_SETUP ((__force snd_pcm_state_t) 1) /* stream has a setup */
#define SNDRV_PCM_STATE_PREPARED ((__force snd_pcm_state_t) 2) /* stream is ready to start */
#define SNDRV_PCM_STATE_RUNNING ((__force snd_pcm_state_t) 3) /* stream is running */
#define SNDRV_PCM_STATE_XRUN ((__force snd_pcm_state_t) 4) /* stream reached an xrun */
#define SNDRV_PCM_STATE_DRAINING ((__force snd_pcm_state_t) 5) /* stream is draining */
#define SNDRV_PCM_STATE_PAUSED ((__force snd_pcm_state_t) 6) /* stream is paused */
#define SNDRV_PCM_STATE_SUSPENDED ((__force snd_pcm_state_t) 7) /* hardware is suspended */
#define SNDRV_PCM_STATE_DISCONNECTED ((__force snd_pcm_state_t) 8) /* hardware is disconnected */
#define SNDRV_PCM_STATE_LAST SNDRV_PCM_STATE_DISCONNECTED
在ALSA最底层写函数snd_pcm_lib_write1中,首先检测当前PCM的状态,是 SNDRV_PCM_STATE_PREPARED、SNDRV_PCM_STATE_RUNNING、 SNDRV_PCM_STATE_PAUSED这三种状态下,驱动就开始transfer数据,如果为SNDRV_PCM_STATE_XRUN返回-EPIPE错误,如果为SNDRV_PCM_STATE_SUSPENDED,就返回 -ESTRPIPE;其他状态均返回-EBADFD错误。
在打开一个PCM流时,PCM的状态为SNDRV_PCM_STATE_OPEN ,给这个流配置参数,如channel数、采样率、采样精度后,PCM的状态配置为SNDRV_PCM_STATE_SETUP,在写数据之前,需要提示驱动去各继各位,准备工作,这时候去prepare一下,就像是考试前的动员大会一样,让你热血沸腾、充满信心,prepare后PCM流的状态为SNDRV_PCM_STATE_PREPARED,开始写数据的过程中,PCM的状态会设置为 SNDRV_PCM_STATE_RUNNING,播放完数据后,通常需要去drain一下来sync,在调snd_pcm_drain过程中,PCM的状态为PCM的状态,drain完以后PCM的状态为会设置为SNDRV_PCM_STATE_SETUP,因此在再次写数据的时候,驱动就会返回-EBADFD的错误,那么如何有效避免呢?
办法当然是有的,alsa-lib中提供了非常完善的接口。笔者也曾碰到过-EBADFD的错误,很烦人啊!出了错后,alsa驱动就需要恢复错误状态,或者干脆重新打开一次播放流程,但是这样做的后果就是有可能带来“兹兹”噪音或者如“都-都”这样的怪音,这种现象出来对产品来说肯定是不利的,因此在上层再次调用alsa写函数的时候,首先去获取一些当前PCM的状态是什么,用snd_pcm_state函数,如果当前PCM为 SNDRV_PCM_STATE_SETUP,只需要snd_pcm_prepare一下,就可以进行正常的数据写操作。
在系统进入省电模式时,驱动会执行suspend,PCM状态变为SNDRV_PCM_STATE_SUSPENDED,在唤醒系统后,笔者在adroid系统中发现,PCM的状态仍然为SNDRV_PCM_STATE_SUSPENDED状态,在这种状态下,直接进行写操作,无疑会带来-EBADFD的错误。如果在写数据的之前,检测到的pcm状态为SNDRV_PCM_STATE_SUSPENDED,就可以先snd_pcm_resume,再snd_pcm_prepare一下,alsa就可以回到正常工作状态,可以正常的写数据。
-EBADFD错误的产生,就是因为alsa的驱动状态没有控制好,得严格按照alsa要求的状态转换去工作,否则会碰壁。