系列目录 :第一篇:全景图与调用链路概览 | 第二篇:内核层---USB驱动与uevent | 第三篇:Native层---vold与NetlinkManager | 第四篇:Framework层(上)---UsbHostManager | 第五篇:Framework层(下)---StorageManagerService | 第六篇:广播分发与SystemUI响应 | 第七篇:应用层---MediaScanner与SAF | 第八篇:实战调试与案例分析
一、引言
当用户把 U 盘插入手机的 Type-C 接口时,第一响应者不是你写的 Java 代码,也不是 vold 守护进程,而是沉睡在 Linux 内核深处的 USB Core 子系统。
从硬件电平变化到用户态收到 uevent,内核层完成了整条链路中最底层、也最容易被忽视的工作:
- VBUS 供电检测 → 硬件中断触发
- 设备枚举 → 获取描述符、分配地址、绑定驱动
- 块设备节点创建 →
/dev/sda问世 - uevent 广播 → 用户态程序开始工作
本文将以 AOSP 12 使用的 Linux Kernel 5.10 为主线(也兼容 5.4/5.15),逐函数拆解这一过程。
二、USB Core 子系统架构
2.1 三层驱动模型
Linux USB 子系统遵循经典的分层架构:
┌─────────────────────────────────────────────────┐
│ USB Device Drivers │
│ usb-storage / usbhid / usbaudio / cdc_acm ... │
│ (设备功能驱动层) │
├─────────────────────────────────────────────────┤
│ USB Core (usbcore) │
│ hub.c / driver.c / generic.c / urb.c ... │
│ (核心协议层:枚举、URB、驱动匹配) │
├─────────────────────────────────────────────────┤
│ Host Controller Driver (HCD) │
│ xhci-hcd / ehci-hcd / dwc3 / musb ... │
│ (控制器驱动层:硬件寄存器操作) │
└─────────────────────────────────────────────────┘
对于手机场景,Host Controller 通常由 SoC 的 DWC3(DesignWare USB 3.0 Controller) 驱动管理,它在 Device 模式和 Host 模式之间动态切换(通过 OTG/Type-C 角色协商)。
2.2 关键数据结构
理解源码前,先认识几个核心结构体:
c
// include/linux/usb.h
struct usb_device {
int devnum; // 总线地址(1-127)
struct usb_device_descriptor descriptor; // 设备描述符
struct usb_host_config *config; // 当前激活的配置
struct usb_host_endpoint *ep0; // 控制端点0
struct usb_device *parent; // Hub场景下的父设备
struct device dev; // 通用设备模型
// ...
};
struct usb_interface {
struct usb_host_interface *altsetting; // 接口描述符数组
struct usb_driver *driver; // 绑定的驱动
struct device dev; // 通用设备模型
// ...
};
// 驱动注册结构
struct usb_driver {
const char *name;
int (*probe) (struct usb_interface *intf,
const struct usb_device_id *id);
void (*disconnect) (struct usb_interface *intf);
const struct usb_device_id *id_table; // 设备匹配表
// ...
};
三、Hub 端口检测:从硬件中断开始
3.1 硬件如何感知插入
U 盘插入 Type-C 母座后,物理层面发生两件事:
| 信号 | 作用 | 管理方 |
|---|---|---|
| VBUS | Host 向设备供电(5V),设备端上拉识别 | PMIC / USB PHY |
| CC1/CC2 | Type-C 角色协商、方向检测 | TCPM(Type-C Port Manager) |
| DP/DM 上拉 | USB 2.0 速度识别(全速/低速设备通过上拉电阻区分) | USB PHY |
对于 USB 2.0 OTG,由 ID 引脚 电平决定谁是 Host(ID 接地 = Host);对于 Type-C,由 CC 引脚通信决定。
3.2 hub_events() ------ 一切的总调度
无论 SoC 的根 Hub 还是外接 Hub,端口状态变化都由 hub_events() 这个工作队列函数统一处理。
源码路径 :drivers/usb/core/hub.c
c
// hub_events() ------ usbcore 的心脏,通过 hub_wq 工作队列周期性调度
static void hub_events(void)
{
struct list_head *tmp;
struct usb_device *hdev;
struct usb_hub *hub;
// 遍历所有已注册的hub
while (1) {
// 获取hub状态变化的bitmap
// hub->event_bits[0] 的每个bit对应一个端口
for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
if (test_bit(i, hub->event_bits)) {
// 读取端口状态寄存器
status = hub_port_status(hub, i, &portstatus, &portchange);
// ★ 关键判断:是否有连接变化?
if (portchange & USB_PORT_STAT_C_CONNECTION) {
clear_port_feature(hub->hdev, i, USB_PORT_FEAT_C_CONNECTION);
// 进入连接处理逻辑
hub_port_connect_change(hub, i, portstatus, portchange);
}
}
}
}
}
3.3 hub_port_connect_change() ------ 识别新设备
c
// drivers/usb/core/hub.c
static void hub_port_connect_change(struct usb_hub *hub, int port1,
u16 portstatus, u16 portchange)
{
// 1. 判断设备是否已断开(可能在处理过程中拔出)
status = hub_port_debounce_be_stable(hub, port1);
// 2. 为设备分配新的 usb_device 结构体
udev = usb_alloc_dev(hdev, hdev->bus, port1);
// 3. ★ 核心:从这里进入设备枚举
status = hub_port_init(hub, udev, port1, retry_counter);
// 4. ★ 选择配置并注册设备
if (status == 0)
status = usb_new_device(udev); // → 驱动匹配 & 接口绑定
}
四、USB 设备枚举:获取描述符的全流程
hub_port_init() 是 USB 枚举的核心,按 USB 规范完成以下步骤:
4.1 枚举状态机
[设备插入,端口检测到连接]
│
├──[1] 端口复位(Reset)
│ hub_port_reset() → 发送 SE0 复位信号至少 10ms
│ → 设备进入 Default 状态,响应地址 0
│
├──[2] 获取设备描述符(前8字节)
│ usb_get_device_descriptor(udev, 8)
│ → 主要是为了获取 ep0 的最大包大小
│
├──[3] 分配唯一地址
│ choose_address(udev)
│ hub_set_address(udev, devnum)
│ → 设备进入 Address 状态
│
├──[4] 获取完整设备描述符(18字节)
│ usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE)
│ → 得到 vid/pid/class/设备版本等
│
├──[5] 获取配置描述符
│ usb_get_configuration(udev)
│ → 获取配置 + 接口 + 端点 完整描述符树
│
├──[6] 设置配置
│ usb_set_configuration(udev, udev->config[0])
│ → 设备进入 Configured 状态,功能可用
│
▼
[枚举完成]
4.2 获取设备描述符(核心代码)
c
// drivers/usb/core/hub.c → hub_port_init()
static int hub_port_init(struct usb_hub *hub, struct usb_device *udev,
int port1, int retry_counter)
{
// 复位端口
retval = hub_port_reset(hub, port1, udev, delay, false);
// 获取前8字节设备描述符(取 maxpacket0)
retval = usb_get_device_descriptor(udev, 8);
// 实际调用 usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
// USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8, 0,
// buf, size, timeout);
// 分配设备地址
devnum = usb_alloc_devnum();
retval = hub_set_address(udev, devnum);
// 获取完整18字节设备描述符
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
// 此时 udev->descriptor 包含完整的 vid/pid/class/subclass/protocol
// 获取配置描述符
retval = usb_get_configuration(udev);
// → 内部通过 GET_DESCRIPTOR(CONFIGURATION) 标准请求获取
return 0;
}
4.3 实际 U 盘描述符示例
用 lsusb -v 抓到的典型 U 盘描述符:
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 3.00 ← USB 3.0
bDeviceClass 0 ← 接口级别定义
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 9
idVendor 0x0781 SanDisk
idProduct 0x5591
bcdDevice 1.00
iManufacturer 1 SanDisk
iProduct 2 Ultra USB 3.0
iSerial 3 4C530001260121106363
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 44
bNumInterfaces 1
bConfigurationValue 1
bmAttributes 0x80 (Bus Powered)
MaxPower 896mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 8 Mass Storage
bInterfaceSubClass 6 SCSI transparent
bInterfaceProtocol 80 (Bulk-Only Transport)
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2 Transfer Type: Bulk
wMaxPacketSize 1024 Bytes per SuperSpeed packet
bInterval 0
关键字段解读 :
bInterfaceClass = 0x08表示这是 Mass Storage 设备,bInterfaceSubClass = 0x06表示 SCSI transparent command set,bInterfaceProtocol = 0x50表示 Bulk-Only Transport(BOT),这些都是后续 usb-storage 驱动绑定的匹配条件。
五、驱动绑定:usb_new_device() 的匹配流程
枚举完成后,usb_new_device() 负责将设备和驱动配对:
5.1 调用链
usb_new_device()
└── device_add(&udev->dev)
└── bus_probe_device(&usb_bus_type)
└── usb_device_match() ← 匹配逻辑
└── usb_device_probe()
└── 遍历 interface
└── usb_match_one_id()
└── driver->probe(intf)
5.2 匹配逻辑
c
// drivers/usb/core/driver.c
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
// 接口级别匹配(最常见)
if (is_usb_interface(dev)) {
struct usb_interface *intf = to_usb_interface(dev);
struct usb_driver *usb_drv = to_usb_driver(drv);
// ★ 核心:用 id_table 匹配
return usb_match_device(intf, usb_drv->id_table);
}
// 设备级别匹配(少见)
return 0;
}
static int usb_match_device(struct usb_interface *intf,
const struct usb_device_id *id)
{
for (; id->match_flags; id++) {
if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
id->idVendor != dev->descriptor.idVendor)
continue;
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
id->bDeviceClass != dev->descriptor.bDeviceClass)
continue;
// ... 更多匹配标志
return 1; // 匹配成功
}
return 0;
}
5.3 usb-storage 驱动的 id_table
c
// drivers/usb/storage/usb.c
static const struct usb_device_id storage_usb_ids[] = {
// 匹配所有 Mass Storage 设备(class=8, subclass=6, protocol=80)
{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) },
// ... 特殊设备 quirks
{ } // 终止
};
static struct usb_driver usb_storage_driver = {
.name = "usb-storage",
.probe = storage_probe,
.disconnect = usb_stor_disconnect,
.id_table = storage_usb_ids,
};
匹配成功后,storage_probe() 创建 /dev/sda 等块设备节点。
六、uevent:内核到用户态的桥梁
6.1 什么是 uevent
uevent(user event)是 Linux 内核通过 netlink socket 向用户态进程广播设备热插拔事件的机制。格式为 key=value 文本,以 \0 分隔。
ACTION=add
DEVPATH=/devices/platform/soc/.../block/sda/sda1
SUBSYSTEM=block
MAJOR=8
MINOR=1
DEVNAME=sda1
DEVTYPE=partition
SEQNUM=2847
6.2 kobject_uevent_env() ------ 核心发送函数
源码路径 :lib/kobject_uevent.c
c
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
char *envp_ext[])
{
struct kobj_uevent_env *env;
const char *action_string = kobject_actions[action];
// action_string → "add" / "remove" / "change" / "move" / "online" / "offline"
// 1. 分配并初始化 env
env = kzalloc(sizeof(*env), GFP_KERNEL);
// 2. 添加基本字段
add_uevent_var(env, "ACTION=%s", action_string); // ACTION=add
add_uevent_var(env, "DEVPATH=%s", devpath); // DEVPATH=...
add_uevent_var(env, "SUBSYSTEM=%s", subsystem); // SUBSYSTEM=block
// 3. 调用 subsystem 的 uevent 回调,补充额外字段
if (kobj->kset && kobj->kset->uevent_ops) {
kobj->kset->uevent_ops->uevent(kobj, env);
// → 补充 MAJOR/MINOR/DEVNAME/DEVTYPE 等
}
// 4. ★ 通过 netlink 广播
netlink_broadcast_filtered(uevent_sock, skb, 0, 1, GFP_KERNEL,
kobj_bcast_filter, kobj);
// 5. 通过 /sbin/hotplug 调用(传统方式,Android 中不使用)
// ...
return 0;
}
6.3 Android 中哪些 uevent 与 U 盘相关
| uevent | 触发时机 | 关键字段 |
|---|---|---|
KOBJ_ADD → block subsystem |
块设备节点创建 | DEVNAME=sda, MAJOR=8, SUBSYSTEM=block |
KOBJ_ADD → block partition |
分区节点创建 | DEVNAME=sda1, DEVTYPE=partition |
KOBJ_CHANGE |
介质变化(如 SD 卡锁定开关) | SUBSYSTEM=block |
KOBJ_REMOVE |
设备拔出 | ACTION=remove |
对于 U 盘场景,最关键的 uevent 是:
ACTION=add
DEVPATH=/devices/platform/soc/a600000.usb/.../host0/target0:0:0/0:0:0:0/block/sda
SUBSYSTEM=block
MAJOR=8
MINOR=0
DEVNAME=sda
DEVT