EISA バスサポート Marc Zyngier この文書群は、新しい EISA/sysfs API への EISA ドライバ移植に関するラン ダムノートです。 バージョン 2.5.59 以降、EISA バスは、PCI や USB といった、もっと主流で ある他のバスとほぼ同じ状況となっています。この状況は、sysfs により達成 されました。sysfs は、バス、デバイス、およびドライバを管理するための、 十分な抽象化セットを定義しています。 新しい API は非常に簡単に使用できますが、既存のドライバを新しいインフラ ストラクチャへと変更するのは、簡単な作業ではありません (検出コードが、 概して ISA カードをプローブするためにも使用されている、というのが主な 理由です)。さらに、ほとんどの EISA ドライバが、もっとも古い Linux ドラ イバ群に含まれるものであるため、ご想像のとおり、何年ものあいだゴミが 残り続けているのです。 EISA インフラストラクチャは、三つの部分で構成されています。 - バスコードは、汎用コードのほとんどを実装しています。バスコードは、 EISA コードが実行される全てのアーキテクチャで共有されます。バス コードは、バスのプローブ (バス上に存在する EISA カードの検出) を 実装し、I/O リソースの割当てをおこない、sysfs で装飾名を与え、 ドライバが登録するインターフェースを提供します。 - バスルートドライバは、バスハードウェアと汎用バスコード間の接合部分 を実装します。バスを実装するデバイスの発見と、そのデバイスがあとで バスコードによりプローブされるようにセットアップすることに、責任を 持ちます。これは、x86 上で I/O リージョンを予約するという程度の簡 単なものから、hppa EISA コードのように、もっと複雑なものになる場合 もあります。ここが、「新しい」プラットフォームで EISA を動かすため に実装しなければならない部分です。 - ドライバは、管理するデバイスのリストをバスに提供し、要求された ときにいつでもデバイスのプローブと解放をおこなえるようにするため に必要なコールバックを実装します。 下記の関数と構造体はすべて 内にありますが、これらは に大きく依存します。 バスルートドライバ ------------------ int eisa_root_register (struct eisa_root_device *root); eisa_root_register 関数は、デバイスを EISA バスのルートとして宣言する ために使用します。eisa_root_device 構造体は、プローブ用のパラメータと 一緒にこのデバイスの参照を保持します。 struct eisa_root_device { struct device *dev; /* ブリッジデバイスへのポインタ */ struct resource *res; unsigned long bus_base_addr; int slots; /* 最大スロット数 */ int force_probe; /* スロット 0 がなくてもプローブする */ u64 dma_mask; /* ブリッジデバイスから */ int bus_nr; /* eisa_root_register によりセットされる */ struct resource eisa_root_res; /* 同上 */ }; node : eisa_root_register で内部的に使用されます dev : ルートデバイスへのポインタ res : ルートデバイス I/O リソース bus_base_addr : このバス上のスロット 0 アドレス slots : プローブする最大スロット数 force_probe : スロット 0 が空 (EISA メインボードが無い) でもプローブします dma_mask : デフォルト DMA マスク。通常、ブリッジデバイスの dma_mask. bus_nr : 一意のバス ID で、eisa_root_register によりセットされます ドライバ -------- int eisa_driver_register (struct eisa_driver *edrv); void eisa_driver_unregister (struct eisa_driver *edrv); 何をする関数かは十分明確ですよね? struct eisa_device_id { char sig[EISA_SIG_LEN]; unsigned long driver_data; }; struct eisa_driver { const struct eisa_device_id *id_table; struct device_driver driver; }; id_table : ヌルで終端された EISA ID 文字列の配列であり、最後は 空文字列。各文字列は、ドライバ依存の値 (driver_data) と任意で組にすることができます。 driver : Documentation/driver-model/driver.txt で説明されて いる汎用ドライバ。.name, .probe, .remove メンバだけ は必須です。 3c59x ドライバが例となります。 static struct eisa_device_id vortex_eisa_ids[] = { { "TCM5920", EISA_3C592_OFFSET }, { "TCM5970", EISA_3C597_OFFSET }, { "" } }; static struct eisa_driver vortex_eisa_driver = { .id_table = vortex_eisa_ids, .driver = { .name = "3c59x", .probe = vortex_eisa_probe, .remove = vortex_eisa_remove } }; デバイス -------- sysfs フレームワークは、デバイスの発見と削除の際、.probe 関数と .remove 関数を呼びます (.remove 関数が呼ばれるのは、ドライバがモジュールとして ビルドされている場合のみであることに注意してください)。 どちらの関数も、下記に示す 'struct eisa_device' 内にある 'struct device' へのポインタを受け取ります。 struct eisa_device { struct eisa_device_id id; int slot; int state; unsigned long base_addr; struct resource res[EISA_MAX_RESOURCES]; u64 dma_mask; struct device dev; /* 汎用デバイス */ }; id : デバイスから読みとる EISA ID. id.driver_data は、対応する ドライバ EISA ID をもとにセットされます。 slot : デバイスが検出されたスロットの番号 state : デバイスの状態を示すフラグのセット。現在のフラグは EISA_CONFIG_ENABLED と EISA_CONFIG_FORCED. res : このデバイスに割り当てられた 4 つの 256 バイト I/O リー ジョンのセット dma_mask : 親デバイスから引き継いだ DMA マスク dev : 汎用デバイス (Documentation/driver-model/device.txt 参照) 'to_eisa_device' マクロを使い、'struct device' から 'struct eisa_device' を取得できます。 その他 ------ void eisa_set_drvdata (struct eisa_device *edev, void *data); デバイスの driver_data 領域にデータを格納します。 void *eisa_get_drvdata (struct eisa_device *edev): デバイスの driver_data 領域に格納されているポインタを取得します。 int eisa_get_region_index (void *addr); 渡されたアドレスのリージョン番号を返します (0 以上 EISA_MAX_RESOURCES 未満)。 カーネルパラメータ ------------------ eisa_bus.enable_dev : 有効にするスロットをカンマ区切りでリストしたもの。ファームウェアが カードを無効にしている場合でも有効にします。ドライバは、そのような 状況の場合にも、適切にデバイスを初期化できなければなりません。 eisa_bus.disable_dev : 無効にするスロットをカンマ区切りでリストしたもの。ファームウェアが カードを有効にしている場合でも無効にします。このデバイスを操作する ために当該ドライバが呼ばれることはありません。 virtual_root.force_probe : EISA 準拠のメインボードを発見できない場合でも (スロット 0 に何も ない場合でも)、プローブコードに強制的に EISA スロットをプローブ させます。デフォルトは 0 (強制しない) ですが、CONFIG_ALPHA_JENSEN もしくは CONFIG_EISA_VLB_PRIMING がセットされている場合は、1 (プ ローブを強制) にセットされます。 ランダムノート -------------- EISA ドライバを新しい API に変更する作業は、ほとんどの場合、コードの 「削除」がおこなわれます (現在はコア EISA コードにプローブコードが存在 するからです)。残念ながら、ほとんどのドライバは、ISA, MCA, EISA 間で 自身のプローブルーチンを共有しています。これらの局所的な変更が他のバス に影響を与えることがないよう、EISA コードを取り除くときには特別な注意が 必要です。 eisa_driver_register から戻ったときに何らかの EISA デバイスが検出されて いることを期待しては「いけません」。というのは、バス自体がまだプローブ されていない可能性があるからです。事実、たいていの場合はそうです (バス ルートドライバは通常、どちらかというとブートプロセスのあとのほうで動作 し始めます)。残念ながら、ほとんどのドライバは自分でプローブをおこなって おり、自らのプローブルーチンから抜けるときにはマシン全体を調べ終わって いるものと期待しています。 例えば、お好みの EISA SCSI カードを「ホットプラグ」モデルに切り替える ことは、"the right thing"(tm) です。(訳注:"right thing" には、「でも、 正常な人ならそんなことはしないけどね」、というニュアンスもあります。 http://www.catb.org/~esr/jargon/html/R/Right-Thing.html を参照のこと) 謝辞 ---- ご協力していただいたことに対して、次の方々に感謝したいと思います。 - Xavier Benigni : 素晴らしい Alpha Jensen を私に貸してくれました。 - James Bottomley, Jeff Garzik : これをカーネルに取り込んでくれました。 - Andries Brouwer : 多数の EISA ID を提供してくれました。 - Catrin Jones : 自宅で非常にたくさんのマシンに対応してくれました。 ------------------------------------------------------------ 翻訳団体: JF プロジェクト < http://www.linux.or.jp/JF/ > 翻訳日: 2004/05/23 翻訳者: 川崎 貴彦