IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Linux ALSA 之二:ALSA 声卡与设备 -> 正文阅读

[系统运维]Linux ALSA 之二:ALSA 声卡与设备

一、ALSA Sound 初始化

1、alsa_sound_init() 入口函数

在我们创建声卡前,ALSA Sound 会先有相应的初始化,即 sound.c 的入口函数,位于 sound/core/sound.c,主要是用于申请一个字符设备的主设备号(116),后面的 pcmcontrol 等逻辑设备都是这个主设备号下的次设备。

/*
 *  INIT PART
 */

static int __init alsa_sound_init(void)
{
	snd_major = major;
	snd_ecards_limit = cards_limit;

	//获取字符设备主设备号,即声卡的主设备号,其他声卡设备都是其下的次设备号
	if (register_chrdev(major, "alsa", &snd_fops)) {
		pr_err("ALSA core: unable to register native major device number %d\n", major);
		return -EIO;
	}

	//创建 snd_proc_root 目录为 /proc/sound
	if (snd_info_init() < 0) {
		unregister_chrdev(major, "alsa");
		return -ENOMEM;
	}
#ifndef MODULE
	pr_info("Advanced Linux Sound Architecture Driver Initialized.\n");
#endif
	return 0;
}

其中 struct snd_fops 定义如下,所有次设备共用一个 open 接口,

static const struct file_operations snd_fops =
{
	.owner =	THIS_MODULE,
	.open =		snd_open,
	.llseek =	noop_llseek,
};

snd_open 函数定义如下:

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;
	int err = 0;

	if (minor >= ARRAY_SIZE(snd_minors))
		return -ENODEV;
	mutex_lock(&sound_mutex);

	//获取到具体的声卡设备,即次设备比如 control、pcm 设备等
	mptr = snd_minors[minor];
	if (mptr == NULL) {
		mptr = autoload_device(minor);
		if (!mptr) {
			mutex_unlock(&sound_mutex);
			return -ENODEV;
		}
	}

	//获取次设备的 f_ops 文件结构体
	new_fops = fops_get(mptr->f_ops);
	mutex_unlock(&sound_mutex);
	if (!new_fops)
		return -ENODEV;

	//用次设备的 file_operations 替换
	replace_fops(file, new_fops);

	//执行该次设备的文件 open 函数
	if (file->f_op->open)
		err = file->f_op->open(inode, file);
	return err;
}

如上述注释所述,在 snd_open 函数中利用次设备号根据全局数组 snd_minors 找到相应的次设备 file_operations 并替换,最后调用相应次设备的 open 函数。(备注:很多设备框架都是使用这种做法)
snd_minors 是定义在 sound.c 中的全局变量,表示主设备号下的次设备比如 control、pcm 设备等,定义如下:

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 */
};

static struct snd_minor *snd_minors[SNDRV_OS_MINORS];

其中包含的设备类型如下,位于 include/sound/minors.h

enum {
	SNDRV_DEVICE_TYPE_CONTROL,
	SNDRV_DEVICE_TYPE_SEQUENCER,
	SNDRV_DEVICE_TYPE_TIMER,
	SNDRV_DEVICE_TYPE_HWDEP,
	SNDRV_DEVICE_TYPE_RAWMIDI,
	SNDRV_DEVICE_TYPE_PCM_PLAYBACK,
	SNDRV_DEVICE_TYPE_PCM_CAPTURE,
	SNDRV_DEVICE_TYPE_COMPRESS,
};

2、init_soundcore() 入口函数

该入口函数是 sound_core.c 的入口函数,主要用于创建 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
	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);

二、声卡结构体与创建、注册

1、struct snd_card

结构体 snd_card 是整个 ALSA 音频驱动最顶层的一个结构体,整个声卡的软件逻辑结构开始于该结构,几乎所有与声音相关的逻辑设备都是在 snd_card 的管理之下,声卡驱动的第一个动作就是创建一个 snd_card 结构体,其定位于 include/sound/core.h,如下:

/* main structure for soundcard */

struct snd_card {
	int number;			/* number of soundcard (index to
								snd_cards) */

	char id[16];			/* id string of this card */
	char driver[16];		/* driver name */
	char shortname[32];		/* short name of this soundcard */
	char longname[80];		/* name of this soundcard */ //会在具体驱动中设置,主要反映在/proc/asound/cards中
	char irq_descr[32];		/* Interrupt description */
	char mixername[80];		/* mixer name */
	char components[128];		/* card components delimited with
								space */
	struct module *module;		/* top-level module */

	void *private_data;		/* private data for soundcard */ //声卡的私有数据,可以在创建声卡时通过参数指定数据的大小
	void (*private_free) (struct snd_card *card); /* callback for freeing of
								private data */
	struct list_head devices;	/* devices */ //记录该声卡下所有逻辑设备的链表

	struct device ctl_dev;		/* control device */
	unsigned int last_numid;	/* last used numeric ID */
	struct rw_semaphore controls_rwsem;	/* controls list lock */
	rwlock_t ctl_files_rwlock;	/* ctl_files list lock */
	int controls_count;		/* count of all controls */
	int user_ctl_count;		/* count of all user controls */
	struct list_head controls;	/* all controls for this card */ //记录该声卡下所有控制单元的链表
	struct list_head ctl_files;	/* active control files */ //用于管理该card下的active的control设备

	struct snd_info_entry *proc_root;	/* root for soundcard specific files */
	struct snd_info_entry *proc_id;	/* the card id */
	struct proc_dir_entry *proc_root_link;	/* number link to real id */

	struct list_head files_list;	/* all files associated to this card */
	struct snd_shutdown_f_ops *s_f_ops; /* file operations in the shutdown
								state */
	spinlock_t files_lock;		/* lock the files for this card */
	int shutdown;			/* this card is going down */
	struct completion *release_completion;
	struct device *dev;		/* device assigned to this card */ //和card相关的设备
	struct device card_dev;		/* cardX object for sysfs */ //card用于在sys中显示,用于代表该card
	const struct attribute_group *dev_groups[4]; /* assigned sysfs attr */
	bool registered;		/* card_dev is registered? */
	wait_queue_head_t remove_sleep;

#ifdef CONFIG_PM
	unsigned int power_state;	/* power state */
	wait_queue_head_t power_sleep;
#endif

#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
	struct snd_mixer_oss *mixer_oss;
	int mixer_oss_change_count;
#endif
};

其中 snd_card 的 driver 字段保存着芯片的 ID 字符串,用户空间的 alsa-lib 会使用到该字符串,所以必须保证该 ID 的唯一性,shortname 字段更多地用于打印信息,longname 字段则会出现在 /proc/asound/cards 中。

2、声卡创建流程

1、创建 snd_card 实例

/**
 *  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);

通过如上描述的 snd_card_new() 函数可以创建一个声卡实例,参数 & 功能描述如上,api 主要 flow 如下,

snd_card_new
  --> 根据 extra_size 参数大小用 kzalloc 分配一个 snd_card,如果 extra_size > 0 则将 snd_card->private_data 指向 extra_size addr;
    --> 初始化 snd_card 结构体的必要字段,id、idx 及 card->card_dev;
  	  --> snd_ctl_create() [建立逻辑设备:Control]
  	    --> snd_info_card_create(card) [建立 proc 文件中的 info 节点:通常就是 /proc/asound/card0]

声卡的专用数据主要用于存放一些资源信息,例如中断资源、io资源、dma资源等,根据 extra_size 参数分有两种方式创建 snd_card,

  • 通过 snd_card_create 函数创建 private_data
// 创建 struct my_chip_priv 结构体
struct my_chip_priv {
	...
};

// 创建 snd_card
snd_card_new(dev, idx, xid, THIS_MODULE, sizeof(my_chip_priv), &card);

//从 snd_card->private_data 取数据
struct my_chip_priv *my_chip = card->private_data;
  • 自己创建 priv_buf,并将 snd_card->private_data 指向它
// 创建 struct my_chip_priv 结构体(内部成员包含 snd_card)
struct my_chip_priv {
	struct snd_card *card;
	...
};
struct snd_card *card;
struct my_chip_priv *my_chip;
// 手动创建
my_chip = kzalloc(sizeof(*chip), GFP_KERNEL);
// 创建 snd_card
snd_card_new(dev, idx, xid, THIS_MODULE, 0, &card);

// 专用数据记录snd_card实例
my_chip->card = card;

2、设置 snd_card 的 Driver ID 和 Name

strcpy(card->driver, "My Chip");
strcpy(card->shortname, "My Own Chip 123");
sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->ioport, chip->irq);

snd_card 的 driver 字段保存着芯片的 ID 字符串,user空间的 alsa-lib 会使用到该字符串,所以必须要保证该 ID 的唯一性。shortname 字段更多地用于打印信息,longname 字段则会出现在 /proc/asound/cards 中。

3、创建声卡的功能部件(逻辑设备)
还记得 snd_card 结构体中的 devices 字段吗?在注册声卡的时候会对该 devices 链表中的所有逻辑设备都进行注册,故在注册声卡前需要调用 snd_device_new() 来生成一个 snd_device 实例,并将该实例链接到 snd_card 的 devices 链表中。

通常,alsa-driver的已经提供了一些常用的部件的创建函数,而不必直接调用snd_device_new(),常见的如下:

	PCM  ----       snd_pcm_new()
 	RAWMIDI --    	snd_rawmidi_new()
	CONTROL --   	snd_ctl_create()
	TIMER   --      snd_timer_new()
	INFO    --      snd_card_proc_new()
	JACK    --      snd_jack_new()

API 详细见下方逻辑设备描述~

4、注册声卡

/**
 *  snd_card_register - register the soundcard
 *  @card: soundcard structure
 *
 *  This function registers all the devices assigned to the soundcard.
 *  Until calling this, the ALSA control interface is blocked from the
 *  external accesses.  Thus, you should call this function at the end
 *  of the initialization of the card.
 *
 *  Return: Zero otherwise a negative error code if the registration failed.
 */
int snd_card_register(struct snd_card *card);

注册声卡时需要 Call snd_card_register() 函数,如注释所述,该 api 内部会 register 所有 devices,关于该 API 的主要 Flow 如下:

snd_card_register
  --> device_add(&card->card_dev) 创建 /sys/devices 下的设备;
  	--> snd_device_register_all(card) 通过 snd_card 的 devices 链表,遍历所有的 snd_device,
  	--> 并且调用 snd_device 的 ops->dev_register() 来实现格子设备的注册;

经过上述的创建声卡步骤后,声卡的逻辑结构则如下图所示:
声卡的软件逻辑结构

3、声卡创建过程使用举例

/sound/arm/pxa2xx-ac97.c

三、声卡之 pcm 设备

四、声卡之 control 设备

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-07-03 11:10:24  更:2022-07-03 11:13:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 12:40:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码