libusb  1.0.24
USBデバイスにアクセスするためのクロス・プラットフォームのユーザー・ライブラリ
同期・非同期デバイス入出力

はじめに

アプリケーションでlibusbを使用している場合は、あなたはおそらくデバイスで入出力を実行したい、あなたはUSBデータ転送を実行したいと思うでしょう。

libusbは、デバイス入出力用に2つの別個のインターフェースを提供します。 このページは、どちらがアプリケーションに適しているかを判断するのに役立つように、その2つを紹介することを目的としています。ケース・バイ・ケースで各転送を検討することにより、アプリケーションで両方のインターフェイスの使用を選択することもできます。

以下の説明を読み終えたら、詳細については詳細なAPI文書を参照してください:

論理レベルで転送

論理レベルでは、USB転送は通常2つの部分で行われます。たとえば、エンドポイントからデータを読み取る場合:

  1. データの要求がデバイスに送信されます
  2. しばらくして、受信データがホストによって受信されます

またはエンドポイントにデータを書き込む場合:

  1. データはデバイスに送信されます
  2. しばらくして、ホストはデータが転送されたという確認応答をデバイスから受信します。

2つのステップの間に無期限の遅延がある場合があります。ユーザーが押すことができるボタンを備えた架空のUSB入力デバイスについて考えてみます。ボタンがいつ押されたかを判断するために、バルク・エンドポイントまたは割り込みエンドポイントでデータを読み取り、データが到着するのを待つ要求を送信する可能性があります。ユーザーがボタンを押すとデータが到着しますが、これは数時間後になる可能性があります。

libusbは、USB転送を実行するための同期インターフェイスと非同期インターフェイスの両方を提供します。主な違いは、同期インターフェイスが上記の両方のステップを1つの関数呼び出しに結合するのに対し、非同期インターフェイスはそれらを分離することです。

同期インターフェイス

同期入出力インターフェイスを使用すると、1回の関数呼び出しでUSB転送を実行できます。関数呼び出しが戻ると、転送が完了し、結果をパースできます。

あなたが以前にlibusb-0.1を使用したことがある場合、この入出力スタイルはおなじみのだと思います。libusb-0.1は同期インターフェイスのみを提供していました。

我々の入力デバイスの例では、ボタンの押下を読み取るために、以下のスタイルでコードを記述できます:

unsigned char data[4];
int actual_length;
int r = libusb_bulk_transfer(dev_handle, LIBUSB_ENDPOINT_IN, data, sizeof(data), &actual_length, 0);
if (r == 0 && actual_length == sizeof(data)) {
// 取引(transaction)の結果はそのデータ・バッファに現れます
// データ・バッファをパースしボタン押下を報告します
} else {
error();
}

このモデルの主な利点は単純さです。1回の単純な関数呼び出しですべてを実行できます。

けれども、このインターフェイスには制限があります。アプリケーションは、取引がが完了するまで libusb_bulk_transfer() 内でスリープします。ユーザーがボタンを押すのに3時間かかる場合、アプリケーションはその3時間スリープします。実行はライブラリ内で束縛されます。つまり、その間、スレッド全体は役に立たなくなります。

もう1つの問題として、スレッドをその単一の取引に結び付けることにより、取引ごとに1つのスレッドを作成しない限り、複数のエンドポイントや複数のデバイスで入出力を同時に実行することが不可能なことです。

加えて、要求(request)の送信(submit)後に転送をキャンセルすることはできません。

同期APIの使用方法の詳細については 同期入出力API解説 を参照してください。

非同期インターフェイス

非同期入出力は、libusb-1.0の最も重要な新機能です。これはより複雑なインターフェイスですが、上記のすべての問題を解決します。

入出力が完了するまでブロックする関数を提供する代わりに、libusbの非同期インターフェイスは、転送を開始してすぐに戻る非ブロック関数を提供します。アプリケーションは、この非ブロッキング関数へのコールバック関数ポインタを渡します。libusbは、取引が完了すると、取引の結果と共にこのコールバック関数を呼び出します。

非ブロッキング関数を介して送信(submit)された転送は、別の関数呼び出しでキャンセルできます。

このインターフェイスのノン・ブロッキングの性質により、スレッドを使用せずに、複数のデバイス上の複数のエンドポイントに対して同時に入出力を実行できます。

ただし、この追加された柔軟性には、いくつかの問題が伴います。

  • 軽量(lightweight)ライブラリであるために、libusbはスレッドを作成せず、アプリケーションがそれを呼び出しているときにのみ動作できます。イベントを処理する準備ができたら、アプリケーションはメイン・ループからlibusbを呼び出す必要があります。または、他の仕組みを使用して、libusbが必要な作業を実行できるようにする必要があります。
  • libusbは、転送タイムアウトを正確に処理するために、特定の固定箇所で呼び出す必要もあります。
  • メモリ処理はより複雑になります。スタックを持つ関数が転送コールバックの実行が終了するまで戻らないことが保証されていない限り、スタック・メモリを使用することはできません。
  • 転送要求の送信は転送結果が処理される場所とは別の関数で行われるため、通常、ソース・コードの流れからある程度の線形性が失われます。 これは、以前の転送の結果に基づいて2番目の転送を送信する場合に特に明白になります。

内部的には、libusbの同期インターフェイスは、非同期インターフェイスへの関数呼び出しの観点から表現されます。

非同期APIの使用方法の詳細については、 非同期入出力API を参照してください。

libusb_bulk_transfer
int libusb_bulk_transfer(libusb_device_handle *dev_handle, unsigned char endpoint, unsigned char *data, int length, int *actual_length, unsigned int timeout)
定義: sync.c:274
LIBUSB_ENDPOINT_IN
@ LIBUSB_ENDPOINT_IN
In: デバイスからホスト。
定義: libusb.h:322