RK3588 kernel-6.1:sound/core 六文件专项分析

RK3588 kernel-6.1:sound/core 六文件专项分析

源码基准:/home/work2/SeagullYpcEncode/rk3588/kernel-6.1/sound/core/。本文仅覆盖:

文件 编入核心模块
sound.c snd.oCONFIG_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_*_userioremapped 窗口,需 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_initregister_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->devtdevice_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_ptr get_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_registerdevice_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_alldevice_del,从 snd_cards 摘除。
  • snd_card_free / snd_card_free_when_closedwait_for_completion 等到引用归零;内部 snd_device_free_all
  • snd_card_file_add / snd_card_file_remove:跟踪仍打开的 struct fileget_device/put_deviceremove_sleep 唤醒,支撑 「先 disconnect,等全关闭再释放」
  • 其它:snd_card_lockedslots 模块参数、snd_power_*(PM)、snd_component_add 等。

2.2 解决的问题

  • 热插拔/USB 声卡卸载:用户进程仍持有 /dev/snd/* fd 时,断开阶段不能把内核对象立刻 kfree;通过 dummy f_ops + 文件列表 + snd_card_free_when_closed 协调释放顺序。
  • probe 失败与 devressnd_card_free_on_error 文档警告 devres 顺序导致的 UAF,需在错误路径显式 snd_card_free
  • 多卡共存snd_cards_lock bitmap + slots 把逻辑卡槽与模块名绑定。

3. memory.c

3.1 作用

仅两个导出函数:

  • copy_to_user_fromio:从 volatile void __iomem * 读到用户空间。
  • copy_from_user_toio:从用户空间写到 ioremapped MMIO。

在非 __i386__ / CONFIG_SPARC32 上,使用 栈上 256 字节缓冲区memcpy_fromiocopy_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_opensnd_lookup_minor_data(..., SNDRV_DEVICE_TYPE_CONTROL)snd_card_file_add,分配 snd_ctl_file(事件队列、change_sleeppreferred_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_eventwake_up + snd_kill_fasync(异步 IO);可在 原子上下文调用。
  • snd_ctl_newsnd_ctl_add(及变体):创建 snd_kcontrolvd[] 多元素、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_rwsemctl_files_rwlock;关闭 fd 时清理 snd_ctl_file 归属的 volatile 控件

说明:32 位 ABI 的 ioctl 兼容在 control_compat.c(同一 snd.o),本文不展开。


5. misc.c

5.1 作用

  • CONFIG_SND_DEBUGdebug 模块参数 + __snd_printk(可选 CONFIG_SND_VERBOSE_PRINTK 带文件名行号)。
  • release_and_free_resourcerelease_resource + kfree,避免驱动重复样板。
  • CONFIG_PCIsnd_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->devicesdevice_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_CONTROLSNDRV_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
2
3
4
5
6
7
8
9
10
11
12
13
14
enum snd_device_type {
SNDRV_DEV_LOWLEVEL,
SNDRV_DEV_INFO,
SNDRV_DEV_BUS,
SNDRV_DEV_CODEC,
SNDRV_DEV_PCM,
SNDRV_DEV_COMPRESS,
SNDRV_DEV_RAWMIDI,
SNDRV_DEV_TIMER,
SNDRV_DEV_SEQUENCER,
SNDRV_DEV_HWDEP,
SNDRV_DEV_JACK,
SNDRV_DEV_CONTROL, /* NOTE: this must be the last one */
};

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.csnd_kill_fasyncpcm_libsnd_pcm_period_elapsed 末尾 snd_kill_fasync(runtime->fasync, ...) 配合,影响 用户态 SIGIO/POLL 行为。
  • memory.c纯 ASoC + regmap 路径通常不是热点,保留为 ALSA 通用基础设施。

8. 小结

这六个文件共同构成 ALSA Core 的「设备模型 + 字符入口 + 控制面 + 杂项工具」

  • sound.c:对谁 open、minor 是什么;
  • init.csnd_card 何时对用户可见、如何 disconnect/free
  • device.c:卡上子对象的 顺序化注册与释放
  • control.cmixer/控件事件通知
  • memory.cMMIO ↔ 用户 拷贝;
  • misc.c调试、PCI quirk、fasync 延后

文章互动

阅读 --

留言

0 条留言

正在加载留言…