はじめに
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 が使える環境なら、そちらのほうが速くて安定します
実験的なプロジェクトで、無保証です。 自己責任でお試しください。
動作環境
| 項目 | 内容 |
|---|---|
| OS | macOS 26(Tahoe) |
| CPU | Intel(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)

- GitHub の Releases から、
RT2800-WiFi-0.1.pkgをダウンロードします - ダウンロードしたファイルを、右クリック →「開く」 で開きます。署名のないインストーラーなので、ダブルクリックだけでは警告が出て開けません
- 画面の案内に従って、インストールします。管理者のパスワードを聞かれます
- 子機を USB に挿します
- 少し待つと、メニューバーに 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 Mbps | 16〜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 ドライバを作るための公開された仕組みがありません。そのため、次のように割り切っています。
- ドライバ(
rt2800usb)は、Linux のものをそのまま使う。
チップの制御は、これに任せます - Linux のカーネルの機能を、ユーザー空間で再現する。
skbやmac80211などの Linux の部品を、互換層として用意しました - Wi-Fi の「接続」の手順(認証、アソシエーション、4-way ハンドシェイク)は、自前で書く。 Linux では mac80211 と wpa_supplicant が担当している部分です
- 通信は、
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 で公開しています。


コメント