RK3588 kernel-6.1:sound/core 六文件专项分析
源码基准:/home/work2/SeagullYpcEncode/rk3588/kernel-6.1/sound/core/。本文仅覆盖:
| 文件 | 编入核心模块 |
|---|---|
sound.c |
snd.o(CONFIG_SND) |
init.c |
snd.o |
memory.c |
snd.o |
control.c |
snd.o |
misc.c |
snd.o |
device.c |
snd.o |
更完整的 core 目录说明见:rk3588-kernel-6.1-sound-core分析.md、源码走读见:rk3588-kernel-6.1-sound-core源码详细分析.md。
总览:各自解决什么问题
| 文件 | 作用(一句话) | 主要解决的工程问题 |
|---|---|---|
sound.c |
ALSA 字符设备入口与 minor 注册表 | 统一 /dev/snd/* major、按 minor 分发 f_ops、类型校验与 snd_card 引用计数 |
init.c |
snd_card 生命周期与 热插拔/关闭同步 |
声卡创建/devres、slots 绑定、register 前对外阻塞控制接口、断开时替换 f_op 防 UAF |
memory.c |
MMIO ↔ 用户空间的 安全拷贝 | 非 i386/sparc32 上不能直接 copy_*_user 到 ioremapped 窗口,需 memcpy_fromio/toio 分段缓冲 |
control.c |
Mixer/控制元素 与用户 ioctl | snd_kcontrol 链表、读写、事件 snd_ctl_notify、用户创建控件配额 max_user_ctl_alloc_size |
misc.c |
调试打印、PCI quirk、fasync 延后唤醒、release_resource 封装 |
kill_fasync 与 tasklist 锁死锁;驱动重复 snd_pci_quirk_lookup 样板 |
device.c |
挂在 snd_card 上的 子设备链表 |
PCM/control 等 注册顺序、disconnect/free 顺序、CONTROL 最后释放 |
1. sound.c
1.1 作用
alsa_sound_init:register_chrdev注册 major(默认CONFIG_SND_MAJOR= 116),snd_fops仅实现open;调用snd_info_init;调试下创建debugfs sound。snd_register_device:分配struct snd_minor,按CONFIG_SND_DYNAMIC_MINORS选择 静态 minor 编码或 扫描空闲槽位,设device->devt,device_add,填入snd_minors[minor]。snd_open:根据iminor(inode)取snd_minor,必要时autoload_device(模块化下request_module("snd-card-%d")或snd-seq/snd-timer),replace_fops后调用真实子驱动的open。
1.2 解决的问题
- 单一 major、多子设备类型:避免每种声卡各自注册字符设备号。
snd_lookup_minor_data(minor, type):打开前校验 设备类型,并对card_ptrget_device,要求调用方snd_card_unref,避免snd_card_disconnect与并发 open 竞态导致悬空指针。snd_unregister_device:按struct device *反查 minor,device_del并释放snd_minor。
2. init.c
2.1 作用(核心 API)
snd_card_new/snd_devm_card_new:分配snd_card(可选extra_size嵌入私有数据),snd_card_init初始化ctl_dev、链表、电源与slots逻辑等;devm 路径在snd_card_register后自动排队snd_card_free。snd_card_register:device_add(card_dev)→snd_device_register_all→ 填入全局snd_cards[]→snd_info_card_register;注释强调:此前控制接口对外不可用。snd_card_disconnect:置shutdown,对snd_monitor_file链表把file->f_op换成snd_shutdown_f_ops(仅release有效),snd_device_disconnect_all,device_del,从snd_cards摘除。snd_card_free/snd_card_free_when_closed:wait_for_completion等到引用归零;内部snd_device_free_all。snd_card_file_add/snd_card_file_remove:跟踪仍打开的struct file,get_device/put_device与remove_sleep唤醒,支撑 「先 disconnect,等全关闭再释放」。- 其它:
snd_card_locked、slots模块参数、snd_power_*(PM)、snd_component_add等。
2.2 解决的问题
- 热插拔/USB 声卡卸载:用户进程仍持有
/dev/snd/*fd 时,断开阶段不能把内核对象立刻 kfree;通过 dummyf_ops+ 文件列表 +snd_card_free_when_closed协调释放顺序。 - probe 失败与 devres:
snd_card_free_on_error文档警告 devres 顺序导致的 UAF,需在错误路径显式snd_card_free。 - 多卡共存:
snd_cards_lockbitmap +slots把逻辑卡槽与模块名绑定。
3. memory.c
3.1 作用
仅两个导出函数:
copy_to_user_fromio:从volatile void __iomem *读到用户空间。copy_from_user_toio:从用户空间写到ioremappedMMIO。
在非 __i386__ / CONFIG_SPARC32 上,使用 栈上 256 字节缓冲区:memcpy_fromio → copy_to_user(或反向),避免把 __iomem 指针非法当作普通指针参与 copy_to_user。
3.2 解决的问题
- 架构上与 MMIO 访问模型不一致:legacy ISA/PCI 声卡驱动在 ioctl 或 hwdep 里批量读写寄存器时,需要 正确、可移植 的搬运例程;RK3588 上以 SoC MMIO + regmap 为主,直接用到本文件的场景较少,但 Core 仍保留给通用 ALSA 驱动。
4. control.c
4.1 作用
snd_ctl_open:snd_lookup_minor_data(..., SNDRV_DEVICE_TYPE_CONTROL),snd_card_file_add,分配snd_ctl_file(事件队列、change_sleep、preferred_subdevice),挂入card->ctl_files。snd_ctl_release:从控件上剥离owner(用户创建的 volatile 控件)、snd_fasync_free、清空事件队列、snd_card_file_remove。snd_ctl_notify/snd_ctl_notify_one:向已subscribe的 ctl 客户端投递snd_kctl_event,wake_up+snd_kill_fasync(异步 IO);可在 原子上下文调用。snd_ctl_new→snd_ctl_add(及变体):创建snd_kcontrol(vd[]多元素、MAX_CONTROL_COUNT),分配numid,挂card->controls,向用户空间广播添加事件。- ioctl 层:
SNDRV_CTL_IOCTL_*(枚举控件、读写值、订阅等);snd_ctl_layer_ops钩子供扩展;snd_register_*_ioctl允许其它子系统注册额外 ioctl。 - 模块参数:
max_user_ctl_alloc_size(默认 8MiB)限制 用户态分配型控件,减轻恶意/故障进程的内存放大。
4.2 解决的问题
- 统一 mixer 与控制通道:音量、路由、开关等与 PCM 数据路径解耦,并通过 poll/epoll 感知变化(热点切换、HDMI ELD 等常经
snd_ctl_notify)。 - 并发与安全:
controls_rwsem、ctl_files_rwlock;关闭 fd 时清理 按snd_ctl_file归属的 volatile 控件。
说明:32 位 ABI 的 ioctl 兼容在
control_compat.c(同一snd.o),本文不展开。
5. misc.c
5.1 作用
CONFIG_SND_DEBUG:debug模块参数 +__snd_printk(可选CONFIG_SND_VERBOSE_PRINTK带文件名行号)。release_and_free_resource:release_resource+ kfree,避免驱动重复样板。CONFIG_PCI:snd_pci_quirk_lookup(_id):按 subsystem vendor/device 匹配snd_pci_quirk表(子设备 mask),供 PCI 声卡 quirks。snd_fasync_helper/snd_kill_fasync/snd_fasync_free:用schedule_work(snd_fasync_work)在工作队列里调用kill_fasync,注释写明动机是 避开tasklist_lock等与kill_fasync相关的死锁;PCM/control 等fasync应走此封装。
5.2 解决的问题
- 在中断或持锁上下文触发异步通知:推迟
kill_fasync,降低与进程扫描锁的交互风险。 - PCI DMI/SSID 桌面条目:集中 quirk 查找逻辑。
6. device.c
6.1 作用
snd_device_new:按enum snd_device_type递增顺序插入card->devices(device_data指针唯一标识子对象)。snd_device_register/snd_device_register_all:调用ops->dev_register,状态BUILD → REGISTERED。snd_device_disconnect_all:反向遍历,dev_disconnect。snd_device_free_all:先释放 除SNDRV_DEV_CONTROL与SNDRV_DEV_LOWLEVEL外 的所有节点,再 第二轮释放剩余,保证 control 最后 teardown。snd_device_get_state:查询 BUILD / REGISTERED / DISCONNECTED。
6.2 解决的问题
- 注册次序语义:
include/sound/core.h规定SNDRV_DEV_CONTROL必须枚举最后一项,以便 mixer 在 PCM/rawmidi 等设备就绪后再暴露。 - 卸载次序与引用:防止 control 过早拆除导致其它子设备仍暴露 sysfs/ioctl 路径。
1 | enum snd_device_type { |
7. 与 RK3588 / ASoC 的实务关系
- Rockchip 机器驱动与 Codec 主要在
sound/soc,但最终仍调用snd_card_new/snd_card_register,并通过snd_device_new挂上 PCM、control 等;理解init.c+device.c有助于排查 probe 顺序、snd_card_register时机 问题。 misc.c的snd_kill_fasync与pcm_lib中snd_pcm_period_elapsed末尾snd_kill_fasync(runtime->fasync, ...)配合,影响 用户态 SIGIO/POLL 行为。memory.c对 纯 ASoC + regmap 路径通常不是热点,保留为 ALSA 通用基础设施。
8. 小结
这六个文件共同构成 ALSA Core 的「设备模型 + 字符入口 + 控制面 + 杂项工具」:
sound.c:对谁open、minor 是什么;init.c:snd_card何时对用户可见、如何 disconnect/free;device.c:卡上子对象的 顺序化注册与释放;control.c:mixer/控件 与 事件通知;memory.c:MMIO ↔ 用户 拷贝;misc.c:调试、PCI quirk、fasync 延后。
正在加载留言…