一:初始化/注册声卡设备

(1)注册ALSA

kernel\sound\core:sound.cint __init alsa_sound_init(void)
{... ...if (register_chrdev(major, "alsa", &snd_fops)) ... ...
}static const struct file_operations snd_fops =
{.owner =  THIS_MODULE,.open =        snd_open,.llseek = noop_llseek,
};static int snd_open(struct inode *inode, struct file *file)
{unsigned int minor = iminor(inode);struct snd_minor *mptr = NULL;const struct file_operations *new_fops;mptr = snd_minors[minor];... ...new_fops = fops_get(mptr->f_ops);... ...
}static struct snd_minor *snd_minors[SNDRV_OS_MINORS];struct snd_minor {int type;           /* SNDRV_DEVICE_TYPE_XXX */int card;            /* card number */int device;            /* device number */const struct file_operations *f_ops; /* file operations */void *private_data;        /* private data for f_ops->open */struct device *dev;        /* device for sysfs */struct snd_card *card_ptr;    /* assigned card instance */
};

通过追踪代码可以发现声卡初始化中最终调用的file_operations对象是通过snd_minors[minor]结构成员获取。继续追踪snd_minors[minor]来源。

int snd_register_device(int type, struct snd_card *card, int dev,const struct file_operations *f_ops,void *private_data, struct device *device)
{int minor;int err = 0;struct snd_minor *preg;if (snd_BUG_ON(!device))return -EINVAL;preg = kmalloc(sizeof *preg, GFP_KERNEL);if (preg == NULL)return -ENOMEM;preg->type = type;preg->card = card ? card->number : -1;preg->device = dev;preg->f_ops = f_ops;preg->private_data = private_data;preg->card_ptr = card;mutex_lock(&sound_mutex);minor = snd_find_free_minor(type, card, dev);if (minor < 0) {err = minor;goto error;}preg->dev = device;device->devt = MKDEV(major, minor);err = device_add(device);if (err < 0)goto error;snd_minors[minor] = preg;error:mutex_unlock(&sound_mutex);if (err < 0)kfree(preg);return err;
}

可以看到在snd_register_device声卡注册接口中对snd_minor对象preg的成员分别进行了赋值(设备类型,设备号,file_operations等等)。最后将其赋值给需要用到的snd_minors[minor]。后续我们就需要用snd_register_device接口来分别对不同设备进行注册,最终会通过总线将它们匹配起来。

(2)ALSA接口信息

kernel\sound\core:sound.cint __init alsa_sound_init(void)
{... ...if (snd_info_init() < 0) { ... ...
}kernel\sound\core:info.cint __init snd_info_init(void)
{snd_proc_root = snd_info_create_entry("asound", NULL);if (!snd_proc_root)return -ENOMEM;snd_proc_root->mode = S_IFDIR | 0555;snd_proc_root->p = proc_mkdir("asound", NULL);if (!snd_proc_root->p)goto error;... ...if (snd_info_version_init() < 0 ||snd_minor_info_init() < 0 ||snd_minor_info_oss_init() < 0 ||snd_card_info_init() < 0 ||snd_info_minor_register() < 0)goto error;return 0;error:snd_info_free_entry(snd_proc_root);return -ENOMEM;
}

同样在声卡入口函数中对ALSA的接口信息进行了初始化,并通过“proc_mkdir”内核函数在根目录proc下创建了“asound”目录作为存储路径,见下图:

card0/card7:其中0和7代表的是声卡号。其子目录包含了pcmp,pcmc

该节点是由snd_card_new接口调用创建,详细见后续对于snd_card_new的分析。

cards:列出系统中可用的,注册的声卡。

devices:列出系统card下所有注册的device,包括control,pcm,timer,seq等等。

pcm:系统的pcm设备,包括capture和playback。

timers:    描述一些ALSA相关的定时器信息。

version:  描述ALSA版本信息,详细可追踪接口snd_info_version_init

二:注册声卡功能部件

(1)control

kernel\sound\core:control.c/** registration of the control device*/
static int snd_ctl_dev_register(struct snd_device *device)
{struct snd_card *card = device->device_data;return snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,&snd_ctl_f_ops, card, &card->ctl_dev);
}/** create control core:* called from init.c*/
int snd_ctl_create(struct snd_card *card)
{static struct snd_device_ops ops = {.dev_free = snd_ctl_dev_free,.dev_register =    snd_ctl_dev_register,.dev_disconnect = snd_ctl_dev_disconnect,};int err;if (snd_BUG_ON(!card))return -ENXIO;if (snd_BUG_ON(card->number < 0 || card->number >= SNDRV_CARDS))return -ENXIO;snd_device_initialize(&card->ctl_dev, card);dev_set_name(&card->ctl_dev, "controlC%d", card->number);err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);if (err < 0)put_device(&card->ctl_dev);return err;
}

可以看到ctl部件通过snd_ctl_create函数中的snd_device_ops成员对象间接调用snd_ctl_dev_register-->>snd_register_device接口来注册相应接口。

下面逐一分析snd_ctl_create的内容。

(1.1)snd_device_initialize

kernel\sound\core:init.c/*** snd_device_initialize - Initialize struct device for sound devices* @dev: device to initialize* @card: card to assign, optional*/
void snd_device_initialize(struct device *dev, struct snd_card *card)
{device_initialize(dev);if (card)dev->parent = &card->card_dev;dev->class = sound_class;dev->release = default_release;
}
EXPORT_SYMBOL_GPL(snd_device_initialize);

device_initialize(dev)接口用来准备后续用到的device数据,重点在于sound_class

kernel\sound:sound_core.cstruct class *sound_class;
EXPORT_SYMBOL(sound_class);static char *sound_devnode(struct device *dev, umode_t *mode)
{if (MAJOR(dev->devt) == SOUND_MAJOR)return NULL;return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));
}static int __init init_soundcore(void)
{int rc;rc = init_oss_soundcore();if (rc)return rc;sound_class = class_create(THIS_MODULE, "sound");if (IS_ERR(sound_class)) {cleanup_oss_soundcore();return PTR_ERR(sound_class);}sound_class->devnode = sound_devnode;return 0;
}subsys_initcall(init_soundcore);

分析sound_core.c文件可知最终会创建“snd/%s”“/dev”目录下,“%s”为要注册的功能部件,后续会讲到。

(1.2)dev_set_name(&card->ctl_dev, "controlC%d", card->number)

kernel\drivers\base: core.c/*** dev_set_name - set a device name* @dev: device* @fmt: format string for the device's name*/
int dev_set_name(struct device *dev, const char *fmt, ...)
{va_list vargs;int err;va_start(vargs, fmt);err = kobject_set_name_vargs(&dev->kobj, fmt, vargs);va_end(vargs);return err;
}
EXPORT_SYMBOL_GPL(dev_set_name);

分析dev_set_name可了解到该函数用来设置声卡功能部件的名称“controlC%d”

(1.3)snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops)

kernel\sound\core:device.c/*** snd_device_new - create an ALSA device component* @card: the card instance* @type: the device type, SNDRV_DEV_XXX* @device_data: the data pointer of this device* @ops: the operator table** Creates a new device component for the given data pointer.* The device will be assigned to the card and managed together* by the card.** The data pointer plays a role as the identifier, too, so the* pointer address must be unique and unchanged.** Return: Zero if successful, or a negative error code on failure.*/
int snd_device_new(struct snd_card *card, enum snd_device_type type,void *device_data, struct snd_device_ops *ops)
{struct snd_device *dev;struct list_head *p;if (snd_BUG_ON(!card || !device_data || !ops))return -ENXIO;dev = kzalloc(sizeof(*dev), GFP_KERNEL);if (!dev)return -ENOMEM;INIT_LIST_HEAD(&dev->list);dev->card = card;dev->type = type;dev->state = SNDRV_DEV_BUILD;dev->device_data = device_data;dev->ops = ops;/* insert the entry in an incrementally sorted list */list_for_each_prev(p, &card->devices) {struct snd_device *pdev = list_entry(p, struct snd_device, list);if ((unsigned int)pdev->type <= (unsigned int)type)break;}list_add(&dev->list, p);return 0;
}
EXPORT_SYMBOL(snd_device_new);

最终通过该接口完成声卡功能部件ctl节点的创建,如下图所示:

继续追踪会发现snd_ctl_create接口是由snd_card_new调用,其中还涉及开篇讲到的card info的创建snd_info_card_create。

kernel\sound\core:init.c/***  snd_card_new - create and initialize a soundcard structure*  @parent: the parent device object*  @idx: card index (address) [0 ... (SNDRV_CARDS-1)]*  @xid: card identification (ASCII string)*  @module: top level module for locking*  @extra_size: allocate this extra size after the main soundcard structure*  @card_ret: the pointer to store the created card instance**  Creates and initializes a soundcard structure.**  The function allocates snd_card instance via kzalloc with the given*  space for the driver to use freely.  The allocated struct is stored*  in the given card_ret pointer.**  Return: Zero if successful or a negative error code.*/
int snd_card_new(struct device *parent, int idx, const char *xid,struct module *module, int extra_size,struct snd_card **card_ret)
{struct snd_card *card;... ...device_initialize(&card->card_dev);card->card_dev.parent = parent;card->card_dev.class = sound_class;card->card_dev.release = release_card_device;card->card_dev.groups = card->dev_groups;card->dev_groups[0] = &card_dev_attr_group;err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);if (err < 0)goto __error;snprintf(card->irq_descr, sizeof(card->irq_descr), "%s:%s",dev_driver_string(card->dev), dev_name(&card->card_dev));/* the control interface cannot be accessed from the user space until *//* snd_cards_bitmask and snd_cards are set with snd_card_register */err = snd_ctl_create(card);if (err < 0) {dev_err(parent, "unable to register control minors\n");goto __error;}err = snd_info_card_create(card);if (err < 0) {dev_err(parent, "unable to create card info\n");goto __error_ctl;}*card_ret = card;return 0;__error_ctl:snd_device_free_all(card);__error:put_device(&card->card_dev);return err;
}
EXPORT_SYMBOL(snd_card_new);

综上,后续对于ctl声卡部件的创建,我们直接调用snd_card_new即可。

(2)pcm

kernel\sound\core:pcm.cstatic int snd_pcm_dev_register(struct snd_device *device)
{struct snd_pcm *pcm;pcm = device->device_data;mutex_lock(&register_mutex);err = snd_pcm_add(pcm);if (err)goto unlock;for (cidx = 0; cidx < 2; cidx++) {... .../* register pcm */err = snd_register_device(devtype, pcm->card, pcm->device,&snd_pcm_f_ops[cidx], pcm,&pcm->streams[cidx].dev);if (err < 0) {list_del_init(&pcm->list);goto unlock;}for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)snd_pcm_timer_init(substream);}pcm_call_notify(pcm, n_register);unlock:mutex_unlock(&register_mutex);return err;
}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 = {.dev_free = snd_pcm_dev_free,.dev_register =    snd_pcm_dev_register,.dev_disconnect = snd_pcm_dev_disconnect,};static struct snd_device_ops internal_ops = {.dev_free = snd_pcm_dev_free,};if (snd_BUG_ON(!card))return -ENXIO;if (rpcm)*rpcm = NULL;pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);if (!pcm)return -ENOMEM;pcm->card = card;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));err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK,playback_count);if (err < 0)goto free_pcm;err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count);if (err < 0)goto free_pcm;err = snd_device_new(card, SNDRV_DEV_PCM, pcm,internal ? &internal_ops : &ops);if (err < 0)goto free_pcm;if (rpcm)*rpcm = pcm;return 0;free_pcm:snd_pcm_free(pcm);return err;
}

可以看到与ctl部件一致,pcm也是通过“_snd_pcm_new”接口函数的成员对象snd_device_ops来间接调用。阅读代码可知道设备的节点"pcmC%iD%i%c"是在snd_pcm_new_stream中创建。

/*** 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** Creates a new stream for the pcm.* The corresponding stream on the pcm must have been empty before* calling this, i.e. zero must be given to the argument of* snd_pcm_new().** Return: Zero if successful, or a negative error code on failure.*/
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];struct snd_pcm_substream *substream, *prev;... ...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');... ...
}
EXPORT_SYMBOL(snd_pcm_new_stream);

最终声卡pcm部件的创建是由接口snd_pcm_new来完成。

/*** 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** Creates a new PCM instance.** The pcm operators have to be set afterwards to the new instance* via snd_pcm_set_ops().** Return: Zero if successful, or a negative error code on failure.*/
int snd_pcm_new(struct snd_card *card, const char *id, int device,int playback_count, int capture_count, struct snd_pcm **rpcm)
{return _snd_pcm_new(card, id, device, playback_count, capture_count,false, rpcm);
}
EXPORT_SYMBOL(snd_pcm_new);

(3)其他

timer, seq于ctl和pmc类似不再缀叙。

Linux Alsa声卡驱动(2):代码分析相关推荐

  1. linux alsa声卡驱动原理分析- 设备打开过程和数据流程,linux alsa声卡驱动原理分析解析- 设备打开过程跟数据流程资料.ppt...

    linux alsa声卡驱动原理分析解析- 设备打开过程跟数据流程资料 Linux ALSA声卡驱动原理分析 -设备打开过程和数据流程;目 录;目 录;一.导 读;目 录;二.ALSA架构简介;二. ...

  2. Linux ALSA声卡驱动之四:Codec 以及Codec_dai

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  3. Linux ALSA声卡驱动之五:Machine 以及ALSA声卡的注册

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  4. Linux ALSA声卡驱动之三:Platform之Cpu_dai

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  5. Linux ALSA声卡驱动之八:ASoC架构中的Platform

    1.  Platform驱动在ASoC中的作用 前面几章内容已经说过,ASoC被分为Machine,Platform和Codec三大部件,Platform驱动的主要作用是完成音频数据的管理,最终通过C ...

  6. Linux ALSA声卡驱动之二:Platform

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  7. Linux ALSA声卡驱动之七:录音(Capture) 调用流程

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  8. Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  9. Linux ALSA声卡驱动之六:ASoC架构中的Machine

    前面一节的内容我们提到,ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上 ...

  10. Linux ALSA声卡驱动之七:ASoC架构中的Codec

    1.  Codec简介 在移动设备中,Codec的作用可以归结为4种,分别是: 对PCM等信号进行D/A转换,把数字的音频信号转换为模拟信号 对Mic.Linein或者其他输入源的模拟信号进行A/D转 ...

最新文章

  1. PMP考试错题记录(2)
  2. ACCP8.0Y2Web前端框架与移动应用开发第5章Bootstrap制作微票儿首页
  3. HyperLedger Fabric 错误记录
  4. 在Microsoft Teams中的Visio协作
  5. HBase1.2.3 数据模型
  6. Android Studio 2.2:新布局、Firebase、OpenJDK以及Java 8
  7. 称洗澡时突遭电击 承租人起诉“自如”索赔77735元
  8. 使用Python编写一个聪明的尼姆游戏
  9. win8 无法打开任务管理器
  10. PLC可编程控制器的结构和工作原理
  11. 通信原理及系统系列7—— 什么是码间串扰
  12. pytorch —— 正则化之weight_decay
  13. docke网络之bridge、host、none
  14. 【JAVA】关于自动化测试所需要学习的java基础知识笔记
  15. U盘在别人电脑上正常显示,插在自己电脑读不出来(只显示CD驱动器)
  16. 消失的信用卡(2)——0-days
  17. 恭敬观世音菩萨( 远离色欲)
  18. C#语言入门、xamarin基础、.NET MAUI全栈开发技术综合笔记
  19. 浏览器趋势2016年4月:三星的浪潮
  20. 千万别错过!C/C++实现经典围棋大战,秒杀挫败柯洁的AlphaGo

热门文章

  1. 张跃平教授:无线电科学与技术中的因子4
  2. leaflet + proj4 加载ARCGIS CGCS2000地理坐标栅格瓦片
  3. Python进阶编程问题集
  4. 怎么用ALOAM跑kitti数据集的bag包
  5. 2021SC@SDUSC基于人工智能的多肽药物分析问题(十三)
  6. kettle详细使用oracle教程,Kettle入门教程(详细介绍控件使用方法)_kettle详细使用教程,kettle控件介绍...
  7. MIKE 21 教程 1.4 网格搭建界面介绍之高程数据输入与网格导出 (Mesh Generator 工具)
  8. Word分词标题 和JDK的contain的测试日志显示本地的笔记本 的效率基本上都是1秒以上,显然是Word分词标题 占优势,可是服务器上JDK与Word分析显然无区别,针对8W数据的检索
  9. python匹配ip地址
  10. 智能辅助系统在配电室内的施工方案 安装位置