
Raspberry Pi Pico W用ファームウェア。ドライバ不要のUSB Wi-Fiアダプタを作成し、透過的なレイヤ2ブリッジ、WPA2/WPA3認証、および帯域外管理コンソールを提供します。
= pico-usb-wifi :toc: macro :toclevels: 3 :idprefix: :idseparator: -
pico-usb-wifiは、Raspberry Pi Pico Wをドライバ不要のUSB Wi-Fiアダプタに変えるファームウェアで、USB CDC-NCMデバイスとして列挙されます。
:figure-caption: AIスロップ
.pico-usb-wifi 図 image::images/openrouter-banana2-rpi-pico.png[]
このファームウェアは、Pico Wの無線インターフェースとUSBインターフェースの間でフレームを転送する透過的なレイヤ2ブリッジとして動作します。 ホストのUSBインターフェースはPico WのWi-FiステーションのMACアドレスを採用し、エンドツーエンドで単一のMACおよびIP識別子を提供します。
ホスト側のドライバ、カーネルモジュール、ワイヤレススタックは不要です。<<no-host-side-wi-fi-stack,ホスト側Wi-Fiスタック不要>>を参照。
ホストに必要なのは、現代のすべてのLinux、macOS、Windows、モバイルOSに標準搭載されているcdc_ncmおよびcdc_acmドライバのみです。
== 機能
pico-usb-wifiは以下の機能を提供します:
.現実世界の状況 image::images/slop_2.png[]
== 存在理由
今後の組み込みLinuxプロジェクトでUSB Wi-Fiアダプタが必要だった。 安価なUSB Wi-Fiドングルを持っていなかったので、実店舗で5ドルで買いに行く代わりに、長い週末の2日間と約100万Claude Codeトークンを使ってこのファームウェアを構築した。
白一百, pico-usb-wifiの著者
Googleは実現不可能と言っていた:
.pico-usb-wifi "実現不可能" image::images/gemini_says_not_possible.png[]
toc::[]
== ホスト側Wi-Fiスタック不要
USB Wi-Fiドングルとは異なり、このアダプタはホストにイーサネットライクなインターフェースのみを公開します。 Pico Wが無線側全体(無線機、アソシエーション、WPA2/WPA3サプリカント、各国の規制ドメイン)を内包しています。
これにより、システムはwpa_supplicant、cfg80211/mac80211ワイヤレススタック、各国の規制データベース、チップセットファームウェアやベンダドライバのインストールを回避できます。
Wi-Fi認証情報のプロビジョニングは、ホスト側のワイヤレスツールではなく、デバイス上の帯域外管理コンソールを介して行われます。
これにより、リソースが制限されたアプライアンスホストや、ワイヤレスドライバがないホスト、あるいはベンダカーネルにワイヤレスドライバが不足しているホストでも、汎用CDCクラスドライバのみを使用して無線ネットワークに接続できます。
== 動作の仕組み
[#fig-topology] .トポロジ図 image::images/topology.svg[透過的なレイヤ2ブリッジトポロジ,820]
ホストのUSBインターフェースにはPico WのWi-FiステーションのMACアドレスが割り当てられるため、エンドツーエンドで単一のMACアドレスが存在し、Pico WはイーサネットフレームをUSBとWi-Fi間でそのまま転送できます。 Wi-Fiステーションは複数のMACアドレスをブリッジできないため、ホストとステーションを一つのMACに統合することで透過ブリッジが可能になります。 完全な根拠、データパス、IPv6/マルチキャスト処理については、<<architecture,アーキテクチャ>>を参照してください。
== ホスト要件
ホストには標準搭載のcdc_ncmおよびcdc_acmドライバが必要です。
どちらも10年以上メインラインLinuxの一部となっているため、現在サポートされているカーネルにはすべて含まれています。
外部モジュール、ファームウェアブロブ、ベンダドライバは一切不要です。
同じクラスドライバはmacOS、Windows 10以降、Android、iOSにも存在します。
[NOTE] 他のオペレーティングシステムではテストしていません。
== ビルド
このプロジェクトは標準のpico-sdk CMakeプロジェクトです。 ARM組み込みツールチェーン、CMake、ビルドバックエンド(NinjaまたはMake)、Python 3、およびサブモジュールを含むpico-sdkのチェックアウトが必要です。 pico-sdkにバンドルされているTinyUSBとlwIPはそのまま使用されます。
=== 依存関係
Arch系システム(Arch、CachyOS、Manjaro)では、公式リポジトリからツールチェーンが提供されます:
arm-none-eabi-newlibは組み込みCライブラリとヘッダを提供します。これがないとクロスコンパイラはstdint.hなどのヘッダを見つけられません。
libusbはpicotoolにのみ必要で、pico-sdkは最初のconfigure時にソースからビルドしてUF2を生成します。別途picotoolパッケージは不要です。
=== ビルド手順
git clone -b 2.2.0 --recurse-submodules https://github.com/raspberrypi/pico-sdk export PICO_SDK_PATH="$PWD/pico-sdk"
cp src/wifi_config.h.example src/wifi_config.h # その後SSID/パスワードを編集、または空白のまま cmake -S . -B build -G Ninja -DPICO_BOARD=pico_w -DCMAKE_BUILD_TYPE=Release cmake --build build
-G Ninjaフラグはオプションです。省略するとデフォルトのMakeジェネレータが使用されます(その場合はcmake --build build -j)。
wifi_config.hはコンパイル時のデフォルト認証情報を保持し、.gitignoreされています。
空白のままにすると、ベイクされた認証情報がないイメージが生成され、管理コンソール(<<management-console,管理コンソール>>)を使用して実行時にプロビジョニングされます。認証情報を記入すると、デフォルトネットワークがベイクされます。
== ファームウェアの書き込み
ここではボードへのファームウェアの書き込み手順を示します。
. BOOTSELボタンを押しながら、ボードをUSBで接続します。
ボードはRPI-RP2 USBマスストレージボリュームとしてマウントされます。通常は/run/media/<ユーザー>/RPI-RP2または/media/<ユーザー>/RPI-RP2にマウントされます。
. pico-usb-wifi.uf2をそのボリュームにコピーします。
ボードは自動的にファームウェアで再起動します。
. Wi-Fi接続を受け取るホストにボードを接続します。
== Linuxホストでの使用
デバイスをホストに接続し、管理コンソール(<<management-console,管理コンソール>>)を介してWi-Fi認証情報を一度プロビジョニングします。 ホストのインターフェースは、アクセスポイントのネットワーク上の有線接続と同様に動作します。
インターフェースを自動管理するホスト(NetworkManager、systemd-networkd、dhcpcd)は設定不要です。ブリッジ上でDHCPとSLAACを実行し、有線クライアントと同様に単一のIPv4アドレス、IPv6アドレス、アクセスポイントのゲートウェイ、DNSを取得します。
デバイス側のアドレスやゲートウェイを設定する必要はありません。Picoは何も保持していません。
インターフェースのMACアドレスはWi-FiステーションのMACであり、これによって単一の識別子がネットワークに提示されます。
ここでのipコマンドの出力は、結果のインターフェースを示しています:アクセスポイントのサブネット上の通常のDHCP/SLAACクライアントであり、ステーションのMACを持ち、Picoの痕跡はありません。
== 管理コンソール
管理コンソールは設定フロントエンドで、最初のCDC-ACMシリアル機能(通常は/dev/ttyACM0)にあります。
デバイスが列挙されるとすぐに(Wi-Fiがアソシエートされる前でも)アクセス可能なため、プロビジョニングにネットワークは不要です。
picocomやscreenなどのシリアルターミナルで開きます。USB CDCではボーレートは関係ありません。
コンソールは入力をエコーバックし、プロンプトを表示します。すべてのコマンドはデバイスの全状態を出力します。
Wi-Fi認証はWPA2-PSKまたはWPA3-SAE(AES)のいずれかです。
パスワードはネットワークのパスフレーズ、またはパスワードを空白にした場合はオープンです。
パスワードで保護されたプロファイルはWPA2/WPA3トランジションモードを使用するため、どちらの種類のアクセスポイントにも接続します。
コンソールは最大8つの認証情報_プロファイル_を保存します。1つは_アクティブ_プロファイルで、デバイスはそれにアソシエートします。
set ssid/set passはアクティブプロファイルを編集し、list/use/delはセットを管理し、scanは近隣のネットワークを検出し、番号付きリストから一つに接続します。これは、入力しづらい文字を含むSSIDに便利です。
コマンドワードは大文字小文字を区別しません。コンソールは小文字で表示します。
以下のセッションでは、スキャンによってネットワークをプロビジョニングしています。
$ picocom /dev/ttyACM0
変更は即座に反映され、アクティブプロファイルで再アソシエートします。再起動は不要です。
さらにネットワークを保存するには繰り返します。listで表示し、use <n>でアクティブプロファイルを切り替えます:
saveはすべてのプロファイルをフラッシュに永続化します。restoreは保存されたレコードを再読み込みすることで未保存の編集を破棄します。
以下の表はコマンドセットです。
[#tbl-config-commands] .管理コンソールコマンド [cols="2,3", options="header"] |=== |コマンド |効果
|set ssid <テキスト>
|アクティブプロファイルのSSIDを設定し(値にはスペースを含めることができます)、再アソシエートします。プロファイルが存在しない場合は最初のプロファイルを作成します。
|set pass <テキスト>
|アクティブプロファイルのWPA2/WPA3パスフレーズを設定し(オープンネットワークの場合は空白)、再アソシエートします。
|set country <CC\|WORLDWIDE>
|各国の規制コードを設定します(次回起動時に完全に適用されます)。
|set debug <on\|off>
|デバッグコンソールで診断情報をストリーム出力します。<<debug-console,デバッグコンソール>>を参照。
|list
|保存されたプロファイルを一覧表示し、アクティブなものをマークします。
|use <n>
|プロファイル_n_をアクティブにして再アソシエートします。
|del <n>
|プロファイル_n_を削除します。
|scan
|近隣のネットワークをスキャンし、スキャンサブメニューに入ります(back、join <n>、scanで繰り返し、またはliveで継続的な非アソシエートストリーム)。joinは選択したネットワークをアクティブプロファイルとしてステージし、set passの準備をします。
|save
|すべてのプロファイルと設定をフラッシュに永続化します。
|restore
|保存された設定を再読み込みして未保存の変更を破棄します。
|===
ホストに割り当てられたアドレスは、状態ダンプでhost IPv4およびhost IPv6として表示されます。Picoは報告するアドレスを持たないため、ブリッジトラフィックからパッシブにスヌーピングされます。
設定セクタはフラッシュの末尾にあり、プログラムイメージ(先頭)とは分離されているため、通常のpico-usb-wifi.uf2の再書き込みでは保存されたプロファイルはそのまま残ります(全チップ消去を行うと消去されます)。
例外はv1.1.0_への_アップグレードです。レコードレイアウトが複数プロファイルを保持するように変更されたため、v1.1.0より前のレコードは破棄され、ネットワークを再度入力する必要があります(変更履歴を参照)。
== デバッグコンソール
デバッグコンソールは、2番目のCDC-ACMシリアル機能(通常は/dev/ttyACM1)上の書き込み専用の診断ストリームです。
管理コンソールでset debug onが発行されるまで沈黙しているため、オフのときはコストがなく、管理を妨げることはありません。
有効にすると、アソシエーションの変更と定期的なブリッジ統計行を報告します(以下のセッションを参照)。
-DTRACE_FRAMES=1ビルドは、ブリッジされた各フレームの1行要約を追加しますが、負荷がかかるとコンソールが溢れるため、デフォルトではオフです。
統計フィールドは以下の表で説明します。
[#tbl-debug-stats] .デバッグ統計フィールド [cols="1,3", options="header"] |=== |フィールド |意味
|->wifi
|ホストからWi-Fiに転送されたフレーム。
|->host
|Wi-Fiからホストに転送されたフレーム。
|txdrop
|ステーションがまだアソシエートしていないためにドロップされたホスト→Wi-Fiフレーム(ホストが再試行)。
|rxdrop
|USB側が十分に速く処理できなかったためにドロップされたWi-Fi→ホストフレーム。
|refl
|アクセスポイントによって反射された、ホスト自身の送信であるためにドロップされたWi-Fi→ホストフレーム。
|poolfail
|lwIPのpbufプールが一時的に枯渇したためにドロップされたホスト→Wi-Fiフレーム。
|ringpk
|前回の統計行_以降_のピークWi-Fi→ホストUSB-TXキュー深度(32段階中)。その後リセットされるライブゲージで、値が32に近いとUSBがWi-Fiの配信速度に追いついていないことを示します。(全期間最大値とは異なり、バーストが過ぎると再び減少します。)
|link
|ステーションのWi-Fiリンクステータス:up(アソシエート済み)、join/down(アソシエート中)、または障害理由:badauth(間違ったパスフレーズ)、nonet(SSIDが見つからない)、fail。
|hangs
|最後のコールド電源投入以降、ウォッチドッグがファームウェアをハングから回復させた回数。<<automatic-recovery,自動復旧>>を参照。
|faults
|ファームウェアが最後のコールド電源投入以降に回復したハードフォールトの回数。
|faultpc
|最新のハードフォールトのアドレス(ない場合は0x00000000)。addr2lineでマッピングする際に使用。
|freeram
|空きRAM(バイト)。バッファサイズの調整時にヘッドルームを評価するために使用。
|===
フレームごとのトレースは、ブリッジトラフィックと同じUSB Full-Speedリンクを共有するため、スループットを低下させ、コンソールを溢れさせます。これはビルド時オプション(-DTRACE_FRAMES=1)であり、深いデバッグのみを目的としています。
== 自動復旧
ファームウェアがメインループのサービスを停止した場合(ロックアップやドライバデッドロック)、ハードウェアウォッチドッグがデバイスを再起動します。そのため、デバイスは手動で抜き差ししなくても数秒以内に自動的に再列挙されます。 別のハードフォールトハンドラがCPUフォールトを即座にキャッチし、フォールトアドレスを記録します。
クラッシュ直前のブリッジカウンタは、初期化されていないRAMに残り、再起動後も存続します。
復旧時、デバッグコンソールにそれらのカウンタ(およびハードフォールトの場合はフォールトアドレス)を含むRECOVERED from ...の1行レポートを出力し、実行中のstats:行にhangs、faults、faultpcの集計が表示されるため、クラッシュは自己消去するものの、診断トレースが残ります。
== オンボードLED状態
以下の表は、オンボードLEDのパターンとその意味を示しています。
[#tbl-led] .オンボードLEDパターン [cols="1,3", options="header"] |=== |パターン |意味
|点灯 |アクセスポイントにアソシエート済み - 通常動作状態。
|ゆっくり点滅 - 1 Hz |Wi-Fi設定済み、アソシエート中または未アソシエート。
|高速点滅 - 5 Hz |Wi-Fi未設定。管理コンソールでプロビジョニングしてください。
|ダブルフラッシュ - 2回の短いパルス、その後休止 |ライブ(連続)スキャン実行中。デバイスは非アソシエート状態で、キーが押されるまで管理コンソールに近隣のアクセスポイントをストリーム出力します。
|消灯 |USB準備未完了。 |===
== 将来の作業
ブリッジはRP2040のネイティブFull-Speed USB(12 Mbit/s)上で動作するため、スループットはTCPペイロードで約4〜5 Mbit/sが上限です。ダッシュボードや制御面には十分ですが、ハードリミットです。 ボトルネックはUSBリンクであり、Wi-Fi無線ではありません。 改善の方向性を、おおよその労力順に示します:
いずれもファームウェアの意図された使用には必須ではありません。より高いスループットを求める人々の出発点です。
== 上流ライブラリとクレジット
このファームウェアはいくつかの上流プロジェクトから構成されています。以下の表に記録します。
[#tbl-upstream] .上流コンポーネント [cols="1,2,1,4", options="header"] |=== |コンポーネント(ツリー内ファイル) |上流 |ライセンス |役割
|USBNet |https://github.com/mattmyne/usbnet[mattmyne/usbnet] |MIT a|USBネットワークモジュールのベース、USB記述子、メインスケルトン。ここでWi-Fiブリッジに拡張。
usb_network.c、usb_network.h - L2ブリッジとして書き直しusb_descriptors.c - 複合CDC-NCM + デュアルCDC-ACMを含むように変更tusb_config.h - 変更|TinyUSB |https://github.com/hathach/tinyusb[hathach/tinyusb] |MIT a|USB CDC-NCMおよびCDC-ACMデバイススタック。pico-sdkにバンドルされているものをそのまま使用。
|pico-sdk 2.2.0 |https://github.com/raspberrypi/pico-sdk[raspberrypi/pico-sdk] |BSD-3-Clause a|ボードサポート、ビルドシステム、バンドルされたTinyUSB、lwIP、cyw43-driver。
pico_sdk_import.cmake - 正確なコピーlwipopts.h - pico_wの例からトリミング|TinyUSB net_lwip_webserver の例
|Peter Lawrence および Ha Thach、https://github.com/hathach/tinyusb[hathach/tinyusb]経由
|MIT
a|USBネットワークグルーの元のベース。CDC-NCMパスに縮小。
|lrndis |https://github.com/fetisov/lrndis[fetisov/lrndis] |MIT a|USBネットワークアプローチへのデザイン上の影響
残りのソースはこのプロジェクトのオリジナルです:
main.cconfig.cconfig.hconfig_proto.cconfig_proto.hserial_console.cserial_console.hwifi_scan.cwifi_scan.hdebug_console.cdebug_console.h== ライセンス
このプロジェクトはMITライセンスです。link:LICENSE[LICENSE]を参照してください。 上流コンポーネントは、<<upstream-libraries-and-credits,上流ライブラリとクレジット>>に記録されたそれぞれのライセンスを保持します。
== アーキテクチャ
=== 概要
デバイスはUSB CDC-NCMペリフェラルであり、ホストをWi-Fiにブリッジします。
Pico WがWi-Fiステーションを実行し、イーサネットフレームをUSBリンクと無線機の間でシャトルします。
ホストは自身のIPスタックを実行し、単一のネットワーク識別子を保持します。Picoは自身のIPを持ちません。
ホストに必要なのは、標準搭載のcdc_ncmおよびcdc_acmドライバのみです。
=== MAC採用によるレイヤ2ブリッジの理由
目標は、Picoを見えなくしたまま、ホストが通常のデバイスとして一つのアドレスでWi-Fiネットワークに現れることです。 これを達成する方法を、一つのハードな物理層の制約が形作ります。
Wi-Fiステーションは複数のMACアドレスを透過的にブリッジできません。 Infineon CYW43がアクセスポイントにステーションモードでアソシエートすると、そのアソシエーションは正確に一つのMACアドレスを許可し、送信する802.11データフレームはそのステーションMACにバインドされます。 アクセスポイントもサポートして許可する必要がある4アドレス(WDS)フレームを使用しない限り、無線機は背後にある他のMACアドレスに代わってフレームを運ぶことができません。 これが、Wi-Fiクライアントをブリッジできないというよく知られた制限です。このファームウェアは、その制約と戦うのではなく、それを取り除きます。 ホストのUSBインターフェースはWi-FiステーションのMACアドレスを採用するように指示されるため、エンドツーエンドでMACアドレスはちょうど1つになります。 ホストとステーションが同一のIDを共有することで、Picoはレイヤー2のブリッジとして機能し、USBとWi-Fiの間でイーサネットフレームをそのまま転送し、レイヤー2より上には一切関与しません。 アクセスポイントは単一の通常のステーションとして認識し、ホスト自身がDHCP、SLAAC、近隣探索を実行し、取得したアドレスを保持します。
その結果は、こちらの表にまとめられています。
[#tbl-bridge-effects] .MACアドレス採用ブリッジの特性 [cols="1,3", options="header"] |=== |特性 |理由
|1つのIP、ホストが保持 |Picoは自身にアドレスを割り当てないため、アクセスポイントの独自サブネット上で単一のIDとなります。プライベートなテザリングサブネットではありません。
|IPv4とIPv6の両対応 |レイヤー2で転送するため、SLAAC、DHCPv6、ルーター広告、近隣探索は変更されずに通過し、バージョン固有のコードは不要です。
|NATやポートフォワードなし |何も書き換えられないため、インバウンド接続は直接ホストに到達し、マスカレードやマッピングは不要です。
|ホスト側のWi-Fiスタック不要
|Picoがアソシエーションとサプリカントを管理するため、ホストはwpa_supplicant、規制データベース、ワイヤレスドライバーは不要で、CDCクラスドライバーのみで動作します。
|===
=== データパス
USB側では、TinyUSBがCDC-NCMデバイスを提供し、ホストのインターフェースMACは起動時にステーションMACに設定されます(usb_network_set_host_mac、列挙前)。
ホストからWi-Fiへ:フレームはtud_network_recv_cb経由で到着し、ステージングされ、メインループでcyw43_send_ethernetを使用して無線に送信されます。
Wi-Fiからホストへ:ステーションのnetifのinputハンドラが置き換えられ、cyw43ドライバーが受信したすべてのフレームがlwIPではなくブリッジに渡され、キューイングされ、tud_network_xmitでホストに送信されます。
USB側にはlwIP IPインターフェースは存在せず、ステーションのnetifにもIPは割り当てられません。lwIPはcyw43 netifのリンク状態とpbufプールにのみ使用されます。
=== 並行モデル
このファームウェアはpico_cyw43_arch_lwip_threadsafe_backgroundを使用します。
Wi-FiはバックグラウンドのIRQと非同期コンテキストで処理されるため、USBが枯渇することはありません。USBのtud_task()はメインループで実行されます。
この構成には、ブリッジにとって厳格な結果が1つあります。
TinyUSBはメインループからのみ操作する必要がありますが、Wi-Fiフレームはバックグラウンドコンテキストで受信されます。
そのため、Wi-Fi受信ハンドラは各フレームをリングバッファにエンキューするだけで、メインループがそのリングをtud_network_xmitにドレインします。
このルールに違反すると、ホスト側でNETDEV WATCHDOG: transmit queue timed outが発生し、USBが切断されました。
ホストフレームをWi-Fiに送信する処理はメインループで行われ、cyw43呼び出しの前後でcyw43_arch_lwip_begin()/cyw43_arch_lwip_end()を保持します。
=== マルチキャストとIPv6
デフォルトでは、ステーションは参加したマルチキャストグループのみを受信します。 ブリッジは独自のIPスタックを実行せず、何にも参加しないため、何もしなければ無線はIPv6が依存するマルチキャストをドロップし、ホストのIPv6はブリッジ経由で動作しません。 ルーター広告、重複アドレス検出、アドレス解決はすべてマルチキャストに依存しています。
各アソシエーション時に、ファームウェアはCYW43のallmulti iovarを設定するため、フィルターに関係なくステーションはすべてのマルチキャストフレームを配信します。
これは、モニターモードに入らずに公開のcyw43_ioctlをWLC_SET_VARに対して使用して行われ、イーサネットフレーム形式は変更されません。
IGMPまたはMLDスヌーピング対応スイッチの背後にあるネットワークでは、ホストが要求していない一部のマルチキャストが削除される可能性があります。フラットなホームアクセスポイントではフラッディングされます。
=== 自己反射フィルター
ホストがステーションMACを共有しているため、ホストが送信したマルチキャストまたはブロードキャストはアクセスポイントによってステーションにフラッディングされ、ステーションはすべてのマルチキャストを受信するため、ホスト自身のフレームとしてホストに戻される可能性があります。
受信ハンドラは、送信元MACがステーションMACであるWi-Fiからホストへのフレームをすべてドロップします。そのようなフレームはアクセスポイントによって反射されたホスト自身の送信にすぎないためです。
ブリッジはステーションのフレームをステーションにエコーバックしてはなりません。このドロップはデバッグ統計でreflとしてカウントされます。
=== 2つのシリアルコンソール
管理と診断は、帯域外で行われ、ネットワーク上ではなく、同じ複合USBデバイスの2つのCDC-ACM機能で行われます。 これは透過性の意図的な結果です。ネットワーク側はホストのトラフィックのみを伝送し、Picoが応答するためのアドレスを持ちません。
管理コンソール(/dev/ttyACM0)は構成ラインプロトコルを実行し、USBが列挙された瞬間にアクセス可能で、Wi-Fiが起動する前であるため、デバイスは常にIPなしでプロビジョニング可能です。
デバッグコンソール(/dev/ttyACM1)は書き込み専用の診断ストリームです。アソシエーションイベント、定期的なブリッジカウンター、およびオプションのフレーム単位のパケットサマリーを、有効にされた場合のみ出力するため、オフのときはコストがかからず、管理コンソールを乱雑にしません。
どちらのコンソールもネットワークに触れないため、ホストファイアウォールがログに記録するトラフィックは発生しません。
=== 構成ストレージ
ランタイム設定(最大8つのWi-Fi認証プロファイル、アクティブプロファイルインデックス、規制国、デバッグフラグ)は、フラッシュの先頭にあるプログラムイメージから離れた最後のフラッシュセクターの単一レコードに格納されます。
レコードは複数のフラッシュページにまたがるため、saveはセクター全体を一度にプログラムします(消去は常にセクター全体)。
起動時、レコードはマジックナンバーと構造体の残り部分に対するCRC-32が完全に一致した場合のみ受け入れられ、不一致の場合はコンパイル時デフォルトがロードされます。
バージョン管理された移行はありません。レコードレイアウトの変更は単にマジック/CRCチェックに失敗し、デフォルトにフォールバックします。これは、コンパイル時デフォルトが最初のプロファイルをシードするため許容可能です(これが、レコードをプロファイルリストに拡張したv1.1.0へのアップグレードで、v1.1.0より前のレコードが破棄される理由です)。
saveはflash_safe_executeを介してレコードを書き戻します。これは、他のコアと調整して消去とプログラムを実行し、数ミリ秒間だけ割り込みを無効にします。
この書き込みは、lwIPロックが保持されている状態でコンソールハンドラから実行されます。短い割り込み無効ウィンドウはバックグラウンドのWi-Fi処理を一時停止しますが、これは稀なホスト起動の保存であれば許容可能です。
=== ハードウェアおよびツールチェーンの癖
RP2040、Infineon CYW43、pico-sdkのこれらの動作は、実際のデバッグ時間を消費し、簡単に再導入されます。
==== バックグラウンド処理にはメインループ専用のTinyUSBが必要
これは、<<concurrency-model,並行モデル>>で述べた並行ルールです。 バックグラウンド処理では、Wi-Fi受信はTinyUSBを呼び出せないコンテキストで実行されるため、遅延送信リングが存在します。
==== アソシエーションはIPではない
cyw43ヘルパーcyw43_tcpip_link_statusは、ステーションがIPアドレスを保持した場合のみCYW43_LINK_UPを報告しますが、このステーションは意図的にIPを取得しません。
そのため、アソシエーション状態はnetifリンクフラグ(netif_is_link_up)から読み取られ、ブリッジは転送するかどうかの判断にもこれを使用します。
==== 変更されたIDには新しいプロダクトIDが必要
USBベンダーIDとプロダクトIDを同じに保ちながらインターフェースセットを変更する複合デバイスは、ホストによってキャッシュされた記述子を提供される可能性があります。
プロダクトIDは有効なクラスから派生するため、各CDC-ACM関数を追加するとシフトし(cafe:4020からcafe:4022)、ホストは新しいレイアウトを再読み取りします。
==== リリースビルドではassertが無効になる
CMAKE_BUILD_TYPE=ReleaseはNDEBUGを定義し、assert()を何もしないようにコンパイルするため、アサーションでしか保護されていない割り当ては失敗をすり抜けてNULLを参照します。
tud_taskが実行される前のファームウェアクラッシュは、ホスト側ではUSB列挙エラー-110(デバイス記述子読み取りタイムアウト)として現れます。
起動パスでは、アサーションではなく実際のNULLガードが使用されます。
=== 検証済み環境
動作確認が取れている構成は、pico-sdk 2.2.0、arm-none-eabi GCCツールチェーン、およびpico-sdkにバンドルされた未変更のTinyUSBとlwIPを使用しています。
リファレンスボードはPico W (RP2040)です。
Pico 2 W (RP2350)でも動作する見込みです。
ビルドにより、約670 KBのbuild/pico-usb-wifi.uf2が生成されます。