ALSA driver--PCM Interrupt handle

PCM 中断处理函数的主要作用是用来更新buffer position.

在PCM的interrupt handle里面通过snd_pcm_period_elapsed来通知alsa-core buffer position随着peroid的变化。

在声卡中可以有几种类型的中断产生方式:

1.每隔peroid就产生interupt

在这种中断模式下,可以在每次中断是调用snd_pcm_peroid_elapsed. snd_pcm_peroid_elapsed以substream作为参数,因此我们必须要能够在芯片专用数据chip中能够访问到substream,在chip中保存当前正在running的substream.可以在PCM的open 函数中将substream保存到chip(chip作为PCM的private_data)中.

典型的中断处理函数如下:

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;
  }

2.高频率时间中断(High frequency timer interrupts)

高频率时间中断用在当硬件每隔固定的时间产生中断,而不是每隔peroid产生中断。我们必须在产生中断时检测hardware position,并计算处理的数据长度,当处理的数据长度超过peroid size时,就调用snd_pcm_peroid_elapsed。

  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;
  }

 

在上面两种中断方式中,如果硬件已经处理了多个peroid size的数据,我们只需要调用一次snd_pcm_peroid_elapsed,alsa core就会去检查hardware position 并更新到最新状态。

posted @ 2016-12-19 22:49  fellow_jing  阅读(1957)  评论(0编辑  收藏  举报