---
title: PCIe 与 USB 驱动模型
url: https://doc.liz6.com/linux-kernel/10-drivers-and-device-model/02-pcie-and-usb-driver-model
locale: zh
area: linux-kernel
tags:
- linux-kernel
- 驱动与设备模型
date: 2026-06-30
modified: 2026-07-11
description: '覆盖: PCIe 枚举 (BAR/MMIO/MSI) → pci_driver → USB 协议栈 (URB/endpoint) → usb_driver → 热插拔 → 电源管理 内核版本: 2.6 ~ 6.x'
---

# PCIe 与 USB 驱动模型

> 覆盖: PCIe 枚举 (BAR/MMIO/MSI) → pci_driver → USB 协议栈 (URB/endpoint) → usb_driver → 热插拔 → 电源管理
> 内核版本: 2.6 ~ 6.x

## PCIe

### 总线枚举

<svg viewBox="0 0 720 380" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="PCIe 总线自枚举四步流程:扫描拓扑到分配中断">
  <defs><marker id="pcie-ah" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#475569"/></marker></defs>
  <rect width="720" height="380" fill="#ffffff"/>
  <text x="360" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">PCIe 总线自枚举:上电后自动发现拓扑与分配资源</text>
  <text x="360" y="50" text-anchor="middle" font-size="12" fill="#64748b">自枚举总线,不需要 DT/ACPI 描述设备</text>

  <rect x="110" y="66" width="500" height="46" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="126" y="85" font-size="12.5" font-weight="700" fill="#3730a3">① 扫描 PCIe 拓扑</text>
  <text x="126" y="102" font-size="11" fill="#4f46e5">BIOS/UEFI(或内核)扫描总线 → bus/device/function</text>
  <line x1="360" y1="112" x2="360" y2="128" stroke="#475569" stroke-width="1.7" marker-end="url(#pcie-ah)"/>

  <rect x="110" y="130" width="500" height="46" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="126" y="149" font-size="12.5" font-weight="700" fill="#3730a3">② 读取 configuration space</text>
  <text x="126" y="166" font-size="11" fill="#4f46e5">→ vendor ID / device ID / class code</text>
  <line x1="360" y1="176" x2="360" y2="192" stroke="#475569" stroke-width="1.7" marker-end="url(#pcie-ah)"/>

  <rect x="110" y="194" width="500" height="46" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="126" y="213" font-size="12.5" font-weight="700" fill="#3730a3">③ 分配 BAR(Base Address Register)</text>
  <text x="126" y="230" font-size="11" fill="#4f46e5">→ MMIO/IO 地址范围</text>
  <line x1="360" y1="240" x2="360" y2="256" stroke="#475569" stroke-width="1.7" marker-end="url(#pcie-ah)"/>

  <rect x="110" y="258" width="500" height="46" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="126" y="277" font-size="12.5" font-weight="700" fill="#3730a3">④ 分配 MSI/MSI-X</text>
  <text x="126" y="294" font-size="11" fill="#4f46e5">→ 中断向量</text>

  <rect x="110" y="322" width="500" height="42" rx="8" fill="#e0e7ff"/>
  <text x="360" y="347" text-anchor="middle" font-size="12.5" font-weight="600" fill="#3730a3">每个 PCIe 设备由 BDF(Bus:Device.Function)唯一标识</text>
</svg>

### pci_dev

```c
// include/linux/pci.h
struct pci_dev {
    struct pci_bus  *bus;               // 所在总线
    unsigned int    devfn;              // device + function
    unsigned short  vendor, device;     // 匹配 pci_device_id
    unsigned short  subsystem_vendor, subsystem_device;
    u32             class;              // 设备类 (NVMe, VGA, USB, ...)

    struct resource resource[6];        // BAR 0~5 (MMIO/IO 地址范围)
    unsigned int    irq;                // 中断号 (可能是 MSI 后分配的新号)
    struct pcie_link_state *link_state; // PCIe 链路 (gen, width)

    // DMA
    u64             dma_mask;           // 可寻址范围
    struct device   dev;                // 内嵌 device (设备模型)
};
```

### pci_driver

```c
struct pci_driver {
    const struct pci_device_id *id_table;  // 匹配表
    int (*probe)(struct pci_dev *, const struct pci_device_id *);
    void (*remove)(struct pci_dev *);
    int (*suspend)(struct pci_dev *, pm_message_t);
    int (*resume)(struct pci_dev *);
    struct device_driver driver;           // 内嵌 driver (注册到 PCI bus)
};

// 注册: pci_register_driver(&mydrv)
// 绑定: PCI bus 枚举 → match vendor:device → probe()
```

### MSI/MSI-X

```c
// 基于消息的中断替代 INTx 线中断:
//   MSI: 单个向量或少量向量
//   MSI-X: 最多 2048 个独立向量

// 分配:
int nvec = pci_alloc_irq_vectors(pdev, 1, 16, PCI_IRQ_MSIX);
// → 16 个 MSI-X 向量 → pci_irq_vector(pdev, i) 获取 Linux IRQ 号

// 每个向量独立亲和性 → 可绑到不同 CPU
// NVMe: 每 IO 队列一个 MSI-X → 中断并行处理
```

---

## USB

### USB 协议栈

```c
// drivers/usb/core/
// 分层:
//   USB Core (usbcore): 协议实现, URB 管理
//   Host Controller Driver (HCD): xHCI, EHCI, OHCI
//   Device Driver: usb_driver (如 usb-storage, usbhid)

// USB 设备的描述符层次:
//   Device Descriptor → Config Descriptor → Interface Descriptor → Endpoint Descriptor
//   一个 USB 设备: 1 device, N configs, M interfaces, K endpoints

// Endpoint 类型:
//   Control:     setup packet (配置命令)
//   Bulk:       大块传输 (存储, 打印机)
//   Interrupt:  定时轮询 (HID: 键盘/鼠标)
//   Isochronous: 实时流 (音视频, 无重传)
```

### URB (USB Request Block)

```c
// include/linux/usb.h
struct urb {
    struct usb_device   *dev;       // 目标设备
    unsigned int        pipe;       // endpoint + direction 编码
    void                *transfer_buffer;
    u32                 transfer_buffer_length;
    int                 actual_length;

    usb_complete_t      complete;   // 完成回调

    int                 status;     // 完成状态 (0=OK, -EPROTO, -ETIMEDOUT, ...)
    int                 interval;   // 中断/等时传输的轮询间隔
};
```

### usb_driver

```c
struct usb_driver {
    const struct usb_device_id *id_table;  // VID/PID 匹配
    int (*probe)(struct usb_interface *, const struct usb_device_id *);
    void (*disconnect)(struct usb_interface *);
    struct device_driver drvwrap;          // 内嵌 driver (注册到 USB bus)
};
```

**关键**: probe 的对象是 `usb_interface`，不是 `usb_device`——因为一个 USB 设备可以有多个接口（如 webcam: video interface + audio interface），不同接口可以绑定不同驱动。

---

## 热插拔

<svg viewBox="0 0 720 330" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="PCIe 与 USB 热插拔事件如何汇聚到 uevent/udev 流程">
  <defs><marker id="hot-ah" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#475569"/></marker></defs>
  <rect width="720" height="330" fill="#ffffff"/>
  <text x="360" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">热插拔:PCIe 与 USB 事件如何变成可用设备</text>

  <rect x="40" y="56" width="300" height="100" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="56" y="78" font-size="13" font-weight="700" fill="#3730a3">PCIe 热插拔</text>
  <text x="56" y="100" font-size="11" fill="#4f46e5">物理(服务器):native hotplug → pciehp</text>
  <text x="56" y="118" font-size="11" fill="#4f46e5">虚拟(VM):QEMU device_add(热插拔模拟)</text>

  <rect x="380" y="56" width="300" height="100" rx="8" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="396" y="78" font-size="13" font-weight="700" fill="#115e59">USB 热插拔</text>
  <text x="396" y="100" font-size="11" fill="#0f766e">连接:hub 检测 D+ pull-up → 枚举新设备</text>
  <text x="396" y="118" font-size="11" fill="#0f766e">断开:D+ pull-down → disconnect()</text>

  <line x1="190" y1="156" x2="145" y2="210" stroke="#475569" stroke-width="1.6" marker-end="url(#hot-ah)"/>
  <line x1="530" y1="156" x2="175" y2="210" stroke="#475569" stroke-width="1.6" marker-end="url(#hot-ah)"/>

  <rect x="70" y="210" width="170" height="46" rx="8" fill="#e0e7ff"/>
  <text x="155" y="238" text-anchor="middle" font-size="12" font-weight="600" fill="#3730a3">内核发 uevent</text>
  <line x1="240" y1="233" x2="256" y2="233" stroke="#475569" stroke-width="1.6" marker-end="url(#hot-ah)"/>

  <rect x="260" y="210" width="130" height="46" rx="8" fill="#e0e7ff"/>
  <text x="325" y="238" text-anchor="middle" font-size="12" font-weight="600" fill="#3730a3">udev 处理</text>
  <line x1="390" y1="233" x2="406" y2="233" stroke="#475569" stroke-width="1.6" marker-end="url(#hot-ah)"/>

  <rect x="410" y="210" width="250" height="46" rx="8" fill="#dcfce7" stroke="#4ade80"/>
  <text x="535" y="238" text-anchor="middle" font-size="12" font-weight="600" fill="#166534">模块加载(modalias)+ 设备节点</text>

  <rect x="60" y="272" width="600" height="42" rx="8" fill="#eef2ff"/>
  <text x="360" y="298" text-anchor="middle" font-size="12" fill="#3730a3">PCIe 与 USB 触发路径不同,但都收敛到 uevent → udev,统一完成模块加载与设备节点创建</text>
</svg>

---

## 参考

- **源码**: `drivers/pci/`, `drivers/usb/core/`, `include/linux/pci.h`, `include/linux/usb.h`
- **内核文档**: `Documentation/PCI/`, `Documentation/driver-api/usb/`
- **LWN**: "The PCI subsystem", "USB in the Linux kernel"

*关键词: PCIe, BAR, MSI-X, pci_driver, USB, URB, endpoint, usb_driver, hotplug, udev*
