[alsa-devel] Ends up with write error (Ning Zhang)

Takashi Iwai tiwai at suse.de
Mon Feb 25 13:17:10 CET 2008


At Sun, 24 Feb 2008 18:18:34 +0800,
Ning Zhang wrote:
> 
> Hi all,
> 
> There is no response so far, maybe I am not clear. So in sum, here are the
> questions:
> 1. In meddle-level driver, how comes the runtime->control->avail_min? I mean
> which function set the value, and according what to set the value?  Does it
> have some relationship to the hardware configuration in the device driver(
> i.e snd_pcm_hardware )?

It's not what you change in the driver code but you refer to.  This is
a parameter that application sets.

> 2. Should I  use the snd_pcm_period_elapsed function even I don't use the
> interrupt mechanism?

Yes.  This is independent from the IRQ or whatever.  The driver has to
notify to the PCM core when the period has been processed.

>  If yes, In which callback function should I call
> it(snd_pcm_period_elapsed)?


> 3,Can anybody do me a favor to explain the mechanism of the middle-level
> driver or give me some hints?

It's not sure what do you mean as "the middle-level driver".  Could
you clarify this as a first step?  Or, explain a bit more about your
hardware.

In the ALSA framework, the driver needs to have a certain notification
mechanism.  The PCM callbacks are all passive and invoked from the PCM
core stuff.  And, the PCM core is driven asynchronously.

My rough guess is that you expect that the copy callback blocks the
operation until the write completion.  It's actually not.  The copy
callback is to copy the given data to the hardware buffer as quickly
as possible and return.  Then, PCM core blocks the operation until
it's notified via snd_pcm_period_elapsed().

Unfortunately, ALSA PCM core implementation isn't well suited for the
hardwares that need the explicit write-loop and blocking.  In such a
case, you need some workarounds, e.g. using workqueue.


Takashi


> BRs,
> 
> David
> 
> 
> 
> >
> > ------------------------------
> >
> > Message: 7
> > Date: Wed, 20 Feb 2008 16:30:54 +0800
> > From: "Ning Zhang" <ning.zhang06 at gmail.com>
> > Subject: [alsa-devel] Ends up with write error
> > To: alsa-devel <alsa-devel at alsa-project.org>
> > Message-ID:
> >         <48a1cba90802200030v65794b96j7c956406fdc0df9c at mail.gmail.com>
> > Content-Type: text/plain; charset=GB2312
> >
> > Hi all,
> > I am writing a ALSA driver for my ARM 926 platform. The ARM communicates
> > with the DSP by accessing a 128-byte shared memory. I don't use IRQ
> > mechanism.(In fact, I am not sure the platform has this mechanism)
> > Here is some code slices:
> >
> > static struct snd_pcm_hardware snd_arch_playback_hw = {
> >
> >    .info = (SNDRV_PCM_INFO_MMAP |
> >
> >             SNDRV_PCM_INFO_INTERLEAVED |
> >
> >             SNDRV_PCM_INFO_BLOCK_TRANSFER),
> >
> >    .formats = SNDRV_PCM_FMTBIT_S16_LE,
> >
> >    .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
> >
> >              SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
> >
> >              SNDRV_PCM_RATE_48000),
> >
> >    .rate_min = 8000,
> >
> >    .rate_max = 48000,
> >
> >    .channels_min = 2,
> >
> >    .channels_max = 2,
> >
> >    .buffer_bytes_max = 128,
> >
> >    .period_bytes_min = 2,
> >
> >    .period_bytes_max = 128,
> >
> >    .periods_min = 1,
> >
> >    .periods_max = 64,
> >
> > };
> >
> >
> >
> > static snd_pcm_uframes_t snd_arch_pcm_pointer(struct snd_pcm_substream
> >
> >                                                 *substream)
> >
> > {    ?
> >
> >    /*
> >
> >     * get the current hardware pointer
> >
> >     */
> >
> >    current_ptr = arch_fw_read_dsp_reg(chip, AFE_RWADDR) & AFE_RD_ADDR;
> >
> >
> > return bytes_to_frames(runtime, current_ptr);
> >
> > }
> >
> > static int snd_arch_pcm_playback_copy(struct snd_pcm_substream *substream,
> >
> >                                         int channel,
> >
> >                                         snd_pcm_uframes_t pos,
> >
> >                                         void __user *src,
> >
> >                                         snd_pcm_uframes_t count)
> >
> > {  ??
> >
> >
> >
> >    count = frames_to_bytes(runtime, count);
> >
> >    pos = frames_to_bytes(runtime, pos);
> >
> >    chip->bufpos = pos;
> >
> >                ?.
> >
> >    spin_lock(chip->reg_lock);
> >
> >    if ( count > 0 && count <= MAX_BUF_SIZE ) {
> >
> >            if ( copy_from_user(chip->bufptr, src, count) ) {
> >
> >                     snd_printk(KERN_ERR "can not copy user data!\n");
> >
> >                     return -EFAULT;
> >
> >            }
> >
> >            if ( arch_fill_rx_buf(chip,
> >
> >                                         (signed short*)(chip->bufptr),
> > count) < 0 ) {
> >
> >                     snd_printk(KERN_ERR "fill rx buffer failed!\n");
> >
> >                     return -1;
> >
> >            }
> >
> >              ?????
> >
> >    }
> >            spin_unlock(chip->reg_lock);
> >
> >    return 0;
> >
> > }
> >
> > The problem is my driver can make sounds, but ends up a write
> > error.  After
> > much debugging, I think there might be some problem in the
> > interworking  between
> > middle-level driver and the device driver.
> >
> >            I found the runtime->control->avail_min=32,  and the
> > runtime->buffer_size and runtime->period_size are both 32. So at first
> > middle-level driver call snd_pcm_lib_write1 transfers 32 frames(128 bytes)
> > to the device driver's copy callback function and trigger function.  Then
> > when middle-level driver wants to transfer another 32 frames, it calls the
> > pointer callback function and gets the position, because at this time the
> > whole bytes has not been processed completely, so the pointer function's
> > return value must less than 32, and the avail must less than
> > runtime->control_->avail_min, and goes to this flow and ends up a write
> > error.
> >
> >
> > _______________________________________________________________________________
> >
> > if (((avail < runtime->control->avail_min && size > avail) ||
> >
> >                      (size >= runtime->xfer_align && avail <
> > runtime->xfer_align))) {
> >
> >                             ?????...
> >
> >                           long tout;
> >
> >                           init_waitqueue_entry(&wait, current);
> >
> >                           add_wait_queue(&runtime->sleep, &wait);
> >
> >                           while (1) {
> >
> >                                    if (signal_pending(current)) {
> >
> >                                             state = SIGNALED;
> >
> >                                             break;
> >
> >                                    }
> >
> >                                    set_current_state(TASK_INTERRUPTIBLE);
> >
> >                                    snd_pcm_stream_unlock_irq(substream);
> >
> >                                    tout = schedule_timeout(10 * HZ);
> >
> >                                    snd_pcm_stream_lock_irq(substream);
> >
> >                                    if (tout == 0) {
> >
> >                                             if (runtime->status->state !=
> > SNDRV_PCM_STATE_PREPARED &&
> >
> >                                                 runtime->status->state !=
> > SNDRV_PCM_STATE_PAUSED) {
> >
> >                                                      state =
> > runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;
> >
> >                                                      break;
> >
> >                                             }
> >
> >                                    }
> >
> >                                    switch (runtime->status->state) {
> >
> >
> > ????..
> >
> >                                    :
> >
> >                                             state = ERROR;
> >
> >                                             goto _end_loop;
> >
> >
> >
> >                                    default:
> >
> >                                             break;
> >
> >                                    }
> >
> >                                    avail =
> > snd_pcm_playback_avail(runtime);
> >
> >                                    if (avail >=
> > runtime->control->avail_min)
> > {
> >
> >                                             state = READY;
> >
> >                                             break;
> >
> >                                    }
> >
> >                           }
> >
> >                          _end_loop:
> >
> >                           remove_wait_queue(&runtime->sleep, &wait);
> >
> >
> >
> >                           switch (state) {
> >
> >                           ??.
> >
> >                           case EXPIRED:
> >
> >                                    snd_printd("playback write error (DMA
> > or
> > IRQ trouble?)\n");
> >
> >                                    err = -EIO;
> >
> >                                    goto _end_unlock;
> >
> >                           ???
> >
> >                           }
> >
> >                   }
> >
> > This code is a  part of  snd_pcm_lib_write1() in sound/core/pcm_lib.c
> >
> >   I think that I misunderstood the mechanism of middle-level driver. Could
> > you please correct me?
> >
> > What should the pointer callback function return?  What's it  for?
> >
> > How comes the runtime->control->avail_min?
> >
> > Because I don't use the IRQ, so should I use the snd_pcm_period_elapsed
> > function? And where?
> >
> > ALSA Version is 1.0.14rc1 and I use alsa-lib-1.0.15.
> >
> > Can someone comment on this and give me some hints to solve this
> > problem?  Thanks
> > in advance.
> >
> >   BRs,
> >
> > David
> >
> > ------------------------------
> >
> > _______________________________________________
> > Alsa-devel mailing list
> > Alsa-devel at alsa-project.org
> > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
> >
> >
> > End of Alsa-devel Digest, Vol 12, Issue 69
> > ******************************************
> >
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel at alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
> 


More information about the Alsa-devel mailing list