1394OHCIのメモ


1394 Open Host Controller Interface

OHCI仕様

最近のIEEE1394 PCIボードを挿すとWindows98以降はOS標準のドライバとして、「OHCI仕様IEEE1394ホストコントローラ」というのがインストールされます。これはPCI-IEEE1394コントローラにはOpen Host Controller Interface(OHCI)という、ハードウェア仕様が定められていて、これに準拠していればどこの会社の製品でも、基本的にはソフトウェアからの扱いは同等で動くというわけです。ただ、規格書外の部分などで差がついてきてどこのは駄目だとか言うことはあります。では特定のボードがどうやってOHCI準拠か調べるかというと、PCI Configuration Registerが以下の条件を満たしていればOHCI準拠です。

ちなみにうちの実験環境は以下のような感じです。

本体PC-9821Ra266/W30R改
OHCIコントローラVIA VT6306(CHANPON2'TURBO-PCI(玄人志向))
OSWindows98 DOSプロンプト

OHCIでIEEE1394バスにパケットを流す

IEEE1394バスに流すパケットのCRCを除いた大部分は自前で作成せねばなりません。さらにそのパケットはただコントローラに出力すればよいのではありません(てゆうか400Mbpsでさえ余りCPUでIOしたいものではありませんからね)。どのように集めてどのように出力するのかというコントローラへの指示も含めたテーブル(Command Descriptor)をメモリ中に作って、OHCI準拠なコントローラにそのアドレスを渡します。そうした上で「指示とデータを取りにきて仕事を始めろ」と指示すると始めてIEEE1394バスにパケットが流れ始めるわけです。これを語を用いて書くと以下のようになります。

  1. パケットの大部分を作成する
  2. command descriptorを作成する
  3. command pointer registerにアドレスを書き込む(メモリマップトIO)
  4. command context registerのRUN bitを立てる
  5. コントローラがcommand descriptorをDMAでとりにきて解釈し、IEEE1394バスへパケットをつなぎ合わせて出力する

非同期(アシンクロナス)転送の場合

IEEE1394にはリアルタイム性を保証するアイソクロナス転送(等時性〜)とそうでなくても構わないアシンクロナス転送があります。USBでもそんな感じで転送モードは何種類かありますよね。前者は等時性を要求されるマルチメディアなどに、後者は要求されないストレージなどに使われます。SBP2でネゴシエーションのために出力するコマンドはアシンクロナス転送を用います。アシンクロナス転送のcommand descriptor関係のレジスタは4グループあります。Transmit(OHCIから相手方ノードへ)とReceive(相手方ノードからOHCIへ)、それぞれに対してRequestとResponse用の4グループです。シリアルバスでデータ線は共用ですから、送ってよこせといわないとデータは読み出せないわけですね。データを送りつけるのがWrite Requestで、データを送ってよこせというのがRead Requestになります。いずれもTransmit Request registerでパケットを送出します。そのそれぞれにWrite responseと、Read Responseが返ってきます。これらはReceive Responseの指すメモリにストアされます。は要求されたデータが入ってきます。
各DMAコンテクストの処理対象

  1. Receive Request (相手方ノードからの要求を記載したパケットを指定したアドレスにストア)
  2. Receive Response (OHCIからの要求を記載したパケットに対して相手方ノードが応答したパケットを、指定したアドレスにストア)
  3. Transmit Request (OHCIの相手方ノードに対して、指定したアドレスに構築したパケットを送付)
  4. Transmit Response (相手方ノードから送付されたパケットに対して、指定したアドレスに構築したパケットで応答)

上記4種のパケットにつき、たとえばReceive RequestについてCommand Pointer Registerと、制御用のContext Register(SET)とContext Register(CLR)があります。SETとCLRというのは、Read時は同じ値が読み込めるのですが、bitを立てて書き込んだ時にSETまたはCLEARの動作を及ぼすということです。

AT command pointer registerへ渡す表の形式はおおよそ次のようになります。これを見たら分かるように、command pointer registerへはcontext programの開始アドレスと、そのサイズを知らせれば後は勝手に走って行けるわけです。branchがあるときはその値がちょうどcommand pointer registerのポインタの形式と同じなので、同様に解釈して行けます。なお、Immediateとは「即値」の意味ですね。パケットのヘッダ部(header_CRCの前)はOHCIが解釈してIEEE1394の規定するパケット形式を完成させます。具体的にはsource ID(つまりOHCIのノードID)やheader_CRCを付加したりとか色々する必要があり、これはImmediateで渡さねばなりません。したがって、Immediateのパケットは必ずひとつあります。ではヘッダ以外のデータ部は即値で送れるかというとそうではなくて、例えばSBP-2のORB pointerは8バイトにしか過ぎませんが、これを(Block write requestパケットのヘッダを記述したOUTPUT MORE Immediateに続いて)OUTPUT LAST Immediateで送ろうとすると、Interrupt Event Register(SET)に0100000Chが返ってきます。unrecoverable errorという奴です。端的に言うと、ヘッダ部はImmediateで、そこに収まらなかったデータ部はImmediateではないdescriptorで送らないと行けないわけです。つまり、これらのdescriptorはOUTPUT LAST Headerとか、OUTPUT MORE Dataとかいう名前でも良かったわけです。
context programの様式

初期化について

これまで見てきたように1394OHCI仕様のコントローラはPC内のメモリ空間を1つのIEEE1394ノードとしてIEEE1394バスに見えるようにし、データのやり取りはDMAで行われます。起動直後はこれらのポインタが適切に設定されておらず不定なままですので、どこに何が書かれるかわかりません。そのため、再起動後はIEEE1394バスにはOHCIコントローラは接続されていない扱いとなります。バスリセットがあると、その後各ノードからself-IDパケットがブロードキャストされます。self-IDパケットに代表されるこれらの物理層間のパケットPHYパケットはあて先のアドレスを持ちません。self-IDパケットをPCのRAM内に格納するためにSelf ID Buffer Pointer RegisterSelf ID Count Registerがあり、稼動状態にするにはLinkControl registerRcvSelfIDを立てる必要があります。そしてHCControlレジスタのLINK bitを立てます。そうしてケーブルを抜き差ししてバスリセットを起こしてSelf ID Bufferをのぞくと以下のようになります。ちなみにNEC D72874ではその後ろにFF FF FF FF 00 00 00 00がついてきます。ですから、パケットを直接スキャンしてSelf IDパケットの個数を決めるのは駄目で、68hにあるSelf ID Count Registerから合計のパケットの個数を決める事になります。なお、self-IDパケットは1ノードから2quadletだけしか送られないという保証は無いので、単純に4引いてから8で割ってもノード数は分かりません。

挿した
00 00 0C 00
64 89 7F 80
9B 76 80 7F
D2 84 7F 81
2D 7B 80 7E

抜いた
00 00 0D 00
56 89 7F 80
A9 76 80 7F
D2 84 7F 81
2D 7B 80 7E
----中略-----
何だかよくわからんのでNode identification and status register(0E8h)を調べるようにした
挿したときEAX=C800FFC1hつまりnode ID=1hのとき
00 00 10 00
92 84 7F 80
6D 7B 80 7F
74 89 7F 81
8B 76 80 7E

抜いたときEAX=C800FFC0hつまりnode ID=0hのとき
00 00 11 00
56 89 7F 80
A9 76 80 7F
74 89 7F 81
8B 76 80 7E

とりあえずOHCI_1.1.pdfの11.3から言うとインクリメントされている3バイト目がselfIDGenerationでselfIDバッファの1quadlet目が更新された回数、端的に言うとバスリセット回数をあらわしているようです。問題はそれに続くselfIDのbit順序とbyteの並びです。ちょっと注意すると、最後の抜いたときの2,3行目と4,5行目がよく似ていることに気がつきます。特に直前の挿したときの4,5行目と最後の抜いたときの4,5行目とは全く同じです。そこで、最後の抜いたときの4,5行目が直前の痕跡だと気がつけば後はうまく解釈できます。80hを10b 000000b、81hを10b 000001bと解釈してみます。つまり、10b=self ID packet identification、000001b=physical IDと解釈するわけです。なお、送られてくるパケットは4バイトに続いて、論理反転した4バイトがあり、8バイト単位になっています。

コネクタ外下段に挿してnode ID=1hのとき
00 00 10 00
92 84 7F 80;|こちらが外付けCD-RW
6D 7B 80 7F;|(92 84 7F 80)と(6D 7B 80 7F)のXORはFF FF FF FFとなっている
74 89 7F 81
8B 76 80 7E

抜いてnode ID=0hのとき(つまりボードには何も接続されていない)
00 00 11 00
56 89 7F 80;現在の値 56h=0101 0110b
A9 76 80 7F
74 89 7F 81;痕跡 74h=0111 0100b
8B 76 80 7E

89h=1000 1001b : sp=10b,del=00b,c=1b,pwr=001b
7Fh=0111 1111b : #0, L=1,gap_cnt=11 1111b

コネクタ外上段に挿した
94 89 7F 80;94h=1001 0100b : p0が親ノードに接続
内蔵コネクタに挿した
5C 89 7F 81;5Ch=0101 1100b : p2が子ノードに接続

他のノードへ要求を出す

さて、他のノードが把握できたことで、これでOHCIが他のノードにRead RequestないしWrite Requestを出せるようになるかというと、そうではありません。他のノードからのResponseが返ってくるからです。Write Responseは多くの場合スーパーのレシートと同じようなもので、見ずに捨てても良いんでしょうが、Read Responseはそこにまさに求めるデータがあるので、捨てるわけには行きません。受け取るためには、AR Rsp Cmd Ptrを設定し、AR Rsp ContextをRUN=1にしておく必要があります。AR Rsp Command Ptrで指すのは、バッファアドレスではなく、バッファアドレスとそのサイズや実行ステータスを知らせるための構造体であるInput more Descriptorであり、これもあらかじめ用意しないと行けません。これをせずにパケットを流しても、一方的に垂れ流すだけになります。適切な準備があって、Configuration ROMなんかも読めるようになるわけです。

他のノードからの要求に応える

AR Reqの設定だけをしたとき(応答できない)

では逆にOHCIが他のノードからのRequestを受けつけるようにするにはどうか?具体的には、OHCIの属するコンピュータのメモリ中への書きこみ、読み出しです。例えばSBP-2デバイスなんかではよくある状況です。ちなみにこのメモリアクセスにはプロセッサの保護は働きません。PCIデバイスが勝手にメモリを読み書きするんですから。パソコン2台を1394OHCIで繋いだらお互い好き放題相手のメモリアドレスを読める、はずです。確かWindows2000のカーネルデバッガがIEEE1394経由でなかったかしら。さて、何も用意をせずにORB PointerをSBP-2対応ノードに書き込みするとします。まずBlock Write RequestがOHCIからSBP-2デバイスに行き、SBP-2デバイスからOHCIにWrite Responseが返ってきます。この次にSBP-2デバイスからOHCIにBlock Read Requestが来ます。これはあらかじめ準備をしてないと無視されます。AR Req Command Ptrを用意している状態だと、そこにBlock Read Requestパケットが格納されます。しかし、応答はしないのでノード次第でしょうが、2-3回同じ要求内容のパケットを送りつけてあきらめます。

Physical Request Filtersも設定することで要求に応えることが出来る

そこで、Asynchronous Request FiltersPhysical Request Filterレジスタを設定して、特定のノードからのリクエストには、要求した通りにOHCIが応答してあげるように設定します。名称がPHYパケットと紛らわしいですが、PHYパケットが各ノードの物理層へのものであるのに対し、Physical Requestは端的に言うとOHCIを持つパソコンのメモリへのアクセスという内容となります。ローカルバスの62個までのノードを指定するために、OHCIは以下のレジスタを持っています。bitを立てると、当該ノードからのRequestを受けつけて応答することになります。なお、バスリセット時にはノード番号が変わる関係上、これらのbitはクリアされるので、バスリセット毎に設定しなければなりません。

Request Filter Registers
名称Offset内容
AsynchronousRequestFilterHi100h(SET) 104h(CLR)bit31=asynReqResourceAll bit30=#62....bit0=#32
AsynchronousRequestFilterLo108h(SET) 10Ch(CLR)bit31=#31....bit0=#0
PhysicalRequestFilterHi110h(SET) 114h(CLR)bit31=physReqResourceAllBuses bit30=#62....bit0=#32
PhysicalRequestFilterLo118h(SET) 11Ch(CLR)bit31=#31....bit0=#0

さて、Hiのほうのbit31だけは特別なbitで、バスリセット後も値を保持します。Asyncの方は、端的にいうと1にしたときにローカルバスだけでなくほかのバスも含めた、全デバイスに応答するわけですが、Physicalのほうは、1にしたときに、ローカルバス以外の全てのバスのデバイスに応答するわけです。つまり、physReqResourceAllBusesを1にしてもローカルバスのデバイスへの応答は変わらないわけで、個別にBITを立てる必要があります。


Valid HTML 4.01 Strict