Interrupt Handler

Interrupts at the period (fragment) boundary
High frequency timer interrupts
On calling snd_pcm_period_elapsed()

The rest of pcm stuff is the PCM interrupt handler. The role of PCM interrupt handler in the sound driver is to update the buffer position and to tell the PCM middle layer when the buffer position goes across the prescribed period size. To inform this, call the snd_pcm_period_elapsed() function.

There are several types of sound chips to generate the interrupts.

Interrupts at the period (fragment) boundary

This is the most frequently found type: the hardware generates an interrupt at each period boundary. In this case, you can call snd_pcm_period_elapsed() at each interrupt.

snd_pcm_period_elapsed() takes the substream pointer as its argument. Thus, you need to keep the substream pointer accessible from the chip instance. For example, define substream field in the chip record to hold the current running substream pointer, and set the pointer value at open callback (and reset at close callback).

If you acquire a spinlock in the interrupt handler, and the lock is used in other pcm callbacks, too, then you have to release the lock before calling snd_pcm_period_elapsed(), because snd_pcm_period_elapsed() calls other pcm callbacks inside.

Typical code would be like:

Example 5.3. Interrupt Handler Case #1


  static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
  {
          struct mychip *chip = dev_id;
          spin_lock(&chip->lock);
          ....
          if (pcm_irq_invoked(chip)) {
                  /* call updater, unlock before it */
                  spin_unlock(&chip->lock);
                  snd_pcm_period_elapsed(chip->substream);
                  spin_lock(&chip->lock);
                  /* acknowledge the interrupt if necessary */
          }
          ....
          spin_unlock(&chip->lock);
          return IRQ_HANDLED;
  }

            


High frequency timer interrupts

This happense when the hardware doesn't generate interrupts at the period boundary but issues timer interrupts at a fixed timer rate (e.g. es1968 or ymfpci drivers). In this case, you need to check the current hardware position and accumulate the processed sample length at each interrupt. When the accumulated size exceeds the period size, call snd_pcm_period_elapsed() and reset the accumulator.

Typical code would be like the following.

Example 5.4. Interrupt Handler Case #2


  static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
  {
          struct mychip *chip = dev_id;
          spin_lock(&chip->lock);
          ....
          if (pcm_irq_invoked(chip)) {
                  unsigned int last_ptr, size;
                  /* get the current hardware pointer (in frames) */
                  last_ptr = get_hw_ptr(chip);
                  /* calculate the processed frames since the
                   * last update
                   */
                  if (last_ptr < chip->last_ptr)
                          size = runtime->buffer_size + last_ptr 
                                   - chip->last_ptr; 
                  else
                          size = last_ptr - chip->last_ptr;
                  /* remember the last updated point */
                  chip->last_ptr = last_ptr;
                  /* accumulate the size */
                  chip->size += size;
                  /* over the period boundary? */
                  if (chip->size >= runtime->period_size) {
                          /* reset the accumulator */
                          chip->size %= runtime->period_size;
                          /* call updater */
                          spin_unlock(&chip->lock);
                          snd_pcm_period_elapsed(substream);
                          spin_lock(&chip->lock);
                  }
                  /* acknowledge the interrupt if necessary */
          }
          ....
          spin_unlock(&chip->lock);
          return IRQ_HANDLED;
  }

            


On calling snd_pcm_period_elapsed()

In both cases, even if more than one period are elapsed, you don't have to call snd_pcm_period_elapsed() many times. Call only once. And the pcm layer will check the current hardware pointer and update to the latest status.