> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/man/man4/sndstat.4.md).

# sndstat.4

`sndstat` — 基于 nvlist 的 PCM 音频设备枚举接口

## 名称

`sndstat`

## 概要

`要将此驱动编译进内核，请在你的内核配置文件中加入以下行：`

> device sound

## 描述

由 `/dev/sndstat` 设备提供的 ioctl 接口允许调用者枚举可用于使用的 PCM 音频设备。换言之，它提供了获取系统中所有可用音频设备列表的手段。

## IOCTL

对于带参数的 ioctl 调用，使用以下结构：

```sh
struct sndstioc_nv_arg {
	size_t nbytes;
	void *buf;
};
```

下面是一个 nvlist 对象的示例，并附有常见字段的解释：

```sh
dsps (NVLIST ARRAY): 1
	from_user (BOOL): FALSE
	nameunit (STRING): [pcm0]
	devnode (STRING): [dsp0]
	desc (STRING): [Generic (0x8086) (Analog Line-out)]
	pchan (NUMBER): 1
	rchan (NUMBER): 0
	info_play (NVLIST):
		min_rate (NUMBER): 48000
		max_rate (NUMBER): 48000
		formats (NUMBER): 16
		min_chn (NUMBER): 2
		max_chn (NUMBER): 2
	provider_info (NVLIST):
		unit (NUMBER): 0
		status (STRING): on hdaa0
		bitperfect (BOOL): FALSE
		pvchan (BOOL): TRUE
		pvchanrate (NUMBER): 48000
		pvchanformat (NUMBER): 0x00000010
		rvchan (BOOL): TRUE
		rvchanrate (NUMBER): 48000
		rvchanformat (NUMBER): 0x00000010
		channel_info (NVLIST_ARRAY): 1
			name (STRING): dsp0.virtual_play.0
			parentchan (STRING): dsp0.play.0
			unit (NUMBER): 1
			caps (NUMBER): 0x073200
			latency (NUMBER): 2
			rate (NUMBER): 48000
			format (NUMBER): 0x201000
			pid (NUMBER): 1234
			comm (STRING): mpv
			interrupts (NUMBER): 0
			feedcount (NUMBER): 0
			xruns (NUMBER): 0
			left_volume (NUMBER): 45
			right_volume (NUMBER): 45
			hwbuf_fmt (NUMBER): 0x200010
			hwbuf_rate (NUMBER): 48000
			hwbuf_size (NUMBER): 0
			hwbuf_blksz (NUMBER): 0
			hwbuf_blkcnt (NUMBER): 0
			hwbuf_free (NUMBER): 0
			hwbuf_ready (NUMBER): 0
			swbuf_fmt (NUMBER): 0x201000
			swbuf_rate (NUMBER): 48000
			swbuf_size (NUMBER): 16384
			swbuf_blksz (NUMBER): 2048
			swbuf_blkcnt (NUMBER): 8
			swbuf_free (NUMBER): 16384
			swbuf_ready (NUMBER): 0
			feederchain (STRING):
				[userland ->
				feeder_root(0x00201000) ->
				feeder_format(0x00201000 -> 0x00200010) ->
				feeder_volume(0x00200010) -> hardware]
	provider (STRING): [sound(4)]
```

**`min_rate`** 支持的最低采样率。

**`max_rate`** 支持的最高采样率。

**`formats`** 支持的样本格式。

**`min_chn`** 声道布局中支持的最小声道数。

**`max_chn`** 声道布局中支持的最大声道数。

**`min_rate`** 支持的最低采样率。

**`max_rate`** 支持的最高采样率。

**`formats`** 支持的样本格式。

**`min_chn`** 声道布局中支持的最小声道数。

**`max_chn`** 声道布局中支持的最大声道数。

**`name`** 声道名称。

**`parentchan`** 父声道名称（例如虚拟声道的情况）。

**`unit`** 声道单元。

**`caps`** OSS 能力。

**`latency`** 延迟。

**`rate`** 采样率。

**`format`** 采样格式。

**`pid`** 占用该声道的进程的 PID。

**`comm`** 占用该声道的进程的名称。

**`interrupts`** 自声道打开以来的中断次数。

**`xruns`** 溢出/欠载次数，具体取决于声道方向。

**`feedcount`** 自声道打开以来读/写的字节数。

**`left_volume`** 左侧音量。

**`right_volume`** 右侧音量。

**`hwbuf_format`** 硬件缓冲区格式。

**`hwbuf_rate`** 硬件缓冲区采样率；

**`hwbuf_size`** 硬件缓冲区大小。

**`hwbuf_blksz`** 硬件缓冲区块大小。

**`hwbuf_blkcnt`** 硬件缓冲区块数。

**`hwbuf_free`** 硬件缓冲区中的可用空间（以字节为单位）。

**`hwbuf_ready`** 可从硬件缓冲区读/写的字节数。

**`swbuf_format`** 软件缓冲区格式。

**`swbuf_rate`** 软件缓冲区采样率；

**`swbuf_size`** 软件缓冲区大小。

**`swbuf_blksz`** 软件缓冲区块大小。

**`swbuf_blkcnt`** 软件缓冲区块数。

**`swbuf_free`** 软件缓冲区中的可用空间（以字节为单位）。

**`swbuf_ready`** 可从软件缓冲区读/写的字节数。

**`feederchain`** 声道 feeder 链。

**`unit`** 声卡单元。

**`status`** 状态字符串。通常报告设备所附加到的驱动。

**`bitperfect`** 声卡是否启用了 bit-perfect 模式。

**`pvchan`** 是否启用播放虚拟声道。

**`pvchanrate`** 播放虚拟声道采样率。

**`pvchanformat`** 播放虚拟声道格式。

**`rvchan`** 是否启用录制虚拟声道。

**`rvchanrate`** 录制虚拟声道采样率。

**`rvchanformat`** 录制虚拟声道格式。

**`channel_info`** 声道信息。此字段内包含若干名/值对：

**`from_user`** PCM 音频设备节点是由内核内音频子系统还是由用户空间提供者创建。

**`nameunit`** 设备标识，形式为子系统加上单元号。

**`devnode`** devfs 中 PCM 音频设备节点的相对路径。

**`desc`** PCM 音频设备的描述。

**`pchan`** 硬件支持的播放声道数。如果此 PCM 音频设备完全不支持播放，则可为 0。

**`rchan`** 硬件支持的录制声道数。如果此 PCM 音频设备完全不支持录制，则可为 0。

**`info_play`** 播放方向所支持的配置。仅当此 PCM 音频设备支持播放时才存在。此字段内包含若干名/值对：

**`info_rec`** 录制方向所支持的配置。仅当此 PCM 音频设备支持录制时才存在。此字段内包含若干名/值对：

**`provider_info`** 提供者特定的字段。如果 PCM 音频设备并非由内核内接口提供，则此字段可能不存在。如果 provider 字段为空字符串，则此字段不存在。对于 sound(4) 提供者，此字段内包含若干名/值对：

**`provider`** 指定 PCM 音频设备提供者的字符串。

提供以下 ioctl 供使用：

**`SNDSTIOC_REFRESH_DEVS`** 丢弃之前获取的任何 PCM 音频设备列表快照。此 ioctl 不接受参数。

**`SNDSTIOC_GET_DEVS`** 生成和/或向调用者返回 PCM 音频设备列表快照。此 ioctl 接受指向 `struct sndstioc_nv_arg` 的指针作为第一个也是唯一的参数。调用者需要提供足够大的缓冲区来容纳序列化的 nvlist。如果所打开的 sndstat `fd` 的内部结构中没有现成的 PCM 音频设备列表快照，将自动生成一份新的 PCM 音频设备列表快照。调用者必须将 `nbytes` 设为 0 或所提供缓冲区的大小。如果 `nbytes` 为 0，则在 `nbytes` 中返回容纳当前快照的序列化 nvlist 流所需的缓冲区大小，并忽略 `buf`。否则，如果缓冲区不够大，ioctl 返回成功，并将 `nbytes` 设为 0。如果所提供缓冲区足够大，则将 `nbytes` 设为写入所提供缓冲区的序列化 nvlist 的大小。一旦 PCM 音频设备列表快照成功返回到用户空间，给定 `fd` 的子系统内部结构中所存储的快照将被释放。

**`SNDSTIOC_ADD_USER_DEVS`** 将调用者提供的 PCM 音频设备列表添加到 `/dev/sndstat` 设备。此 ioctl 接受指向 `struct sndstioc_nv_arg` 的指针作为第一个也是唯一的参数。调用者必须提供容纳序列化 nvlist 的缓冲区。`nbytes` 应设为序列化 nvlist 的字节长度。`buf` 应指向存储序列化 nvlist 的缓冲区。用户空间支撑的 PCM 音频设备节点应列在序列化 nvlist 之内。

**`SNDSTIOC_FLUSH_USER_DEVS`** 清除之前由调用者添加的任何 PCM 音频设备。此 ioctl 不接受参数。

## 文件

**`/dev/sndstat`**

## 实例

以下代码枚举所有可用的 PCM 音频设备：

```sh
#include <sys/types.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/nv.h>
#include <sys/sndstat.h>
#include <sysexits.h>
#include <unistd.h>
int
main()
{
	int fd;
	struct sndstioc_nv_arg arg;
	const nvlist_t * const *di;
	size_t i, nitems;
	nvlist_t *nvl;
	/* 首先以只读方式打开 sndstat 节点 */
	fd = open("/dev/sndstat", O_RDONLY);
	if (ioctl(fd, SNDSTIOC_REFRESH_DEVS, NULL))
		err(1, "ioctl(fd, SNDSTIOC_REFRESH_DEVS, NULL)");
	/* 当 nbytes = 0 时获取快照大小 */
	arg.nbytes = 0;
	arg.buf = NULL;
	if (ioctl(fd, SNDSTIOC_GET_DEVS, &arg))
		err(1, "ioctl(fd, SNDSTIOC_GET_DEVS, &arg)");
	/* 获取快照数据 */
	arg.buf = malloc(arg.nbytes);
	if (arg.buf == NULL)
		err(EX_OSERR, "malloc");
	if (ioctl(fd, SNDSTIOC_GET_DEVS, &arg))
		err(1, "ioctl(fd, SNDSTIOC_GET_DEVS, &arg)");
	/* 反序列化 nvlist 流 */
	nvl = nvlist_unpack(arg.buf, arg.nbytes, 0);
	free(arg.buf);
	/* 获取 DSPs 数组 */
	di = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &nitems);
	for (i = 0; i < nitems; i++) {
		const char *nameunit, *devnode, *desc;
		/*
		 * 检查每个设备的 nvlist 项
		 */
		nameunit = nvlist_get_string(di[i], SNDST_DSPS_NAMEUNIT);
		devnode = nvlist_get_string(di[i], SNDST_DSPS_DEVNODE);
		desc = nvlist_get_string(di[i], SNDST_DSPS_DESC);
		printf("Name unit: `%s`, Device node: `%s`, Description: `%s`n",
		    nameunit, devnode, desc);
	}
	nvlist_destroy(nvl);
	return (0);
}
```

## 参见

sound(4), [nv(9)](https://github.com/FreeBSD-Ask/freebsd-man-sc/blob/main/man9/nv.9.md)

## 历史

`sndstat` 设备的基于 nvlist 的 ioctl 支持首次出现于 FreeBSD 13.0。

## 作者

本手册页由 Ka Ho Ng <khng@FreeBSD.org> 编写。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://book.bsdcn.org/man/man4/sndstat.4.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
