Linux Power Management Support この文書は、Linux システムの電源管理の使用方法と、Linux をサポートする 電源管理のドライバの追加方法を簡単に説明します。 APM と ACPI のどちら? ---------------------- 比較的新しいモバイルかデスクトップもしくはサーバの x86 システムを持っ ているなら、たぶん、Advanced Power Management (APM) か Advanced Configuration and Power Interface (ACPI) のどちらかはサポートしている と思われます。ACPI は、2つの技術の新しい方で、電源管理をオペレーティ ングシステムの管理下に置き、BIOS が制御する APM がなしうる電源管理より 多くの賢い管理を許可します。 二者択一できるなら、どれがシステムをサポートするか決めるための最良の方 法は、ACPI と APM の両方を有効 (2.3.x において、ACPI はデフォルトで有 効です) にしてカーネルを構築することです。動作する ACPI 実装が検出され れば、ACPI ドライバは APM に優先し、APM を無効にし、そうでなければ、 APM ドライバが使われるでしょう。 いえ、すみません、ACPI と APM の両方を同時に有効にして実行させることは できません。半端な ACPI か APM の実装を持つ人々は、動作する特徴のすべ てを手に入れ、両方を使いたいと思っていますが、2つを整合することは簡単 にはできません。同時にマシンを制御できる電源管理インターフェースは、た だ一つです。考えてみてください・・・ ユーザ空間のデーモン -------------------- APM と ACPI の両方を、完全に機能させるには、ユーザ空間のデーモンを必要 とし、それは、それぞれ apmd と acpid です。両方のデーモンを Linux ディ ストリビューションもしくはインターネット (下記参照のこと) から入手し、 システムブート時のどこかで、必ず起動させてください。ACPI か APM がシス テム上で利用できないなら、関連するデーモンはしとやかに終了するでしょう。 apmd: http://worldvisions.ca/~apenwarr/apmd/ acpid: http://phobos.fs.tum.de/acpi/ ドライバインターフェース ------------------------ あなたが、新しいドライバを書いているか、古いドライバの管理をしているな ら、電源管理サポートを含むべきです。電源管理サポートのないドライバは、 電源管理能力を持ち、(安全に) サスペンドできるシステムを、妨げるかもし れません。 概要 - 1) "pm_register" コールにより、デバイスの各々のインスタンスを登録しま す。 2) ハードウェアにアクセスする前に "pm_access" をコールします (ハードウェ アが起動中・待機状態であるかどうか確かめます)。 3) サスペンド状態 (ACPI D1-D3) に移行する前か、サスペンドからのリジュー ム (ACPI D0) の後には、(あなたの用意した) pm_callback がコールされ ます。 4) デバイスを使用しなくなったとき、"pm_dev_idle" をコールします (コー ルするかは任意ですが、コールすればデバイスのアイドル検出は改善します)。 5) アンロードするとき、"pm_unregister" コールにより、デバイスを未登録 にします。 /* * 概要 - デバイスを電源管理サブシステムに登録します * * 引数 - * type - デバイス種別 (PCI device, system device, ...) * id - インスタンス番号もしくは一意の名前 * cback - callback ハンドラの要求 (suspend, resume, ...) * * 戻り値 - 登録された PM デバイス番号か、エラーを示す NULL * * 例 - * dev = pm_register(PM_SYS_DEV, PM_SYS_VGA, vga_callback); * * struct pci_dev *pci_dev = pci_find_dev(...); * dev = pm_register(PM_PCI_DEV, PM_PCI_ID(pci_dev), callback); */ struct pm_dev *pm_register(pm_dev_t type, unsigned long id, pm_callback cback); /* * 概要 - デバイスを電源管理サブシステムから未登録にします * * 引数 - * dev - PM デバイス番号で、以前の pm_register からの戻り値 */ void pm_unregister(struct pm_dev *dev); /* * 概要 - 一致する callback 関数を持つすべてのデバイスを未登録にします * * 引数 - * cback - 以前登録した callback の要求 * * 注意 - 古い APM インターフェースを簡単に移植するために提供されてい * ます */ void pm_unregister_all(pm_callback cback); /* * デバイスがアイドルか使用中かを検出するために * * 一般的に、すべてのデバイスのためのドライバは、ハードウェアにアクセ * スする前 (すなわち、ハードウェアのレジスタに読み書きする前) に * "pm_access" をコールすべきです。要求もしくはパケットにより駆動され * るドライバは、さらに、デバイスが使われなくなった時、"pm_dev_idle" * をコールすべきです。 * * 例 - * 1) キーボードドライバは、キーが押される時はいつでも、pm_access をコー * ルするでしょう。 * 2) ネットワークドライバは、パケットの送信もしくは受信の操作を行う前 * に pm_access をコールし、送信と受信の待ち行列が空になった時に * pm_dev_idle をコールするでしょう。 * 3) VGA ドライバは、ビデオコントローラのレジスタのいずれかにアクセス * する前に pm_access をコールするでしょう。 * * 最終的に、PM ポリシーマネージャは、個々のデバイスをサスペンドする時 * もしくは、システムすべてをサスペンドする時を決定するために、 * pm_access と pm_dev_idle の情報を、使います。 */ /* * 概要 - デバイスへのアクセスタイムを更新し、必要なら、デバイスを起こ * します * * 引数 - * dev - PM デバイス番号で、以前の pm_register からの戻り値 * * 詳細 - 割り込みハンドラからコールされるなら、pm_access はアクセス * タイムを更新しますが、デバイスを起こす必要は絶対にないはずで * す (デバイスが割り込みを生成するなら、すでに、起きているはず * です)。これは、重要で、割り込みハンドラからはデバイスを起こ * せません。 */ void pm_access(struct pm_dev *dev); /* * 概要 - デバイスが、現在アイドル中であると明示します * * 引数 - * dev - PM デバイス番号で、以前の pm_register からの戻り値 * * 詳細 - pm_dev_idle コールは、デバイスをスリープさせる信号を、ポリシー * マネージャに送るかもしれません。新しいデバイスの要求が、 * pm_dev_idle コールと pm_callback の呼び戻しの間に、届くなら、 * ドライバは、pm_callback の要求を失敗します。 */ void pm_dev_idle(struct pm_dev *dev); /* * 電源管理要求の callback * * 概要 - * dev - PM デバイス番号で、以前の pm_register からの戻り値 * rqst - 要求の種別 * data - 要求に関連するデータがあれば、そのデータ * * 戻り値 - 要求が成功したなら、0 * 要求がサポートされてなければ、EINVAL * デバイスが使用中で、要求が扱えなければ、EBUSY * メモリが原因で、要求を扱えなかったなら、ENOMEM * * 詳細 - デバイス要求の callback は、デバイスあるいはシステムがサスペ * ンド状態に入る (ACPI D1-D3) 前か、サスペンドからデバイスある * いはシステムがリジュームした (ACPI D0) 後、コールされます。 * PM_SUSPEND では、ACPI D 某の状態になる時に、引数 "data" とし * て、その状態が callback に渡されます。デバイスドライバは、 * callback 要求がコールされる時、デバイスに関するメモリおよび * レジスタを保存 (PM_SUSPEND) もしくは復元 (PM_RESUME) するは * ずです。 * * ドライバがサスペンド要求に 0 (成功) を戻すと、"pm_access" コー * ルをするまで、これ以上の要求の処理も、デバイスのハードウェアへ * のアクセスも、行えないはずです。 */ typedef int (*pm_callback)(struct pm_dev *dev, pm_request_t rqst, void *data); ドライバの詳細 -------------- 本物のドライバの作者向け電源管理の手引書が入手可能になるまでの急場しの ぎとして、ちょっとだけ、簡単な Q&A で、ドライバの説明をします。 Q: いつ、デバイスはサスペンドされますか? 直接的なユーザ要求 (例えば、ラップトップの蓋を閉める)、システム電源ポ リシー (例えば、コンソールの使用がなくなって、30 分後にスリープ) もし くは、デバイス電源ポリシー (例えば、使用されなくなって、5 分後にデバイ スの電源断) に基づき、デバイスをサスペンドできます。 Q: ドライバはサスペンド要求を必ず引き受けなければなりませんか? いいえ、 ドライバはサスペンド要求に EBUSY を戻すことができ、これにより、 システムはサスペンドを止めるでしょう。サスペンド要求が失敗した時、サス ペンドされたすべてのデバイスは、リジュームされ、システムは実行を続けま す。サスペンドは、後で、リトライできます。 Q: ドライバは、サスペンドあるいはリジュームの要求をブロックできますか? はい、デバイスが要求を扱う準備をするまで、ドライバは、サスペンドもしく はリジュームの要求の戻りを遅らせることができます。サスペンドあるいはリ ジュームが終了した順で、要求から可能な限り早く戻すことが、好都合です。 Q: サスペンドもしくはリジュームは、どのコンテキストから開始されますか? サスペンドもしくはリジュームは、カーネルスレッドコンテキストから開始さ れます。メモリのブロックや割り当て、要求の開始もしくは何か別のカーネル 内でできることが、安全にできます。 Q: サスペンド後に届いた要求は継続しますか? 要求はくるかもしれません。サスペンド要求に成功を戻した後のすべて要求は、 ドライバの責任で、待ち (注)、失敗もしくは落とします。デバイスのバスが 使用できないので、リジューム要求を受け取るまで、ドライバはそのデバイス にアクセスしません。これは重要なことです。 (注) ドライバがリジューム後処理するために要求を待ち行列に入れたなら、 デバイスやネットワークなどが、サスペンド時と異なる状態にあるかも しれないことを承知しておいてください。ストレージデバイスのドライ バでなければ、要求を落としたほうが、たぶん、いいでしょう。 Q: バス固有の電源管理レジスタを管理しなければなりませんか? いいえ。PCI, USB などの電源管理レジスタの管理は、バスドライバの責任で す。また、バスドライバか電源管理サブシステムは、デバイスが持つ wake-on 機能も有効にします。 Q: それで、実際、サスペンドあるいはリジュームをサポートするには何をす ればいいの? デバイスの電源を切るなら、消えてしまうであろうデバイスのコンテキストを 保存し、そして、それをリジューム時に復元することが必要です。ACPI が動 作している時、デバイスのサスペンド状態は3つあります - D1, D2 そして D3 (サスペンド状態は、引数 "data" として、デバイスの callback に渡され ます)。D3 で、デバイスは電源が切られ、すべてのコンテキストは失われます。 D1 と D2 は節電状態で、最低限必要なデバイスコンテキストが保持されます。 安全に行うには、単に、サスペンドですべてのものを保存し、リジュームです べてのものを復元するだけです。 Q: サスペンド用のデバイスコンテキストはどこに格納しますか? メモリのどこかに、バッファを kmalloc するか、デバイス記述子の中に格納 します。メモリの内容は、システムがディスクをサスペンドした時でさえ、リ ジューム前に、復元され、アクセス可能になることが保証されています。 Q: ACPI 用には、APM などと比して何かすることが必要ですか? ドライバは、動作している電源管理の仕様を知る必要はありません。単に、電 源管理システムに基づくサスペンドもしくはリジュームの要求時のことだけ承 知しておいてください。 Q: デバイスの依存関係はどうですか? ドライバがデバイスを登録する時、電源管理サブシステムはデバイスの依存関 係のツリーを組み立てるために供給された情報を使います (例えば、USB デバ イス X は、USB コントローラ Y 上にあり、それは、PCI バス Z 上にありま す)。電源管理がデバイスをサスペンドしたい時、始めにデバイスドライバへ、 次にバスドライバへ、そしてシステムバスに至るまでのドライバへ、サスペン ド要求を送ります。デバイスのリジュームは、反対の方向で行われます。 Q: 私の固有のドライバあるいはデバイス向けに電源管理を有効にする追加情 報は誰に聞けばいいの? ACPI4Linux のメーリングリスト - acpi@phobos.fs.tum.de システムインターフェース ------------------------ 新しい電源管理サポートを Linux に提供している (すなわち、APM や ACPI のようなサポートを追加している) なら、現存の一般的な電源管理インター フェースを通じて、ドライバと交信するべきです。 /* * 一つのデバイスへ要求を送ります * * 引数 - * dev - PM デバイス番号で、以前の pm_register か pm_find からの戻り * 値 * rqst - 要求の種別 * data - 要求に関連するデータがあれば、そのデータ * * 戻り値 - 要求が成功したなら、0 * エラーの戻り値は "pm_callback" の項を参照のこと * * 詳細 - 要求をデバイスの callback へ回送し、要求がサスペンドかリジュー * ムなら、pm_dev の "状態 (state)" フィールドを更新します。 */ int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data); /* * すべてのデバイスへ要求を送ります * * 引数 - * dev - PM デバイス番号で、以前の pm_register か pm_find からの戻り * 値 * rqst - 要求の種別 * data - 要求に関連するデータがあれば、そのデータ * * 戻り値 - 要求が成功したなら、0 * エラーの戻り値は "pm_callback" の項を参照のこと * * 詳細 - 登録されたデバイスの一覧を探知し、それぞれのデバイスへ * pm_send を、すべて完了するか、エラーに遭遇するまで、コールし * ます。サスペンド要求でエラーに遭遇したなら、サスペンド要求の * 前の状態にすべてのデバイスを復帰します。 */ int pm_send_all(pm_request_t rqst, void *data); /* * 一致するデバイスを探します * * 引数 - * type - デバイス種別 (PCI デバイス、システムデバイスもしくは、0 で * 一致するすべてのデバイス) * from - 以前の一致場所からか、NULL で初めから * * 戻り値 - 一致したデバイス番号か、見つからなければ NULL */ struct pm_dev *pm_find(pm_dev_t type, struct pm_dev *from); ------------------------------------------- 著者:Andy Henroid 日本語訳:野本浩一 校正:中野武雄さん 山田慎也さん