次のページ 前のページ 目次へ

6. ドライバ自身について

さて本来の作業、つまりドライバの主要な機能を sdc_driver.c ファイル に書く作業に取りかかりましょう。まず、ドライバが提供しなければいけない 機能の概要は次のとおりです:

  1. チップセット記述子のデータ構造をサーバに提供します。 このデータ構造にはドライバ関数へのポインタに加えて いくつかのデータ構造に対する初期値も含まれます。
  2. チップセットのレジスタ内容を保持するためにドライバ内部で 使用するデータ構造を提供します。このデータ構造には一般的な 部分とドライバ固有の部分があります。これはチップセットの 初期状態を保存するために使用され、またチップセットの 表示モードを変更するためにドライバーによって初期化されます。
  3. ドライバがサポートしているチップセットの一覧を呼び出すために サーバが使う識別機能を提供します。
  4. ドライバがサポートしているチップセットがインストールされていれば、 そのチップセットを他のチップセットと異なるものとして認識し プラスの値を返すが、そうでなければマイナスの値を返すという 探査機能を提供します。
  5. そのボードで使用可能なドットクロックを選択する機能を提供します。
  6. ドライバ内部で使用するデータ構造の保存、回復、初期化機能を 提供します。
  7. ビデオメモリ内にディスプレイの開始アドレスを設定する機能を提供します。 これはサーバが仮想スクリーンを実現するために必要な機能です。
  8. たぶん VT 切り替えを行うための機能を提供したほうが良いでしょう。
  9. 指定された各モードがそのチップセットの使用するモードとして ふさわしいかどうかチェックする機能を提供したほうが良いでしょう。

ドライバファイルの具体的な中身に踏み込んでいく前に知っておくべき、 重要な情報がここにあります:

  1. ドライバがカラーとモノクロサーバの両方をサポートする場合、 一つのファイルに両方の場合のコードが含まれていることに 注意してください。これらはほとんど同じものですが、MONOVGA を #define してある所で両者を区別することが出来ます。 16 色サーバをサポートする場合、このサーバーに固有のコードは XF86VGA16 の #define によって有効化されるように してください。ほとんどの場合、次の指定を stub_driver.c ファイル の先頭近くに置いておけば充分です。
       #ifdef XF86VGA16
       #define MONOVGA
       #endif
    
  2. カラーサーバは SVGA の 8-ビットパックトピクセルモード を使用します。モノクロと vga16 サーバは VGA の 16-色モード (4 ビットプレーン)を使用します。モノクロサーバでは 1 プレーンだけ使用します。
    [ 訳注 : パックトピクセル: 1 ドットのピクセル(画面上の点) を表現するのに、連続した 8 ビットを 4 つ並べて表わす方式。 4 ビットプレーン: 1 ドットを表わすのに、それぞれの色毎にプ レーンを持ち各プレーン上の連続しない 8 ビット 4 つを使って 表わす方式。]
  3. モノクロサーバをバンク切り替えを使わないように定義することが 可能です。これは表示領域のメモリをたった 64k しか使えないため、 あまり魅力的ではありません。
これらのことを踏まえて、自分の使う SVGA チップセットの中で所定の機能を 制御するレジスタを探す必要があります。 特に、以下の項目を制御できるレジスタは重要です:
  1. クロック選択ビット。低位の 2 ビットは標準 Misc 出力レジスタ (Miscellaneous Output Register) の一部です。ほとんどの SVGA チップセットはこれに加えてさらに 1 あるいは 2 ビットを備えて おり、これによって 8 または 16 の離散的なクロック値を使用 できます。 [ 訳注:最近のチップではプログラマブルクロックを使用する ことが一般的になっているようです。詳細は既存のコードを参照。 ]
  2. バンク選択。SVGA チップセットは読み書きするバンクの選択を 制御するためのレジスタを一つか二つ持っているでしょう。
  3. CRTC 機能拡張。標準 VGA レジスタは高解像度表示を行うのに 十分なビット数を持っていません。 このため SVGA チップセットは拡張ビットを持っています。
  4. インターレースモード。標準 VGA はインターレース表示 をサポートしていません。このため SVGA チップセットは インターレースモードを制御するビットをどこかに持っています。 ある種のチップセットではインターレースモードを制御するために 独自に追加されたレジスタを設定しなければなりません。
  5. 開始アドレス。標準 VGA では表示用メモリの開始アドレスを指定するために 16 ビットしか使えません。これは仮想スクリーン機能で使う画面サイズ (解像度) を制限します。SVGA チップセットは通常一つかそれ以上の 拡張ビットを持っています。
  6. レジスタロック。多くの SVGA チップセットではレジスタを最初に ``unlocked'' にしない限り拡張レジスタの内容を変更できません。 レジスタの内容を変更したい場合には、まずこの保護を無効にする ことが必要です。
  7. その他の機能。例えば、ある種のチップセットでは拡張 VGA メモリ (IBM-標準の 265k を超える)にアクセスするために、前もって 特定のビットを設定しておく必要があります。あるいは他にも なにか必要な機能があるかもしれません。 各レジスタの詳細な説明をすべて読み通して、何か重要な機能が 記載されていないかどうか、ちゃんとチェックしておきましょう。

運がよければ、チップセットのベンダーがデータブックに様々な BIOS モード用の レジスタ設定表を掲載してくれているでしょう。この表をじっくり見てそれぞれの BIOS モードを調べることによって、どんな操作をしなければならないかについて 多くのことを学ぶことが出来ます。

6.1 複数のチップセットとオプション

同じベンダーから出荷している複数のチップセットを単一のドライバで サポートすることが可能であり、むしろ望ましいとされています。 複数のチップセットをサポートする場合、それらに対応する一連の #define と、ドライバコード中でチップセットの識別が必要な時に 使用する`SDCchipset' 変数を用意することになるでしょう。 具体的な例としては Trident と PVGA1/WD を参照してください (Tseng ET3000 と ET4000 はこれに沿っていない例です。 これらのコードは複数のチップセットをサポートするための ドライバインターフェースが開発される前に実装されたものであり、 現在ではこのような実装方法に従うべきではありません。) ドライバーがチップセットのバージョンを区別して認識するべきなのは、 それらが互いに異なる扱いを必要とする場合だけであることに注意して ください。例えば、SDC ドライバが SDC-1a,SDC-1b および SDC-2 と いうチップセットをサポートしているとしましょう。さらにこのとき、 チップセット -1a と-1b は実質的に同じものであり、-2 は異なるもの だとします。このような場合には、ドライバーコードは -1 と -2 の 2 種類のチップセットとしてサポートするべきであり、 -1a と -1b を 区別するべきではありません。これによってエンドユーサの設定作業も 単純になることでしょう。

ユーザにドライバの挙動を制御させたい場合、または何かを決定するために ユーザによる介入を必要とする場合には、``option'' フラッグを使いましょう。 例えば SDC チップセットを使うビデオボードのベンダーが、 8 クロックに 対応するボードと 16 クロックに対応するボードを選択できるようにしていた ならば、これらのボードをチップセットに対する探査の結果から識別する方法は ありませんので、 option フラッグを提供し、ユーザが XF86Config ファイルを 使ってドライバの挙動を選択できるようにするべきです。 option フラッグは ``xf86_option.h'' ファイルに定義します。 再利用可能な option が既に存在していないかどうか、最初に 確認しておきましょう。もし存在する場合はそれを使ってください。 再利用可能なものが無かった場合は、新たに #define を追加し、 同じファイルにある「文字列->シンボル」の対応を定義するテーブル中に 追加する option フラッグで使う文字列を定義します。 option フラッグの使い方を理解するには ET4000, PVGA1/WD と Trident の ドライバを参照してください。

6.2 データ構造

上記の説明から何が必要なのか理解したら、ドライバのデータ構造を 記入する段階に進みましょう。 最初に `vgaSDCRec' の構造を作成します。このデータ構造は SVGA の 状態情報を保持するためにドライバ内部で使用するデータ構造です。 このデータ構造の最初の項目は「常に」 `vgaHWRec std' とします。 この項目は汎用 VGA に関する部分の情報を保持するためのものです。 これに続いて、追加するドライバによって操作されるレジスタのひとつ ひとつに対応する `unsigned char' の領域を用意してください。 これが最初のデータ構造の全てです。

次に `SDC' 構造 (`vgaVideoChipRec' 型) の初期化をしなければいけません。 これは追加するドライバをサーバに識別させるためのグローバルな構造体です。 この構造体の名称はすべて大文字で `SDC' としなければなりません。 これはつまり、追加するドライバコードの存在するディレクトリの名称と 一致させる必要がある、ということです。これはリンクキットの再構成時に 必要なディレクトリとグローバルなデータ構造をすべて認識させるための 条件です。

この構造体の最初の部分は単にドライバ関数へのポインタを保持しておく ために使用されます。

次に、チップセットがバンク切り替えをどう扱うかについての情報を 初期化しなければなりません。次に示すような内容の領域を用意する 必要があります:

  1. ChipMapSize - サーバのアドレス空間に割り付けるメモリ量。 ほとんどの場合には 64k (0xA0000 から 0xAFFFF まで) です。 チップセットによっては 128k (0xA0000 から 0xBFFFF まで) を使います。もし使用するチップセットがどちらにも対応して いるのなら 64k の窓を使ってください。128k の窓を使用すると Hercules またはモノクロディスプレイアダプタのカードを SVGA サーバーで使うことができなくなります。
  2. ChipSegmentSize - ChipMapSize の窓内のバンクの大きさ。 これもまた通常 64k ですが、チップセットによっては 割り付けられた窓を読み込み部分と書き込み部分に分割して扱います (例えば PVGA1/Western Digital チップセット)。
  3. ChipSegmentShift - バンク番号をマスクするためにアドレスを 右シフトする際のビット数。 これは 2 を底にした ChipSegmentSize の対数 (log) になります。
  4. ChipSegmentMask - 与えられたバンク内のアドレスをマスクオフ するときのビットマスク。これは (ChipSegmentSize-1) になります。
  5. ChipReadBottom,ChipReadTop - 割り付けられた窓内で読み込み 操作を行うためのアドレス。 チップセットが窓を読み込み用と書き込み用に分割する 場合を除けば、通常はそれぞれ 0 と 64k になります。
  6. ChipWriteBottom,ChipWriteTop - 書き込み操作のためのもの であるという点以外は、上記と同様です。
  7. ChipUse2Banks - チップセットのバンクレジスタが 1 つ または 2 つのどちらであるかを示すブーリアン値。 これはスクリーンからスクリーンへの操作を適切に設定するために 使用されます。
これに加えてさらに 3 個の領域を用意しなければなりません:

  1. ChipInterlaceType - VGA_NO_DIVIDE_VERT または VGA_DIVIDE_VERT のどちらかになります。チップセットによってはインタレースモード 用に垂直方向の調整数値を 2 で割ったものを必要とします。 このフラッグを設定することによって、サーバがそれに応じた処理を してくれます。
  2. ChipOptionFlags - データ構造の初期化では常に `{0,}' とします。 これはこのドライバで有効な Option フラッグを示すビット列です。 探査関数の終了時に適正な値に初期化されます。
  3. ChipRounding - 256 色サーバの場合、仮想スクリーンの幅は ここで設定した値の倍数になるよう丸められます。 この値は通常 8 ですが、チップセットによっては 4 とか 16 に する場合もあります。

6.3 Ident() 関数

Ident() 関数は大変単純な関数です。サーバは自分自身に 組み込まれているドライバの一覧を出力するときに、返り値が NULL に なるまでこの関数を繰り返し呼び出します。 Ident() 関数はサポートしているチップセットのそれぞれに ついてその名称を返さなければなりません。 この関数には繰り返して呼ばれるたびに 0 から順に 1 つずつ増加して いく数値が渡されます。

6.4 ClockSelect() 関数

ClockSelect() 関数は、クロックを探査する(これはつまり、 XF86Config ファイルで `Clocks' 行を指定しなかった場合ですが) 際に、パラメータの一部として渡された数値により指示されたドットクロックを 選択するために使用されます。この関数は、渡された数値に従ってチップセット の clock-select ビットを設定しなければなりません。 これに加えて 2 種類のダミーとしての値 (CLK_REG_SAVE, CLK_SAVE_RESTORE) がこの関数に渡される場合もあります。 CLK_REG_SAVE を受け取った場合には、関数がクロックの選択中に内容を変更する 可能性のあるすべてのレジスタについて、その内容を保存しておくようにします。 CLK_REG_RESTORE を受け取った場合には、これらのレジスタについて、関数が 保存しておいた内容を回復するようにします。 これは、クロック探査によってレジスタの内容が破壊されないことを確実にする ためのものです。

渡されたインデックスが不正な値であるか、もしくは何らかの理由によって クロックを設定できなかった場合、この関数は FALSE を返すべきです。

6.5 Probe() 関数

Probe() 関数はドライバの中で多分最も重要でかつ 最も直感的でない関数でしょう。 Probe 関数は他のすべてのチップセットを誤認識することなく、対象とする チップセットを識別しなければなりません。もし XF86Config ファイルに `Chipset' 行が指定されているのなら、単純な文字列比較を実行 させるだけです。それ以外の場合には、どんなチップセットがシステムに インストールされているか、何か別の方法で判定しなければなりません。 運がよければ、チップセットに認識機構 (識別/バージョンレジスタなど) が 用意されていて、かつそのことがデータブックに説明されているでしょう。 さもなければ、次に述べるような基準を使い、何らかの手順を決めて認識 させなければなりません。

認識手順としてよく使われるのは、対象とするチップに固有のパターンが レジスタ中に存在するかどうかを調べる方法、またはそのチップに固有の 拡張レジスタが存在するかどうかを調べる方法です。あるいはボードや チップセットによっては、特定の署名文字列を BIOS から読み取ることに よって必要な情報を入手することの可能な場合もあります。 既存の探査関数を勉強すること、および参考資料をきちんと使うことが 最善のアドバイスです。またここで追加する探査関数のコードが非破壊的な ものであることを確認しておかなければなりません。 これは、もし探査中にレジスタの内容を変更するのであれば、 最初に元のレジスタ設定を保存しておかなければいかないこと、 そして最後に自分が保存した内容を復元しておかなければいけないことを 意味します。

うまくチップセットを認識できたら、Probe() 関数は さらに他の部分の初期化をも行う必要があります。

  1. XF86Config ファイルに `VideoRam' パラメタが指定されて いない場合に備えて、搭載メモリ量を判定する必要があります。
  2. XF86Config ファイルに `Clocks' パラメタが指定されて いない場合に備えて、使用可能なドットクロックの値を判定する 必要があります。 これは vgaGetClocks() 関数を呼び出して、 使用できるクロックの数と ClockSelect() 関数への ポインタを渡してやることで実現できます。
  3. サーバの `vga256InfoRec' 構造体に用意されている `maxClock' 領域に ドライバーが認識したチップセットで許容可能な最大ドットクロックを (KHz 単位で) 指定しておくことをお勧めします。 探査時にこの値を指定しなければ、標準値 (現在は 90MHz) が 使用されます。
  4. サーバの `vga256InfoRec' 構造体に用意されている `chipset' 領域を 搭載チップセットの名称によって初期化しなければなりません。
  5. ドライバがモノクロサーバと組み合わせて使用されるのなら、 サーバの `vga256InfoRec' 構造体に用意されている `bankedMono' 領域を適切に設定して、モノクロドライバがバンク切り替えを サポートしているかどうかを示さなければいけません。
  6. 新しく作成するドライバーが何らかのオプションフラッグを サポートする場合には、 `vgaVideoChipRec' 型として定義した `SDC' 構造の中に含まれている `ChipOptionFlags' 構造体を OFLG_SET() マクロを用いて初期化し、使用を認める オプションフラッグをこの中に設定しておくことが必要です。

6.6 EnterLeave() 関数

EnterLeave() 関数はサーバを起動した仮想コンソールに入ったり 出たりする度に呼ばれます(仮想コンソール機能をもたない OS では、 この関数はまずサーバーの開始時に呼ばれ、終了時にもう一度呼ばれます)。 この関数の目的は (そうすることが必要な OS の場合に) I/O の権限を 譲渡したり剥奪したりすること、およびドライバが操作しなければならない ``保護された''レジスタへのアクセスをロック解除したり再ロックしたり することです。 これは非常に簡単な関数であり、スタブドライバ中のコメントに書かれた 方法で実装できます。

6.7 Restore() 関数

Restore() 関数は保存しておいたビデオ状態を回復するために 使用されます。この `restore' という名称は少しばかり誤った名称であり、 実際には保存された状態を回復するためばかりでなく、サーバが新規に 作成した状態へと移行するためにこの関数を使用することもあります。 Restore() 関数は次の動作を完全に実行しなければなりません。

  1. バンク 0 が選択されていること、および新しい状態へ移行する前に 必要となるその他の状態情報がすべて設定済みであることを確認します。
  2. vgaHWRestore() を呼び出して状態情報の汎用 VGA 部分を 回復させます。この関数は vgaHW.c ファイルの中で定義されています。
  3. 状態情報のチップセットに特有部分を回復します。これは単純に レジスタに書き込むか、あるいはもしレジスタ中の一部のビット だけを変更する必要がある場合には read/modify/write 操作を 実行します。クロック選択ビットを扱う方法について、サンプル ドライバ中のコメントを確認しておいてください。

6.8 Save() 関数

Save() 関数はサーバ起動時にビデオカードの初期状態に関する情報を 取り出すために使用されます。Save() 関数は次の動作を完全に実行 しなければなりません。

  1. バンク 0 が選択されていることを確認します。
  2. vgaHWSave() を呼び出して状態情報の汎用 VGA 部分を 取り出します。この関数は vgaHW.c ファイルの中で定義されています。
  3. 状態情報のチップセット特有部分を取り出す。

6.9 Init() 関数

Init() 関数はドライバの中で (Probe() 関数に次いで) 二番目に重要な関数です。これを使って、サーバの中に定義された各表示モードに 対するデータ構造を初期化します。この関数は `vgaSDCRec' データ構造全体を 初期化して、SVGA チップセットを目的の状態に移行させるために必要となる 情報を設定しなければなりません。このデータ構造の汎用 VGA 部分は (これも vgaHW.c 内にある) vgaHWInit() を呼び出して初期化します。

いったん汎用の部分が初期化されたら、Init() 関数は必要に応じて 汎用レジスタの中身を好きなように変更できます。その他必要な領域のすべて に適正な初期化情報を設定します。初期化される特定のモードについての情報 は `DisplayModeRec' 構造体へのポインタである `mode' パラメタを経由して 渡されます。このポインタを辿ることで必要なパラメータを決定できます。

レジスタの特定ビットの初期化方法だけが分かっている場合には、ここで初期化 しましょう。特定ビットだけを操作する場合には Restore() 関数を 使って read/modify/write を実行するのだということをしっかり覚えておいて 下さい。繰り返しますが、例えばこの関数の中で何が実行されるのかについて、 既存のドライバーコードを参照して調べておいてください。

6.10 Adjust() 関数

<!_- The Adjust() function is another fairly basic function. It is called whenever the server needs to adjust the start of the displayed part of the video memory, due to scrolling of the virtual screen or when changing the displayed resolution. All it does is set the starting address on the chipset to match the specified coordinate. Follow the comments in the stub driver for details on how to implement it. --> Adjust() 関数はまた別の、とても基本的な関数です。 仮想スクリーンのスクロールや表示解像度の変更によって、 サーバがビデオメモリの表示用領域の開始点を調整する必要のあるときは いつも呼ばれます。 この関数の担当は、指定された座標に合わせてチップセットの開始アドレスを 設定することだけです。 実装方法の詳細は、スタブドライバ中のコメントを参照してください。

6.11 ValidMode() 関数

ValidMode() 関数は、このドライバーで扱うチップセットに固有の 事情によって、特定のグラフィックモードを使用できない場合があるかどうかを 確認するために使用されるものであり、必ず用意しなければいけません。 この関数は Probe() 実行後により上位のプログラムから呼ばれます。 多くの場合、この関数の中で特別な調査を実行する必要は無く、常に TRUE を 返すような単純なものでも充分でしょう。

6.12 SaveScreen() 関数

SaveScreen() 関数はほとんどのチップセットでは必要ありません。 この関数が必要になるのは、 SVGA チップセット上で同期リセットが実行された時に、 ドライバの動作に必要な拡張レジスタの内容も一緒に変更されてしまうような場合です。 (このような場合、データブックに説明が記載されているはずです。) この関数が必要 *無い* 場合には、単にこれを定義しないでおいて、 vgaVideoChipRec 構造体を初期化する際には、この関数を指定するための フィールドに `NoopDDA' を設定します。 (NoopDDA は汎用の中身の無い関数です)。

この関数が *必要* な場合には、やるべきことは至極単純です。 この関数は必ず二回 (一回目はリセット前、二回目はリセット後) 呼ばれます。 前者の場合には引数として SS_START が渡され、後者の場合は SS_FINISH が 渡されます。必要な作業は、 SS_START で呼び出された際にはリセットによって 影響を受けるレジスタの内容をすべて保存すること、またここで保存した内容を 次に SS_FINISH で呼び出された際に回復することです。

6.13 GetMode() 関数

GetMode() 関数は XFree86 1.3 では使われていません。 vgaVideoChipRec 中に用意されているこの関数のためのフィールドは、 `NoopDDA' を指定して初期化してください。

将来的には、この関数を使用することによって、サーバーおよびサーバーの ドライバーライブラリを利用したスタンドアローンプログラム、またはその どちらかからビデオモードを対話的に調節することが可能となる予定です。 この関数は、 SVGA レジスタを読み込み、DisplayModeRec 構造体に現在の ビデオモードを書き出します。

[ 訳注:最新の公開版である XFree86 3.3.3.1 でも、 この GetMode()関数をまともに実装したドライバは vga256/drivers 以下に 30種ほどあるドライバのうち、わずかに ati ドライバーだけです。 他に 2 種類ほど、中身の無い定義だけの関数を用意した例がありますが、 それ以外のドライバーでは `NoopDDA' が指定されています。 現状ではこの関数はあまり必要とされていないということなのかもしれません。 ]

6.14 FbInit() 関数

FbInit() 関数はアクセラレータグラフィックスをサポートする ドライバが必ず用意しなければならない関数です。 この関数は標準の cfb.banked 関数を、それぞれのチップに合わせた アクセラレータ対応版の関数に置き換えるために使用されます。 vga256LowlevFuncs は置き換え可能な関数の一覧を含む構造体です。 この構造体は vga256.h で定義されています。 FbInit() 関数の例は et4000, pvga1 および cirrus のドライバに あります。

[ 訳注: XFree86 3.3.3.1 では、 vga256/drivers 以下のドライバー の多くがアクセラレータ対応となっており、多くのドライバーでこの関数が 定義されています。例えば chips(C&T) や tvga8900(trident)、 それに neo(NeoMagic) などのドライバーにも用意されています。 ]

この関数が必要 *無い* 場合には、単にこれを定義しないでおいて、 vgaVideoChipRec 構造体を初期化する際には、この関数を指定するための フィールドに `NoopDDA' を設定します。


次のページ 前のページ 目次へ