接続されている USB デバイス一覧を取得する

Recipe ID: hw-007

rusb (libusb のラッパー) を使用して、システムに接続されている USB デバイスの情報を取得します。

1. 依存関係の追加

src-tauri ディレクトリに移動して、以下のコマンドを実行します。

cargo add rusb

※ Windows ではドライバの問題(WinUSBドライバが必要)があるため、単にリストアップするだけであれば cargo add sysinfo などでシステム情報を取得する方が簡単な場合がありますが、ここでは USB 通信の前段階として rusb を紹介します。

2. 実装コード

use rusb::{Device, DeviceDescriptor, Context, UsbContext};

#[derive(serde::Serialize)]
struct UsbDevice {
    bus: u8,
    address: u8,
    vid: u16,
    pid: u16,
    manufacturer: Option<String>,
    product: Option<String>,
}

#[tauri::command]
fn list_usb_devices() -> Result<Vec<UsbDevice>, String> {
    let context = Context::new().map_err(|e| e.to_string())?;
    let devices = context.devices().map_err(|e| e.to_string())?;

    let mut result = Vec::new();

    for device in devices.iter() {
        let device_desc = device.device_descriptor().map_err(|e| e.to_string())?;
        
        let mut manufacturer = None;
        let mut product = None;

        // ハンドルを開いてデスクリプタを読み取る
        // (権限がないと失敗するため、失敗時は無視してIDのみ返す)
        if let Ok(handle) = device.open() {
            let timeout = std::time::Duration::from_secs(1);
            let languages = handle.read_languages(timeout).unwrap_or_default();
            
            if let Some(&language) = languages.first() {
                manufacturer = handle.read_manufacturer_string(language, &device_desc, timeout).ok();
                product = handle.read_product_string(language, &device_desc, timeout).ok();
            }
        }

        result.push(UsbDevice {
            bus: device.bus_number(),
            address: device.address(),
            vid: device_desc.vendor_id(),
            pid: device_desc.product_id(),
            manufacturer,
            product,
        });
    }

    Ok(result)
}

注意点

rusb (libusb) でデバイスを開く(open())には、OS 側で適切なドライバ(WindowsならWinUSB、Linuxならudevルール)が設定されている必要があります。単に接続されていることを確認するだけであれば vid / pid のチェックのみで十分です。