JavaScript is required to for searching.
ナビゲーションリンクをスキップ
印刷ビューの終了
デバイスドライバの記述     Oracle Solaris 10 8/11 Information Library (日本語)
search filter icon
search icon

ドキュメントの情報

はじめに

パート I Solaris プラットフォーム用デバイスドライバの設計

1.  Solaris デバイスドライバの概要

2.  Solaris カーネルとデバイスツリー

3.  マルチスレッド

4.  プロパティー

5.  イベントの管理とタスクのキュー

6.  ドライバの自動設定

7.  デバイスアクセス: プログラム式入出力

8.  割り込みハンドラ

9.  ダイレクトメモリーアクセス (DMA)

10.  デバイスメモリーおよびカーネルメモリーのマッピング

11.  デバイスコンテキスト管理

12.  電源管理

電源管理システムのフレームワーク

デバイス電源管理

システム電源管理

デバイス電源管理モデル

電源管理の部品

複数の電源管理部品

電源管理状態

電源レベル

電源管理の依存関係

デバイスの自動電源管理

デバイス電源管理インタフェース

ビジーとアイドルの状態遷移

デバイスの電源状態の遷移

power() エントリポイント

システム電源管理モデル

自動停止しきい値

ビジー状態

ハードウェア状態

システムの自動電源管理

システム電源管理で使用されるエントリポイント

detach() エントリポイント

attach() エントリポイント

電源管理のデバイスアクセスの例

電源管理の制御フロー

電源管理インタフェースの変更点

13.  Solaris ドライバの強化

14.  階層化ドライバインタフェース (LDI)

パート II 特定の種類のデバイスドライバの設計

15.  文字デバイスのドライバ

16.  ブロックデバイスのドライバ

17.  SCSI ターゲットドライバ

18.  SCSI ホストバスアダプタドライバ

19.  ネットワークデバイスのドライバ

20.  USB ドライバ

パート III デバイスドライバの構築

21.  ドライバのコンパイル、ロード、パッケージ化、およびテスト

22.  デバイスドライバのデバッグ、テスト、およびチューニング

23.  推奨されるコーティング方法

パート IV 付録

A.  ハードウェアの概要

B.  Solaris DDI/DKI サービスの概要

C.  64 ビットデバイスドライバの準備

D.  コンソールフレームバッファードライバ

索引

デバイス電源管理モデル

ここでは、デバイス電源管理モデルの詳細を説明します。このモデルには次の要素が含まれます。

電源管理の部品

デバイスがアイドル状態のときにデバイスの電力消費を削減できる場合、そのデバイスは電源管理に対応しています。概念的には、電源管理に対応したデバイスは、電源管理に対応した多数のハードウェアユニットで構成され、このユニットを部品と呼びます。

デバイスドライバは、デバイス部品と、各部品の電源レベルをシステムに通知します。そのため、ドライバの初期化中に、ドライバの attach(9E) エントリポイントで pm-components(9P) プロパティーが作成されます。

電源管理に対応したほとんどのデバイスは、単一部品のみを実装します。電源管理に対応した単一部品デバイスの例がディスクです。ディスクは、アイドル状態のときにスピンドルモーターを停止することによって消費電力を節減できます。

デバイスに電源管理に対応したユニットが複数あり、それらが個別に制御可能である場合、そのデバイスは複数の部品を実装します。

電源管理に対応した 2 部品デバイスの例は、モニターを備えるフレームバッファーカードです。最初の部品 [部品 0] はフレームバッファー電子回路です。使用されていないときにフレームバッファーの電力消費を削減できます。 2 番目の部品 [部品 1] はモニターです。モニターが使用されていないとき、モニターを低電力モードに切り替えることもできます。フレームバッファー電子回路とモニターは、システムからは 2 つの部品で構成される 1 つのデバイスとして認識されます。

複数の電源管理部品

電源管理フレームワークから見た場合、すべての部品が等価であり、互いに完全に独立しています。部品の状態間に完全な互換性がない場合は、デバイスドライバによって、望ましくない状態の組み合わせが発生しないようにする必要があります。たとえば、フレームバッファー/モニターカードがとりうる状態は D0D1D2、および D3 です。カードに接続されたモニターがとりうる状態は OnStandbySuspend、および Off です。これらの状態が相互互換であるとは限りません。たとえば、モニターが On 状態の場合、フレームバッファーは D0 状態 (完全にオン) である必要があります。フレームバッファーが D3 状態のときに、モニターの電源を入れて On 状態にする要求をフレームバッファードライバが受け取った場合、ドライバはモニターを On に設定する前に pm_raise_power(9F) を呼び出してフレームバッファーを起動する必要があります。モニターが On 状態の間は、フレームバッファーの電源レベルを下げるシステム要求がドライバによって拒否される必要があります。

電源管理状態

デバイスの各部品の状態はビジーまたはアイドルのいずれかです。デバイスドライバは pm_busy_component(9F) および pm_idle_component(9F) を呼び出すことによって、デバイス状態の変化をフレームワークに通知します。部品が最初に作成された時点では、その部品はアイドル状態とみなされます。

電源レベル

デバイス電源管理フレームワークは、デバイスがエクスポートする pm-components プロパティーに基づいて、そのデバイスがサポートする電源レベルを認識します。電源レベルの値は正の整数である必要があります。電源レベルの解釈はデバイスドライバの開発者が決定します。pm-components プロパティーに、増分が一定の昇順で電源レベルを列挙する必要があります。電源レベル 0 は「オフ」としてフレームワークに解釈されます。依存関係に従ってフレームワークがデバイスの電源を入れる必要があるとき、フレームワークは各部品を最高の電源レベルに設定します。

次の例は、ドライバの .conf ファイルの pm-components エントリを示します。このエントリは、1 個のディスクスピンドルモーターで構成される、電源管理に対応した単一部品を実装します。ディスクスピンドルモーターは部品 0 です。スピンドルモーターは 2 段階の電源レベルをサポートします。各レベルは「停止」と「全速回転」を表します。

例 12-1 pm-component エントリの例

pm-components="NAME=Spindle Motor", "0=Stopped", "1=Full Speed";

次の例は、ドライバの attach() ルーチンで例 12-1 を実装する方法を示します。

例 12-2 pm-components プロパティーを使用した attach(9E) ルーチン

static char *pmcomps[] = {
    "NAME=Spindle Motor",
    "0=Stopped",
    "1=Full Speed"
};
/* ... */
xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
    /* ... */
    if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
        "pm-components", &pmcomp[0],
        sizeof (pmcomps) / sizeof (char *)) != DDI_PROP_SUCCESS)
        goto failed;
    /* ... */

次の例は、2 つの部品を実装するフレームバッファーを示します。部品 0 は、4 段階の電源レベルをサポートするフレームバッファー電子回路です。部品 1 は、接続されたモニターの電源管理の状態を表します。

例 12-3 複数部品の pm-components エントリ

pm-components="NAME=Frame Buffer", "0=Off", "1=Suspend", \
    "2=Standby", "3=On",
    "NAME=Monitor", "0=Off", "1=Suspend", "2=Standby", "3=On";

デバイスドライバが最初に接続された時点で、フレームワークはデバイスの電源レベルを認識していません。次のタイミングで電源遷移が発生する可能性があります。

電源遷移のあとで、フレームワークはデバイスの各部品の電源レベルの追跡を開始します。ドライバが電源レベルをフレームワークに通知した場合にも追跡が行われます。ドライバは pm_power_has_changed(9F) を呼び出すことによって、電源レベルの変化をフレームワークに通知します。

システムは、起きる可能性のある電源遷移ごとにデフォルトのしきい値を計算します。これらのしきい値はシステムのアイドル状態しきい値に基づいています。pmconfig または power.conf(4) を使用するとデフォルトのしきい値をオーバーライドできます。部品の電源レベルが不明なときは、システムのアイドル状態しきい値に基づく別のデフォルトしきい値が使用されます。

電源管理の依存関係

デバイスの中には、別のデバイスの電源も切れているときにのみ電源を切ることが望ましいものがあります。たとえば、CD-ROM ドライブの電源を切ることが許可されていると、CD の取り出しなどの必要な機能が利用できなくなる可能性があります。

単独でデバイスの電源を切れないようにするために、あるデバイスと、電源が入っている可能性が高い別のデバイスの間に依存関係を設定できます。ユーザーがシステムを利用しているときは常にモニターの電源が入っているのが一般的なため、通常はデバイスとフレームバッファーの間に依存関係を設定します。

デバイス間の依存関係は power.conf(4) ファイルで指定します (デバイスツリーにおける親ノードは、その子ノードに黙示的に依存します。この依存関係は電源管理フレームワークによって自動的に処理されます)。次の形式の power.conf(4) エントリを使用すると、特定の依存関係を指定できます。

device-dependency dependent-phys-path phys-path

dependent-phys-path は、CD-ROM ドライブなど、電源が入った状態を維持するデバイスです。phys-path は、フレームバッファーなど、その電源状態が依存の対象になるデバイスを表します。

新しいデバイスをシステムに接続するたびに、エントリを power.conf に追加することは面倒な作業です。次の構文では、より一般的な形式で依存関係を指示できます。

device-dependency-property property phys-path

property プロパティーをエクスポートするデバイスが、phys-path で指定されたデバイスに依存するようにエントリを記述する必要があります。この依存関係は特にリムーバブルメディアデバイスに適用されるため、/etc/power.conf にはデフォルトで次の行が含まれます。

device_dependent-property  removable-media  /dev/fb

この構文は、コンソールフレームバッファーの電源も切らないかぎり、removable-media プロパティーをエクスポートするデバイスの電源を切ることができないことを意味します。

詳細は、power.conf(4) および removable-media(9P) のマニュアルページを参照してください。

デバイスの自動電源管理

pmconfig または power.conf(4) によって自動電源管理が有効化されている場合、pm-components(9P) プロパティーを持つすべてのデバイスは自動的に電源管理を使用します。部品がアイドル状態になってからデフォルトの許容時間を経過すると、部品の電源レベルが自動的に 1 段階下がります。デフォルトの許容時間は電源管理フレームワークによって自動的に計算され、デバイス全体が、システムのアイドル状態しきい値の範囲内でもっとも低いレベルの電源状態に設定されます。


注 - 初回出荷が 1999 年 7 月 1 日以降のすべての SPARC デスクトップシステムで、自動電源管理はデフォルトで有効です。それ以外のすべてのシステムで、この機能はデフォルトで無効です。使用中のマシンで自動電源管理が有効かどうかを調べる手順については、power.conf(4) のマニュアルページを参照してください。


フレームワークによって計算されたデフォルト設定は、power.conf(4) を使用するとオーバーライドできます。

デバイス電源管理インタフェース

電源管理に対応した部品を含むデバイスをサポートするデバイスドライバは、pm-components(9P) プロパティーを作成する必要があります。このプロパティーは、電源管理に対応した部品がデバイスに含まれることをシステムに指示します。pm-components は、利用可能な電源レベルもシステムに指示します。ドライバは通常、ドライバの attach(9E) エントリポイントから ddi_prop_update_string_array(9F) を呼び出すことによってシステムに通知します。システムへの通知に driver.conf(4) ファイルを使用する方法もあります。詳細は、pm-components(9P) のマニュアルページを参照してください。

ビジーとアイドルの状態遷移

デバイスの状態がビジーとアイドルの間で切り替わったとき、ドライバはその変化をフレームワークに継続的に通知する必要があります。これらの遷移が発生する箇所はシステムごとにまったく異なります。ビジーとアイドルの状態遷移は、デバイスの性質と、個別の部品によって抽象化される機能に依存します。たとえば、SCSI ディスクターゲットドライバは通常、SCSI ターゲットディスクドライブが回転しているかどうかを表す単一部品をエクスポートします。ドライブに対する要求が多いときは常に、部品はビジー状態とマークされます。キューの最後の要求が完了した時点で、部品はアイドル状態とマークされます。作成される部品の中には、決してビジー状態とマークされないものもあります。たとえば、pm-components(9P) によって作成される部品はアイドル状態で作成されます。

pm_busy_component(9F) および pm_idle_component(9F) インタフェースは、ビジーとアイドルの状態遷移を電源管理フレームワークに通知します。pm_busy_component(9F) の呼び出しの構文は次のとおりです。

int pm_busy_component(dev_info_t *dip, int component);

pm_busy_component(9F)component をビジー状態とマークします。部品がビジー状態の間、その部品の電源を切ることは望ましくありません。部品の電源がすでに切れている場合、その部品をビジー状態とマークしても電源レベルは変化しません。このことに対処するため、ドライバは pm_raise_power(9F) を呼び出す必要があります。pm_busy_component(9F) の呼び出しは累積的であり、部品をアイドル状態にするには pm_idle_component(9F) が同じ回数だけ呼び出されている必要があります。

pm_idle_component(9F) ルーチンの構文は次のとおりです。

int pm_idle_component(dev_info_t *dip, int component);

pm_idle_component(9F)component をアイドル状態とマークします。アイドル状態の部品は電源を切っても安全です。部品をアイドル状態にするには、pm_busy_component(9F) の呼び出しごとに pm_idle_component(9F) を 1 回呼び出す必要があります。

デバイスの電源状態の遷移

デバイスドライバで pm_raise_power(9F) を呼び出すと、部品を少なくとも所定の電源レベルに設定することを要求できます。電源が切れている部品を使用する前に、この方法で電源レベルを設定する必要があります。たとえば、ディスクの電源が切れている場合に、SCSI ディスクターゲットドライバの read(9E) ルーチンでディスクを回転させることが必要になったとします。pm_raise_power(9F) 関数は、デバイスの電源状態の遷移を開始し、電源レベルを上げることを電源管理フレームワークに要求します。通常、部品の電源レベルの低下はフレームワークによって開始されます。ただし、使用されていないデバイスの電源消費をできるかぎり削減するためには、デバイスドライバを切り離すときに pm_lower_power(9F) を呼び出すことが推奨されます。

一部のデバイスでは、電源レベルを下げることにリスクが伴います。たとえば、テープドライブの電源を切るとテープが損傷することがあります。同様に、パワーサイクルのたびにヘッドが接触するため、一部のディスクドライブではパワーサイクルの許容範囲が制限されています。デバイスドライバがデバイスのすべてのパワーサイクルを制御することをシステムに通知するには、no-involuntary-power-cycles(9P) プロパティーを使用します。この方法により、デバイスドライバがその detach(9E) エントリポイントから pm_lower_power(9F) を呼び出してデバイスの電源を切った場合を除いて、デバイスドライバを切り離している最中にデバイスの電源が切れるのを防ぐことができます。

操作に必要な部品の電源レベルが不足していることをドライバが検出すると、pm_raise_power(9F ) 関数が呼び出されます。ドライバはこのインタフェースを使用して、部品の現在の電源レベルを必要なレベルにまで上げます。このデバイスに依存するすべてのデバイスも、この呼び出しによってフルパワーに復帰します。

デバイスへのアクセスが不要になったら、デバイスを切り離すときに pm_lower_power(9F) 関数を呼び出します。pm_lower_power (9F) を呼び出すと、各部品の電源レベルが最低に設定されるため、使用されていない間はデバイスが最小限の電力しか消費しなくなります。pm_lower_power() 関数は detach() エントリポイントから呼び出す必要があります。ドライバのほかの箇所から pm_lower_power() 関数を呼び出しても効果がありません。

pm_power_has_changed(9F) 関数は、電源遷移についてフレームワークに通知するために呼び出されます。デバイスの電源レベルの変更自体によって遷移が起きる場合もあれば、サスペンド-レジュームのような操作によって遷移が起きる場合もあります。pm_power_has_changed(9F) の構文は pm_raise_power(9F) と同じです。

power() エントリポイント

電源管理フレームワークは power(9E) エントリポイントを使用します。

power() の構文は次のとおりです。

int power(dev_info_t *dip, int component, int level);

部品の電源レベルを変更する必要があるとき、システムは power(9E) エントリポイントを呼び出します。このエントリポイントで実行される処理はデバイスドライバによって異なります。前出の SCSI ターゲットディスクドライバの例では、電源レベルを 0 に設定すると、ディスクの回転を停止する SCSI コマンドが送信されます。一方、電源レベルをフルパワーに設定すると、ディスクを回転させる SCSI コマンドが送信されます。

電源遷移が原因でデバイスの状態が失われる可能性がある場合、ドライバは必要な状態をすべてメモリーに保存し、あとから復元できるようにする必要があります。電源遷移の際に、以前に保存された状態を復元しないとデバイスがふたたび使用可能にならない場合は、ドライバがその状態を復元する必要があります。電源自動管理デバイスの状態が失われる原因となる電源遷移や、状態の復元が必要になる電源遷移の種類について、フレームワークの側では想定されていません。次の例はサンプルの power() ルーチンを示しています。

例 12-4 単一部品デバイスに対する power() ルーチンの使用

int
xxpower(dev_info_t *dip, int component, int level)
{
    struct xxstate *xsp;
    int instance;

    instance = ddi_get_instance(dip);
    xsp = ddi_get_soft_state(statep, instance);
   /*
    * Make sure the request is valid
    */
    if (!xx_valid_power_level(component, level))
        return (DDI_FAILURE);
    mutex_enter(&xsp->mu);
   /*
    * If the device is busy, don't lower its power level
    */
    if (xsp->xx_busy[component] &&
        xsp->xx_power_level[component] > level) {
        mutex_exit(&xsp->mu);
        return (DDI_FAILURE);
    }

    if (xsp->xx_power_level[component] != level) {
       /*
        * device- and component-specific setting of power level
        * goes here
        */
        xsp->xx_power_level[component] = level;
    }
    mutex_exit(&xsp->mu);
    return (DDI_SUCCESS);
}

次の例は、2 つの部品で構成されるデバイスに対する power() ルーチンです。部品 1 がオンのときは部品 0 がオンである必要があります。

例 12-5 複数部品デバイスに対する power(9E) ルーチン

int
xxpower(dev_info_t *dip, int component, int level)
{
    struct xxstate *xsp;
    int instance;

    instance = ddi_get_instance(dip);
    xsp = ddi_get_soft_state(statep, instance);
   /*
    * Make sure the request is valid
    */
    if (!xx_valid_power_level(component, level))
        return (DDI_FAILURE);
    mutex_enter(&xsp->mu);
   /*
    * If the device is busy, don't lower its power level
    */
    if (xsp->xx_busy[component] &&
        xsp->xx_power_level[component] > level) {
        mutex_exit(&xsp->mu);
        return (DDI_FAILURE);
    }
   /*
    * This code implements inter-component dependencies:
    * If we are bringing up component 1 and component 0 
    * is off, we must bring component 0 up first, and if
    * we are asked to shut down component 0 while component
    * 1 is up we must refuse
    */
    if (component == 1 && level > 0 && xsp->xx_power_level[0] == 0) {
        xsp->xx_busy[0]++;
        if (pm_busy_component(dip, 0) != DDI_SUCCESS) {
           /*
            * This can only happen if the args to 
            * pm_busy_component()
            * are wrong, or pm-components property was not
            * exported by the driver.
            */
            xsp->xx_busy[0]--;
            mutex_exit(&xsp->mu);
            cmn_err(CE_WARN, "xxpower pm_busy_component() 
                failed");
            return (DDI_FAILURE);
        }
        mutex_exit(&xsp->mu);
        if (pm_raise_power(dip, 0, XX_FULL_POWER_0) != DDI_SUCCESS)
            return (DDI_FAILURE);
        mutex_enter(&xsp->mu);
    }
    if (component == 0 && level == 0 && xsp->xx_power_level[1] != 0) {
        mutex_exit(&xsp->mu);
        return (DDI_FAILURE);
    }
    if (xsp->xx_power_level[component] != level) {
       /*
        * device- and component-specific setting of power level
        * goes here
        */
        xsp->xx_power_level[component] = level;
    }
    mutex_exit(&xsp->mu);
    return (DDI_SUCCESS);
}