From: Yanteng Si <si.yanteng@linux.dev>
To: Kefan Bai <baikefan@leap-io-kernel.com>, alexs@kernel.org
Cc: dzm91@hust.edu.cn, corbet@lwn.net, linux-doc@vger.kernel.org,
doubled@leap-io-kernel.com
Subject: Re: [PATCH v4 7/8] docs/zh_CN: Add usbmon.rst translation
Date: Thu, 4 Dec 2025 10:47:00 +0800 [thread overview]
Message-ID: <ab2b68b0-a5eb-4e6b-bbda-9674408159ff@linux.dev> (raw)
In-Reply-To: <ae4d7e78d730aa2033afd28315dbbee18a0ad1ae.1764674650.git.baikefan@leap-io-kernel.com>
在 2025/12/2 19:56, Kefan Bai 写道:
> Translate .../usb/usbmon.rst into Chinese
>
> Update the translation through commit 788183a6e8b0
> ("docs: usb: fix literal block marker in usbmon verification example")
>
> Signed-off-by: Kefan Bai <baikefan@leap-io-kernel.com>
Reviewed-by: Yanteng Si <siyanteng@cqsoftware.com.cn>
Thanks,
Yanteng
> ---
> .../translations/zh_CN/usb/usbmon.rst | 380 ++++++++++++++++++
> 1 file changed, 380 insertions(+)
> create mode 100644 Documentation/translations/zh_CN/usb/usbmon.rst
>
> diff --git a/Documentation/translations/zh_CN/usb/usbmon.rst b/Documentation/translations/zh_CN/usb/usbmon.rst
> new file mode 100644
> index 000000000000..c2eebaef67ff
> --- /dev/null
> +++ b/Documentation/translations/zh_CN/usb/usbmon.rst
> @@ -0,0 +1,380 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +.. include:: ../disclaimer-zh_CN.rst
> +
> +:Original: Documentation/usb/usbmon.rst
> +:翻译:
> +
> + 白钶凡 Kefan Bai <baikefan@leap-io-kernel.com>
> +
> +:校译:
> +
> +
> +
> +======
> +usbmon
> +======
> +
> +简介
> +====
> +小写形式的"usbmon"指的是内核中的一个功能,用于收集USB总线上的I/O追踪信息。
> +这个功能类似于网络监控工具tcpdump(1)或Ethereal所使用的数据包套接字。
> +同样地,我们会使用像usbdump或USBMon(大写形式)这样的工具来检查usbmon
> +生成的原始追踪数据。
> +
> +usbmon报告的是各个外设驱动程序向主机控制器驱动程序(HCD)发出的请求。
> +因此,如果HCD本身有bug,那么usbmon报告的追踪信息可能无法精确对应实际的
> +总线事务。这种情形与tcpdump是一样的。
> +
> +目前实现了两种API:文本和二进制。二进制API通过/dev命名空间中的字符设备提供,
> +并且是一个ABI。文本API自内核2.6.35起废弃,但为了兼容仍然可用。
> +
> +如何使用usbmon收集原始文本追踪信息
> +==================================
> +
> +与数据包套接字不同,usbmon提供了一种接口,可以输出文本格式的追踪信息。
> +这样做有两个目的:
> +第一,在更完善的格式最终确定之前,它作为工具间通用的追踪交换格式;
> +第二,在不使用工具的情况下,人们也可以直接阅读这些信息。
> +
> +要收集原始文本追踪信息,请按以下步骤进行操作。
> +
> +1. 准备
> +--------
> +
> +首先必须在你的内核配置中启用debugfs,之后挂载debugfs。
> +如果usbmon是作为模块构建的,那么还要加载usbmon模块。
> +如果usbmon已经编入内核,则无需加载usbmon模块。
> +
> +命令示例::
> +
> + # mount -t debugfs none_debugs /sys/kernel/debug
> + # modprobe usbmon
> + #
> +
> +确认总线socket是否存在::
> + # ls /sys/kernel/debug/usb/usbmon
> + 0s 0u 1s 1t 1u 2s 2t 2u 3s 3t 3u 4s 4t 4u
> + #
> +
> +现在,你可以选择使用'0u'用来捕获所有总线上的数据包,并跳到第#3步,
> +或者使用第#2步找到你的设备所在的总线。
> +这样可以过滤掉那些持续输出数据的烦人设备。
> +
> +
> +
> +2. 查找目标设备连接的是哪条总线
> +-------------------------------
> +
> +运行"cat /sys/kernel/debug/usb/devices",找到对应设备的T行。
> +通常你可以通过vendor字符串来查找。如果你有许多类似设备,
> +可以拔掉其中一个设备,并比较前后两次
> +/sys/kernel/debug/usb/devices的输出。
> +T行会包含总线编号。
> +
> +示例::
> +
> + T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 0
> + D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
> + P: Vendor=0557 ProdID=2004 Rev= 1.00
> + S: Manufacturer=ATEN
> + S: Product=UC100KM V2.00
> +
> +"Bus=03"表示设备在总线3上。或者,你可以查看lsusb的输出,并从对应行得到总线编号。
> +
> +示例:
> +Bus 003 Device 002: ID 0557:2004 ATEN UC100KM V2.00
> +
> +
> +3. 启动 'cat'
> +
> +::
> +
> + # cat /sys/kernel/debug/usb/usbmon/3u > /tmp/1.mon.out
> +
> +用于监听单条总线。
> +
> +如果要监听所有总线,则输入::
> +
> + # cat /sys/kernel/debug/usb/usbmon/0u > /tmp/1.mon.out
> +
> +此进程会一直读取,直到被终止。
> +因为输出通常会非常长,所以更推荐将输出重定向到某个位置。
> +
> +
> +4. 在USB总线上执行期望的操作
> +----------------------------
> +
> +此处你需要执行一些会产生USB流量的动作,比如:插入U盘、拷贝文件、操作摄像头等。
> +
> +
> +5. 停止cat
> +-----------
> +
> +这一步通常需要通过键盘中断(Control-C)来完成。
> +
> +此时输出文件(本例中为 /tmp/1.mon.out)可以保存、通过电子邮件发送,
> +或使用文本编辑器查看。
> +如果使用最后一种方式,请确保文件不会大到编辑器无法打开。
> +
> +
> +原始文本数据格式
> +================
> +
> +目前支持两种格式:原始格式(即1t格式)和1u格式。
> +1t格式在内核2.6.21中就已经被废弃。
> +1u格式增加了一些新字段,如ISO帧描述符、interval等。
> +它生成的行稍微长一些,但在格式上是1t的严格超集。
> +
> +如果程序需要区分上述两种格式,可以查看address字段(见下)。
> +若包含两个冒号,则为1t格式,否则为1u。
> +
> +任何文本格式的数据由一系列事件组成,如URB提交、URB回调、提交错误等。
> +每个事件对应单独的一行文本,由使用空白符间隔的若干字段组成。
> +字段的数量与位置可能取决于事件类型,但以下字段对所有类型都通用:
> +
> +共有字段按照从左到右的顺序依次列出:
> +
> +- URB Tag。用于标识URB,通常是URB结构体在内核中的地址(以十六进制表示),
> + 但也可能是序号或其他合理的唯一字符串。
> +
> +- 时间戳(微秒)。十进制数字。精度取决于可用时钟,因此可能比1微秒更差。
> +
> +- 事件类型。表示事件的格式,而不是URB的类型。
> + 有效类型包括:S - 提交,C - 回调,E - 提交错误。
> +
> +- Address字段(以前称为pipe)。包含四个冒号分隔的字段:
> + URB类型与方向,总线号,设备地址,端点号。
> + 类型与方向的编码如下:
> +
> + == == =====================
> + Ci Co 控制input/output
> + Zi Zo 等时input/output
> + Ii Io 中断input/output
> + Bi Bo 批量input/output
> + == == =====================
> +
> + 总线号、设备地址和端点号使用十进制,但可能有前导零。
> +
> +- URB状态字。这个字段可以是一个字母,也可以是由冒号分隔的几个数字:
> + URB状态、间隔(interval)、起始帧(start frame)以及错误计数(error count)。
> + 与地址字段不同,除了状态之外,其余所有字段都是可选的。
> + interval仅在中断和等时URB中打印;
> + 起始帧仅在等时URB中打印;
> + 错误数量仅在等时回调事件中打印。
> +
> + 状态字段是一个十进制数字,有时为负数,对应URB的状态字段。
> + 对于提交(submission),这个字段没有实际意义,
> + 但是为了便于脚本解析,所以依然存在。
> + 当发生错误时,该字段包含错误码。
> +
> + 在提交控制数据包的情况下,该字段包含一个Setup Tag,而不是一组数字。
> + 判断是否存在Setup Tag很容易,因为它从来不是数字。
> + 因此,如果脚本在该字段中发现一组数字,
> + 它们会继续读取数据长度(等时URB除外)。
> + 如果发现的是其他内容,比如一个字母,
> + 那么脚本会在读取数据长度或等时描述符之前,会先读取Setup包。
> +
> +- Setup包由5个字段构成:bmRequestType, bRequest, wValue, wIndex, wLength。
> + 符合USB 2.0 规范。如果Setup标签为's',这些字段是安全可解码的。
> + 否则,Setup数据包虽然存在,但未被捕获,其字段中会填充占位内容。
> +
> +- 等时传输帧描述符数量及其内容:
> + 如果一次同步传输事件包含一组描述符,首先打印URB中描述符的总数,
> + 然后依次打印每个描述符的一个字段,最多打印5个字段。
> + 每个字段由三个用冒号分隔的十进制数字组成,
> + 分别表示状态(status)、偏移(offset)和长度(length)。
> + 对于提交(submission),打印的是初始长度;
> + 对于回调(callback),打印的是实际长度。
> +
> +- 数据长度:
> + 对于提交,表示请求的长度;对于回调,表示实际传输的长度。
> +
> +- 数据标签:
> + 即使数据长度非零,usbmon也可能不会捕获数据。仅当标签为'='时,才会有数据字段存在。
> +
> +- 数据字段:
> + 按大端十六进制格式显示。
> + 注意,这些并非真正的机器字,而只是将字节流拆分为若干字,
> + 以便阅读。因此最后一个字可能包含1到4个字节。
> + 收集的数据长度是有限的,可能小于数据长度字段中报告的值。
> + 因为数据长度字段只统计实际接收到的字节,而数据字段包含整个传输缓冲区,
> + 所以,在等时输入(Zi)完成且缓冲区中接收到的数据稀疏的情况下,
> + 收集的数据长度可能大于数据长度字段的值。
> +
> +
> +
> +示例:
> +
> +获取端口状态的输入控制传输::
> +
> + d5ea89a0 3575914555 S Ci:1:001:0 s a3 00 0000 0003 0004 4 <
> + d5ea89a0 3575914560 C Ci:1:001:0 0 4 = 01050000
> +
> +向地址为5的存储设备发送31字节Bulk封装的SCSI命令0x28(READ_10)的输出批量传输::
> +
> + dd65f0e8 4128379752 S Bo:1:005:2 -115 31 = 55534243 ad000000 00800000 80010a28 20000000 20000040 00000000 000000
> + dd65f0e8 4128379808 C Bo:1:005:2 0 31 >
> +
> +原始二进制格式与API
> +===================
> +API的整体架构与上面提到的基本相同,
> +只是事件以二进制格式传输。
> +每个事件使用以下结构发送(名称为自定义,方便引用)::
> +
> +
> + struct usbmon_packet {
> + u64 id; /* 0: URB ID - 从提交到回调 */
> + unsigned char type; /* 8: 与文本相同;可扩展 */
> + unsigned char xfer_type; /* ISO (0), 中断, 控制, 批量 (3) */
> + unsigned char epnum; /* 端点号和传输方向 */
> + unsigned char devnum; /* 设备地址 */
> + u16 busnum; /* 12: 总线号 */
> + char flag_setup; /* 14: 与文本相同 */
> + char flag_data; /* 15: 与文本相同;二进制零也可 */
> + s64 ts_sec; /* 16: gettimeofday */
> + s32 ts_usec; /* 24: gettimeofday */
> + int status; /* 28: */
> + unsigned int length; /* 32: 数据长度(提交或实际) */
> + unsigned int len_cap; /* 36: 实际传输长度 */
> + union { /* 40: */
> + unsigned char setup[SETUP_LEN]; /* 仅用于控制 S类型 */
> + struct iso_rec { /* ISO专用 */
> + int error_count;
> + int numdesc;
> + } iso;
> + } s;
> + int interval; /* 48: 仅用于中断和ISO */
> + int start_frame; /* 52: ISO专用 */
> + unsigned int xfer_flags; /* 56: URB的transfer_flags副本 */
> + unsigned int ndesc; /* 60: 实际ISO描述符数量 */
> + }; /* 64 总长度 */
> +
> +可以使用read(2),ioctl(2)或通过mmap访问缓冲区,从字符设备读取这些事件。
> +然而,出于兼容性原因,使用read(2)只返回前48字节。
> +
> +字符设备通常命名为/dev/usbmonN,其中N是USB总线号。
> +编号为零的设备 (/dev/usbmon0) 表示所有总线。
> +请注意,具体命名策略由Linux发行版设定。
> +
> +如果手动创建/dev/usbmon0,确保它归root所有,并且权限为0600。
> +否则,非特权用户将能够监听键盘数据。
> +
> +以下MON_IOC_MAGIC为0x92的ioctl可用:
> +
> + MON_IOCQ_URB_LEN,定义为_IO(MON_IOC_MAGIC, 1)
> +
> +该调用返回下一个事件的数据长度。注意大多数事件不包含数据,
> +因此如果该调用返回零,并不意味着没有事件。
> +
> + MON_IOCG_STATS,定义为_IOR(MON_IOC_MAGIC, 3, struct mon_bin_stats)
> +
> +参数是指向以下结构的指针::
> +
> + struct mon_bin_stats {
> + u32 queued;
> + u32 dropped;
> + };
> +
> +成员queued表示当前缓冲区中排队的事件数量(不是自上次重置以来处理的事件数量)。
> +
> +成员dropped表示自上次调用MON_IOCG_STATS以来丢失的事件数量。
> +
> + MON_IOCT_RING_SIZE,定义为_IO(MON_IOC_MAGIC, 4)
> +
> +此调用设置缓冲区大小。参数为以字节为单位的缓冲区大小。
> +大小可能会向下取整到下一个块(或页)。
> +如果请求的大小超出该内核的 [未指定] 范围,则调用失败并返回-EINVAL。
> +
> +
> +
> +MON_IOCQ_RING_SIZE,定义为_IO(MON_IOC_MAGIC, 5)
> +
> +该调用返回缓冲区当前大小(以字节为单位)。
> +
> +MON_IOCX_GET,定义为_IOW(MON_IOC_MAGIC, 6, struct mon_get_arg)
> +MON_IOCX_GETX,定义为_IOW(MON_IOC_MAGIC, 10, struct mon_get_arg)
> +
> +如果内核缓冲区中没有事件时,这些调用会一直等待事件到达,然后返回第一个事件。
> +参数是指向以下结构的指针::
> +
> + struct mon_get_arg {
> + struct usbmon_packet *hdr;
> + void *data;
> + size_t alloc; /* 数据长度可以为零 */
> + };
> +
> +
> +调用前,应填写hdr、data和alloc字段。
> +调用返回后,hdr指向的区域包含下一个事件的结构。
> +如果有数据,那么数据缓冲区包含数据。
> +该次调用会从内核缓冲区中移除该事件。
> +
> +MON_IOCX_GET将48字节的数据复制到hdr区域,MON_IOCX_GETX会复制64字节。
> +
> +MON_IOCX_MFETCH,定义为_IOWR(MON_IOC_MAGIC, 7, struct mon_mfetch_arg)
> +
> +当应用程序通过mmap(2)访问缓冲区时,主要使用该ioctl。
> +其参数是指向以下结构的指针::
> +
> + struct mon_mfetch_arg {
> + uint32_t *offvec; /* 获取的事件偏移向量 */
> + uint32_t nfetch; /* 要获取的事件数量(输出:已获取) */
> + uint32_t nflush; /* 要刷新事件数量 */
> + };
> +
> +
> +ioctl操作分为三个阶段:
> +
> +首先,从内核缓冲区移除并丢弃最多nflush个事件。
> +实际丢弃的事件数量存储在nflush中。
> +
> +其次,除非伪设备以O_NONBLOCK打开,否则会一直等待,直到缓冲区中有事件出现。
> +
> +第三,将最多nfetch个偏移量提取到mmap缓冲区,并存储
> +到offvec中。实际的事件偏移数量存储在nfetch中。
> +
> + MON_IOCH_MFLUSH,定义为_IO(MON_IOC_MAGIC, 8)
> +
> +此调用从内核缓冲区移除若干事件。其参数为要移除的事件数量。
> +如果缓冲区中的事件少于请求数量,则移除所有事件,且不报告错误。
> +当没有事件时也可使用。
> +
> + FIONBIO
> +
> +如有需要,将来可能实现ioctl FIONBIO。
> +
> +除了ioctl(2)和read(2),可以使用
> +select(2)和poll(2)轮询二进制API的特殊文件。
> +但lseek(2)无效。
> +
> +* 二进制API的内核缓冲区内存映射访问
> +
> +基本思想很简单:
> +
> +准备阶段,获取当前大小并使用mmap(2)映射缓冲区。
> +然后执行类似下面伪代码的循环::
> +
> + struct mon_mfetch_arg fetch;
> + struct usbmon_packet *hdr;
> + int nflush = 0;
> + for (;;) {
> + fetch.offvec = vec; // 有N个32位字
> + fetch.nfetch = N; // 或少于N
> + fetch.nflush = nflush;
> + ioctl(fd, MON_IOCX_MFETCH, &fetch); // 同时处理错误
> + nflush = fetch.nfetch; // 完成后要刷新这么多包'/
> + for (i = 0; i < nflush; i++) {
> + hdr = (struct ubsmon_packet *) &mmap_area[vec[i]];
> + if (hdr->type == '@') // 填充包
> + continue;
> + caddr_t data = &mmap_area[vec[i]] + 64;
> + process_packet(hdr, data);
> + }
> + }
> +
> +
> +
> +因此,主要思想是每N个事件只执行一次ioctl。
> +
> +虽然缓冲区是环形的,但返回的头和数据不会跨越缓冲区末端,
> +因此上面的伪代码无需任何合并操作。
> --
> 2.52.0
>
next prev parent reply other threads:[~2025-12-04 2:47 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-02 11:56 [PATCH v4 0/8] Add Chinese translation for USB subsystem Kefan Bai
2025-12-02 11:56 ` [PATCH v4 1/8] docs/zh_CN: Add index.rst translation Kefan Bai
2025-12-03 2:34 ` Yanteng Si
2025-12-05 7:24 ` BaiKefan
2025-12-02 11:56 ` [PATCH v4 2/8] docs/zh_CN: Add acm.rst translation Kefan Bai
2025-12-02 11:56 ` [PATCH v4 3/8] docs/zh_CN: Add authorization.rst translation Kefan Bai
2025-12-03 2:29 ` Yanteng Si
2025-12-02 11:56 ` [PATCH v4 4/8] docs/zh_CN: Add chipidea.rst translation Kefan Bai
2025-12-03 5:31 ` Yanteng Si
2025-12-02 11:56 ` [PATCH v4 5/8] docs/zh_CN: Add dwc3.rst translation Kefan Bai
2025-12-04 2:28 ` Yanteng Si
2025-12-02 11:56 ` [PATCH v4 6/8] docs/zh_CN: Add ehci.rst translation Kefan Bai
2025-12-04 2:35 ` Yanteng Si
2025-12-02 11:56 ` [PATCH v4 7/8] docs/zh_CN: Add usbmon.rst translation Kefan Bai
2025-12-04 2:47 ` Yanteng Si [this message]
2025-12-02 11:56 ` [PATCH v4 8/8] docs/zh_CN: Add CREDITS translation Kefan Bai
2025-12-04 2:51 ` Yanteng Si
2025-12-04 2:41 ` [PATCH v4 0/8] Add Chinese translation for USB subsystem Yanteng Si
2025-12-05 10:01 ` BaiKefan
2025-12-05 12:10 ` Alex Shi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ab2b68b0-a5eb-4e6b-bbda-9674408159ff@linux.dev \
--to=si.yanteng@linux.dev \
--cc=alexs@kernel.org \
--cc=baikefan@leap-io-kernel.com \
--cc=corbet@lwn.net \
--cc=doubled@leap-io-kernel.com \
--cc=dzm91@hust.edu.cn \
--cc=linux-doc@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).