# 通知設計書 17-INTERFACE イベント

## 概要

本ドキュメントは、FreeBSDカーネルにおけるUSB INTERFACEイベント通知の設計を記述する。USBデバイスの各インターフェースについて接続（ATTACH）または切断（DETACH）時にdevctl(4)サブシステムを通じてユーザーランドに通知を送信する仕組みについて定義する。

### 本通知の処理概要

USB INTERFACEイベント通知は、USBデバイスのアタッチまたはデタッチ時に、デバイスレベルの通知（No.16 DEVICE イベント）に続いて、各インターフェースごとに個別にdevctl_notifyにより発行されるカーネル通知である。インターフェース固有の情報（インターフェース番号、エンドポイント数、クラス、サブクラス、プロトコル）が含まれる。

**業務上の目的・背景**：USBデバイスは複数のインターフェースを持つことがあり（複合デバイス）、各インターフェースが異なる機能を提供する。例えば、USBヘッドセットはオーディオインターフェースとHIDインターフェースを持つ。ユーザーランドのデバイス管理デーモンがインターフェースレベルでの詳細なデバイス検知を行い、適切なドライバのバインディングやサービスの起動を行うために本通知が必要である。

**通知の送信タイミング**：usb_notify_addq()関数内のDEVICE通知発行後、有効なインターフェースディスクリプタを持つ各インターフェースに対して順次送信される。

**通知の受信者**：devctl(4)デバイスをオープンしているユーザーランドプロセス。主にdevd(8)デーモンが受信する。

**通知内容の概要**：システム名「USB」、サブシステム名「INTERFACE」、タイプ「ATTACH」または「DETACH」、データとしてUSBデバイス・インターフェース詳細情報（ベンダーID、プロダクトID、インターフェース番号、エンドポイント数、インターフェースクラス、サブクラス、プロトコル等）が通知される。

**期待されるアクション**：devd(8)がインターフェースレベルのイベントを検知し、インターフェースクラスに基づいて適切なドライバやサービスの起動・停止を行う。

## 通知種別

カーネルdevctl通知（devctl_notify経由）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（devctl(4)キュー経由） |
| 優先度 | 通常 |
| リトライ | なし |

### 送信先決定ロジック

devctl(4)デバイスファイル(/dev/devctl)をオープンしているすべてのプロセスに対してブロードキャスト送信される。

## 通知テンプレート

### devctl通知の場合

| 項目 | 内容 |
|-----|------|
| システム名 | USB |
| サブシステム名 | INTERFACE |
| タイプ | ATTACH / DETACH |
| データ | USBインターフェース情報（下記参照） |

### 本文テンプレート

```
!system=USB subsystem=INTERFACE type=ATTACH ugen={ugen_name} cdev={ugen_name} vendor=0x{idVendor} product=0x{idProduct} devclass=0x{bDeviceClass} devsubclass=0x{bDeviceSubClass} sernum="{serial}" release=0x{bcdDevice} mode={host|device} interface={bInterfaceNumber} endpoints={bNumEndpoints} intclass=0x{bInterfaceClass} intsubclass=0x{bInterfaceSubClass} intprotocol=0x{bInterfaceProtocol}
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| ugen | ugenデバイス名 | udev->ugen_name | Yes（USB_HAVE_UGEN有効時） |
| cdev | キャラクタデバイス名 | udev->ugen_name | Yes（USB_HAVE_UGEN有効時） |
| vendor | ベンダーID | udev->ddesc.idVendor | Yes |
| product | プロダクトID | udev->ddesc.idProduct | Yes |
| devclass | デバイスクラス | udev->ddesc.bDeviceClass | Yes |
| devsubclass | デバイスサブクラス | udev->ddesc.bDeviceSubClass | Yes |
| sernum | シリアル番号 | usb_get_serial(udev) | Yes |
| release | リリース番号 | udev->ddesc.bcdDevice | Yes |
| mode | USBモード | udev->flags.usb_mode | Yes |
| interface | インターフェース番号 | iface->idesc->bInterfaceNumber | Yes |
| endpoints | エンドポイント数 | iface->idesc->bNumEndpoints | Yes |
| intclass | インターフェースクラス | iface->idesc->bInterfaceClass | Yes |
| intsubclass | インターフェースサブクラス | iface->idesc->bInterfaceSubClass | Yes |
| intprotocol | インターフェースプロトコル | iface->idesc->bInterfaceProtocol | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| カーネル内部 | USBデバイスアタッチ/デタッチ | 有効なインターフェースディスクリプタが存在する場合 | DEVICE通知の後に各インターフェースごとに発行 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| インターフェースがNULL | usbd_get_iface()がNULLを返した場合、ループを終了 |
| ディスクリプタがNULL | iface->idescがNULLの場合、そのインターフェースをスキップ |
| USB_IFACE_MAX到達 | インターフェースインデックスがUSB_IFACE_MAXに達した場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[DEVICE通知発行後] --> B[i = 0からループ開始]
    B --> C[usbd_get_iface udev, i]
    C --> D{iface == NULL?}
    D -->|Yes| E[ループ終了]
    D -->|No| F{iface->idesc == NULL?}
    F -->|Yes| G[continue 次のインターフェースへ]
    F -->|No| H[sbuf_new_auto]
    H --> I[インターフェース情報をsbufに書き込み]
    I --> J[sbuf_finish]
    J --> K[devctl_notify USB/INTERFACE/type/sbuf_data]
    K --> L[sbuf_delete]
    L --> M[i++ 次のインターフェースへ]
    M --> C
    G --> M
    E --> N[終了]
```

## データベース参照・更新仕様

該当なし（カーネル内メモリ構造体の操作のみ）

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| usb_device構造体 | USBデバイス情報の参照 | デバイスレベルの情報 |
| usb_interface構造体 | USBインターフェース情報の参照 | インターフェースレベルの情報 |
| usb_interface_descriptor | USBインターフェースディスクリプタ | クラス、サブクラス、プロトコル等 |

### テーブル別参照項目詳細

#### usb_interface構造体

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| idesc->bInterfaceNumber | インターフェース番号 | 直接参照 |
| idesc->bNumEndpoints | エンドポイント数 | 直接参照 |
| idesc->bInterfaceClass | インターフェースクラス | 直接参照 |
| idesc->bInterfaceSubClass | インターフェースサブクラス | 直接参照 |
| idesc->bInterfaceProtocol | インターフェースプロトコル | 直接参照 |

### 更新テーブル一覧

該当なし（通知の発行のみ）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| devctl送信失敗 | devctlキューが満杯 | 通知は破棄される |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし。

## セキュリティ考慮事項

- devctl(4)デバイスへのアクセスにはroot権限が必要
- USBデバイスのシリアル番号がインターフェース通知にも含まれる
- インターフェースクラス情報によりデバイス機能の詳細が推定可能

## 備考

- INTERFACE通知はusb_notify_addq()関数内のループ（2747-2791行目）で発行される
- devctl_notify()呼び出しは2789行目で行われる
- USB_IFACE_MAXまでのインターフェースがスキャンされる
- 1つのUSBデバイスに対して複数のINTERFACE通知が発行される可能性がある
- DEVICE通知（No.16）とINTERFACE通知は同一のusb_notify_addq()関数内で連続して発行される

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

USBインターフェース構造体とインターフェースディスクリプタの構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | usb.h | `sys/dev/usb/usb.h` | usb_interface_descriptor構造体 |
| 1-2 | usb_device.h | `sys/dev/usb/usb_device.h` | usb_interface構造体、USB_IFACE_MAX定数 |

**読解のコツ**: USBインターフェースはusbd_get_iface()でインデックスにより取得される。idescがNULLのインターフェースは未使用（またはインターフェースディスクリプタが存在しない）と判断される。

#### Step 2: INTERFACE通知ループを理解する

usb_notify_addq()関数内のインターフェース走査ループを追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | usb_device.c | `sys/dev/usb/usb_device.c` | usb_notify_addq()関数のインターフェースループ（2747-2791行目） |

**主要処理フロー**:
- **2747行目**: for (i = 0; i < USB_IFACE_MAX; i++) ループ開始
- **2748行目**: usbd_get_iface(udev, i)でインターフェース取得
- **2749-2750行目**: iface == NULLでbreak
- **2751-2752行目**: iface->idesc == NULLでcontinue
- **2754行目**: sbuf_new_auto()で新しいsbuf作成
- **2755-2787行目**: sbuf_printf()でインターフェース情報を書き込み
- **2788行目**: sbuf_finish()
- **2789行目**: devctl_notify("USB", "INTERFACE", type, sbuf_data(sb))
- **2790行目**: sbuf_delete(sb)

### プログラム呼び出し階層図

```
usb_notify_addq() [sys/dev/usb/usb_device.c:2699]
    |
    +-- devctl_notify("USB", "DEVICE", type, ...)  -- No.16
    |
    +-- [for i = 0..USB_IFACE_MAX]
           |
           +-- usbd_get_iface(udev, i)
           +-- sbuf_new_auto() / sbuf_printf() / sbuf_finish()
           +-- devctl_notify("USB", "INTERFACE", type, sbuf_data)
           +-- sbuf_delete()
```

### データフロー図

```
[入力]                       [処理]                          [出力]

usb_device (udev) --->  usb_notify_addq()           --->  devctl(4)キュー
usb_interface[]          |                              |
type                     +-- 各インターフェース走査       +-- devd(8)
                         +-- sbufでデータ生成
                         +-- devctl_notify(INTERFACE)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| usb_device.c | `sys/dev/usb/usb_device.c` | ソース | usb_notify_addq()の実装、devctl_notify呼び出し |
| usb_device.h | `sys/dev/usb/usb_device.h` | ヘッダー | usb_device、usb_interface構造体の定義 |
| usb.h | `sys/dev/usb/usb.h` | ヘッダー | USB定数・ディスクリプタ構造体の定義 |
| devctl.h | `sys/sys/devctl.h` | ヘッダー | devctl_notify()のプロトタイプ |
