您的位置 首页 五金工具

pcm设备

pcm设备pcm设备

  4.设备文件节点的建立(dev/snd/pcmCxxDxxp、pcmCxxDxxc)

本文引用地址:http://www.eepw.com.cn/article/201612/341593.htm

  4.‰1structsnd_mi?nor



  每个snd_minor结构体保存了声卡下某个逻辑设备的上下文信息,他在逻辑设备建立阶段被填充,在逻辑设备被使用时就可以从该≥结构体中得到相应的信息。pcm设备也不例外,也需要使用该结构体。该结构体在inclu?de/sound/core.h中定义。

  [c-sharp]viewplaincopystructsnd_minor{

 ? inttype?;/*S≠NDRV_<DEVICE_TYPE_XXX*/

  intcard;/*cardn♂umber*/

  ?intdevice;/*﹢devicenumber*/

  conststructfile_operations*f_ops;/*fileoperations*/

  void*private_data;/*privateda?taforf_ops-∶>open*/

  structdevice*@dev;/*deviceforsysfs*/

  };

  在sound/sound.c中定义了一个s?nd_minor指针的全局数组:

  [c-sharp]view÷plainc㈱opystaticstructsnd_minor*snd_minors[256];

  前面说过,在声卡的注册阶段(snd_card_register),会调用pcm的回调函数s≤nd_pcm_?dev_register(),这个函数里会调用?函数snd_register_device_for_dev():

  [c-sharp]viewplain∠c?opystaticintsnd_pcm_de?v_register(structs?nd_device*device)

  {

  ……

?  /*registerpcm*/

  err=snd_register_device_for_dev(dmolevtype,pcm->card,

  pcm->device,

  ?&snd_pcm_f_ops[cidx],℅

  pcm,str,dev);

  ……

  }

  我们再进入snd_register_device_for_dev()?:

  [c-sharp]viewpolainco﹢pyintsnd_register_device_for_dev(inttype,structsnd_card*card,intdev,

  conststru?ctfile_operations*f_ops,

  void*private_data,

  constchar*name,structdevic?e*device)

  {

  intminor;

  structsnd_mi?nor*preg;

  if(snd_BUG_ON(!name))

  r≮eturn-EINVAL;

  preg=kmalloc(sizeof*preg,GFP_K∟ERNEL);?

∣  if(preg==NULL)

  return-ENOMEM;

  preg->type=t?ype;

  preg->card=card?card->number:≧-1;

  preg->device=dev;

  preg?->f_ops=f_ops;

  preg->private_data=private_data;

  mutex?_lock(&s?ound_mu⊿tex);

  #ifdefCONFIG_SND_DYNAMIC_MINORS

  minor=snd_find_free_minor();

  #else

  minor=sn?d_kernel_minor(type,card,dev);

×

  if(minor?>=?0&&snd_minors[minor])

  minor=-EBUSY;

  #endif∧

  if(minor<0){

  mutex_unlock(&sound_mutex?);

  kfree(preg);

  returnmi2nor;

  }

  snd_minors[minor?]=preg;

  preg->dev=device_create(sound_class,device,MKDEV(major,minor),

  private_d?at?a,"%s",name);

  if(IS_ERR(preg->dev)){

  snd_minors[minor]=NULL;

  mutex_unlock?(&sound_mutex);

  minor==PTR_ERR(preg->dev);

  kfree(preg);

  returnminor;

  }

  mutex_unlock(&sound_mutex);

  return0;

  }

㏄  首先,分配并初始化一个snd_minor结构中的各字段

  type:SNDRV_DEVICE∪_TYPE_PCM_PLAYBA¥CK/SNDRV_DEVICE_TYPE_PCM_CAPTURE

  card:card的编号

  devi℡ce:pcm实例的编号,大多数情况为0

  f?_ops:snd_pcm_f_ops

  pr?ivate_data:指向该pcm的实例

  根据type,ca?rd和p?cm的编号,确定数组的索§引值minor,mino?r也作为p?cm∏设备3的此设备号

  把该snd_minor结构的地址放?入全?局数组snd_minors[minor]中

  最后,调用device_create创建设备?节点

  4.2设备文件的建立

  在4.1节的最后,设备文件已经建立,不过4.1节的重?点在于s?nd_minors数组的赋值过程,在本节中,我们把重点放在设备文件中。

  回到pcm的回调函数snd_pcm_dev_register()中:

  [c-sharp∨]viewplaincopystaticintsnd_pcm_dev_regi∷ster(structsnd_device*device)?

  {?

  intcidx,err;

  cha?rstr[∈16];

  structsnd_pc?m*pcm;

  st?ructdevice*dev;

  pcm=devic?e->device_data;

  ……

  for(cidx=0;cidx<2;cidx++){

  ……

  switch(c?idx){

  caseSNDRV_〒P?CM_STREAM_PLAYBACK:

  sprintf(str,&quot≌;pcmC%iD%ip&quo?t;,pcm->c?ard-&1gt;number,pcm->device);

  devtype=SNDRV_DEVICE_TYPE_PCM_PLAYBACK;

?  break;

  caseSNDRV_PCM_STRE√AM_CAPTURE:

  sprintf(str※,"pcmC%iD?%ic",pcm->card->number,pcm->device);

  devt*ype=SNDRV_DEVI≯CE_TYPE_PCM_CAPTURE;

  break;

  }

  /*devicepointertouse,pcm->devtakesprecedenc3eif

  *itisassigned,otherwisefallbacktocard'sdevice

  *ifpossible*/

  dev=pcm->dev;

  if(!dev)

  dev=snd_card_get_device_link(pcm->card);

  /㎎*registerpcm*/

  err=snd_register_dev?ice_for_dev(devtype,pcm->card,

  pcm->device,

  &sn?d_pcm_f_ops[cidx£],

  pcm,str,dev);

  ……

  }

  .?…..

  }

  以上代码我们可以看?出,对于一个pcm设备,可以生成两个设备?文件,一个用于playback,一个用于capture,代码中?也确定了他们的命名规则:

  playba?ck–pcmCxDxp,通常系统中只有一各声卡和一个pcm,它就是pcmC0D0p㏕

  capture–pcmCxDxc,通常系统中只有一各声卡和一个pcm,它就是pcmC0D0c

  snd_pcm_f_ops

  snd_pcm_f_ops是一个标准的文件系统file_operations结构数组,它的定义在sound/core£/pcm_native.?c?中:

  [∩c-sharp]viewplaincopyconststructfile_operationssnd_pcm_f_ops[2]={

  {

  .owner=THIS_MODULE,

  .wri?te=sn?d_pcm_write,

  .aio_write=snd∴_pcm_aio_write,

  ml.open=snd_pcm_playback_″open,

  .release=snd_pcm_relea⊕㎜se,

  .lls$eek=no_llseek,

  .poll=?snd_pcm_playback_poll,

  .unlocked_ioctl=snd_pcm_playback_ioctl,

  .compat_ioctl=snd㏒_pcm_ioctl_compat,

  .mmap=snd_pcm_mmap,

  .fasync=snd_pc?m_fasync,

  .get_unma﹤pped_area=snd_pcm_get_unmapped_area,

  },

  {

  .o㎡wner=THIS_MODULE,

  .read=snd_pcm_read,

  .aio_read=snd_pcm_aio_read,

  .open=snd_pcm?_capture_open,

  .rele¢ase=snd_pcm_release,

  .llseek=no_llseek,

  .poll=snd_pcm_capture_poll,

?

℉ ? .unlocked_ioc∑tl=snd_pcm_capture_ioctl,

  .compat_i¬octl=snd﹣_pcm_ioctl_compat,

?

  .mmap?=sn%d_pcm_m×map,

  .fasync=snd_pcm_fasync,

  .get_unmapped_area=snd_pcm_get_unmapped?_area,

  }

  };

  snd_pcm_f_㎝ops作为snd_register_device_for_de∝v的参数被传入,并被记录在snd_minors[minor]中的字段f_ops中。最后,在snd_register_device_for_dev中创建设备节点:

  [c-sharp]viewpl?aincopysn﹥d_mi㏑nors[minor]=preg;

  pre?g-m>dev=device_create(sound_class,device,MKDEV(maj?or,m>inor),

  private_data,"%s",name);

  4.3层层深入,从应用程序到驱动层pcm

  4.3.1字符设备注册

 ? 在sound/core/sound.c中有alsa?_sound_init()函数,定义如下:

  [c-sh⌒arp]viewplaincop°yst♀aticint__initalsa_sound_init(void)

㎏  {

  snd_major=major;

=  snd_ecards_limit=car?ds_*limit;

  if(register_c?hrdev(major,"alsa",&snd_mfops)){

  snd_printk(KERN_ERR"unabletoregisternativemajordevicenumber%d/n&q¥uot;,major);

  return-EIO;

  }

  if(snd_info_ini?t?()<0){

  unregister_chrdev(major,"al∮sa");

  return-ENOMEM;

  }

  snd_info_minor_register();

  return0;

  }?

  register_chrdev中的参数major与之前创建pcm设备是device_create时的major是同一个,这样的结果是,当应用程序open设备文件/dev/snd/·pcmCxDxp时,会进入snd_fops的open回调?函数,我们将在下一节中讲述open的?过程。

  ∥4.3.2打开pcm设备

  从上一节中我们得知,open一个pcm设备时,将会调用snd_fops的open回调函数,我们先看看snd_fops的定义:

?  [c-sha?rp]v∵iewplaincopy?staticconststructfile№_operationssnd_fo?ps=

  {

  .owner=THIS_MODUL≦E,

  .open=snd_open

  };

?

  跟入snd_⊙open函数,它首先从inode中取出此设备号,然后以次设备号为索引,从snd_?minors全局数组中取出当初注册pc?m设备时填充的snd_minor结构(参看4.%1节的内容),然后从snd_m﹣inor结构中∞取出pcm设备的f_ops,并且把file->f_op替换为pcm设备?的f_ops,紧接着直接调用pcm设备的f_ops->open(),然后返回。因为file->f_op已经被替换,以后,应用程序的所有read/write/ioctl调用都会进入pcm设备自己的回调函数中,也就是4.2节中提到的snd_pcm_f_ops结构中定义的回调。

  [c-sharp]viewplaincopy+staticintsnd_open(structinode*inode,structfi ?le*file)

  {

  unsignedintminor=imin◎or(inode);

  structsnd_minor*mptr=NULL;

  conststructfile_operations*ol?d_fops;

  interr=0;㎞

  if(minor>=ARRAY_SIZE(snd_minors))

  -re/turn-E?NODEV;

  mutex_lock(&sound_mut?ex);

  mptr=snd_minors[mi℃nor];

  if(mptr==NUL∽L){

  mpt≒r=autoload_device(minor);

  if(≡!mpt?r){

  mutex_unl?ock(&sound_mutex);

  return-ENODEV?;

  }

  }

  old_fops=file->f_op;

  file-&?gt;f_op=fops_get(mptr->f_ops);

  if(file->f_op==′NULL){

  file->f_op=old_fops;

  err=-ENODEV;

  }

  mutex_unlock(&so¤und_mutex);

  if(err<0)

  returnerr;

  if(file->f_op-&gt?;open){

  err=file-&?gt;f_op->open(μino?de,file);

  if(err){∷

  fops_put(file-&gt?;f_op);

  file->f_op=fops_get(old_fops);

  }

  }

  fops_put(old_fops);

  r⊥etu?rnerr;

 ? }

  下面的序列±图展示了应用程序如何最终调用到snd_pcm_f_ops结构中的?回调函数:

  图4.3.2.1应用程序操作pcm设备

关于更多pcm设备内容,可以收藏本网页。中国女子篮球甲级联赛 LinuxALSA声卡驱动之三:PCM设备的创建

pcm设备pcm设备

关于作者: houswang

热门文章