世界を動かす技術を、日本語で。

Linuxドライバについて何も知らなくても基本的なLinuxデバイスドライバを書く方法

概要

  • Nanoleaf Pegboard Desk Dock をLinuxで動かすためのドライバ開発記録
  • 公式プロトコル情報 と逆アセンブル結果をもとに進行
  • libusb を使ったユーザースペースドライバの作成方針
  • udevルール による権限設定やNixOS対応の方法も紹介
  • Rustとrusbクレート を用いた実装の基本手順解説

Nanoleaf Pegboard Desk DockのLinux対応挑戦記

  • Nanoleaf Pegboard Desk Dock はWindows/macOS専用設計
  • Linuxで利用するため、 独自ドライバ 開発が必要となる状況
  • 公式サポートへ問い合わせたところ、 プロトコル仕様書 を迅速に入手
  • 仕様書には 電源・輝度管理 など未発見の機能も記載
  • 公式仕様と逆アセンブル結果の 照合作業 を実施

USBデバイスの基礎知識整理

  • Linuxでは lsusbコマンド でUSBデバイス情報を取得可能
  • idVendoridProduct でデバイスを一意に識別
  • USBデバイスは 複数の構成(Configuration)インターフェース(Interface) を持つ
  • Human Interface Device(HID) クラスとして認識されるが、RGB LED制御には未対応
  • 汎用HIDドライバ が動作するものの、LED制御などの拡張機能は未実装

ドライバ開発方針の選択

  • カーネルドライバ として/sys/class/leds配下に実装する案
    • 独自製品のため、カーネル統合のメリットが限定的
  • ユーザースペースドライバ (libusb利用)として実装する案
    • 実装・配布のハードルが低く、 rusbクレート でRustからも制御可能
  • 本記事では ユーザースペースドライバ 方式を採用

udevルールによるユーザー権限設定

  • USBデバイス制御にはroot権限 が必要だが、一般ユーザーで操作可能にしたい

  • /etc/udev/rules.d/70-pegboard.rules に以下の内容を記述

    ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ATTRS{idVendor}=="37fa", ATTRS{idProduct}=="8201", MODE="0770", TAG+="uaccess"
    
    • idVendorとidProductはlsusb出力から取得
    • TAG+="uaccess" で現在のユーザーにアクセス権付与
  • NixOSの場合、カスタムパッケージ作成とservices.udev.packages拡張で対応

    • pkgs.writeTextFileを利用したルールファイル生成

Rust + rusbでのドライバ実装入門

  • 新規Rustプロジェクト作成 とrusbクレート追加

    cargo new gamer-driver
    cd gamer-driver
    cargo add rusb
    
  • デバイスハンドル取得例

    use rusb::{Context, UsbContext};
    const VENDOR: u16 = 0x37fa;
    const DEVICE: u16 = 0x8201;
    
    fn main() {
        let context = Context::new().expect("cannot open libusb context");
        let device = context.open_device_with_vid_pid(VENDOR, DEVICE).expect("cannot get device");
        let descriptor = device.device().device_descriptor().expect("cannot describe device");
        println!("{descriptor:#?}");
    }
    
  • インターフェースのクレーム

    • bInterfaceNumberが0であることを確認
    • claim_interface メソッドで取得
    • 失敗時(Busy)は カーネルドライバがデバイスを保持 している可能性
  • カーネルドライバのデタッチ

    if device.kernel_driver_active(INTERFACE).expect("cannot get kernel driver") {
        device.detach_kernel_driver(INTERFACE).expect("cannot detach kernel driver");
    }
    device.claim_interface(INTERFACE).expect("unable to claim interface");
    
    • 必要に応じて attach_kernel_driver で再接続も可能

次のステップ:データ送信準備

  • write_bulk, write_control, write_interrupt 等のメソッドでデータ送信可能
  • どのメソッドを利用するかは デバイス仕様エンドポイント属性 に依存
  • 以降は ペイロード送信LED制御コマンド 実装へ発展予定

この流れで、 ユーザースペースから安全かつ手軽にNanoleaf Pegboard Desk DockをLinuxで制御 するための基礎が整います。

Hackerたちの意見

ユーザースペースのUSB HIDドライバがRustで作られてるんだって。正直、カーネルドライバよりこっちの方が自分には面白いし、役に立ちそうだな。

「ベンダーに連絡して、プロトコルに関する仕様書やドキュメントを共有してもらえないか聞いてみようと思ったんだ。驚いたことに、Nanoleafのテクニカルサポートが4時間以内に返事をくれて、Desk DockやRGBストリップで使われているプロトコルの詳細を教えてくれた。すごくない?まだまだ多くのベンダーは、こういう些細なことに貴重な知的財産があると思ってるみたい。仕様を自由に配布することが、自分たちよりも競合に利益をもたらすってことに気づいてないんだよね。」

同じ反応だった!ナノリーフはほんとにクールだよね。

10年くらい前に、人気のPC液冷ハードウェアのメーカーで似たような経験をしたことがある。Linux用のドライバーを開発したいってメールしたら、すぐにマネージャーとエンジニアチームに繋いでもらえた。結構進展があったんだけど、結局その話は頓挫しちゃった。というのも、彼らの部品供給業者が契約違反だって脅してきたから。どうやらその供給業者は「データセンター」向け(一般消費者向けじゃない)バージョンのハードウェアを売ってて、その管理ソフトウェアに高額なライセンス料を取ってたみたい。面白いことに、数年後に誰かがUSBアナライザーでその全貌を逆解析して公開したんだよねXD。(私じゃないけど)

その記事の部分、めっちゃ笑っちゃった。大学の時に同じことをやったことがあるんだ。研究室でWi-Fi信号の研究をしたくて、SomeSmallTech社のWi-Fiアダプタをいくつか持ってたんだけど、その時はLinux用のドライバーがなかった。だから、会社の公開メールアドレスに「科学のために」データシートをお願いするメールを送ったんだ。残念ながら、PR担当者から「学術研究とのコラボレーションに関する会社の方針はない」と返事が来た。(でも、すぐに返信してくれたのは良かった。)面白いことに、数年後にその会社で働くことになった。最初に会社のネットワークにログインした時、あのデータシートを探したんだ。PDFには「機密」ってスタンプがいっぱい押されてたよ :)

あるドイツのブランドのソーラーインバータを持ってるんだけど、ホームアシスタントを使いたくてrs232データが必要だったんだ。サポートに連絡したら、NDAにサインしてくれって言われた。まあ、いいけど。偽名と住所でサインしたら、ファイルを送ってくれたんだ。でも、そのファイルはオンラインで公開されてた。顔面パン!だから、私の質問は、データシートにどんな「IP」が保護される必要があるのかってこと。しかも、これは特別な製品じゃなくて、何百万も売られてる一般的なソーラー製品なんだけど。RS-232プロトコル?マジで?

IPの問題じゃなくて、残念ながら人々の反応なんだよね。助けてくれる人には感謝されるけど、他の人はすぐに「試したけどうまくいかなかったから、全部書き直してくれ」とか「プロジェクトをやってくれ」とか「自分の希望に合わせて製品を再設計してくれ」って言ってくる。で、丁寧に断ると、すぐに「ビジネスを潰すぞ」って脅しが来たりするから、最も安全なのは詳細を全く教えないか、他の返信にあるように「漏らす」ことだね。

この投稿の書き方がすごく楽しかった。コードや実行方法、失敗した道筋なんかが含まれてて、まるで著者の旅を一緒に体験してるみたい。何とか動くものを作るために、必要なことを見つけ出していく過程がわかるんだよね。

この投稿楽しんだけど、実際の「プロダクション」ユーザースペースドライバの次のステップがどうなるのか気になるな。これって、通常は起動時に設定されるデーモンみたいなものなの?それから、何かのソケットを通じて設定GUIが通信する感じ?

「もう一度実行して、偶然じゃなかったか確認しよう!その引用、わかったよ。」

ノートパソコンでFreeBSDを動かしたいんだけど、Wi-Fiカードの完全なドライバがないんだ。AIコーディングアシスタントエージェントを使って、動くドライバを作るのに役立てられないか考えたけど、AIエージェントの扱いが面倒でそのままにしてる。 (VSCodeは使ってないんだ。)

ClaudeのコードはCLIインターフェースだから、君のスタイルに合うかも?でも高いけどね。

著者はOpenRGBでサポートを実装した方が、他の人たちにとってもっと役立ったかもしれないけど、これでも十分クールだね。

Cでやってくれたらよかったのに、Rustを学ばなくて済んだのに。でも、そろそろRustを学ぶ時期なのかな。

Rustって実はそんなに難しくないよ。普通のソフトウェアを作るなら、Cよりもいろんな面で簡単だし、特別なデータ構造のライブラリみたいなのじゃなければね。

素晴らしい投稿だね!私はNanoleafの3Dのやつを持ってて、テレビのアンビライトみたいに使いたかったんだけど、体験は…微妙だった。Hyperionプロジェクトのことは知ってるし、いつか設定したいと思ってるけど、今のところこのコードを使ってLinuxボックスからストリップを制御することを試してみようかな。

すごいね!でもちょっと悲しい気持ちにもなる。昔のパラレルポートやISAインターフェースは、比較するとすごくシンプルだったし、抽象化のレイヤーも少なかったよね。単にワイヤーを繋いで、ポートに書き込むだけ。子供の頃、電子パーツ屋のクリアランスコーナーでISAヘッダーのついたブレイクアウトボードを見つけたのを覚えてる。なんとなく持ち帰って、7セグメントLEDをワイヤラップして接続したんだ。電源とグラウンドは簡単だったし、各セグメントはデータラインに繋いで、シンプルなバッファICを使った。エン enableポートには最小限のアドレスラインだけ使って、簡単なANDゲートか何かで推測して接続したんだ。あのアドレスに書き込んで、最初の試みで動いたときは本当に驚いた!今はUSBみたいなプロトコルを見て、何百ページもある文書に圧倒されちゃう。あの頃のワクワク感や実現可能性を感じることができないな。