USB バルク転送要求の送信方法 (UWP アプリ)
このトピックでは、USB 一括転送と、USB デバイスと通信する UWP アプリから転送要求を開始する方法について説明します。
USB フル スピード、高速、および SuperSpeed デバイスは、一括エンドポイントをサポートできます。 これらのエンドポイントは、USB フラッシュ ドライブとの間でデータを転送するなど、大量のデータを転送するために使用されます。 バルク転送はエラー検出が可能であり、データがホストまたはデバイスに確実に受信されるようにするための再試行回数が制限されているため、信頼性が高くなります。 バルク転送は、時間が重要ではないデータに使用されます。 データは、バスで使用できる未使用の帯域幅がある場合にのみ転送されます。 そのため、バスが他の転送でビジー状態の場合、一括データは無期限に待機できます。
一括エンドポイントは一方向であり、1 回の転送で、データは IN 方向または OUT 方向で転送できます。 一括データの読み取りと書き込みをサポートするには、デバイスで一括 IN エンドポイントと一括 OUT エンドポイントをサポートする必要があります。 一括 IN エンドポイントはデバイスからホストにデータを読み取るために使用され、一括 OUT エンドポイントはホストからデバイスにデータを送信するために使用されます。
一括転送要求を開始するには、アプリにエンドポイントを表すパイプへの参照が必要です。 パイプは、デバイスの構成時にデバイス ドライバーによって開かれるコミュニケーション チャネルです。 アプリの場合、パイプはエンドポイントの論理表現です。 エンドポイントからデータを読み取るために、アプリは関連付けられている一括 IN パイプからデータを取得します。 エンドポイントにデータを書き込むには、アプリは一括 OUT パイプにデータを送信します。 パイプの一括読み取りと書き込みの場合は、UsbBulkInPipe クラスと UsbBulkOutPipe クラスを使用します。
アプリでは、特定のポリシー フラグを設定することで、パイプの動作を変更することもできます。 たとえば、読み取り要求の場合、パイプのストール状態を自動的にクリアするフラグを設定できます。 これらのフラグの詳細については、UsbReadOptions と UsbWriteOptions を参照してください。
開始する前に
- デバイスを開き、UsbDevice オブジェクトを取得している必要があります。 「USB デバイスへの接続方法 (UWP アプリ)」を参照してください。
- このトピックに示されている完全なコードは、CustomUsbDeviceAccess sample の Scenario4_BulkPipes ファイルで確認できます。
手順 1: 一括パイプ オブジェクトを取得する
転送要求を開始するには、バルク パイプ オブジェクト (UsbBulkOutPipe または UsbBulkInPipe) への参照を取得する必要があります。 すべてのインターフェイスのすべての設定を列挙することで、パイプを取得できます。 ただし、データ転送では、アクティブな設定のパイプのみを使用する必要があります。 関連付けられているエンドポイントがアクティブな設定にない場合、パイプ オブジェクトが null の場合。
目的 | このプロパティ値を使用する |
---|---|
一括パイプにデータを送信し、UsbBulkOutPipe への参照を取得します。 | UsbDevice.DefaultInterface.BulkOutPipes[n] (デバイス構成で 1 つの USB インターフェイスが公開されている場合)。 デバイスでサポートされている複数のインターフェイスで一括 OUT パイプを列挙するための UsbDevice.Configuration.UsbInterfaces[m].BulkOutPipes[n]。 インターフェイスの設定によって定義された一括 OUT パイプを列挙するための UsbInterface.InterfaceSettings[m].BulkOutEndpoints[n].Pipe。 一括 OUT エンドポイントのエンドポイント記述子からパイプ オブジェクトを取得するための UsbEndpointDescriptor.AsBulkOutEndpointDescriptor.Pipe。 |
一括パイプからデータを受信すると、UsbBulkInPipe オブジェクトを取得できます。 | UsbDevice.DefaultInterface.BulkInPipes[n] (デバイス構成で 1 つの USB インターフェイスが公開されている場合)。 デバイスでサポートされている複数のインターフェイスで一括 IN パイプを列挙するための UsbDevice.Configuration.UsbInterfaces[m].BulkInPipes[n]。 インターフェイスの設定によって定義された一括 IN パイプを列挙するための UsbInterface.InterfaceSettings[m].BulkInEndpoints[n].Pipe。 一括 IN エンドポイントのエンドポイント記述子からパイプ オブジェクトを取得するための UsbEndpointDescriptor.AsBulkInEndpointDescriptor.Pipe。 |
Note
アクティブな設定であるかを確認する null チェックが必要です。
手順 2: バルク パイプを構成する (省略可能)
取得した一括パイプに特定のフラグを設定することで、読み取りまたは書き込み操作の振る舞いを変更できます。
デバイスから読み取る場合は、UsbBulkInPipe.ReadOptions プロパティを UsbReadOptions で定義されている値のいずれかに設定します。 書き込みの場合は、UsbBulkOutPipe.WriteOptions プロパティを UsbWriteOptions で定義されているいずれかの値に設定します。
目的 | このフラグを設定する |
---|---|
データ フローを停止せずにエンドポイントのエラー状態を自動的にクリアする | AutoClearStall 詳細については、「ストール条件のクリア」を参照してください。 このフラグは、読み取りと書き込みの両方の転送に適用されます。 |
最大限の効率で複数の読み取り要求を送信します。 エラーチェックをバイパスして、パフォーマンスを向上させます。 | OverrideAutomaticBufferManagement データ要求は 1 つ以上の転送に分割できます。各転送には、最大転送サイズと呼ばれる特定のバイト数が含まれます。 複数の転送の場合、ドライバーによって実行されるエラー チェックにより、2 つの転送をキューに入れるときに遅延が発生する可能性があります。 このフラグは、そのエラー チェックをバイパスします。 最大転送サイズを取得するには、UsbBulkInPipe.MaxTransferSizeBytes プロパティを使用します。 要求サイズが UsbBulkInPipe.MaxTransferSizeBytes の場合は、このフラグを設定する必要があります。 重要: このフラグを設定する場合は、パイプの最大パケット サイズの倍数でデータを要求する必要があります。 その情報はエンドポイント記述子に格納されます。 サイズは、デバイスのバス速度によって異なります。 フル スピード、高速、および SuperSpeed 用。最大パケット サイズは、それぞれ 64 バイト、512 バイト、1024 バイトです。 この値を取得するには、UsbBulkInPipe.EndpointDescriptor.MaxPacketSize プロパティを使用します。 このフラグは、読み取り転送にのみ適用されます。 |
長さ 0 のパケットを使用して書き込み要求を終了する | ShortPacketTerminate OUT 転送の終了を示す長さ 0 のパケットを送信します。 このフラグは、書き込み転送にのみ適用されます。 |
短いパケットの読み取りを無効にする (エンドポイントでサポートされている最大パケット サイズ未満) | IgnoreShortPacket 既定では、デバイスが最大パケット サイズより小さいバイトを送信した場合、アプリはそれらを受信します。 短いパケットを受信しない場合は、このフラグを設定します。 このフラグは、読み取り転送にのみ適用されます。 |
ステップ3: データストリームを設定する
デバイスから一括データが送信されると、一括パイプの入力ストリームと同様にデータが受信されます。 入力ストリームを取得する手順を次に示します。
- UsbBulkInPipe.InputStream プロパティを取得して、入力ストリームへの参照を取得します。
- DataReader コンストラクターで入力ストリームを指定して、DataReader オブジェクトを作成します。
デバイスにデータを書き込むには、アプリが一括パイプの出力ストリームに書き込む必要があります。 出力ストリームを準備する手順を次に示します。
- UsbBulkOutPipe.OutputStream プロパティを取得して、出力ストリームへの参照を取得します。
- DataWriter コンストラクターで出力ストリームを指定して、DataWriter オブジェクトを作成します。
- 出力ストリームに関連付けられているデータ バッファーを設定します。
- データ型に応じて、WriteBytes などのDataWriter メソッドを呼び出して、出力ストリームに転送データを書き込みます。
手順 4: 非同期転送操作を開始する
一括転送は非同期操作によって開始されます。
一括データを読み取る場合は、DataReader.LoadAsync を呼び出して非同期読み取り操作を開始します。
一括データを書き込むには、DataWriter.StoreAsync を呼び出して非同期書き込み操作を開始します。
手順 5: 読み取り転送操作の結果を取得する
非同期データ操作が完了すると、タスク オブジェクトから読み取りまたは書き込まれたバイト数を取得できます。 読み取り操作の場合は、ReadBytes などの DataReader メソッド を呼び出して、入力ストリームからデータを読み取ります。
ストール状態のクリア
場合によっては、アプリでデータ転送に失敗することがあります。 転送の失敗は、エンドポイントのストール状態が原因である可能性があります。 エンドポイントがストールしている限り、データを書き込んだり読み取ったりすることはできません。 データ転送を続行するには、関連付けられているパイプのストール状態をアプリでクリアする必要があります。
アプリでは、ストール状態が発生したときに自動的にクリアされるようにパイプを構成できます。 これを行うには、UsbBulkInPipe.ReadOptions プロパティを UsbReadOptions.AutoClearStall に設定するか、UsbBulkOutPipe.WriteOptions プロパティを UsbWriteOptions.AutoClearStall に設定します。 この自動構成により、アプリでは転送の失敗が発生せず、データ転送エクスペリエンスがシームレスになります。
ストール状態を手動でクリアするには、一括 IN パイプに対して UsbBulkInPipe.ClearStallAsync を呼び出し、一括 OUT パイプに対して UsbBulkOutPipe.ClearStallAsync を呼び出します。
Note
ストール状態は、空のエンドポイントを示していません。 エンドポイントにデータがない場合、転送は完了しますが、長さは 0 バイトです。
読み取り操作の場合、新しい転送リクエストを開始する前に、パイプ内の保留中のデータをクリアする必要がある場合があります。 これを行うには、UsbBulkInPipe.FlushBuffer メソッドを呼び出します。
USB 一括転送コードの例
このコード例では、一括パイプに書き込む方法を示します。 この例では、既定のインターフェイスの最初の一括 OUT パイプにデータを送信します。 転送の最後に長さ 0 のパケットを送信するようにパイプを構成します。 転送が完了すると、バイト数が表示されます。
private async void BulkWrite()
{
String dataBuffer = "Hello World!";
UInt32 bytesWritten = 0;
UsbBulkOutPipe writePipe = usbDevice.DefaultInterface.BulkOutPipes[0];
writePipe.WriteOptions |= UsbWriteOptions.ShortPacketTerminate;
var stream = writePipe.OutputStream;
DataWriter writer = new DataWriter(stream);
writer.WriteString(dataBuffer);
try
{
bytesWritten = await writer.StoreAsync();
}
catch (Exception exception)
{
ShowStatus(exception.Message.ToString());
}
finally
{
ShowStatus("Data written: " + bytesWritten + " bytes.");
}
}
このコード例では、一括パイプから読み取る方法を示します。 この例では、既定のインターフェイスの最初の一括 IN パイプからデータを取得します。 最大効率を得るためにパイプを構成し、最大パケット サイズのチャンクでデータを受信します。 転送が完了すると、バイト数が表示されます。
private async void BulkRead()
{
UInt32 bytesRead = 0;
UsbBulkInPipe readPipe = usbDevice.DefaultInterface.BulkInPipes[0];
// Warning: Setting IgnoreShortPacket causes LoadAsync to block until you receive a number of packets >= readPipe.EndpointDescriptor.MaxPacketSize.
// Remove the following line if you want to see messages that are less than the max transfer size, for example if you are communicating with a USBTMC device.
readPipe.ReadOptions |= UsbReadOptions.IgnoreShortPacket;
var stream = readPipe.InputStream;
DataReader reader = new DataReader(stream);
try
{
bytesRead = await reader.LoadAsync(readPipe.EndpointDescriptor.MaxPacketSize);
}
catch (Exception exception)
{
ShowStatus(exception.Message.ToString());
}
finally
{
ShowStatus("Number of bytes: " + bytesRead);
IBuffer buffer = reader.ReadBuffer(bytesRead);
using (var dataReader = Windows.Storage.Streams.DataReader.FromBuffer(buffer))
{
dataReader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
ShowData(dataReader.ReadString(buffer.Length));
}
}
}