第9回 8169!

ええ…まあ…そうですね…

さて、みなさん、良い子にしていたかな?


というわけで、今日のテーマは蟹イーサである。
言いわけすると、イーサのドライバ自体は一月くらい前に動いていたのだが、そっからTCP/IP実装しようとして挫折していたのである。何の役にも立たない情報である。

あと、つまり、TCP/IPは挫折しているので、イーサネットしか使えないという状態である。
何の役にも立たなさそうなイーサネットのドライバであるが、しかし、最近は、TCP/IP プロトコルのオーバーヘッドがCO2を発生させていることから、TCP/IPを使わないで、イーサネットだけで通信するのがエコ的に流行しているといわれている。よって、エコドライバである。


さて、蟹イーサは、色々とあるなー今日のテーマは、その中でも、ギガビット対応しておるRTL8168とかRTL8169とかの話である。
あと、RTL8169とインターフェースが互換だが、100Mまでしか出ないRTL8101とかがあるなー。ネットブックとかに付いてる100Mイーサは、これである。

蟹の100Mというと、RTL8139があるが、互換性はあらへん。全くの別物である。


目標は、イーサネットのフレームを送受信できるところまでである。
あとすまんが、Gbitの環境作るのが面倒で確認していないので、動作は100Mでしか確認しておらぬよー。


しかしイーサネットの前に、まず、やるべきことは、PCI関連の問題をクリアすることであるなー。APICを使わない場合の割り込みは、コンフィグレーション空間に書いたおるのを読めばよいが、しかし、I/O APICを使うという選択をしたので、コンフィグレーション空間に書いておる割り込み番号は意味が無い。(PCIは仕様読んでるわけではないので間違っているかもしれない)

しかし、我々にはACPIがあるので、それを使うと、割り込み番号がとれるのであるのだ。しかしこの説明は超地味かつ面倒なのでスキップする。もちろんここはスキップしてはいけない箇所であるが、ここらへんの話は、おそらく100年以内に書かれるであろう完全版で解説される予定である。
コード的には、kernel/pci.cがその処理で、やってることは、PCIのデバイスツリーを作って、RoutingTableが見つかるまでそのツリーをたどっていくという処理である。


というわけで、問題を先送りすることによって、割り込みとかの問題はクリアになったものとした!本題である8169を見るべしである。


データシートはこれだろうか…
http://www.datasheetarchive.com/RTL8169-datasheet.html
なんか手元にあるのとページ数が違うがー…内容は一緒なので、これでよい気がするのだ。


8169はメモリアドレスとIOアドレスの両方に同じレジスタが見えるなー多分どっち使っても問題無い。
あれメモリアドレスといえばふと思ったがMCFGの説明してねぇですね。まあ、これも完全版で書かれる予定でしょうね…。

まずはリセットするひちょうがある。リセットは、Commandレジスタの4bit目を立てればリセットできるなー…て、こういうペースで書いていっては量が多すぎるだろ…

書くのが面倒なので、もっとペースを上げていくなー。レジスタ1bit1bit確認なんてやってられぬー


まずは「デスクリプタ」である。
最近のデバイスは、デスクリプタという概念を理解する必要があるでごわす。

古のデバイスは、どっかのレジスタにデータを書くとバッファへの書き込みになるというものであった…しかし、それはピラミッドとか古墳とかの建設が盛んだったころの話…今はIOとCPUでは速度差がありすぐるので、バイトとかワード単位とかでIO空間に何回もアクセスするというのは死亡フラグであるなー。(誰が死ぬのか知らんが…)

これを防ぐためには、「CPUは一旦メモリへデータを置く」「デバイスはDMAでメモリ上のデータを読む」というような流れにする必要があるなー。
いまどきのメモリはアクセス単位が64byteとか128byteとかなので、64byte転送するのと、1byte転送するのは時間それほど変わらんというのがあって、なので、DMAにすると、かなり速くできるなー。
で、そうするためには、デバイスには、データそのものではなくて、「データが置いてあるアドレス」を送信することになるなー。

しかし、アドレスは一個でよいのだろうか…アドレスが一個しか設定できないと、データを一個のアドレスで指し示すことができる連続領域に置く必要がでてくるのだが、それでは困るなー。例えば、なんでもよいが、100Mぐらいのデータをデバイスに送りたい場合に、それが物理メモリ上に連続することはまず考えられないなー。

そこで、使うのが、デスクリプタを使ったアドレス通知的ななんかである。

ここでは、デスクリプタとは、データのアドレスやら、そのデータの属性を示すフラグとかが入った構造のことだなー。
CPUは、メモリ上に、このデスクリプタをたくさん並べたデスクリプタのテーブルを作るのだなー。
そんで、デバイスには、「デスクリプタテーブルの先頭アドレス」を設定するのだっ。

そうすると、デバイスは、そのデスクリプタテーブルに入ってるデスクリプタを順番に読んで、そのデスクリプタに書いてあるアドレスに対してごにょごにょしたりするなー。

こうすることで、

  • バイスにはレジスタ一個だけ
  • バイスはDMA転送でメモリ上のデータをとってこれる
  • バッファのアドレスは複数指定できる

というような感じに、上で挙げた問題はクリアにできるのよね。

これはイーサネットに限らず、記憶が正しければ、確かUSBのホストであるOHCIとかが同じようになっているし、多分他にもたくさんあると思う。

そんで、今日のテーマである、RTL8169も同様に、ディスクリプタを使った的な転送的なアレであり、上で示したPDFの9.Functional Descriptionあたりに書いてあるのが、そのディスクリプタに書くのは何か、が書いてあるとかそういうアレのアレである。

さて、RTL8169のディスクリプタには、OWNビットというのがあって、このビットが立っていると、そのディスクリプタは「デバイスのものである」となって、立っていないと、「CPUのものである」と、なる。
8169のデスクリプタテーブルは、リング上になっていて、最後のデスクリプタには、EORビットを立てておくのにゃー。で、このOWNとEORというのがあって、デバイスは以下のように動くのだ。

  1. RDSAR(Receive Descriptor Start Address Register), TNPDS(Transmit Normal Priodirity Descriptor Start)で指定されたアドレスのデスクリプタを読む
  2. デスクリプタのOWNが0だったら1になるまで待つ
  3. 1になったらデスクリプタを処理
  4. 処理終わったら次のデスクリプタを読む
  5. EORが立っていたら、最初のデスクリプタを読む
  6. 2. に戻る

これを正しく動くようにするためには、デスクリプタの初期化は以下みたいな感じになるのだと予想できる

  1. メインメモリ上にデスクリプタ用の領域を作る
  2. その領域の全部のデスクリプタのOWNを0にしておく
  3. 最後のデスクリプタのEORを1にしておく
  4. RDSAR, TNPDSを設定する

そんで、送受信処理が必要になったら、デスクリプタを適切に設定後、OWNを1にする、というような感じである。


デスクリプタの説明はこんな感じである。わかる?


それでは、実際にドライバを書いてみよう。おっとPHYの説明をしておらんでござるよ。
がしかし、すまぬーPHYは実はあまり理解していない…

まあ、ともかく、イーサネットとは別に、PHYというのが存在していて、これもまた独特の世界を持っているのである。
といっても、レジスタ一個設定するだけなのだが、このレジスタというのが、8169の向こう側の世界にいるので、8169を通じて、値を設定せねばならぬな。


8169でのPHYの設定方法は以下のとおりである。

  • PHYAR(address=0x60h) に書きたいPHYのレジスタのアドレスを書く(WRITEの場合は、最上位ビットを立てて、下位ビットに値を書き込む)
  • PHYARの最上位ビットが変化するまで待機
  • READの場合は、そのときの下位ビットが読めた値

さっぱり何を言ってるかわからない気味であった。コードにすると↓こんな感じだなー

static void
mdio_write(struct r8169_dev *dev, int phyreg, int val)
{
	w32(dev, PHYAR, 0x80000000 | (phyreg<<16) | val); /* 最上位ビットを立てて、下位ビットに値を書く */

	while (1) {
		uint32_t val = r32(dev, PHYAR);
		if ((val & 0x80000000) == 0) { /* 最上位ビットが変化するまで待機 */
			break;
		}

		wait_usec(100);
	}
}

これで、phyregが示すPHYのアドレスに対して、valの値書き込むことが可能だなー。


実際に、8169の先に付いてるPHYは、MII(100M)、GMII(1G)と呼ばれるもので、そこらへんがなんかだなー。
いまいち理解していないので申し訳ないのだがー…とりあえず、BMCR(0)に、リセットとオートネゴシエーションの有効化ビットを立てておけば動くらしいなー。


相変わらず以上の説明で理解できる人がいるとは思えないがやる気の配分上、PHYの説明はここらへんにするのう。あとうちが理解していないというのがある。

と、ちょっと量が多いので、次回に続くなー。
予想外に量が多いので、次回はもっとペースを上げていく予定である!覚悟しておけよ!

本来なら、全部書き切ってから上げるべきなのだが、画像が時期ものである以上、明日に回すわけにはいかないのであるっ!
このことから、本文よりも画像のほうを先に用意していることがわかるのであり、もはやPC解説というタイトルでありながら画像描いてるだけなんじゃないかという気がしてくるのであった。