第一百四十四章:USB
合道期涉及内核源码:
一
林小源离开 SPI 的大桥,来到一片开阔的平地。平地上矗立着一座巨大的枢纽——枢纽四周伸出无数条通道,通道上连接着各式各样的设备:键盘、鼠标、U盘、摄像头、打印机、手机。
"这是什么?"林小源惊叹道。
"USB。"守山老者说,"Universal Serial Bus——通用串行总线。你在用户空间最常见的接口。"
枢纽的中心站着一个身穿白色铠甲的卫士,他的铠甲上刻着一个 USB 标志。卫士的身边不断有设备插拔——有的设备刚插上去,铠甲上就亮起了绿光;有的设备拔掉后,绿光缓缓熄灭。
"热插拔。"卫士注意到林小源的目光,主动解释道,"USB 支持设备在系统运行时随时连接和断开。你插一个 U 盘,内核自动检测到新设备,读取描述符,加载驱动,创建设备节点。你拔掉 U 盘,内核自动卸载驱动,清理资源。整个过程不需要重启,不需要手动操作。"
/*
* USB 的特点:
*
* 通用: 支持多种设备
* 热插拔: 支持动态连接
* 供电: 可以为设备供电
* 速度: USB 2.0 (480Mbps), USB 3.0 (5Gbps)
*
* USB 驱动:
* struct usb_driver:
* probe — 初始化
* disconnect — 断开
* id_table — 匹配表
*
* USB 通信:
* usb_control_msg — 控制传输
* usb_bulk_msg — 批量传输
* usb_interrupt_msg — 中断传输
*
* USB 设备描述符:
* 设备描述符 → 配置描述符 → 接口描述符 → 端点描述符
*/
printf("=== USB — 通用串行总线 ===\n\n");
printf("USB 的特点:\n");
printf(" 通用: 支持多种设备\n");
printf(" 热插拔: 动态连接\n");
printf(" 供电: 为设备供电\n");
printf(" 速度: USB 2.0 (480Mbps)\n\n");
printf("--- USB 驱动 ---\n");
printf("struct usb_driver my_drv = {\n");
printf(" .name = \"my_usb\",\n");
printf(" .probe = my_probe,\n");
printf(" .disconnect = my_disconnect,\n");
printf(" .id_table = my_ids,\n");
printf("};\n\n");
printf("--- USB 传输类型 ---\n");
printf("控制传输:\n");
printf(" 配置设备\n");
printf(" usb_control_msg()\n\n");
printf("批量传输:\n");
printf(" 大量数据\n");
printf(" U盘、打印机\n\n");
printf("中断传输:\n");
printf(" 定期轮询\n");
printf(" 键盘、鼠标\n\n");
printf("等时传输:\n");
printf(" 实时数据\n");
printf(" 音频、视频\n\n");
printf("--- USB 设备描述符 ---\n");
printf("设备描述符:\n");
printf(" 厂商 ID、产品 ID\n\n");
printf("配置描述符:\n");
printf(" 设备的配置信息\n\n");
printf("接口描述符:\n");
printf(" 设备的功能接口\n\n");
printf("端点描述符:\n");
printf(" 数据传输的端点\n\n");
printf("--- lsusb ---\n");
printf("lsusb 命令查看 USB 设备:\n");
printf(" Bus 001 Device 003:\n");
printf(" ID 046d:c077 Logitech Mouse\n");#include <stdio.h>
/*
* USB 的特点:
*
* 通用: 支持多种设备
* 热插拔: 支持动态连接
* 供电: 可以为设备供电
* 速度: USB 2.0 (480Mbps), USB 3.0 (5Gbps)
*
* USB 驱动:
* struct usb_driver:
* probe — 初始化
* disconnect — 断开
* id_table — 匹配表
*
* USB 通信:
* usb_control_msg — 控制传输
* usb_bulk_msg — 批量传输
* usb_interrupt_msg — 中断传输
*
* USB 设备描述符:
* 设备描述符 → 配置描述符 → 接口描述符 → 端点描述符
*/
int main() {
printf("=== USB — 通用串行总线 ===\n\n");
printf("USB 的特点:\n");
printf(" 通用: 支持多种设备\n");
printf(" 热插拔: 动态连接\n");
printf(" 供电: 为设备供电\n");
printf(" 速度: USB 2.0 (480Mbps)\n\n");
printf("--- USB 驱动 ---\n");
printf("struct usb_driver my_drv = {\n");
printf(" .name = \"my_usb\",\n");
printf(" .probe = my_probe,\n");
printf(" .disconnect = my_disconnect,\n");
printf(" .id_table = my_ids,\n");
printf("};\n\n");
printf("--- USB 传输类型 ---\n");
printf("控制传输:\n");
printf(" 配置设备\n");
printf(" usb_control_msg()\n\n");
printf("批量传输:\n");
printf(" 大量数据\n");
printf(" U盘、打印机\n\n");
printf("中断传输:\n");
printf(" 定期轮询\n");
printf(" 键盘、鼠标\n\n");
printf("等时传输:\n");
printf(" 实时数据\n");
printf(" 音频、视频\n\n");
printf("--- USB 设备描述符 ---\n");
printf("设备描述符:\n");
printf(" 厂商 ID、产品 ID\n\n");
printf("配置描述符:\n");
printf(" 设备的配置信息\n\n");
printf("接口描述符:\n");
printf(" 设备的功能接口\n\n");
printf("端点描述符:\n");
printf(" 数据传输的端点\n\n");
printf("--- lsusb ---\n");
printf("lsusb 命令查看 USB 设备:\n");
printf(" Bus 001 Device 003:\n");
printf(" ID 046d:c077 Logitech Mouse\n");
return 0;
}二
"USB 有四种传输类型。"卫士竖起四根手指。
"控制传输——用来配置设备。设备刚插上时,内核通过控制传输读取描述符、设置地址、选择配置。这是最基本的传输方式。"
"批量传输——用来传大量数据。U 盘读写文件、打印机打印文档——这些场景不关心实时性,但要求数据完整,所以用批量传输。"
"中断传输——别被名字骗了,它不是真正的硬件中断。它是主机定期轮询设备——'你有新数据吗?你有新数据吗?'键盘和鼠标就用这种方式。每次你按一个键,主机在下一次轮询时读到。"
"等时传输——用来传实时数据。音频和视频流不能等、不能重传——丢了就丢了,但必须保证时间上的连续性。等时传输为这类场景预留了带宽。"
林小源默默记下:控制传输是开路先锋,批量传输是搬运工,中断传输是巡逻兵,等时传输是实时流。
三
"USB 设备怎么描述自己?"林小源问。
"通过描述符。"卫士从枢纽上取下一颗水晶,水晶内部浮现出一个层层嵌套的结构。
"最外层是设备描述符——厂商 ID、产品 ID、设备类别、支持的 USB 版本。这是设备的身份证。"
"设备描述符里包含一个或多个配置描述符——每个配置描述符描述设备的一种工作模式。比如一个 USB 摄像头可能有两种配置:低分辨率高帧率、高分辨率低帧率。"
"配置描述符里包含一个或多个接口描述符——每个接口描述符描述设备的一种功能。一个 USB 复合设备(比如带摄像头的耳机)可以同时有音频接口和视频接口。"
"接口描述符里包含一个或多个端点描述符——端点是数据传输的终点。每个端点有自己的传输类型(控制、批量、中断、等时)和方向(输入或输出)。"
林小源看着那颗水晶中层层嵌套的结构,心中感叹:USB 的设计非常精巧——一个设备可以有多种配置、多种功能、多种传输方式,全部通过描述符层次化地组织在一起。这就是"通用"的含义——不管什么设备,都能用同一套框架描述和管理。
道藏笔记
内核启示
USB 是通用的串行总线。
USB 的特点:
- 通用 — 支持多种设备
- 热插拔 — 动态连接
- 供电 — 为设备供电
USB 传输类型:
- 控制传输 — 配置设备
- 批量传输 — 大量数据
- 中断传输 — 定期轮询
- 等时传输 — 实时数据
USB 是"万能"的接口——连接各种设备。
USB 之试
本章讲插入设备后枚举、描述符和端点通信时,核心总线协议是什么?