Linux Alsa声卡驱动(2):代码分析

news/2024/11/26 3:41:30/

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

(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类似不再缀叙。


http://www.ppmy.cn/news/193487.html

相关文章

linux系统声卡安装教程,关于Linux系统声卡驱动的安装与配置

一般的声卡驱动是支持windows的&#xff0c;linux很少&#xff0c;所以安装声卡驱动很麻烦。 Linux下安装声卡驱动&#xff0c;用的是alsa&#xff0c;它就好像是万能的一样&#xff0c;可以支持很多类型的声卡&#xff0c;如&#xff1a; AC97 Codec ALC100,100P ALC200,200P …

关于Win10系统下VIA HD AUDIO威盛声卡没声音问题 - 有效解决办法

我现在用的是Win10系统&#xff0c;是从Win7升级而来的&#xff1b;但升级后用耳机时没声音&#xff0c;当时就想办法解决&#xff0c;用驱动大师&#xff0c;驱动精灵升级驱动都没解决&#xff1b;让公司IT设备支持人员也没搞定。后来今天&#xff0c;我一直网上找办法&#x…

VIA声卡升级驱动后没有控制台怎么办?

前几天换了个主板&#xff0c;换成了华硕H61&#xff0c;这个主板是VIA声卡。现在的主流声卡还是Realtek&#xff0c;用VIA声卡真的很少见。驱动精灵打了驱动之后不像以前的主板一样有一个“Realtek高清晰音频管理器”。我在网上找方法&#xff0c;聪明的网友给出了各种办法&am…

《基于Linux物联网综合项目》常见问题汇总fae

关于该课程说明 1&#xff09;本课程目标 通过web浏览器访问服务器&#xff0c;实现登录、注册、数据库操作、远程操控硬件、采集环境信息、远程监控、拍照、图片显示等功能。 将单片机、linux、html、摄像头、数据库等知识点融入到一个项目中。 2&#xff09;什么群体适合学…

git命令的使用

1. 查看文件 git cat-file -p 仓库路径下右键 Git Bash Here 打开git命令窗口&#xff1a; 复制某个文件的版本号&#xff1a; 粘贴到git命令窗口&#xff0c;会显示文件的提交信息&#xff1a; 查看 tree后面的版本号&#xff0c;则会看到详细提交信息&#xff1a; 查看hell…

类和对象【4】static成员、const对象、友元

全文目录 引言static成员static成员变量static成员函数 const对象友元友元函数友元类 总结 引言 通过前面的三篇文章&#xff0c;相信大家对类和对象已经有了一个基本的认识。 类和对象1&#xff08;初识&#xff09; 类和对象2&#xff08;默认成员函数&#xff09; 类和对象…

android q哪些手机型号,花粉看过来!华为公布8款首批适配安卓Q机型

原标题&#xff1a;花粉看过来&#xff01;华为公布8款首批适配安卓Q机型 昨天&#xff0c;一年一度的谷歌 I/O 2019开发者大会在美国加州正式召开&#xff0c;在大会上谷歌发布了多款重磅新品&#xff0c;其中最引人注意的还是万众期待的Android Q。据谷歌介绍&#xff0c;此次…

微博android升级7.000,华为 Android 7.0 升级计划曝光:G9 青春版 /Nova 也有份

日前&#xff0c;华为已经为 P9 和 Mate 8 开启了基于 Android 7.0 深度定制的 EMUI5 内测&#xff0c;同时荣耀方面也已经面向 V8 和 Mate8 的用户开启内测&#xff0c;相信有不少花粉用户已经体验到了牛轧糖的滋味。 现在&#xff0c;外媒给出了华为 Android 7.0 详细升级计划…