Android12 U盘插拔链路源码全解析(二):内核层USB驱动与uevent机制

系列目录第一篇:全景图与调用链路概览 | 第二篇:内核层---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
相关推荐
天才少年曾牛1 天前
Android新增服务添加selinux权限
android·java·frameworks
天才少年曾牛19 天前
Android14 新增系统服务后,应用调用出现 “hidden api” 警告的原因与解决方案
android·frameworks
天才少年曾牛5 个月前
【无标题】
android·frameworks
AFinalStone1 年前
Android 16系统源码_窗口动画(一)窗口过渡动画层级图分析
android·动画·frameworks
AFinalStone1 年前
Android 12系统源码_系统启动(三)SystemServer进程
android·frameworks
AFinalStone2 年前
Android 12系统源码_RRO机制(一)Runtime Resource Overlay机制实践
android·frameworks
AFinalStone2 年前
Android 12系统源码_应用加载流程(一)资源加载
android·frameworks
AFinalStone2 年前
Android 12系统源码_屏幕设备(二)DisplayAdapter和DisplayDevice的创建
android·frameworks
AFinalStone2 年前
Android 12系统源码_多屏幕(一)多屏幕设备显示Activity
android·frameworks