Micka
Micka

Reputation: 20130

ALSA on embedded System - readi - Input/output error and freeze

I'm still facing problems using ALSA capturing on an embedded system.

I can open the device now from the library, after using the snddevices script. But the application return error messages Input/output error (EIO) on readi calls after a time of about 10 seconds for each call. After trying to stop the device, the whole system freezes and needs a hard-reset.

I can't tell whether ALSA is installed correctly on the system. On startup, ALSA driver is mentioned. It's version 1.0.18.rc3. How can I test whether ALSA and/or devices are installed correctly?

Here's my test code, please tell me whether there is an error inside. I'm not sure whether the frame sizes and period ist set correctly (I don't know much about audio processing) and whether I would have to use interleaved or non-interleaved capturing or whether it doesn't matter.

#include <alsa/asoundlib.h>



const char* print_pcm_state(snd_pcm_state_t state)
{
    switch(state)
    {
    case SND_PCM_STATE_OPEN: return("SND_PCM_STATE_OPEN");
    case SND_PCM_STATE_SETUP: return("SND_PCM_STATE_SETUP");
    case SND_PCM_STATE_PREPARED: return("SND_PCM_STATE_PREPARED");
    case SND_PCM_STATE_RUNNING: return("SND_PCM_STATE_RUNNING");
    case SND_PCM_STATE_XRUN: return("SND_PCM_STATE_XRUN");
    case SND_PCM_STATE_DRAINING: return("SND_PCM_STATE_DRAINING");
    case SND_PCM_STATE_PAUSED: return("SND_PCM_STATE_PAUSED");
    case SND_PCM_STATE_SUSPENDED: return("SND_PCM_STATE_SUSPENDED");
    case SND_PCM_STATE_DISCONNECTED: return("SND_PCM_STATE_DISCONNECTED");
    }
return "UNKNOWN STATE";
}



int main(int argc, char* argv[])
{
    int rc; int err;

    // pcm state
    snd_pcm_state_t pcm_state;
    // handle
    snd_pcm_t *handle;

    // hardware to open
    char *pcm_name;
    pcm_name =  strdup("hw:0,0");

    // parameters
    snd_pcm_hw_params_t *params;

    /* Open PCM device for playback. */
    rc = snd_pcm_open(&handle, pcm_name, SND_PCM_STREAM_CAPTURE, 0);

    if (rc < 0) 
    {

        printf("unable to open pcm device: %s\n", snd_strerror(rc));
        exit(1);
    }

    // Allocate a hardware parameters object.
    //  rc = snd_pcm_hw_params_alloca(&params); // original from a sample code, didn't compile?!?
    rc = snd_pcm_hw_params_malloc(&params);
    if(rc<0)
    {
        printf("cannot allocate hardware parameter structure (%s) ...\n", snd_strerror(rc));
    }

    /* Fill it in with default values. */
    rc = snd_pcm_hw_params_any(handle, params);
    if(rc<0)
    {
        printf("Error: (%s) ...\n", snd_strerror(rc));
    }

    bool interleaved = true;
    // Set the desired hardware parameters. 
    if (interleaved)
    {   
        // Interleaved mode 
        if ((rc = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
        {
            printf("cannot set access type (%s) ...\n", snd_strerror(rc));
            return rc;
        }
        printf("access type = SND_PCM_ACCESS_RW_INTERLEAVED\n");
    }
    else
    {
        if ((rc = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_NONINTERLEAVED)) < 0)
        {
            printf("cannot set access type (%s) ...\n", snd_strerror(rc));
            return rc;
        }
        printf("access type = SND_PCM_ACCESS_RW_NONINTERLEAVED\n");
    }

    // Signed 16-bit little-endian format 
    if ((rc = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE)) < 0) {
        printf("cannot set sample format (%s) ...\n", snd_strerror(rc));
        return 1;
    }

    // TODO: channel number important - will a bad channel number crash the whole capturing?
    int nChannels = 2;
    if ((rc = snd_pcm_hw_params_set_channels(handle, params, nChannels)) < 0) {
        printf("cannot set channel count (%s) ...\n", snd_strerror(rc));
        return 1;   
    }

    // TODO: right?
    unsigned int wanted_rate = 22000;
    unsigned int exact_rate = wanted_rate;
    if ((rc = snd_pcm_hw_params_set_rate_near(handle, params, &exact_rate, 0)) < 0) {
        printf("cannot set sample rate (%s) ...\n", snd_strerror(rc));
        return 1;
    }

    if (wanted_rate != exact_rate) 
    {
          printf("The rate %i Hz is not supported by your hardware.\n" 
                  "==> Using %i Hz instead.\n", wanted_rate, exact_rate);
    }

    // TODO: what about these parts? What are those "frames" and what are the "periods"?
    // must this be set according to the hardware?!?
    snd_pcm_uframes_t frames = 640;
    int periods = 8;

    // Set number of periods. Periods used to be called fragments. 
    if (snd_pcm_hw_params_set_periods(handle, params, periods, 0) < 0) {    
          printf("Error setting periods.\n");
          return 1;
        }

    if ((err = snd_pcm_hw_params_set_period_size_near(handle, params, &frames, 0)) < 0) {
        fprintf(stderr, "Init: cannot set period_size (%s) ...\n", snd_strerror(err));
        return err; 
    }

    // TODO: is this the size needed for a single read-call?
    snd_pcm_uframes_t buff_size = 0;
    err = snd_pcm_hw_params_get_buffer_size(params, &buff_size);
    if (err < 0) {
        printf("Unable to get buffer size for playback: %s\n", snd_strerror(err));
        return err;
    }
    printf("needed buffersize: %i \n", (int)buff_size);

    pcm_state = snd_pcm_state(handle);
    printf("0.m State: %s\n", print_pcm_state(pcm_state));

    // what's this?
    snd_pcm_sw_params_t*  swparams_c;

    // snd_pcm_hw_params will call PREPARE internally!
    if ((err = snd_pcm_hw_params(handle, params)) < 0) {
        fprintf(stderr, "Init: cannot set parameters (%s) ...\n", snd_strerror(err));
        return err;     
    }

    pcm_state = snd_pcm_state(handle);
    printf("0.n State: %s\n", print_pcm_state(pcm_state));

    printf("capture\n");

    // test: start device:
    /*
    err = snd_pcm_start(handle);
    if(err < 0)
    {
        fprintf (stderr, "cannot start device (%s)\n",
                     snd_strerror (err));
                exit (1);
    }
    */


    //state
    pcm_state = snd_pcm_state(handle);  
    printf("1. State: %s\n", print_pcm_state(pcm_state));

    // Is this ok?!?
    //short buf[100*2048];
    //short buf[5120*1024]; // seg-fault?!?
    short buf[5120];

    // only try to read a single time
    int i=0;
    for (i = 0; i < 1; ++i) {
        if(interleaved)
        {
            if ((err = snd_pcm_readi (handle, buf, frames)) < 0)
    //      if ((err = snd_pcm_writei (handle, buf, 1)) < 0)  // ioctl error
            {
                printf("EBADFD %i -> EPIPE %i -> ESTRPIPE %i\n",EBADFD,EPIPE,ESTRPIPE);
                if(err == -EBADFD)
                    printf("-EBADFD: PCM is not in the right state (SND_PCM_STATE_PREPARED or SND_PCM_STATE_RUNNING) \n");
                if(err == -EPIPE) printf("-EPIPE:   an overrun occurred \n");
                if(err == -ESTRPIPE) printf("-ESTRPIPE: a suspend event occurred (stream is suspended and waiting for an application recovery)\n");

                fprintf (stderr, "error %i : interleaved read from audio interface failed (%s)\n",
                     err, snd_strerror (err));

                pcm_state = snd_pcm_state(handle);
                printf("1.1 State: %s\n", print_pcm_state(pcm_state));
                exit (1);
            }
        }
        else
        {
            // TODO: is it hardware dependent whether I can exlusively use readi or readn?
            // how does a readn call have to look like? needs multiple buffers?!?
            printf("non-interleaved capture not implemented\n");



            //if ((err = snd_pcm_readn (handle, buf, frames)) != frames) {
            //  fprintf (stderr, "non-interleaved read from audio interface failed (%s)\n",
            //       snd_strerror (err));
            //  exit (1);
            //}
        }
    }

    pcm_state = snd_pcm_state(handle);
    printf("2. State: %s\n", print_pcm_state(pcm_state));

    printf("close\n");

    snd_pcm_close (handle);

    pcm_state = snd_pcm_state(handle);
    printf("3. State: %s\n", print_pcm_state(pcm_state));

    printf("finish\n");
    return 0;
}

producing this output:

~ # ./audioTest 
access type = SND_PCM_ACCESS_RW_INTERLEAVED
The rate 22000 Hz is not supported by your hardware.
==> Using 16000 Hz instead.
needed buffersize: 5120 
0.m State: SND_PCM_STATE_OPEN
hello, alsa~.
0.n State: SND_PCM_STATE_PREPARED
capture
1. State: SND_PCM_STATE_PREPARED
EBADFD 77 -> EPIPE 32 -> ESTRPIPE 86
error -5 : interleaved read from audio interface failed (Input/output error)
1.1 State: SND_PCM_STATE_RUNNING

freezes afterwards...

if I add another parametrization block before the capture print (taken from the reference implementation):

{
        snd_pcm_uframes_t boundary = 0;
        snd_pcm_sw_params_alloca(&swparams_c);
        /* get the current swparams */
        err = snd_pcm_sw_params_current(handle, swparams_c);
        if (err < 0) {
            printf("Unable to determine current swparams_c: %s\n", snd_strerror(err));
            return err;
        }

        // what's this? necessary?
        /*
        //err = snd_pcm_sw_params_set_tstamp_mode(handle, swparams_c, SND_PCM_TSTAMP_ENABLE);
        err = snd_pcm_sw_params_set_tstamp_mode(handle, swparams_c, SND_PCM_TSTAMP_MMAP);
        if (err < 0) {
            printf("Unable to set tstamp mode : %s\n", snd_strerror(err));
            return err;
        }
        */

        err = snd_pcm_sw_params_set_avail_min(handle, swparams_c, frames);
        if (err < 0) {
            printf("Unable to call snd_pcm_sw_params_set_avail_min(): %s\n", snd_strerror(err));
            return err;
        }

        err = snd_pcm_sw_params_set_start_threshold(handle, swparams_c, (buff_size / frames) * frames);
        if (err < 0) {
            printf("Unable to call snd_pcm_sw_params_set_start_threshold(): %s\n", snd_strerror(err));
            return err;
        }

        err =  snd_pcm_sw_params_get_boundary(swparams_c, &boundary);
        if (err < 0) {
            printf("Unable to call snd_pcm_sw_params_get_boundary(): %s\n", snd_strerror(err));
            return err;
        }


        err = snd_pcm_sw_params_set_stop_threshold(handle, swparams_c, boundary);
        if (err < 0) {
            printf("Unable to call snd_pcm_sw_params_set_stop_threshold(): %s\n", snd_strerror(err));
            return err;
        }

        /* align all transfers to 1 sample */
        err = snd_pcm_sw_params_set_xfer_align(handle, swparams_c, 1);
        if (err < 0) {
            printf("Unable to set transfer align for playback: %s\n", snd_strerror(err));
            return err;
        }

        /* write the sw parameters */
        err = snd_pcm_sw_params(handle, swparams_c);
        if (err < 0) {
            printf("Unable to set swparams_c : %s\n", snd_strerror(err));
            return err;
        }
    }

I end up with messages:

error -5 : interleaved read from audio interface failed (Input/output error)
2.1 State: SND_PCM_STATE_PREPARED

but no freeze. So why might it freeze if the device is "running"?

Any suggestions what to do to make this device/code working?

Sorry for all that code, I'm not sure whether all the parameters are needed. If it is too hard to read tell me whether it might be better to encapsulate the whole init part for readability.

Upvotes: 2

Views: 4299

Answers (1)

psqli
psqli

Reputation: 698

The second case (setting software parameters) is not undefined behavior neither a BUG. It is actually the expected behavior. When state is PREPARED, reading less than start_threshold frames results in blocking the thread. Another thread may start capture. The patch was reverted (see this commit):

ALSA: pcm: Comment why read blocks when PCM is not running

This avoids bringing back the problem introduced by 62ba568f7aef ("ALSA: pcm: Return 0 when size < start_threshold in capture") and fixed in 00a399cad1a0 ("ALSA: pcm: Revert capture stream behavior change in blocking mode"), which prevented the user from starting capture from another thread.


The first case should not have had any problems. When hardware parameters are set, ALSA put default values for software parameters. start_threshold is set to 1. The device is started in the first read. However, there seems to be another timeout resulting in EIO. I'll post an update here if I find a problem in the ALSA core. Anyway it's probably the device driver that was not issuing interrupts and causing the system to freeze.

Upvotes: 3

Related Questions