SSブログ

NuDCLその12 [Mac用USBデバイス工作]

MacOS Xのハードウェアアクセス用のFrameworkであるI/O Kitが持っている、FireWire(IEEE1394)のアイソクロナス転送をサポートするためのNuDCLというメカニズムの勉強のため、AppleのサンプルコードであるIUniversalReceiver.cppのソースを見ている。
前回、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である。
0725fig3.png
コールバックが呼び出された時点で図の左のようになっているはずである。そして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)には書いてある。チャンネルはローカルとリモートのポートをリテインしているということか。

チャンネルオブジェクトのオーバーヘッドがどのくらいかわからないけど、ハードの能力ギリギリのところで使うとデータの取りこぼしよりチャンネルオブジェクトを作り直してポートを繋いで転送をセットアップして、というオーバーヘッドの方がずっと大きくてこっちで律速してしまうように思えるんだけど。
よくわからん。 これはよくわからない。
nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

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

トラックバック 0

献立7/25献立7/26 ブログトップ

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