macOSからPi Picoを使う - その24 [Pi Pico]
前々回と前回とでpico-sdkでのcmakeの使われ方をざっくりみた。詳細までは行きつかなかったけど、大まかな構造はわかった。
さて、これであとは実装を考えればばいい、のはずであるが、macOS側もvendor specific classに対応する必要がある。これがpico-sdkとTinyUSBの組み合わせよりもずっと不親切でわかりにくくて難しい、ということがわかってきた....
ところがこれが実にわかりにくい。ドキュメントもreferenceしかなくて、全体像がわからない。また、どうも古いI/O Kitを直接書く必要のある部分があって、見通しが非常に悪い。さらにハードウェアへのアクセスはsandboxを適切に設定した上にentitlementを与えて、それでできた実行形式のbundle全体に署名しないといけない。もう、なにをどうすればいいんだか全然わからない。
AppleがIOUSBHost全体のまともなOverviewを作るか、誰か親切な人が解説してくれないと手に負えない、と僕は思った。
そこでlibusbを使うことにする。libusbはマルチプラットフォームでmacOSでも使える。Appleと違ってすごく簡単に使えるようになっている。
ただし、libusbのmacOS用の実装はI/O Kitを使って書いてある。まだI/O Kitは使えるし、I/O KitはOS Kernelの基本的な部分を支える重要なアイデアを含んでいるので、macOSから無くなるということはたぶんない。しかしDriverKitとSystem extensionに移行し終わるとおそらくI/O KitのうちユーザランドのAPIの多くは塞がれて、I/O Kitの本体はプログラマからは見えなくなる、と思われる。そうなったときにlibusbが対応してくれるかどうか、というと僕はすごく疑問だと思っている。
どうなるかはわからない。でも今のままではせっかくPi Pico側のTinyUSBがなんとかなりそうなのに、macOS側で挫けることになって、それは非常に面白くない。
ということで、僕としてはまずlibusbでmacOS側を実装して、Pi Picoとの通信を確立し終わってから、IOUSBHostに移行しよう、そのころにはもうすこしIOUSBHost周りも改善されているのではないか、という期待を持って進めることにする。
で、/usr/local/lib(intel macの場合)にlibusb.xxx.dylibへのシンボリックリンクが張られる。これはlibusb1.0でこれまでllibusb0.1に馴染んだ人には古い方をインストールする手段があるらしいけど、僕は1.0でかまわない。
Xcode12ではデフォルトでintelとapple siliconのuniversal binaryがビルドされる設定になっているけど、このlibusbはsingle binary(native architectureだけ)なので、自分でソースからuniversalにビルドするか、XcodeのBuild SettingsでArchitectures→Architectures→$(ARCHS_STANDARD)というのを変更しないと実行時エラーでクラッシュする。
それも含めてlibusbをhomebrewでインストールした場合、targetの設定の変更を
libusbはlinux用のユーザ空間のUSBドライバでUSB3.1も対応している。現時点でhomebrewでインストールされる最新版は1.0.24で、0.1から構造は変わってないけど、関数の呼び方は違ってるので注意が必要。関数を呼び出しを変換するユーティリティレイヤが用意されている。
ドキュメントはレファレンスのレベルだけどDoxygenで生成したページがある。またgithubに簡単なサンプルコードがある。典型的なCのスタイルのドライバで、関数の名前を見ると何をするのかおおよそわかるような的確な(パターン化された)名前になっている。
使い方として基本的には
ひとつのプロセス内で複数のcontextが使える。例えば単一プロセスのアプリで複数のデバイスを扱う、みたいな場合のようである。複数のlibusbオブジェクトが必要ないときにはデフォルトコンテクストを使うようにもできる。
デバイスとの通信は非同期にできる。つまり通信要求をして結果はコールバックで受け取る、というスタイルを使うことができる。非同期でやるほどもない軽い通信用には単純な同期型の通信も用意されているが、それを使うと通信要求から結果を得るまでlibusb関数はブロックする。Pi PicoのUSBはfull speedなので1msec以上ブロックする可能性がある。面倒でなければ非同期を使う方が効率的である。
何度も書いたけど、macOS向けの実装部分にI/O Kitをそのまま使っていて、usbのデバイスファイルを作ったりはしていない。I/O Kitはわかりにくくて使いにくいので、結構ゴリゴリとしたソースになっている。
さて、これであとは実装を考えればばいい、のはずであるが、macOS側もvendor specific classに対応する必要がある。これがpico-sdkとTinyUSBの組み合わせよりもずっと不親切でわかりにくくて難しい、ということがわかってきた....
17 macOSのUSBライブラリ
17.1 IOUSBHostとlibusb
macOS側で汎用のUSBデバイスにアクセスするコードを書くにはDriverKitを書くことになるんだけど、ということはSystem extensionを書く、ということになる。しかし汎用でなくて、単一のアプリでひとつのUSBデバイスと通信できればいい、というだけならDriverKitではなく、昔のI/O Kitでやったような(Kernel空間ではなく)アプリと同じメモリ空間内から呼べるようになっている。それがIOUSBHost frameworkである。ところがこれが実にわかりにくい。ドキュメントもreferenceしかなくて、全体像がわからない。また、どうも古いI/O Kitを直接書く必要のある部分があって、見通しが非常に悪い。さらにハードウェアへのアクセスはsandboxを適切に設定した上にentitlementを与えて、それでできた実行形式のbundle全体に署名しないといけない。もう、なにをどうすればいいんだか全然わからない。
AppleがIOUSBHost全体のまともなOverviewを作るか、誰か親切な人が解説してくれないと手に負えない、と僕は思った。
そこでlibusbを使うことにする。libusbはマルチプラットフォームでmacOSでも使える。Appleと違ってすごく簡単に使えるようになっている。
ただし、libusbのmacOS用の実装はI/O Kitを使って書いてある。まだI/O Kitは使えるし、I/O KitはOS Kernelの基本的な部分を支える重要なアイデアを含んでいるので、macOSから無くなるということはたぶんない。しかしDriverKitとSystem extensionに移行し終わるとおそらくI/O KitのうちユーザランドのAPIの多くは塞がれて、I/O Kitの本体はプログラマからは見えなくなる、と思われる。そうなったときにlibusbが対応してくれるかどうか、というと僕はすごく疑問だと思っている。
どうなるかはわからない。でも今のままではせっかくPi Pico側のTinyUSBがなんとかなりそうなのに、macOS側で挫けることになって、それは非常に面白くない。
ということで、僕としてはまずlibusbでmacOS側を実装して、Pi Picoとの通信を確立し終わってから、IOUSBHostに移行しよう、そのころにはもうすこしIOUSBHost周りも改善されているのではないか、という期待を持って進めることにする。
17.2 libusbのmacOSへのインストール
libusbのインストールはhomebrewが簡単。homebrewがインストールしてあれば$ brew install libusb
Xcode12ではデフォルトでintelとapple siliconのuniversal binaryがビルドされる設定になっているけど、このlibusbはsingle binary(native architectureだけ)なので、自分でソースからuniversalにビルドするか、XcodeのBuild SettingsでArchitectures→Architectures→$(ARCHS_STANDARD)というのを変更しないと実行時エラーでクラッシュする。
それも含めてlibusbをhomebrewでインストールした場合、targetの設定の変更を
- Build Settings→Architectures→Architecturesにx86_64あるいはarm64
- Build Settings→Architectures→Build Active Architecture Onlyにyes
- Build Settings→Search Paths→Header Search Pathsに/usr/local/include/libusb-1.0/
- Build Settings→Search Paths→Library Search Pathに/usr/local/lib
- Targets→General→Frameworks and Librariesにlibusb.1.x.dylibをembed&Signで
- Targets→Signing & Capabilities→App Sandbox→HardwareのUSBにチェック
17.3 libusbの使い方
IOUSBHostと違ってlibusbはユーザも多くてしかもAppleより親切な人が多いとみえて、インターネット上に解説してくれているサイトが日本語英語にかかわらずたくさんみつかる。僕がわざわざ屋上屋を重ねる必要はないんだけど、自分用にまとめを作っておくことにする。libusbはlinux用のユーザ空間のUSBドライバでUSB3.1も対応している。現時点でhomebrewでインストールされる最新版は1.0.24で、0.1から構造は変わってないけど、関数の呼び方は違ってるので注意が必要。関数を呼び出しを変換するユーティリティレイヤが用意されている。
ドキュメントはレファレンスのレベルだけどDoxygenで生成したページがある。またgithubに簡単なサンプルコードがある。典型的なCのスタイルのドライバで、関数の名前を見ると何をするのかおおよそわかるような的確な(パターン化された)名前になっている。
使い方として基本的には
- libusbを初期化
- デバイスリストを得る
- 必要なデバイスを選択、あるいはvendorID、productIDを指定してデバイスをopen
- デバイスとやりとりする
- デバイスを閉じる
- libusbを閉じる
ひとつのプロセス内で複数のcontextが使える。例えば単一プロセスのアプリで複数のデバイスを扱う、みたいな場合のようである。複数のlibusbオブジェクトが必要ないときにはデフォルトコンテクストを使うようにもできる。
デバイスとの通信は非同期にできる。つまり通信要求をして結果はコールバックで受け取る、というスタイルを使うことができる。非同期でやるほどもない軽い通信用には単純な同期型の通信も用意されているが、それを使うと通信要求から結果を得るまでlibusb関数はブロックする。Pi PicoのUSBはfull speedなので1msec以上ブロックする可能性がある。面倒でなければ非同期を使う方が効率的である。
何度も書いたけど、macOS向けの実装部分にI/O Kitをそのまま使っていて、usbのデバイスファイルを作ったりはしていない。I/O Kitはわかりにくくて使いにくいので、結構ゴリゴリとしたソースになっている。
2021-08-11 21:09
nice!(0)
コメント(0)
コメント 0