---
title: PCIe と USB ドライバモデル
url: https://doc.liz6.com/ja/linux-kernel/10-drivers-and-device-model/02-pcie-and-usb-driver-model
locale: ja
area: linux-kernel
tags:
- linux-kernel
- drivers-and-device-model
date: 2026-06-30
modified: 2026-07-16
description: 'カバー範囲: PCIe 列挙 (BAR/MMIO/MSI) → pci_driver → USB プロトコルスタック (URB/エンドポイント) → usb_driver → ホットプラグ → 電源管理。カーネルバージョン: 2.6 ~ 6.x'
---

# PCIe と USB ドライバモデル

> カバー範囲: PCIe 列挙 (BAR/MMIO/MSI) → pci_driver → USB プロトコルスタック (URB/エンドポイント) → 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 バスの自己列挙: 4つのステップ、トポロジのスキャンから割り込みの割り当てまで">
  <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">② 設定スペースの読み取り</text>
  <text x="126" y="166" font-size="11" fill="#4f46e5">→ ベンダー ID / デバイス ID / クラスコード</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（ベースアドレスレジスタ）の割り当て</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;              // デバイス + 機能
    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 バスに登録）
};

// 登録: pci_register_driver(&mydrv)
// バインディング: PCI バス列挙 → 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 を 1 つ割り当て → 割り込みの並列処理
```

---

## USB

### USB プロトコルスタック

```c
// drivers/usb/core/
// 階層構造:
//   USB Core (usbcore): プロトコル実装、URB 管理
//   ホストコントローラドライバ (HCD): xHCI, EHCI, OHCI
//   デバイスドライバ: usb_driver（例: usb-storage, usbhid）

// USB デバイスの記述子階層:
//   デバイス記述子 → 構成記述子 → インターフェース記述子 → エンドポイント記述子
//   1 つの USB デバイス: 1 デバイス、N 構成、M インターフェース、K エンドポイント

// エンドポイントの種類:
//   Control:     セットアップパケット（設定コマンド）
//   Bulk:       大量データ転送（ストレージ、プリンタ）
//   Interrupt:  定期的ポーリング（HID: キーボード/マウス）
//   Isochronous: リアルタイムストリーム（音声/映像、再送なし）
```

### URB（USB リクエストブロック）

```c
// include/linux/usb.h
struct urb {
    struct usb_device   *dev;       // 宛先デバイス
    unsigned int        pipe;       // エンドポイント + 方向のエンコーディング
    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 バスに登録）
};
```

**重要**: `probe` の対象は `usb_device` ではなく `usb_interface` である。1 つの USB デバイスに複数のインターフェースが存在する場合があるため（例: ウェブカメラのビデオインターフェースとオーディオインターフェース）、異なるインターフェースに異なるドライバをバインドできる。

---

## ホットプラグ

<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">物理（サーバー）: ネイティブホットプラグ → 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">接続: ハブが D+ プルアップを検出 → 新規デバイスを列挙</text>
  <text x="396" y="118" font-size="11" fill="#0f766e">切断: D+ プルダウン → 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*
