NuDCLその12 [Mac用USBデバイス工作]
MacOS Xのハードウェアアクセス用のFrameworkであるI/O Kitが持っている、FireWire(IEEE1394)のアイソクロナス転送をサポートするためのNuDCLというメカニズムの勉強のため、AppleのサンプルコードであるIUniversalReceiver.cppのソースを見ている。
前回、NuDCLから呼ばれるコールバック関数の中身を見ていてそれが途中になっていた。今日はその関数を最後まで見て、もうひとつのコールバックであるOverrunDCLCallBack関数も見る。
そしてさらに
左側の状態でDCLが一周してきてsegmentでの処理が終わる前に(繋ぎ変える前に)segmentの持っているバッファにデータを転送しようとしたら、over runに切り替わる。
それを図の右のようにつなぎ変えている。ちょうど今右側の状態でsegmentの位置のデータを処理し終わった。従ってこのsegmentのバッファは転送に使えるということになり、segment-1の次に割り当てている、ということになっている。
これで、もしover runのコマンドが呼ばれたとしたら処理の前にデータが転送され、上書きされた、ということになる。わかりにくいけどこういうやりかたはDCLの常套手段らしい。
このコールバックはかなりの分量になっているけどやっていることは簡単。
はじめの方は
エラーメッセージを作ったあと、
ReleaseChannelを呼ぶとポートをリリースするとヘッダ(IOFireWireLibIsoch.h)には書いてある。チャンネルはローカルとリモートのポートをリテインしているということか。
チャンネルオブジェクトのオーバーヘッドがどのくらいかわからないけど、ハードの能力ギリギリのところで使うとデータの取りこぼしよりチャンネルオブジェクトを作り直してポートを繋いで転送をセットアップして、というオーバーヘッドの方がずっと大きくてこっちで律速してしまうように思えるんだけど。
よくわからん。 これはよくわからない。
前回、NuDCLから呼ばれるコールバック関数の中身を見ていてそれが途中になっていた。今日はその関数を最後まで見て、もうひとつのコールバックであるOverrunDCLCallBack関数も見る。
3.7.4 DCLジャンプ処理
そのあとは(*nuDCLPool)->SetDCLBranch(receiveSegmentInfo[segment].segmentEndDCL, overrunDCL); result = (*localIsocPort)->Notify(localIsocPort, kFWNuDCLModifyJumpNotification, (void**) &receiveSegmentInfo[segment].segmentEndDCL, 1) ;となっている。つまり今処理の終わったsegmentの最後をoverrunDCLにつないでいる。
そしてさらに
UInt32 lastSegment = (segment == 0) ? (isochSegments - 1) : (segment - 1); (*nuDCLPool)->SetDCLBranch(receiveSegmentInfo[lastSegment].segmentEndDCL, receiveSegmentInfo[segment].segmentStartDCL); result = (*localIsocPort)->Notify(localIsocPort, kFWNuDCLModifyJumpNotification, (void**) &receiveSegmentInfo[lastSegment].segmentEndDCL, 1) ;となっている(元のコードがあまりに読みづらいのでちょっと書き換えてある。サンプルコードなんだからもう少し読みやすくして欲しいなあ)。わかりにくいので絵にしたのが図-3である。 コールバックが呼び出された時点で図の左のようになっているはずである。そしてDCLはそのsgement+1が持っているバッファやあるいはそれ以降のバッファにデータを転送中のはずである。ひょっとするとループを一周してsegmentのすぐ上にデータが転送されているかもしれない。
左側の状態でDCLが一周してきてsegmentでの処理が終わる前に(繋ぎ変える前に)segmentの持っているバッファにデータを転送しようとしたら、over runに切り替わる。
それを図の右のようにつなぎ変えている。ちょうど今右側の状態でsegmentの位置のデータを処理し終わった。従ってこのsegmentのバッファは転送に使えるということになり、segment-1の次に割り当てている、ということになっている。
これで、もしover runのコマンドが呼ばれたとしたら処理の前にデータが転送され、上書きされた、ということになる。わかりにくいけどこういうやりかたはDCLの常套手段らしい。
3.7.5 残りの処理
そのあとコールバックはcurrentSegment += 1; if (currentSegment == isochSegments) currentSegment = 0; // If the client has registered a no-data notification, // see if we should reset the timer if ((noDataProc != nil) && (segmentHasData == true)) startNoDataTimer(); }ということをしている。つまりcurrentSegmentを一つ進めて、まえにあったNoDataTimerを延長して終わっている。
このコールバックはかなりの分量になっているけどやっていることは簡単。
3.8 over runコールバック
DCLがover runしたとき呼ばれるコールバックをみてみる。はじめの方は
pthread_mutex_lock(&transportControlMutex); if (transportState == kUniversalReceiverTransportRecording) { // stop/delete the no-data timer if it exists stopNoDataTimer(); // Stop Receiver (*isochChannel)->Stop(isochChannel); (*isochChannel)->ReleaseChannel(isochChannel); transportState = kUniversalReceiverTransportStopped;となっている。つまりtransportControlMutexロックを確保して転送中かどうかのフラグをチェックする。そしてアイソクロナス転送を止めて、チャンネルをリリースしてしまっている。
エラーメッセージを作ったあと、
fixupDCLJumpTargets(); result = (*isochChannel)->AllocateChannel(isochChannel); if (result == kIOReturnSuccess) { result = (*isochChannel)->Start(isochChannel); if (result != kIOReturnSuccess) { (*isochChannel)->ReleaseChannel(isochChannel); } else { transportState = kUniversalReceiverTransportRecording; if (noDataProc) startNoDataTimer(); } } } pthread_mutex_unlock(&transportControlMutex);となっていて、つまりチャンネルを作り直して転送を仕切り直す、ということをしている。これってかなりのオーバーヘッドになると思うんだけど、こうすべきなんだろうか。
ReleaseChannelを呼ぶとポートをリリースするとヘッダ(IOFireWireLibIsoch.h)には書いてある。チャンネルはローカルとリモートのポートをリテインしているということか。
チャンネルオブジェクトのオーバーヘッドがどのくらいかわからないけど、ハードの能力ギリギリのところで使うとデータの取りこぼしよりチャンネルオブジェクトを作り直してポートを繋いで転送をセットアップして、というオーバーヘッドの方がずっと大きくてこっちで律速してしまうように思えるんだけど。
よくわからん。 これはよくわからない。
2010-07-25 21:56
nice!(0)
コメント(0)
トラックバック(0)
コメント 0