Go to main content
Oracle® Solaris 11.3 デバイスドライバの記述

印刷ビューの終了

更新: 2016 年 11 月
 
 

デバイス電源管理モデル

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

  • コンポーネント

  • アイドル状態

  • 電源レベル

  • 依存関係

  • ポリシー

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

  • 電源管理エントリポイント

電源管理コンポーネント

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

デバイスドライバは、デバイス部品と、各部品の電源レベルをシステムに通知します。そのため、ドライバの初期化中に、ドライバの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 段階の電源レベルをサポートします。各レベルは「停止」と「全速回転」を表します。

使用例 48  pm-component エントリの例
pm-components="NAME=Spindle Motor", "0=Stopped", "1=Full Speed";

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

使用例 49  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 は、接続されたモニターの電源管理の状態を表します。

使用例 50  複数部品の 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_raise_power(9F) またはpm_lower_power(9F) を呼び出す。

  • 時間しきい値を超過したため、フレームワークが部品の電源レベルを下げた。

  • このデバイスとの間に依存関係が存在する別のデバイスの電源レベルが変化した。電源管理の依存関係を参照してください。

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

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

電源管理の依存関係

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

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

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

次の構文を使うと、依存関係を一般的な方法で示すことができます。

device-dependency-property property phys-path

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

device_dependent-property  removable-media  /dev/fb

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

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

デバイスの自動電源管理

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


注 - 初回出荷が 1999 年 7 月 1 日以降のすべての SPARC デスクトップシステムで、自動電源管理はデフォルトで有効です。それ以外のすべてのシステムで、この機能はデフォルトで無効です。

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

電源管理に対応したコンポーネントを含むデバイスをサポートするデバイスドライバは、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 が同じ回数だけ呼び出されている必要があります。

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() ルーチンを示しています。

使用例 51  単一部品デバイスに対する 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 がオンである必要があります。

使用例 52  複数コンポーネントデバイスに対する 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);
}