ALSA driver --PCM device 创建过程

前面已经写过PCM device的创建框架,我们现在来看看PCM device是如何创建的。

在调用snd_pcm_new时就会创建一个snd_pcm类型的PCM device.

struct snd_pcm {
  struct snd_card *card;//PCM device 说挂载的声卡
  struct list_head list;//一个Card可能有多个PCM device,PCM device列表
  int device; /* device number *///PCM device的索引
  unsigned int info_flags;
  unsigned short dev_class;
  unsigned short dev_subclass;
  char id[64];
  char name[80];
  struct snd_pcm_str streams[2];//PCM的playback和capture stream
  struct mutex open_mutex;
  wait_queue_head_t open_wait;
  void *private_data;//private_data一般为芯片专用信息
  void (*private_free) (struct snd_pcm *pcm);//用来释放private_data
  bool internal; /* pcm is for internal use only */
  bool nonatomic; /* whole PCM operations are in non-atomic context */
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
  struct snd_pcm_oss oss;
#endif
};

接下来我们来看看在snd_pcm_new中有些什么操作:

static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count, bool internal,
struct snd_pcm **rpcm)
{
  struct snd_pcm *pcm;
  int err;
  static struct snd_device_ops ops = {//PCM device的操作函数
  .dev_free = snd_pcm_dev_free,
  .dev_register = snd_pcm_dev_register,//在PCM device rigister调用
  .dev_disconnect = snd_pcm_dev_disconnect,
  };

  if (snd_BUG_ON(!card))
  return -ENXIO;
  if (rpcm)
  *rpcm = NULL;
  pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);//为PCM device创建空间
  if (!pcm)
    return -ENOMEM;
  pcm->card = card;//将声卡保存在PCM device中
  pcm->device = device;
  pcm->internal = internal;
  mutex_init(&pcm->open_mutex);
  init_waitqueue_head(&pcm->open_wait);
  INIT_LIST_HEAD(&pcm->list);
  if (id)
    strlcpy(pcm->id, id, sizeof(pcm->id));
  if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {//为PCM device 创建playback stream
    snd_pcm_free(pcm);
    return err;
  }
  if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {//为PCM device 创建capture stream
    snd_pcm_free(pcm);
  return err;
  }
  if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {//将PCM device 挂载到Card上。
    snd_pcm_free(pcm);
    return err;
  }
  if (rpcm)
  *rpcm = pcm;
  return 0;
}

一个PCM device分别有一个playback, capture stream,由snd_pcm_new_stream创建。

playback 和capture stream是一个snd_pcm_str的结构体,

struct snd_pcm_str {
  int stream; /* stream (direction) *///是playback stream or capture stream
  struct snd_pcm *pcm;//当前的PCM device
  /* -- substreams -- */
  unsigned int substream_count;//substream的数目。
  unsigned int substream_opened;//已经open的substream数目。每次open +1,close -1.
  struct snd_pcm_substream *substream;//playback stream or capture stream的substream链表
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
  /* -- OSS things -- */
  struct snd_pcm_oss_stream oss;
#endif
#ifdef CONFIG_SND_VERBOSE_PROCFS
  struct snd_info_entry *proc_root;
  struct snd_info_entry *proc_info_entry;
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
  unsigned int xrun_debug; /* 0 = disabled, 1 = verbose, 2 = stacktrace */
  struct snd_info_entry *proc_xrun_debug_entry;
#endif
#endif
  struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */
  struct device dev;//stream的device结构
};

substream的结构体snd_pcm_substream :

struct snd_pcm_substream {
  struct snd_pcm *pcm;//PCM device
  struct snd_pcm_str *pstr;//playback stream or substream
  void *private_data; /* copied from pcm->private_data *///private_data通常是和PCM device的private data相同。
  int number;//当前substream的index
  char name[32]; /* substream name */
  int stream; /* stream (direction) *///是playback or capture的substream
  struct pm_qos_request latency_pm_qos_req; /* pm_qos request */
  size_t buffer_bytes_max; /* limit ring buffer size *///最大的buffer size
  struct snd_dma_buffer dma_buffer;//DMA buffer
  size_t dma_max;
  /* -- hardware operations -- */
  const struct snd_pcm_ops *ops;//substream的操作函数
  /* -- runtime information -- */
  struct snd_pcm_runtime *runtime;//runtime信息
  /* -- timer section -- */
  struct snd_timer *timer; /* timer */
  unsigned timer_running: 1; /* time is running */
  /* -- next substream -- */
  struct snd_pcm_substream *next;
  /* -- linked substreams -- */
  struct list_head link_list; /* linked list member */
  struct snd_pcm_group self_group; /* fake group for non linked substream (with substream lock inside) */
  struct snd_pcm_group *group; /* pointer to current group */
  /* -- assigned files -- */
  void *file;
  int ref_count;
  atomic_t mmap_count;
  unsigned int f_flags;
  void (*pcm_release)(struct snd_pcm_substream *);
  struct pid *pid;
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
  /* -- OSS things -- */
  struct snd_pcm_oss_substream oss;
#endif
#ifdef CONFIG_SND_VERBOSE_PROCFS
  struct snd_info_entry *proc_root;
  struct snd_info_entry *proc_info_entry;
  struct snd_info_entry *proc_hw_params_entry;
  struct snd_info_entry *proc_sw_params_entry;
  struct snd_info_entry *proc_status_entry;
  struct snd_info_entry *proc_prealloc_entry;
  struct snd_info_entry *proc_prealloc_max_entry;
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
  struct snd_info_entry *proc_xrun_injection_entry;
#endif
#endif /* CONFIG_SND_VERBOSE_PROCFS */
/* misc flags */
  unsigned int hw_opened: 1;
};

/**
* snd_pcm_new_stream - create a new PCM stream
* @pcm: the pcm instance
* @stream: the stream direction, SNDRV_PCM_STREAM_XXX
* @substream_count: the number of substreams
*/

int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
{
  int idx, err;
  struct snd_pcm_str *pstr = &pcm->streams[stream];//当前的playback stream or capture stream
  struct snd_pcm_substream *substream, *prev;

#if IS_ENABLED(CONFIG_SND_PCM_OSS)
  mutex_init(&pstr->oss.setup_mutex);
#endif
  pstr->stream = stream;
  pstr->pcm = pcm;
  pstr->substream_count = substream_count;
  if (!substream_count)
    return 0;

  snd_device_initialize(&pstr->dev, pcm->card);
  pstr->dev.groups = pcm_dev_attr_groups;
  dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device, stream == SNDRV_PCM_STREAM_PLAYBACK ? ‘p‘ : ‘c‘);//playback stream的名字

  if (!pcm->internal) {
    err = snd_pcm_stream_proc_init(pstr);//将playback stream挂到/proc目录下,我们通过cat可以查看。
    if (err < 0) {
      pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n");
      return err;
    }
  }
  prev = NULL;
  for (idx = 0, prev = NULL; idx < substream_count; idx++) {//一个playback stream下可有多个substream,创建substream
    substream = kzalloc(sizeof(*substream), GFP_KERNEL);
    if (!substream)
      return -ENOMEM;
    substream->pcm = pcm;
    substream->pstr = pstr;
    substream->number = idx;
    substream->stream = stream;
    sprintf(substream->name, "subdevice #%i", idx);
    substream->buffer_bytes_max = UINT_MAX;
    if (prev == NULL)//将substream链接到playback stream的substream成员中。
      pstr->substream = substream;
    else
      prev->next = substream;

    if (!pcm->internal) {
      err = snd_pcm_substream_proc_init(substream);//将substream挂到/proc下。
      if (err < 0) {
        pcm_err(pcm,"Error in snd_pcm_stream_proc_init\n");
        if (prev == NULL)
          pstr->substream = NULL;
        else
          prev->next = NULL;
        kfree(substream);
        return err;
      }
    }
    substream->group = &substream->self_group;
    spin_lock_init(&substream->self_group.lock);
    mutex_init(&substream->self_group.mutex);
    INIT_LIST_HEAD(&substream->self_group.substreams);
    list_add_tail(&substream->link_list, &substream->self_group.substreams);
    atomic_set(&substream->mmap_count, 0);
    prev = substream;
  }
  return 0;
}

时间: 2024-10-12 23:26:56

ALSA driver --PCM device 创建过程的相关文章

Android Camera 通过V4L2与kernel driver的完整交互过程

http://blog.chinaunix.net/uid-26215986-id-3552456.html 原文地址:Android Camera 通过V4L2与kernel driver的完整交互过程 作者:xinyuwuxian 之前在 Android Camera 的执行流程 http://blog.chinaunix.net/uid-26765074-id-3499537.html 这篇文章中已经详细介绍了Android Camera app到调用open打开camera 设备的完成过

Android4.4 Framework分析——Activity窗口的创建过程(一)

学习android的窗口模块一周多了,感觉自己对这个模块的理解还是比较模糊,先把get的知识点记录一下. 下图是学习过程记录的activity窗口启动过程序列图,没有或者没办法完整的描绘出来,整个过程比较复杂: 整个学习过程中是参照老罗的android之旅博客和<深入理解android内核设计思想>一书来辅助的,非常感谢前辈. Activity的整体启动过程可查看Android4.4 framework分析--Launcher中启动应用程序(startActivity)的过程的序列图,本文关注

一个ActiveX control的创建过程

创建 根据这篇文章的介绍:http://www.cnblogs.com/time-is-life/p/6354152.html 来创建,里面包含了创建的基本过程以及属性事件方法的使用. 使用: 参考文章中建议使用ActiveX Control Pad,这个工具确实很好用,能够自动生成带有Active x控件的网页,而且可以直接设计和配置参数.但是这个工具只在老的32位系统可以使用,我试了Windows server 2008 32位系统,装不上,其他的64位系统也装不上.最后在一台XP的机器上装

图解java对象的创建过程

前面几篇博文分别介绍了JAVA的Class文件格式.JVM的类加载机制和JVM的内存模型,这里就索性把java对象的创建过程一并说完,这样java对象的整个创建过程就基本上说明白了(当然你要有基础才能真正看明白).经常有人问我为什么这么喜欢钻研底层的东西,首先,因为我以前的做硬件的和嵌入式的,兴趣使然:其次,我个人感觉,如果不把上下打通,心里老是有一堵墙过不去,说白了,这是个人因素,与好坏无关(当然,经常有人说,懂底层原理是成为高手的必经之路). 现在来说一下我当初学习JVM的原因,在学习JAV

Android Context创建过程

    特定的资源或者类构成了Android应用程序的运行上下文环境 PackageManager, ClassLoader, Assert等等 Android应用程序窗口的运行上下文环境是通过ContextImpl类来描述的,即每一个Activity组件都关联有一个ContextImpl对象.ContextImpl类继承了Context类,它与Activity组件的关系如图 这个类图在设计模式里面就可以称为装饰模式 Activity组件以后就可以通过这个ContextImpl对象来执行一些具体

设置 Oracle,Sqlplus,Linux 设置为UTF8 专治中文乱码 ,查看表的创建过程

[本文谢绝转载原文来自http://990487026.blog.51cto.com] 思想: 1,设置Linux字符集 为UTF8编码 2,设置Oracle字符集 为UTF8编码 3,设置sqlplus客户端环境 为UTF8编码 4,如果是在Windows使用CRT/Xshell远程工具,请设置软件编码为UTF8[略...] 1,先设置oracle为UTF8 [[email protected] ~]$ sqlplus /nolog SQL> conn /as sysdba; SQL> sh

oracle link的创建过程

下面做一个测试,在测试中,创建数据库链接的库为XJ(WINDOWS 2003 ORACLE 10g 10.2.0.1),被链接的库为DMDB(LINUX AS5 ORACLE 10g 10.2.0.1 RAC)第一部分:如何创建dblinkdblink的创建有两种方式:1.在本地数据库tnsnames.ora文件中配置要访问的目标数据库信息create [public] database link test_link2connect to scott identified by tiger us

netty 5 alph1源码分析(服务端创建过程)

参照<Netty系列之Netty 服务端创建>,研究了netty的服务端创建过程.至于netty的优势,可以参照网络其他文章.<Netty系列之Netty 服务端创建>是 李林锋撰写的netty源码分析的一篇好文,绝对是技术干货.但抛开技术来说,也存在一些瑕疵. 缺点如下 代码衔接不连贯,上下不连贯. 代码片段是截图,对阅读代理不便(可能和阅读习惯有关) 本篇主要内容,参照<Netty系列之Netty 服务端创建>,梳理出自己喜欢的阅读风格. 1.整体逻辑图 整体将服务

深入理解Java对象的创建过程:类的初始化与实例化

"-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 深入理解Java对象的创建过程:类的初始化与实例化 - Rico's Blogs - 博客频道 - CSDN.NET Rico's Blogs 潜心修炼,成为一个更好的人. 目录视图 摘要视图 订阅 [活动]2017 CSDN博客专栏评选 &nbsp [5月书讯]流畅