Linux 音频子系统分析2

Linux 音频子系统分析2(基于Linux6.6)---ALSA Framework介绍

一、ALSA架构

1.1、ALSA架构概述

ALSA 的架构可以分为几个关键组件,它们协同工作以提供全面的音频功能:

1. 硬件抽象层 (Hardware Abstraction Layer, HAL)
  • ALSA 通过硬件抽象层将不同音频硬件的细节封装起来,为上层应用提供统一的接口。这样应用程序和用户空间的音频工具就可以与硬件解耦,避免了与具体硬件的耦合。
2. 内核驱动 (Kernel Driver)
  • ALSA 内核驱动与硬件设备进行直接交互。每个音频硬件通常有一个相应的内核模块来支持 ALSA 的音频接口。音频设备通过设备文件与用户空间进行通信。常见的音频设备如声卡、USB 音频设备、虚拟音频设备等,都可以通过相应的 ALSA 驱动进行管理。
  • 驱动层包含以下几类:
    • PCM 驱动:处理音频数据的播放与录制。
    • MIDI 驱动:处理 MIDI 数据。
    • 混音驱动:提供多个音频流的混合功能。
    • 音量控制:管理音量、静音等操作。
3. ALSA库 (ALSA Library)
  • ALSA 库提供了一个用户空间的接口,简化了应用程序与 ALSA 驱动的交互。应用程序可以通过该库来控制音频设备,进行音频播放、录制等操作。
  • 主要库函数包括:snd_pcm_*(用于PCM数据流的控制),snd_mixer_*(用于音量和音频效果控制),snd_seq_*(用于 MIDI 数据流)。
4. 音频应用接口 (Audio Application Interface)
  • ALSA 提供了对不同音频应用程序的支持。应用程序可以通过 ALSA API(如 snd_pcmsnd_mixer 等)来进行音频数据的读取、写入、音量控制等操作。常见的音频应用程序包括音频播放器、录音软件、音频编辑器等。
  • PCM(Pulse Code Modulation):ALSA 支持 PCM 数据流,是大多数音频应用程序的基本音频格式。它允许应用程序直接读取或写入数字音频数据流。
  • MIDI:ALSA 提供对 MIDI 数据的支持,使得应用程序可以通过 MIDI 协议与外部 MIDI 设备进行通信。
5. 声卡设备接口
  • ALSA 提供了标准的设备文件接口,通常位于 /dev/snd/ 目录下。常见的设备文件包括:
    • /dev/snd/pcmC0D0p:音频播放设备(PCM playback)
    • /dev/snd/pcmC0D0c:音频录制设备(PCM capture)
    • /dev/snd/controlC0:控制设备(例如音量控制)
    • /dev/snd/seq:MIDI 设备文件

这些设备文件通过用户空间应用访问硬件进行音频数据的读写。

6. ALSA Mixer
  • ALSA 提供了一个音量混音器接口,允许用户控制音量、增益、音频效果等。混音器允许管理多个音频流的混合,支持硬件和软件混音。
  • amixer 命令行工具可用于与混音器交互,调节音量、选择输入源等。
7. ALSA 的多通道支持
  • ALSA 提供了对多声道音频的支持,允许在同一时间进行多路音频数据的传输。通过 PCM 驱动,用户可以同时播放或录制多个音频通道(例如,5.1 声道或 7.1 声道音频)。
  • 支持硬件和软件的多通道配置。
8. 中间层(例如,PulseAudio 或 JACK)
  • 虽然 ALSA 本身是一个低层次的音频框架,但许多现代音频应用和桌面环境使用中间层(例如 PulseAudio 或 JACK)来提供更高层次的音频管理功能。
    • PulseAudio:通常作为 ALSA 的上层音频服务器,提供更高层次的音频流管理、音频设备的虚拟化以及更丰富的网络音频功能。
    • JACK:一个专业级的低延迟音频服务器,适用于实时音频应用,特别是音频制作和实时音频处理。

1.2、ALSA 工作流程示意

  1. 应用程序:通过 ALSA 库与内核驱动进行交互,发出音频播放、录制等操作请求。
  2. ALSA 库:将应用程序的请求转换为适当的系统调用,管理 PCM 数据流、MIDI、音量控制等。
  3. 内核驱动:驱动与硬件音频设备通信,将音频数据传输到硬件或从硬件中读取音频数据。
  4. 硬件设备:执行实际的音频数据传输,输出声音或接收音频信号。

1.3、ALSA 特性总结

  • 低延迟:ALSA 提供较低的音频延迟,非常适合实时音频应用。
  • 硬件兼容性:支持多种音频硬件,包括 PCI 声卡、USB 声卡、蓝牙音频设备等。
  • 多通道音频:ALSA 支持多声道音频数据流的处理,可以处理高达 7.1 声道的音频。
  • 灵活的音频控制:ALSA 提供了对音量、混音、增益、特效等音频控制的支持。
  • MIDI 支持:ALSA 支持 MIDI 数据的传输,适用于音频制作和合成器控制。

二、ALSA框架

  • User空间:主要由Alsa Libray API对应用程序提供统一的API接口,各个APP应用程序只要调用 alsa-lib 提供的 API接口来实现放音、录音、控制。现在提供了两套基本的库,tinyalsa是一个简化的alsa-lib库,现在Android的系统中主要使用它。

  • Kernel空间

组件描述功能重用性
ASOC CoreALSA SoC(System on Chip)核心框架提供通用方法和数据结构,用于音频设备驱动的实现,为音频驱动提供 ALSA Driver API。为音频驱动提供标准框架,能被多种硬件平台使用。
ALSA CoreALSA 核心层向上提供逻辑设备(PCM、CTL、MIDI、TIMER 等)的系统调用,向下驱动硬件设备,管理音频硬件和虚拟设备之间的交互。ALSA 核心层为上层应用提供了统一的接口,支持多种硬件设备。
Hardware Driver音频硬件设备驱动包含 Machine、Platform 和 Codec 三大部分,提供 ALSA Driver API,进行音频设备的初始化和工作流程管理。需要根据特定的硬件平台来实现,但其中的 Machine 和 Platform 类可以在不同硬件之间共享。
Codec 类编解码芯片驱动提供音频控制接口、音频读写 I/O 接口,支持 DAPM(动态音频路径管理)。Codec 驱动是平台无关的,并可以用于如 BT、FM、MODEM 模块。可在不同的 SoC(System on Chip)中复用,增强了驱动模块的灵活性。
Platform 类SOC 平台驱动包含音频 DMA 引擎驱动、数字接口驱动(I2S、AC97、PCM)及平台特定的音频 DSP 驱动。可在多个 Machine 类中重用,不依赖特定机器。
Machine 类作为 Codec 和 Platform 之间的桥梁在 Codec 和 Platform 之间建立联系,指定具体的 Platform 和 Codec,实现这两者的协调工作。每种机器(例如设备或 SoC)需要指定自己的 Machine 类,因此 Machine 类为硬件平台特定的实现。

2.1、Hardware Driver三者关系:

组件描述功能关键特性重用性
Platform指某款 SoC 平台的音频模块,如 QCOM、OMAP、Amlogic、ATMEL 等。可以进一步细分为 CPU DAI 和 PCM DMA。1. CPU DAI:负责将音频数据通过 I2S 或 PCM 总线从 TX FIFO 传输到 Codec(或反向)。<br>2. PCM DMA:负责将音频数据从 DMA 缓冲区搬运到 I2S TX FIFO。1. CPU DAI:通常指 SoC 的 I2S、PCM 总线控制器。<br>2. PCM DMA:在一些场景下,某些硬件如调制解调器(modem)可能不需要 DMA。<br>3. 通过 snd_soc_register_platform() 注册音频 DMA 驱动。CPU DAIPCM DMA 是特定于平台的,因此不能在不同硬件间通用;但通常可以通过修改 dai_linkplatform_name 来实现一定程度的复用。
Codec音频编解码器,处理数字和模拟音频信号转换。1. DAC(数字到模拟转换):将数字音频信号转换为模拟信号,输出到扬声器或耳机。<br>2. ADC(模拟到数字转换):将模拟信号转换为数字信号。<br>3. 提供多种音频处理部件,如混音器、音量控制、EQ、DSP 等。1. 常见功能:AIF(音频接口)、DAC、ADC、Mixer、PGA(功率放大器)、Line-in/out 等。<br>2. 高端 Codec 芯片支持更多处理模块:EQ、DSP、AGC(自动增益控制)、噪声抑制等。Codec 驱动 是平台无关的,可在多种 SoC 平台上复用。
Machine某款设备的音频驱动配置,通过 dai_link 将 CPU DAI、Codec DAI 等音频接口串联起来,最终注册 snd_soc_card1. 配置并连接多个音频接口(如 CPU DAI、Codec DAI 等),并指定硬件特性。<br>2. 注册 snd_soc_card,使音频硬件配置生效。<br>3. 处理硬件特性,如 GPIO 控制、时钟源选择等。1. 硬件特性:例如不同平台与 Codec 的差异、DAI 接口连接方式、GPIO 控制 Amplifier、耳机插拔检测、时钟源(MCLK 或 External-OSC)等。<br>2. 针对特定硬件平台配置,因此不具有通用性。Machine 配置是特定于某款设备或 SoC 平台的,几乎不可重用。

 

三、ALSA核心层

  核心层为用户空间提供逻辑设备接口, 同时为驱动提供接口来驱动硬件设备, 主要位于sound/core目录下。



分析是谁调用snd_register_device_for_dev函数来注册sound设备的。

3.1、字符设备注册

sound/core/sound.c 

/*
 *  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;
	}
	if (snd_info_init() < 0) {
		unregister_chrdev(major, "alsa");
		return -ENOMEM;
	}

#ifdef CONFIG_SND_DEBUG
	sound_debugfs_root = debugfs_create_dir("sound", NULL);
#endif
#ifndef MODULE
	pr_info("Advanced Linux Sound Architecture Driver Initialized.\n");
#endif
	return 0;
}

文件操作是snd_fops,snd_open接口比较重要,snd_open接口通过文件节点inode得到了次设备号,通过snd_minors数组得到对应的声音逻辑设备的文件操作,调用对应的open接口,并调用fops_put替换成了对应的逻辑设备的文件操作。

snd_info_init创建proc目录下的asound,并在该目录下分别建立version EVM card0 cards device pcm timers,用来查看相关的信息。

sound/core/sound.c 

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

alsa驱动设备文件结构:

  • controlC0 :用于声卡的控制,例如通道选择,混音,麦克风的控制等
  • pcmC0D0c:用于录音的pcm设备
  • pcmC0D0p:用于播放的pcm设备
  • seq :音序器
  • timer :定时器

其中, C0D0代表的是声卡0中的设备0, pcmC0D0c最后一个c代表capture, pcmC0D0p最后一个p代表playback,这些都是alsa-driver中的命名规则。

  • cards : 可显示系统中存在多少个声卡
  • card0 : 代表某个声卡
  • devices : 可显示系统中存在多少个逻辑设备

/proc/asound/card0:该节点提供该card的一些info.

3.2、数据结构

在 ALSA(Advanced Linux Sound Architecture)中,音频驱动的结构通常由多个层次组成,每个层次都有对应的关键数据结构。这些数据结构用于描述声卡、设备、PCM 流、控制以及相关的操作和功能。下面是一些常见的结构及其描述:

主要数据结构解释:

数据结构描述作用
snd_card表示一个声卡实例,包含多个声卡设备。它是 ALSA 中的顶层数据结构,负责表示整个声卡,包含该声卡的硬件和设备信息。每个声卡通常由多个设备组成。通过 snd_card_new() 创建并注册。
snd_device表示一个声卡设备部件。声卡设备可以有多个不同的部件(例如 PCM、控制设备等),每个部件由 snd_device 数据结构表示。它与硬件设备直接对应。
snd_pcm表示一个 PCM 设备,通常用于播放和录制音频数据。它是声卡的一个设备,用于管理 PCM 流。用于描述 PCM 接口和硬件的功能。通常包括播放(playback)和录制(capture)两个方向。snd_pcm 通过 snd_pcm_new() 创建并与硬件关联。
snd_control表示控制设备,通常用于控制声卡的一些设置,如音量控制、均衡器设置等。控制设备通过 snd_control_new() 创建,允许应用程序与硬件交互,进行音量调节、静音控制等操作。它是 ALSA 音频驱动中非常重要的组成部分。
snd_pcm_str表示 PCM 流,分为播放流(playback)和捕获流(capture)。snd_pcm 内部,PCM 流(snd_pcm_str)用于描述音频数据流,通常分为播放和录制两个子流。这两个子流分别处理音频播放和录制的不同数据方向。
snd_pcm_substream表示 PCM 子流,通常是 PCM 流的一个方向(播放或录制)。一个 snd_pcm_substream 对象对应一个 PCM 流的子流,用于描述播放流(playback)或录制流(capture)。它保存与音频数据流相关的具体信息,如缓冲区、硬件状态等。
snd_pcm_opsPCM 流操作集,包含与 PCM 设备交互的操作函数。这是一个函数指针集合,用于处理 PCM 设备的常见操作,如打开、关闭、准备、写入、读取等操作。每个 PCM 设备都需要实现该操作集。

各个数据结构的详细解释:

  1. snd_card(声卡实例):

    • 功能snd_card 是 ALSA 中用于表示一个声卡的顶层结构,它通常包含多个音频设备(如 PCM 设备、控制设备等)。通过 snd_card_register() 注册声卡实例。每个声卡实例可能有多个设备和子设备。
    • 成员:它包含有关声卡的各种信息,如名称、ID、功能等,还包含对其他设备的引用。
  2. snd_device(设备部件):

    • 功能snd_device 用于表示声卡中的每个具体部件或设备,例如 PCM 设备、控制设备、MIDI 设备等。它通常由声卡的其他设备驱动来管理。
    • 成员:它包含设备类型、状态等信息,可以通过 snd_device_new() 创建。
  3. snd_pcm(PCM 设备):

    • 功能snd_pcm 用于描述 PCM 接口及其相关硬件操作,如音频播放和录制。它表示一个具体的音频流(playback 或 capture)以及其操作。
    • 成员:包括 PCM 流的配置(采样率、位深度等)、缓冲区、状态和相关的操作函数等。
  4. snd_control(控制设备):

    • 功能:控制设备用于设置和管理声卡的控制接口,如音量调节、静音、均衡器设置等。snd_control 通过接口来控制硬件的各种参数。
    • 成员:包括与控制相关的各种操作,如设置、获取音量、修改均衡器设置等。
  5. snd_pcm_str(PCM 流):

    • 功能snd_pcm_str 用于描述 PCM 设备中的一个音频数据流。每个 PCM 设备有两个方向:播放(playback)和录制(capture),这两个方向由不同的 snd_pcm_str 表示。
    • 成员:包含与 PCM 流相关的信息,如数据缓冲区、数据流方向(播放或录制)、采样率等。
  6. snd_pcm_substream(PCM 子流):

    • 功能snd_pcm_substream 用于描述单一的 PCM 流(即播放或录制),每个子流对应一个方向的数据传输。一个 snd_pcm 设备通常会有多个 snd_pcm_substream
    • 成员:包括与该子流相关的缓冲区、状态信息、操作函数等。
  7. snd_pcm_ops(PCM 流操作集):

    • 功能snd_pcm_ops 是 PCM 流的操作集合,包含处理 PCM 流操作的函数指针。例如,用于打开、关闭、准备、读取和写入 PCM 流等操作。
    • 成员:包括许多函数指针,例如 openclosepreparetriggerhw_paramshw_free 等。

snd_card主要字段如下:

include/sound/core.h 

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 */
	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 */
	size_t user_ctl_alloc_size;	// current memory allocation by user controls.
	struct list_head controls;	/* all controls for this card */
	struct list_head ctl_files;	/* active control files */
#ifdef CONFIG_SND_CTL_FAST_LOOKUP
	struct xarray ctl_numids;	/* hash table for numids */
	struct xarray ctl_hash;		/* hash table for ctl id matching */
	bool ctl_hash_collision;	/* ctl_hash collision seen? */
#endif

	struct snd_info_entry *proc_root;	/* root for soundcard specific files */
	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 */
	struct device card_dev;		/* cardX object for sysfs */
	const struct attribute_group *dev_groups[4]; /* assigned sysfs attr */
	bool registered;		/* card_dev is registered? */
	bool managed;			/* managed via devres */
	bool releasing;			/* during card free process */
	int sync_irq;			/* assigned irq, used for PCM sync */
	wait_queue_head_t remove_sleep;

	size_t total_pcm_alloc_bytes;	/* total amount of allocated buffers */
	struct mutex memory_mutex;	/* protection for the above */
#ifdef CONFIG_SND_DEBUG
	struct dentry *debugfs_root;    /* debugfs root for card */
#endif

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

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

snd_device主要字段如下:

include/sound/core.h 

struct snd_device {
	struct list_head list;		/* list of registered devices */
	struct snd_card *card;		/* card which holds this device */
	enum snd_device_state state;	/* state of the device */
	enum snd_device_type type;	/* device type */
	void *device_data;		/* device structure */
	const struct snd_device_ops *ops;	/* operations */
};

snd_pcm主要字段如下:

include/sound/pcm.h 

struct snd_pcm {
	struct snd_card *card;
	struct list_head list;
	int device; /* device number */
	unsigned int info_flags;
	unsigned short dev_class;
	unsigned short dev_subclass;
	char id[64];
	char name[80];
	struct snd_pcm_str streams[2];
	struct mutex open_mutex;
	wait_queue_head_t open_wait;
	void *private_data;
	void (*private_free) (struct snd_pcm *pcm);
	bool internal; /* pcm is for internal use only */
	bool nonatomic; /* whole PCM operations are in non-atomic context */
	bool no_device_suspend; /* don't invoke device PM suspend */
#if IS_ENABLED(CONFIG_SND_PCM_OSS)
	struct snd_pcm_oss oss;
#endif
};

3.3、APIs

该层主要接口如下:

/* 创建和初始化声卡结构体 */
int snd_card_new(struct device *parent, int idx, const char *xid, struct module *, int extra_size, struct snd_card **card_ret);
/* 释放声卡结构体 */
int snd_card_free(struct snd_card * card);
/* 注册声卡 */
int snd_card_register(struct snd_card * card);

/* 创建声卡设备部件, 通常由snd_pcm_new和snd_card_new自动完成 */
int snd_device_new(struct snd_card *, enum snd_device_type type, void *device_data, struct snd_device_ops *ops);
/* 注册声卡设备部件, 通常由snd_card_register自动完成 */
int snd_device_register(struct snd_card *card, void *device_data);

/* 创建PCM设备 */
int snd_pcm_new(struct snd_card *, const char *id, int device, int playback_count, int capture_count, struct snd_pcm **rpcm);
/* 创建PCM流, 通常snd_pcm_new会自动创建capture和playback两个PCM流 */
int snd_pcm_new_stream(struct snd_pcm * pcm, int stream, int substream_count);
/* 设置PCM设备操作集 */
void snd_pcm_set_ops(struct snd_pcm *, int direction, const struct snd_pcm_ops *ops);

四、card的创建与注册

  struct snd_card可以说是整个ALSA音频驱动最顶层的一个结构, 整个声卡的软件逻辑结构开始于该结构, 几乎所有与声音相关的逻辑设备都是在snd_card的管理之下, 声卡驱动的第一个动作通常就是创建一个snd_card结构体。

4.1、snd_card_new

┌──────────────────────────────────────────┐
│ 1. 分配 snd_card + extra_size 空间大小  │
└──────────────────────────────────────────┘
                │
                ▼
┌────────────────────────────────────────────┐
│ 2. 如果 extra_size > 0,                   │
│    将 private_data 指向 extra_size 首地址 │
└────────────────────────────────────────────┘
                │
                ▼
┌────────────────────────────────────────────┐
│ 3. 如果指定了 xid,                       │
│    将 xid 拷贝至 snd_card::id             │
└────────────────────────────────────────────┘
                │
                ▼
┌────────────────────────────────────────────┐
│ 4. 根据 idx 获取可用的声卡索引并赋值给    │
│    snd_card::number                      │
└────────────────────────────────────────────┘
                │
                ▼
┌────────────────────────────────────────────┐
│ 5. 将 parent、module 赋值给 snd_card::dev │
│    和 snd_card::module                    │
└────────────────────────────────────────────┘
                │
                ▼
┌────────────────────────────────────────────┐
│ 6. 初始化链表 snd_card::devices、         │
│    snd_card::controls、                   │
│    snd_card::ctl_files、                  │
│    snd_card::files_list                   │
└────────────────────────────────────────────┘
                │
                ▼
┌────────────────────────────────────────────┐
│ 7. 调用 device_initialize() 初始化 snd_card::card_dev │
│    并设置相关成员变量用于 sysfs          │
└────────────────────────────────────────────┘
                │
                ▼
┌────────────────────────────────────────────┐
│ 8. 调用 snd_ctl_create() 创建控制接口    │
└────────────────────────────────────────────┘
                │
                ▼
┌────────────────────────────────────────────┐
│ 8.1 调用 snd_device_initialize() 初始化  │
│     snd_card::ctl_dev, 并设置相关成员变量 │
└────────────────────────────────────────────┘
                │
                ▼
┌────────────────────────────────────────────┐
│ 8.2 调用 snd_device_new(SNDRV_DEV_CONTROL, │
│     ops) 创建声卡控制设备部件            │
└────────────────────────────────────────────┘
                │
                ▼
┌────────────────────────────────────────────┐
│ 9. 调用 snd_info_card_create() 创建 proc  │
│    对应的文件系统                        │
└────────────────────────────────────────────┘

4.2、snd_card_register

函数的主要目的是注册card下面挂载的所有device; 另外只有该函数成功返回后, 用户空间才能通过control interface来访问底层。

┌──────────────────────────────────────────────────┐
│ 1. 检查 snd_card 是否为空,若为空则返回错误  │
└──────────────────────────────────────────────────┘
                      │
                      ▼
┌──────────────────────────────────────────────────┐
│ 2. 检查是否已经注册过该卡,若已注册则返回错误 │
└──────────────────────────────────────────────────┘
                      │
                      ▼
┌──────────────────────────────────────────────────┐
│ 3. 遍历并注册所有与 snd_card 相关联的设备      │
│    - 注册控制设备接口                          │
│    - 注册其他相关设备                          │
└──────────────────────────────────────────────────┘
                      │
                      ▼
┌──────────────────────────────────────────────────┐
│ 4. 调用 device_register 注册每个设备,确认设备  │
│    成功挂载到 snd_card 中                       │
└──────────────────────────────────────────────────┘
                      │
                      ▼
┌──────────────────────────────────────────────────┐
│ 5. 注册成功后,将 snd_card 的状态设置为已注册  │
└──────────────────────────────────────────────────┘
                      │
                      ▼
┌──────────────────────────────────────────────────┐
│ 6. 成功返回,用户空间可通过 control interface │
│    来访问底层硬件                              │
└──────────────────────────────────────────────────┘
                      │
                      ▼
              ┌─────────────────────────┐
              │ 结束                    │
              └─────────────────────────┘


4.3、struct snd_device

  一个struct snd_device用于描述一个逻辑设备,每个逻辑设备都有一个对应的snd_device_ops, 我们在新增一个逻辑设备时, 需要为此设备准备好该数据结构。

1.snd_device_new

  该API用于创建一个逻辑设备, 主要内容是分配一个struct snd_device空间, 初始化相关字段, 并把该逻辑设备添加到card->devices链表下。注意在添加新设备到card->devices链表时, 采用的是有序插入方式: type小的在前、type大的在后。

2.snd_device_register

  一般在注册声卡时(snd_card_register)会自动调用此处的API; 不过也可以在card注册完毕后, 在手动调用此API注册一个新的逻辑设备。

  该API的实现很简单: 首先回调snd_device_ops->dev_register, 然后把核心层的状态标记为SNDRV_DEV_REGISTERED。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值