macOSからPi Picoを使う - その28 [Pi Pico]
ここんとこPi PicoばっかりいじってるせいでCしか書かなくなってる(C++はずっと昔十数年前のことだけど、何度か挑戦したあと挫けた。結局C++は僕には複雑で大きすぎた、ということのようである。Swiftもその気配が忍び寄っている。C++に比べるとObjective-Cは小さくて簡単だった)。継続的に書いてないと他の言語を忘れてしまう。せっかくなんとなくわかりかけてきたSwiftもなんだかもやもやしてきた。つい先日工場に行ったときに昔書いたObjective-Cのコードに手を加えようとしてうろが来た。あれだけ何年も書いてきたObjective-Cでさえ、あれ?なんだっけ?なんてことになってる。ほんの2、3ヶ月のことなのになんということだ。
というわけでlibusbのおさらい。当然これもCで書いてあるんだよなぁ....
話を続ける前に、前回か、あるいは前々回書かないといけないことを忘れていた。そっちを先に片付ける。
デバイスをオープンしただけでは転送ができない。activeなconfigurationの中のどのinterfaceを使うか、を宣言しないといけなかった。
この関数はUSB経由でPi PicoをBOOTSELモードにリセットするところではちゃんと書いていた。ところがうっかりこれを忘れていてデバイスはオープンできたのに転送は何度やっても失敗するので悩んでしまった。これに気がつくまで半日かかった。バカみたい。
ちなみにこの関数呼び出しは信号としての出入りはない、とドキュメントに書いてある。つまり純粋にlibusb内部の問題だということである。
非同期転送は少しだけ面倒でまず転送用の構造体を用意する。
転送モードに関わらず同じ構造体を使うけど、モードによって無意味なフィールドとかもある。
非同期転送の手順として
転送が終わるとコールバック関数
が呼ばれる。これは構造体のcallbackメンバとして埋め込んでおく。転送が正常に終わったかどうかは構造体のstatusメンバを見る。この中身はenumで
だそうである。
転送用のデータはbufferを使う。lengthはこの確保された長さを指定する。actual_lengthは転送後に実際に転送された長さが入る。user_dataはいわゆるRefCon、つまりこのAPIを使う側が必要なデータを保持させるコンテクストデータである。
bufferは構造体の確保と開放と同時に処理されるけど、user_dataは単にポインタを確保しているだけなので、もしこれがmalloc()なんかで取られた場合は自分でfree()しないといけない。
と言う関数が用意されている。cntrol、bulk、interupt transferをするときにはlibusb_alloc_transfer()の引数を0にする。isochronous transferではpacket descriptorの数を渡す。
この構造体は複数の転送に使い回せるらしい。つまり転送が終わるたびに解放する必要はない、となっている。
ちょっと区切りが悪いけど、長くなってしまうので残りの非同期転送の話は次回に。
というわけでlibusbのおさらい。当然これもCで書いてあるんだよなぁ....
話を続ける前に、前回か、あるいは前々回書かないといけないことを忘れていた。そっちを先に片付ける。
デバイスをオープンしただけでは転送ができない。activeなconfigurationの中のどのinterfaceを使うか、を宣言しないといけなかった。
int libusb_claim_interface(libusb_device_handle *dev_handle, int interface_number);この手続きは代替interfaceを持つconfigurationがあるので必要になっているようだけど、interfaceがひとつしかなくてもこれを呼ばないと転送でエラーになる。従って転送モードごとにinterfaceが分かれているようなconfigurationの場合(例えばbulk転送はinterface0で、interrupt転送はinterface1で、など。あまりそんなことをする必然性はないかもしれないけど)、libusbではendpoint番号が独立であるにもかかわらず、この関数を呼んでinterfaceを切り替える必要がある、ということである。
この関数はUSB経由でPi PicoをBOOTSELモードにリセットするところではちゃんと書いていた。ところがうっかりこれを忘れていてデバイスはオープンできたのに転送は何度やっても失敗するので悩んでしまった。これに気がつくまで半日かかった。バカみたい。
ちなみにこの関数呼び出しは信号としての出入りはない、とドキュメントに書いてある。つまり純粋にlibusb内部の問題だということである。
17.4.3 非同期転送
前回の3つの関数は転送が終わるまで返ってこない。転送のタイミングはホスト(正確にはホスト側のUSBコントローラ)が制御するので、デバイス側でそれを知る手段はない。この時間が惜しいアプリのために非同期転送が用意されている。これはUSBの規格とは関係なく、libusbからのアプリケーション側への配慮である。ただしlibusbの思想にまつわる問題、つまりなんというか、ライブラリとしてなるべく軽量であるべし、という要求から非同期転送に特有の難しさが存在する。その話はあとで。非同期転送は少しだけ面倒でまず転送用の構造体を用意する。
struct libusb_transfer { libusb_device_handle *dev_handle; uint8_t flags; unsigned char endpoint; unsigned char type; unsigned int timeout; enum libusb_transfer_status status; int length; int actual_length; libusb_transfer_cb_fn callback; void *user_data; unsigned char *buffer; int num_iso_packets; struct libusb_iso_packet_descriptor iso_packet_desc[ZERO_SIZED_ARRAY]; };
非同期転送の手順として
- Allocation 構造体の領域確保
- Filling 中身を埋める
- Submission 転送指示
- Completion handling 終わったかどうかの確認
- Deallocation 領域解放
転送が終わるとコールバック関数
typedef void(* libusb_transfer_cb_fn)(struct libusb_transfer *transfer);
LIBUSB_TRANSFER_COMPLETED | 正常終了 |
LIBUSB_TRANSFER_ERROR | なんらかのエラーが起きた |
LIBUSB_TRANSFER_TIMED_OUT | タイムアウト |
LIBUSB_TRANSFER_CANCELLED | 転送がキャンセルされた |
LIBUSB_TRANSFER_STALL | エンドポイントがストールした |
LIBUSB_TRANSFER_NO_DEVICE | 指定されたデバイスがない、あるいはケーブルが抜けた |
LIBUSB_TRANSFER_OVERFLOW | デバイスから送られてきたデータのほうが多い |
だそうである。
転送用のデータはbufferを使う。lengthはこの確保された長さを指定する。actual_lengthは転送後に実際に転送された長さが入る。user_dataはいわゆるRefCon、つまりこのAPIを使う側が必要なデータを保持させるコンテクストデータである。
bufferは構造体の確保と開放と同時に処理されるけど、user_dataは単にポインタを確保しているだけなので、もしこれがmalloc()なんかで取られた場合は自分でfree()しないといけない。
17.4.4 Allocation
libusb_transfer構造体の確保と解放にはstruct libusb_transfer* libusb_alloc_transfer(int iso_packets); void libusb_free_transfer(struct libusb_transfer *transfer);
この構造体は複数の転送に使い回せるらしい。つまり転送が終わるたびに解放する必要はない、となっている。
ちょっと区切りが悪いけど、長くなってしまうので残りの非同期転送の話は次回に。
2021-08-26 21:37
nice!(0)
コメント(0)
コメント 0