zoukankan      html  css  js  c++  java
  • ALSA driver--PCM实例创建框架

    在介绍PCM 之前,我们先给出创建PCM实例的框架。

      #include <sound/pcm.h>
      ....
    
      /* hardware definition */
      static struct snd_pcm_hardware snd_mychip_playback_hw = {
              .info = (SNDRV_PCM_INFO_MMAP |
                       SNDRV_PCM_INFO_INTERLEAVED |
                       SNDRV_PCM_INFO_BLOCK_TRANSFER |
                       SNDRV_PCM_INFO_MMAP_VALID),
              .formats =          SNDRV_PCM_FMTBIT_S16_LE,
              .rates =            SNDRV_PCM_RATE_8000_48000,
              .rate_min =         8000,
              .rate_max =         48000,
              .channels_min =     2,
              .channels_max =     2,
              .buffer_bytes_max = 32768,
              .period_bytes_min = 4096,
              .period_bytes_max = 32768,
              .periods_min =      1,
              .periods_max =      1024,
      };
    
      /* hardware definition */
      static struct snd_pcm_hardware snd_mychip_capture_hw = {
              .info = (SNDRV_PCM_INFO_MMAP |
                       SNDRV_PCM_INFO_INTERLEAVED |
                       SNDRV_PCM_INFO_BLOCK_TRANSFER |
                       SNDRV_PCM_INFO_MMAP_VALID),
              .formats =          SNDRV_PCM_FMTBIT_S16_LE,
              .rates =            SNDRV_PCM_RATE_8000_48000,
              .rate_min =         8000,
              .rate_max =         48000,
              .channels_min =     2,
              .channels_max =     2,
              .buffer_bytes_max = 32768,
              .period_bytes_min = 4096,
              .period_bytes_max = 32768,
              .periods_min =      1,
              .periods_max =      1024,
      };
    
      /* open callback */
      static int snd_mychip_playback_open(struct snd_pcm_substream *substream)
      {
              struct mychip *chip = snd_pcm_substream_chip(substream);
              struct snd_pcm_runtime *runtime = substream->runtime;
    
              runtime->hw = snd_mychip_playback_hw;
              /* more hardware-initialization will be done here */
              ....
              return 0;
      }
    
      /* close callback */
      static int snd_mychip_playback_close(struct snd_pcm_substream *substream)
      {
              struct mychip *chip = snd_pcm_substream_chip(substream);
              /* the hardware-specific codes will be here */
              ....
              return 0;
    
      }
    
      /* open callback */
      static int snd_mychip_capture_open(struct snd_pcm_substream *substream)
      {
              struct mychip *chip = snd_pcm_substream_chip(substream);
              struct snd_pcm_runtime *runtime = substream->runtime;
    
              runtime->hw = snd_mychip_capture_hw;
              /* more hardware-initialization will be done here */
              ....
              return 0;
      }
    
      /* close callback */
      static int snd_mychip_capture_close(struct snd_pcm_substream *substream)
      {
              struct mychip *chip = snd_pcm_substream_chip(substream);
              /* the hardware-specific codes will be here */
              ....
              return 0;
    
      }
    
      /* hw_params callback */
      static int snd_mychip_pcm_hw_params(struct snd_pcm_substream *substream,
                                   struct snd_pcm_hw_params *hw_params)
      {
              return snd_pcm_lib_malloc_pages(substream,
                                         params_buffer_bytes(hw_params));
      }
    
      /* hw_free callback */
      static int snd_mychip_pcm_hw_free(struct snd_pcm_substream *substream)
      {
              return snd_pcm_lib_free_pages(substream);
      }
    
      /* prepare callback */
      static int snd_mychip_pcm_prepare(struct snd_pcm_substream *substream)
      {
              struct mychip *chip = snd_pcm_substream_chip(substream);
              struct snd_pcm_runtime *runtime = substream->runtime;
    
              /* set up the hardware with the current configuration
               * for example...
               */
              mychip_set_sample_format(chip, runtime->format);
              mychip_set_sample_rate(chip, runtime->rate);
              mychip_set_channels(chip, runtime->channels);
              mychip_set_dma_setup(chip, runtime->dma_addr,
                                   chip->buffer_size,
                                   chip->period_size);
              return 0;
      }
    
      /* trigger callback */
      static int snd_mychip_pcm_trigger(struct snd_pcm_substream *substream,
                                        int cmd)
      {
              switch (cmd) {
              case SNDRV_PCM_TRIGGER_START:
                      /* do something to start the PCM engine */
                      ....
                      break;
              case SNDRV_PCM_TRIGGER_STOP:
                      /* do something to stop the PCM engine */
                      ....
                      break;
              default:
                      return -EINVAL;
              }
      }
    
      /* pointer callback */
      static snd_pcm_uframes_t
      snd_mychip_pcm_pointer(struct snd_pcm_substream *substream)
      {
              struct mychip *chip = snd_pcm_substream_chip(substream);
              unsigned int current_ptr;
    
              /* get the current hardware pointer */
              current_ptr = mychip_get_hw_pointer(chip);
              return current_ptr;
      }
    
      /* operators */
      static struct snd_pcm_ops snd_mychip_playback_ops = {
              .open =        snd_mychip_playback_open,
              .close =       snd_mychip_playback_close,
              .ioctl =       snd_pcm_lib_ioctl,
              .hw_params =   snd_mychip_pcm_hw_params,
              .hw_free =     snd_mychip_pcm_hw_free,
              .prepare =     snd_mychip_pcm_prepare,
              .trigger =     snd_mychip_pcm_trigger,
              .pointer =     snd_mychip_pcm_pointer,
      };
    
      /* operators */
      static struct snd_pcm_ops snd_mychip_capture_ops = {
              .open =        snd_mychip_capture_open,
              .close =       snd_mychip_capture_close,
              .ioctl =       snd_pcm_lib_ioctl,
              .hw_params =   snd_mychip_pcm_hw_params,
              .hw_free =     snd_mychip_pcm_hw_free,
              .prepare =     snd_mychip_pcm_prepare,
              .trigger =     snd_mychip_pcm_trigger,
              .pointer =     snd_mychip_pcm_pointer,
      };
    
      /*
       *  definitions of capture are omitted here...
       */
    
      /* create a pcm device */
      static int snd_mychip_new_pcm(struct mychip *chip)
      {
              struct snd_pcm *pcm;
              int err;
    
              err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm);
              if (err < 0) 
                      return err;
              pcm->private_data = chip;
              strcpy(pcm->name, "My Chip");
              chip->pcm = pcm;
              /* set operators */
              snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
                              &snd_mychip_playback_ops);
              snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
                              &snd_mychip_capture_ops);
              /* pre-allocation of buffers */
              /* NOTE: this may fail */
              snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
                                                    snd_dma_pci_data(chip->pci),
                                                    64*1024, 64*1024);
              return 0;
      }

    1.创建pcm实例

    ALSA driver为我们提供接口snd_pcm_new来创建PCM实例。但是我们最好是写一个如上述snd_mychip_new_pcm的函数来来对构建pcm实例的过程进行封装。

    /**
    * snd_pcm_new - create a new PCM instance
    * @card: the card instance
    * @id: the id string
    * @device: the device index (zero based)
    * @playback_count: the number of substreams for playback
    * @capture_count: the number of substreams for capture
    * @rpcm: the pointer to store the new pcm instance
    */
    int snd_pcm_new(struct snd_card *card, const char *id, int device, int playback_count, int capture_count, struct snd_pcm **rpcm)

    第三个参数表示新创建的PCM实例的index(0,1,2,3).可以在一个card上创建多个PCM 实例。每一个PCM又可以包含多个substream.如果芯片支持多路播放,那么将有多个substream.每次open/close都作用于某个substream.在创建PCM的substream时就指定了number(0~playback_count).当App在调用alsa lib API:snd_pcm_open时,alsa core通过snd_pcm_attach_substream函数来open一个空闲的substream.

     2.设置PCM的操作函数

    创建完PCM函数后,就可设置PCM 的操作函数。

      snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
                      &snd_mychip_playback_ops);
      snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
                      &snd_mychip_capture_ops);
    
    

    操作函数即我们写driver时需要实现的功能,以供alsa-core调用。ALSA PCM的操作函数包括:

      static struct snd_pcm_ops snd_mychip_playback_ops = {
              .open =        snd_mychip_pcm_open,
              .close =       snd_mychip_pcm_close,
              .ioctl =       snd_pcm_lib_ioctl,
              .hw_params =   snd_mychip_pcm_hw_params,
              .hw_free =     snd_mychip_pcm_hw_free,
              .prepare =     snd_mychip_pcm_prepare,
              .trigger =     snd_mychip_pcm_trigger,
              .pointer =     snd_mychip_pcm_pointer,
      };

    每个函数都包含一个snd_pcm_substream 的指针,指向当前操作的substream.

    在上面的例子中,每个操作函数里面都包含如下宏调用:

    其中返回的是substream->private_data,sustream的private_data是pcm->private_data的一份拷贝。拷贝动作是在snd_pcm_open时调用的snd_pcm_attach_substream中进行。一般来说pcm的private_data是芯片专用数据,当然我们也可以overwrite以保存别的数据。

    2.1 open

    当open PCM的一路substream时,open函数就会被调用。

      static int snd_xxx_open(struct snd_pcm_substream *substream)
      {
              struct mychip *chip = snd_pcm_substream_chip(substream);
              struct snd_pcm_runtime *runtime = substream->runtime;
    
              runtime->hw = snd_mychip_playback_hw;
              return 0;
      }

    在open函数内,至少应该初始化此substream的runtime->hw结构体.snd_mychip_playback_hw是预先定义的硬件描述。

    也可以在open函数里为substream分配private_data.如下:

    data = kmalloc(sizeof(*data), GFP_KERNEL);
     substream->runtime->private_data = data;

    如果芯片所支持的sample rate,samples等硬件配置有限制,也可以在open函数内设置限制。

    2.2 close

    当PCM的substream close时就会调用到close 函数。

    如果有在open函数内分配了runtime的private_data, 那private data在close函数释放。

      static int snd_xxx_close(struct snd_pcm_substream *substream)
      {
              ....
              kfree(substream->runtime->private_data);
              ....
      }

    2.4 hw_params

    当App在设置substream的buffer size, the period size, the format等硬件参数时,将会调用到hw_params函数。

    在hw_params函数中可以设置许多的硬件参数,包括buffer的分配。buffer分配:

    snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));

    2.5 hw_free

    hw_free用来释放在hw_params中释放的资源,如buffer.hw_free总是在close之前调用。

    2.6 prepare

    当app在调用alsa lib API:snd_pcm_prepare时,prepare函数将被调用,在此函数中可以设置format type, sample rate等参数。与在hw_params中设置参数不同的是每次app调用snd_pcm_prepare时都会去设置参数,而snd_pcm_prepare可能是在recovery undrrun时调用。

    prepare函数并非原子操作,因此必须使用 schedule-related functions保证安全性。

    2.7 trigger

    当PCM在start,stop,pause时,会调用到trigger函数。

    switch (cmd) {
      case SNDRV_PCM_TRIGGER_START:
              /* do something to start the PCM engine */
              break;
      case SNDRV_PCM_TRIGGER_STOP:
              /* do something to stop the PCM engine */
              break;
      default:
              return -EINVAL;
      }

    SNDRV_PCM_TRIGGER_XXX 定义在 <sound/pcm.h>. 至少我们应该在trigger函数中实现 START 和STOP commands。

    当pcm支持pause操作时,必须实现 PAUSE_PUSH and PAUSE_RELEASE commands,PAUSE_PUSH用来pause pcm,PAUSE_RELEASE用来restart pcm.

    trigger函数是atomic 的,因此在其中的操作越少越好,通常只用来trigger DMA.

    2.8 pointer

    当PCM middleware 层(alsa-core)需要获取当前的硬件指针(hardware position)时,就会调用pointer函数。pointer函数需要返回以frame为单位的hardware position(0~buffersize-1).

    pointer通常在buffer-update 过程中调用,由中断函数中的snd_pcm_period_elapsed触发。即每次硬件中断,就会调用snd_pcm_period_elapsed函数来通知alsa-core来读取当前的hardware position,计算buffer中空余空间,唤醒sleep的polling thread.

  • 相关阅读:
    what is NSParameterAssert?
    scrollView的几个属性contentSize contentOffset contentInset
    Explain awakeFromNib, initwithFrame usage
    strong、weak、unsafe_unretained(ARC);retain
    iOS 面试记(二)
    Python 编程规范 by @AKaras
    Python 的类的下划线命名
    Python中数值和进制转换
    Python基本运算符
    Ubuntu Linux系统下轻松架设nginx+php服务器应用
  • 原文地址:https://www.cnblogs.com/fellow1988/p/6200474.html
Copyright © 2011-2022 走看看