SSブログ

macOSからPi Picoを使う - その24 [Pi Pico]

前々回前回とでpico-sdkでのcmakeの使われ方をざっくりみた。詳細までは行きつかなかったけど、大まかな構造はわかった。

さて、これであとは実装を考えればばいい、のはずであるが、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
で、/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の設定の変更を
  • 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にチェック
とする。最後のSandboxの設定はSandboxそのものをなくてしまえば必要ない。しかしSandboxをonにしていないアプリは早晩動作できなくなる可能性が高い。今のうちにSandboxに慣れていた方がいい。

17.3  libusbの使い方

IOUSBHostと違ってlibusbはユーザも多くてしかもAppleより親切な人が多いとみえて、インターネット上に解説してくれているサイトが日本語英語にかかわらずたくさんみつかる。僕がわざわざ屋上屋を重ねる必要はないんだけど、自分用にまとめを作っておくことにする。

libusbはlinux用のユーザ空間のUSBドライバでUSB3.1も対応している。現時点でhomebrewでインストールされる最新版は1.0.24で、0.1から構造は変わってないけど、関数の呼び方は違ってるので注意が必要。関数を呼び出しを変換するユーティリティレイヤが用意されている。

ドキュメントはレファレンスのレベルだけどDoxygenで生成したページがある。またgithubに簡単なサンプルコードがある。典型的なCのスタイルのドライバで、関数の名前を見ると何をするのかおおよそわかるような的確な(パターン化された)名前になっている。

使い方として基本的には
  1. libusbを初期化
  2. デバイスリストを得る
  3. 必要なデバイスを選択、あるいはvendorID、productIDを指定してデバイスをopen
  4. デバイスとやりとりする
  5. デバイスを閉じる
  6. libusbを閉じる
となる。libusbはmacOSのCoreFoundationのようなCによるオブジェクト指向的に呼べるようになっている。オブジェクトは普通のCの構造体で、多くはopaque(構造体の中身はヘッダには現れず、メンバアクセスは関数を使う)になっている。トップレベルのlibusb全体のオブジェクトとしてはlibusb_contextという構造体があって、ライブラリのデータを保持するようになっている。

ひとつのプロセス内で複数のcontextが使える。例えば単一プロセスのアプリで複数のデバイスを扱う、みたいな場合のようである。複数のlibusbオブジェクトが必要ないときにはデフォルトコンテクストを使うようにもできる。

デバイスとの通信は非同期にできる。つまり通信要求をして結果はコールバックで受け取る、というスタイルを使うことができる。非同期でやるほどもない軽い通信用には単純な同期型の通信も用意されているが、それを使うと通信要求から結果を得るまでlibusb関数はブロックする。Pi PicoのUSBはfull speedなので1msec以上ブロックする可能性がある。面倒でなければ非同期を使う方が効率的である。

何度も書いたけど、macOS向けの実装部分にI/O Kitをそのまま使っていて、usbのデバイスファイルを作ったりはしていない。I/O Kitはわかりにくくて使いにくいので、結構ゴリゴリとしたソースになっている。
nice!(0)  コメント(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。