macOS(Hackintosh)で Buffalo WLP-U2-300D の USB Wi-Fi 子機(RT5592)を使えるようにした

macOS
スポンサーリンク
スポンサーリンク

はじめに

Wi-Fi 子機の中には、Linux では普通に動くのに、macOS 用のドライバがないものがたくさんあります。

Buffalo の USB 子機(WLP-U2-300D)で、Ralink の RT5592 チップを積んだもの(USB ID は 0411:0253)も、その一つです。

この記事で紹介するのは、この子機を macOS で使えるようにした自作ドライバです。

Linux 本家の rt2x00 / rt2800usb ドライバを、ほぼ書き換えずに、macOS のユーザー空間で動かしています。

  • 2.4GHz と 5GHz の Wi-Fi(802.11a/b/g/n)に接続できます
  • WPA2-PSK(一般的な家庭用ルーターの設定)と、パスワードなしのネットワークに対応しています
  • IPv4(DHCP)と IPv6 のアドレスを取得できます
  • メニューバーのアプリから、接続、自動接続、切断、ネットワーク一覧の表示ができます
  • ダブルクリックで入れられる GUI インストーラー(.pkg)があります
  • 速度は、5GHz で下り約 58Mbps、上り約 24Mbps です(測り方は後述)

最初に、できないことも書いておきます。

  • macOS 標準の「システム設定 > Wi-Fi」には表示されません。 専用のメニューバーアプリから操作します
  • WPA3 には対応していません。スリープからの復帰も確認していません
  • 通信は専用のネットワークインターフェース(utun)経由です。標準では、macOS のデフォルトの経路は変えません(後述)
  • 有線の LAN や、ほかの Wi-Fi が使える環境なら、そちらのほうが速くて安定します

実験的なプロジェクトで、無保証です。 自己責任でお試しください。

動作環境

項目内容
OSmacOS 26(Tahoe)
CPUIntel(x86_64)
子機Ralink RT5592 搭載のもの(Buffalo、USB 0411:0253)
動作確認した環境Hackintosh の Mac Pro(MacPro7,1)、家庭用ルーター 1 台(2.4GHz は 20MHz 幅、5GHz は 40MHz 幅、WPA2)

動作確認は、上の 1 つの環境だけです。Apple Silicon の Mac や、ほかの macOS のバージョンは確認していません。

子機を挿したときに、USB の情報が 802.11 n WLAN、ベンダー ID 0x0411(Buffalo)、製品 ID 0x0253 になっていれば対象です。「このMacについて > システムレポート > USB」で確認できます。同じ RT5592 を積んだ別メーカーの子機も動く可能性はありますが、試していません。

インストール方法(GUI)

  1. GitHub の Releases から、RT2800-WiFi-0.1.pkg をダウンロードします
  2. ダウンロードしたファイルを、右クリック →「開く」 で開きます。署名のないインストーラーなので、ダブルクリックだけでは警告が出て開けません
  3. 画面の案内に従って、インストールします。管理者のパスワードを聞かれます
  4. 子機を USB に挿します
  5. 少し待つと、メニューバーに Wi-Fi のアイコンが出ます

インストールされるものは、次のとおりです。

場所内容
/usr/local/libexec/rt2800/rt2800dドライバ本体(常駐デーモン)
/usr/local/share/rt2800/rt2870.binチップのファームウェア
/Applications/RT2800 Wi-Fi.appメニューバーアプリ
/usr/local/bin/rt2800-wifiコマンドラインのランチャー
/Library/LaunchDaemons/local.rt2800d.plistデーモンを自動起動する設定
~/Library/LaunchAgents/local.rt2800.wifi.plistログイン時にメニューバーアプリを起動する設定

使い方

メニューバーアプリ

アイコンをクリックすると、次のものが表示されます。

  • 接続中のネットワーク名、IP アドレス、電波の強さ、チャンネル(2.4GHz か 5GHz か)
  • 周辺のネットワークの一覧(電波の強い順。鍵のマークは、パスワードが必要なもの)
  • 再スキャン、切断
  • 自動接続(オンにすると、保存したネットワークに自動で繋ぎ直します)
  • この Wi-Fi を既定の経路にする(オンにすると、通信の出口がこの子機になります)
  • 保存済みネットワークの削除、ログイン時に起動のオン/オフ

ネットワークを選ぶと、パスワードの入力画面が出ます。入力したパスワードは保存され、次回からは選ぶだけで接続できます。

コマンド

ターミナルからも、同じ操作ができます。

rt2800-wifi scan                  # 周辺のネットワークを検索して一覧表示
rt2800-wifi connect "SSID名"      # パスワードを聞かれ、保存されて自動接続されます
rt2800-wifi status                # 接続状況(JSON)
rt2800-wifi disconnect            # 切断
rt2800-wifi forget "SSID名"       # 保存したネットワークを削除
rt2800-wifi log                   # ログを表示

デフォルトの経路は、標準では変えません

接続しても、macOS のデフォルトの経路(インターネットへの出口)は変わりません。有線の LAN などがあれば、そちらが使われ続けます。

この子機で特定の宛先だけ通したいときは、その宛先の経路を足します。utun4 の番号は、ifconfig で、接続先の IP アドレスが付いたものを探してください。

sudo route add -host 8.8.8.8 -interface utun4
ping -c 4 8.8.8.8
sudo route delete -host 8.8.8.8

通信全体をこの子機にしたいときは、メニューの「この Wi-Fi を既定の経路にする」を使います。

速度

速度は、Cloudflare の速度測定(speed.cloudflare.com)に、curl で、この子機の経路だけを通して測りました。5GHz、40MHz 幅のルーターで、電波の強さは -40 から -50 dBm(同じ部屋)です。

5 回の測定下り上り
平均約 58 Mbps約 24 Mbps
範囲43〜70 Mbps16〜27 Mbps

比較のために、同じ Mac につないだ別の Wi-Fi 子機(Wi-Fi 6 対応)で同じ測定をすると、下り 133Mbps、上り 115Mbps でした。この子機は、その半分から 5 分の 1 ほどの速さです。普段使いには十分ですが、速さを求める用途には向きません。

仕組み

Linux には、この子機のチップ用の rt2800usb ドライバが入っています。これを、macOS のユーザー空間で動かしています。

メニューバーアプリ(Swift)/ rt2800-wifi コマンド
      │  制御ソケット(/var/run/rt2800d.sock)
rt2800d(root で動く常駐デーモン)
      ├─ rt2x00 / rt2800usb(Linux 本家のソース、ほぼ無改変)
      ├─ Linux カーネル API の互換層、簡易 mac80211、MLME、WPA2 の supplicant
      ├─ IOKit USB ── 子機
      └─ utun(DHCP・ARP・IPv6 をデーモンが処理)

macOS には、Wi-Fi ドライバを作るための公開された仕組みがありません。そのため、次のように割り切っています。

  1. ドライバ(rt2800usb)は、Linux のものをそのまま使う。
    チップの制御は、これに任せます
  2. Linux のカーネルの機能を、ユーザー空間で再現する。
    skb や mac80211 などの Linux の部品を、互換層として用意しました
  3. Wi-Fi の「接続」の手順(認証、アソシエーション、4-way ハンドシェイク)は、自前で書く。 Linux では mac80211 と wpa_supplicant が担当している部分です
  4. 通信は、utun に載せる。
    暗号化は、チップのハードウェアが行います

この方式は、先に作った TP-Link Archer TX20U Nano(RTL8852B)用の rtw89-macos と、MT7601U 用の mt7601u-macos と同じです。今回は、その土台に、別のドライバを載せました。

苦労した点 ― 繋がった後の「遅い」を潰す

繋がるところまでは、前の 2 つのプロジェクトの土台のおかげで、比較的すぐに行きました。大変だったのは、繋がった後です。最初の速度は、下り 0.11Mbps(1 秒に 14KB)でした。それを、一つずつ測りながら、直していきました。

別のチップだった

最初は、MT7601U 用のドライバに、この子機の USB ID を足せば動くと思いました。しかし、USB のエンドポイントの数が合わず、読み込みで弾かれました。チップのレジスタ(MAC_CSR0)を読むと 0x55920222、つまり RT5592 でした。そこで、rt2800usb を新しく載せることにしました。

スキャン中にクラッシュ

ある USB 転送の後始末を忘れていて、解放済みのデータを後から触っていました。登録を外してから完了処理を呼ぶようにして、直りました。

終了するたびに、チップが固まる

プログラムを終了するとき、ドライバの終了処理を呼んでいませんでした。その結果、チップが応答しなくなり、毎回、子機の抜き差しが必要でした。終了時に、ドライバを正しく止めるようにしました。

5GHz に切り替えると、USB から落ちる

5GHz の最初のチャンネルで送信した瞬間に、子機が USB から消えました。調べると、次の二つが原因でした。

  • 5GHz では、送信を 2 本のアンテナで同時に行うと、USB ポートが出せる電流を超えて、子機が切断されます。送信を 1 本にしたら 安定しました
  • ドライバが持っている 5GHz のチャンネル表には、40MHz 用の中心チャンネル(38、42 など)が混ざっています。これに合わせると、これも落ちます。通常の 20MHz のチャンネルだけを使うようにしました

電波は届くのに、通信できない(MIC の 8 バイト)

ゲートウェイからの ARP の応答が、ログには届いているのに、使われませんでした。原因は、受信したデータの最後の 8 バイトを、暗号の検証用の部分(MIC)だと思って削っていたことです。ハードウェアが、すでに取り除いて渡しているのに、フラグの名前が違うため、気づいていませんでした。ARP の応答は 28 バイトなので、20 バイトに削られて捨てられていました。ICMP の応答や DHCP も、末尾が欠けて、おかしくなっていました。

下りが 0.11Mbps(重複と、ブロックACK)

繋がっても、netstat で見ると、受信するデータが実際の約 3 倍でした。ルーターが、同じフレームを何度も再送していたのです。ブロックACK(複数のフレームをまとめて確認する仕組み)の応答が返らないためで、まずブロックACKを断り、重複したフレームを捨てるようにして、下りは 15Mbps まで上がりました。

上りが 3Mbps(再送回数が 2 回のまま)

上りは、レートを上げても 3Mbps で止まりました。アップロード中のパケットを tcpdump で保存して調べると、再送が 0.8% ありました。往復遅延が 68 ミリ秒だと、TCP の速度は、損失が 0.8% あるだけで、約 2〜3Mbps に絞られます(計算式 1.22 × MSS ÷ (往復遅延 × √損失率) の予測と、実測がほぼ一致しました)。

つまり、無線区間で 1% 近くの損失を、隠せていないことが原因でした。原因は、再送回数の設定が、初期値の 2 回のままだったことです。本物の Linux では、起動時に設定し直されます。これを 10 回に設定すると、フレームが最後まで届かない割合が 1% 近くから 0.05〜0.35% に減り、上りは 3Mbps から、17Mbps 以上に上がりました(その後の改良を重ねた最終的な平均が、約 24Mbps です)。

「崩壊する」と思っていたブロックACKが、動いた

実は、手順 6 で断っていたブロックACKは、再送回数の設定漏れが原因で壊れていました。再送回数を直した後で、もう一度ブロックACK(受信側)を有効にすると、下りは 17Mbps から 66Mbps(40MHz で送信すると 80Mbps)になりました。「原因は別のところにある」と気づくまでに、遠回りをしました。

2.4GHz だけ、DHCP が通らない

5GHz で動いたあと、2.4GHz のネットワークに繋ぐと、IP アドレスが取れませんでした。ログを見ると、そのルーターの 2.4GHz は 20MHz 幅でした。子機は、40MHz 幅で送信していたため、ルーターが受け取れていませんでした。ルーターの帯域幅に合わせて、40MHz で送るのは、ルーターが 40MHz のときだけにしました。

日本語のメッセージが文字化けする

コマンドのパスワード入力で、「MyNetwork」のパスワード: のはずが、「??のパスワード: と出ました。シェルスクリプトで $ssid」 と書くと、UTF-8 の環境では、」 の最初のバイトが変数名の一部だと解釈されて、別の(空の)変数になっていました。${ssid} と、波括弧で囲めば直ります。インストーラーの画面も、日本語を数値文字参照(あ の形)で書いて、どの文字コードでも崩れないようにしました。

うまくいかなかったこと

  • 送信を高いレート(MCS6〜7)に固定すると、損失が急に増えました。標準では MCS5 にして、ACK が返らないフレームが 8% を超えたら、5 秒間だけ 6Mbps に落とす、簡単なフォールバックを入れています
  • 送信側のブロックACK(A-MPDU)と 40MHz 送信を併用したところ、一度、子機が USB から落ちました。標準ではオフにしています
  • 速度の上限は、送信が終わったかどうかを、1 フレームずつ USB で確認する部分です。この処理が、58Mbps 付近で、ほぼ限界でした

教訓

今回は、推測ではなく、測ってから直すことに助けられました。

tcpdump で保存したパケット、netstat の重複、sample コマンドでのスレッドの稼働率、そして自作した usbstat(USB と送信の統計)が、どれも、思い込みを何度か打ち消してくれました。

自分でビルドする

git clone https://github.com/Long0823-1/rt2800-macos.git
cd rt2800-macos
scripts/fetch-firmware.sh      # linux-firmware から rt2870.bin を取得
installer/build-pkg.sh         # dist/RT2800-WiFi-0.1.pkg を作成(Xcode Command Line Tools が必要)

アンインストール

sudo /usr/local/libexec/rt2800/uninstall.sh

デーモン、アプリ、ファームウェアなど、インストールしたものを削除します。保存したネットワーク(パスワードを含む)は、/var/db/rt2800d に残ります。完全に消すには、このフォルダも削除してください。

既知の問題

  • 子機が USB から落ちたときは、抜き差しが必要です
  • 送信は 1 チェーンだけです。速度の上限のひとつです
  • 本格的なレート制御はなく、固定の MCS と簡単なフォールバックだけです
  • ハンドシェイクに失敗するときは、まずパスワードの打ち間違いを確認してください

ライセンスと謝辞

  • ドライバとデーモンは GPL-2.0 です。Linux カーネルの rt2x00 / rt2800usb を、ほぼそのまま使っています
  • ファームウェア rt2870.bin は、Ralink(現 MediaTek)のもので、linux-firmware から取得します。バイナリのまま、無改変での再配布が許されています(インストーラーに同梱しています)
  • 土台にした rtw89-macos と mt7601u-macos も、あわせてご覧ください

おわりに

macOS 用のドライバがない Wi-Fi 子機でも、Linux のドライバがあれば、思ったより遠くまで行けました。繋がるところから、使える速さになるまでの道のりは、ほとんどが「測る」ことでした。同じような子機を持っている方や、macOS のドライバに興味のある方の参考になればうれしいです。

ソースコードとインストーラーは、GitHub で公開しています。

コメント

タイトルとURLをコピーしました