---
title: PCIe and USB Driver Model
url: https://doc.liz6.com/en/linux-kernel/10-drivers-and-device-model/02-pcie-and-usb-driver-model
locale: en
area: linux-kernel
tags:
- linux-kernel
- drivers-and-device-model
date: 2026-06-30
modified: 2026-07-16
description: 'Coverage: PCIe enumeration (BAR/MMIO/MSI) → pci_driver → USB protocol stack (URB/endpoint) → usb_driver → hotplug → power management Kernel version: 2.6 ~ 6.x'
---

# PCIe and USB Driver Model

> Coverage: PCIe enumeration (BAR/MMIO/MSI) → pci_driver → USB protocol stack (URB/endpoint) → usb_driver → hotplug → power management
> Kernel version: 2.6 ~ 6.x

## PCIe

### Bus Enumeration

<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 bus self-enumeration four-step process: scanning topology to allocating interrupts">
  <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 Bus Self-Enumeration: Automatically discovers topology and allocates resources after power-on</text>
  <text x="360" y="50" text-anchor="middle" font-size="12" fill="#64748b">Self-enumerating bus, does not require DT/ACPI device descriptions</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">① Scan PCIe Topology</text>
  <text x="126" y="102" font-size="11" fill="#4f46e5">BIOS/UEFI (or kernel) scans bus → 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">② Read 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">③ Allocate BAR (Base Address Register)</text>
  <text x="126" y="230" font-size="11" fill="#4f46e5">→ MMIO/IO address ranges</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">④ Allocate MSI/MSI-X</text>
  <text x="126" y="294" font-size="11" fill="#4f46e5">→ Interrupt vectors</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">Each PCIe device is uniquely identified by BDF (Bus:Device.Function)</text>
</svg>

### pci_dev

```c
// include/linux/pci.h
struct pci_dev {
    struct pci_bus  *bus;               // Bus it belongs to
    unsigned int    devfn;              // device + function
    unsigned short  vendor, device;     // Matches pci_device_id
    unsigned short  subsystem_vendor, subsystem_device;
    u32             class;              // Device class (NVMe, VGA, USB, ...)

    struct resource resource[6];        // BAR 0~5 (MMIO/IO address ranges)
    unsigned int    irq;                // Interrupt number (may be a new number allocated after MSI)
    struct pcie_link_state *link_state; // PCIe link (gen, width)

    // DMA
    u64             dma_mask;           // Addressable range
    struct device   dev;                // Embedded device (device model)
};
```

### pci_driver

```c
struct pci_driver {
    const struct pci_device_id *id_table;  // Match 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;           // Embedded driver (registered to PCI bus)
};

// Registration: pci_register_driver(&mydrv)
// Binding: PCI bus enumeration → match vendor:device → probe()
```

### MSI/MSI-X

```c
// Message-based interrupts replacing INTx line interrupts:
//   MSI: Single vector or a small number of vectors
//   MSI-X: Up to 2048 independent vectors

// Allocation:
int nvec = pci_alloc_irq_vectors(pdev, 1, 16, PCI_IRQ_MSIX);
// → 16 MSI-X vectors → pci_irq_vector(pdev, i) to get Linux IRQ number

// Each vector has independent affinity → can be bound to different CPUs
// NVMe: One MSI-X per IO queue → parallel interrupt processing
```

---

## USB

### USB Protocol Stack

```c
// drivers/usb/core/
// Layered architecture:
//   USB Core (usbcore): Protocol implementation, URB management
//   Host Controller Driver (HCD): xHCI, EHCI, OHCI
//   Device Driver: usb_driver (e.g., usb-storage, usbhid)

// Descriptor hierarchy for USB devices:
//   Device Descriptor → Config Descriptor → Interface Descriptor → Endpoint Descriptor
//   One USB device: 1 device, N configs, M interfaces, K endpoints

// Endpoint types:
//   Control:     setup packet (configuration commands)
//   Bulk:       Large data transfers (storage, printers)
//   Interrupt:  Periodic polling (HID: keyboard/mouse)
//   Isochronous: Real-time streaming (audio/video, no retransmission)
```

### URB (USB Request Block)

```c
// include/linux/usb.h
struct urb {
    struct usb_device   *dev;       // Target device
    unsigned int        pipe;       // Encodes endpoint + direction
    void                *transfer_buffer;
    u32                 transfer_buffer_length;
    int                 actual_length;

    usb_complete_t      complete;   // Completion callback

    int                 status;     // Completion status (0=OK, -EPROTO, -ETIMEDOUT, ...)
    int                 interval;   // Polling interval for interrupt/isochronous transfers
};
```

### usb_driver

```c
struct usb_driver {
    const struct usb_device_id *id_table;  // VID/PID match
    int (*probe)(struct usb_interface *, const struct usb_device_id *);
    void (*disconnect)(struct usb_interface *);
    struct device_driver drvwrap;          // Embedded driver (registered to USB bus)
};
```

**Key**: The object passed to `probe` is `usb_interface`, not `usb_device`—because a USB device can have multiple interfaces (e.g., webcam: video interface + audio interface), and different interfaces can bind to different drivers.

---

## Hotplug

<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="How PCIe and USB hotplug events converge into the uevent/udev process">
  <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">Hotplug: How PCIe and USB events become usable devices</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 Hotplug</text>
  <text x="56" y="100" font-size="11" fill="#4f46e5">Physical (Server): native hotplug → pciehp</text>
  <text x="56" y="118" font-size="11" fill="#4f46e5">Virtual (VM): QEMU device_add (hotplug simulation)</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 Hotplug</text>
  <text x="396" y="100" font-size="11" fill="#0f766e">Connect: hub detects D+ pull-up → enumerate new device</text>
  <text x="396" y="118" font-size="11" fill="#0f766e">Disconnect: 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">Kernel sends 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 processes</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">Module loading (modalias) + device node creation</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 and USB trigger paths differ, but both converge to uevent → udev, uniformly completing module loading and device node creation</text>
</svg>

---

## References

- **Source Code**: `drivers/pci/`, `drivers/usb/core/`, `include/linux/pci.h`, `include/linux/usb.h`
- **Kernel Documentation**: `Documentation/PCI/`, `Documentation/driver-api/usb/`
- **LWN**: "The PCI subsystem", "USB in the Linux kernel"

*Keywords: PCIe, BAR, MSI-X, pci_driver, USB, URB, endpoint, usb_driver, hotplug, udev*
