NuDCLその9 [Mac用USBデバイス工作]
FireWireのアイソクロナス転送でデータをやりとりするためのMacOS XのI/O Kitによるメカニズムである2種類のDCLを比較している。そして古いDCLで書いたIIDCカメラのドライバを新しいNuDCLを使ったものに書き換えたくて、AppleのサンプルコードであるUniversalReceiveTestの中身を見ている。昨日はアイソクロナス転送をするC++クラスであるUniversalReceiverのコンストラクタを確認した。インスタンス変数の設定以外にほとんど何もしていない、ということはわかった。今日はクライアント側からつぎに呼ばれるメンバ関数の中身を見る。
インターフェイスが手に入ると、CFRunLoopGetCurrent関数で自分の乗っているCFRunLoopを得る。これはCreateUniversalReceiver関数で作られたスレッドの上のランループになっているはず。
ランループにローカルノードのコールバックのディスパッチャを登録したあと、CFMutableSetの配列を作っている。配列の長さはsegmentの個数になっている。これはNuDCLをアップデートするときに使う集合として使われるもの。
それからNuDCLPoolを作っている。
一番内側のループ(cycle)の最初は
その次にフラグの設定などがあって
それに続いて
これは整理すると、
そのすぐあとに
3.4 setupIsocReceiver関数
さっきのUniversalReceiverRTThreadStart関数の中でコンストラクタが呼ばれたあと、次に呼ばれる。この関数でNuDCL列を作っている。これも結構大きな関数だけど、順に見る。3.4.1 ローカルノードのインタフェイスを得る
まずデバイスインターフェイスからローカルノードのインターフェイス、つまりFireWireのMac側のインターフェイスを作っている(これもAVSCommon.cppに定義されたヘルパ関数を使っている)。インターフェイスが手に入ると、CFRunLoopGetCurrent関数で自分の乗っているCFRunLoopを得る。これはCreateUniversalReceiver関数で作られたスレッドの上のランループになっているはず。
ランループにローカルノードのコールバックのディスパッチャを登録したあと、CFMutableSetの配列を作っている。配列の長さはsegmentの個数になっている。これはNuDCLをアップデートするときに使う集合として使われるもの。
それからNuDCLPoolを作っている。
3.4.2 次の作業
そのあとは以前やったのと同じ。- リモートポートを作る
- リモートポート用のハンドラを設定する
- vm_allocateで転送用のバッファ領域を確保する
- DCLのover run用やタイムスタンプ用のバッファのポインタを設定する
3.4.3 NuDCLコマンド列を作る
コマンドをプール関数を使って作っていくが、それが2重ループになっている。for (seg = 0 ; seg < isochSegments ; seg++) { pSegUpdateBags[seg] = CFSetCreateMutable(NULL, 0, NULL); for (cycle = 0 ; cycle < isochCyclesPerSegment ; cycle++) { ...外側がsegment、内側がcycle用になっている。まずsegmentにひとつCFMutableSetを作っている。
一番内側のループ(cycle)の最初は
range.address = (IOVirtualAddress) &pBuffer[bufCnt * cycleBufLen]; range.length = (IOByteCount) cycleBufLen; thisDCL = (*nuDCLPool)->AllocateReceivePacket( nuDCLPool, pSegUpdateBags[seg], 4, 1, &range);となっていて、バッファのアドレスを設定したあとプールからパケットを受け取るNuDCLコマンドを作っている。bufCntというのは最初に0に設定されていて、cycleループの最後にインクリメントされている。
その次にフラグの設定などがあって
if (cycle == 0) { receiveSegmentInfo[seg].segmentStartDCL = thisDCL; (*nuDCLPool)->SetDCLTimeStampPtr(thisDCL, &pTimeStamps[seg]) ; }segmentの最初のcycleの場合にUniversalReceiveSegment構造体のふたつのポインタのうちStartDCLポインタをさっき作ったReceivePacketDCLに設定して、さらにタイムスタンプのバッファも指定している。
それに続いて
else if (cycle == (isochCyclesPerSegment-1)) { receiveSegmentInfo[seg].segmentEndDCL = thisDCL; (*nuDCLPool)->SetDCLUpdateList(thisDCL, pSegUpdateBags[seg]) ; (*nuDCLPool)->SetDCLCallback(thisDCL, UniversalReceiveDCLCallback_Helper) ; } bufCnt++;としている。つまりひとつのsegmentの最後のcycleだった場合、UniversalReceiveSegmentのもう一方のポインタを設定して、アップデートして、DCLコールバックを呼ぶ、ということをしている。cycleとsegmentのループはこれで終わっている。
これは整理すると、
- cycleBufferSize(=cycleBufLen)バイトのパケットを受ける
- cyclesPerSegment(=isochCyclesPerSegment)回繰り返す
- updateする
- コールバックを呼ぶ
- 以上をnumSegments(=isochSegments)回繰り返す
そのすぐあとに
range.address = (IOVirtualAddress) pOverrunReceiveBuffer; range.length = (IOByteCount) cycleBufLen; thisDCL = (*nuDCLPool)->AllocateReceivePacket( nuDCLPool, NULL, // No update-bag needed for this DCL! 4, 1, &range);となっていてもうひとつ追加でパケットを受け取るようになっていてさらに
overrunDCL = thisDCL; .... (*nuDCLPool)->SetDCLCallback(thisDCL, UniversalReceiveOverrunDCLCallback_Helper) ;となっている。overrunDCLというのはインスタンス変数。そしてこのNuDCLコマンドに別のコールバック関数を設定している。古いDCLにもあったover run検出用のコールバックとなっている。
3.4.4 残りの作業
あとは- ローカルポートを作る
- ローカルポートのfinalizeコールバックを設定する
- アイソクロナスチャンネルを作る
- チャンネルにローカルポートをリスナ、リモートポートをトーカとして設定する
- チャンネルのForceStopHandlerコールバックを設定する
- チャンネルの通知をオンにする
- 自分自身のfixupDCLJumpTargets関数を呼ぶ
2010-07-07 22:35
nice!(0)
コメント(0)
トラックバック(0)
コメント 0