デバイスのハードウェア状態の保存をドライバに要求するとき、システム電源管理はドライバの detach(9E) エントリポイントに DDI_SUSPEND コマンドを渡します。デバイスのハードウェア状態の復元をドライバに要求するとき、システム電源管理はドライバの attach(9E) エントリポイントに DDI_RESUME コマンドを渡します。
detach(9E) の構文は次のとおりです。
int detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
reg プロパティーを持つデバイスと、pm-hardware-state プロパティーが needs-suspend-resume に設定されたデバイスは、デバイスのハードウェア状態を保存できる必要があります。フレームワークはドライバの detach(9E) エントリポイントを呼び出して、ドライバで状態を保存し、システムの電源が復旧したときに状態を復元できるようにします。DDI_SUSPEND コマンドを処理するために、detach (9E) は次のタスクを実行する必要があります。
デバイスがレジュームされるまで、 dump(9E) 要求を除き、後続の操作が開始されないようにします。
未処理の操作が完了するまで待ちます。未処理の操作を再開できる場合は、ユーザーがその操作を中止できます。
保留中のタイムアウトおよびコールバックをすべてキャンセルします。
揮発性のハードウェア状態をすべてメモリーに保存します。状態にはデバイスレジスタの内容が含まれ、ダウンロードされたファームウェアが含まれることもあります。
デバイスをサスペンドしてその状態をメモリーに保存する処理をドライバで実行できない場合、ドライバは DDI_FAILURE を返す必要があります。フレームワークはそれ以降のシステム電源管理操作を中止します。
デバイスの電源を切ることに何らかのリスクを伴う場合があります。たとえば、テープが入った状態でテープドライブの電源を切ると、テープが損傷する可能性があります。そのような場合、 attach(9E) で次の処理を実行する必要があります。
ddi_removing_power(9F) を呼び出して、DDI_SUSPEND コマンドによりデバイスの電源が切れる可能性があるかどうかを調べます。
電源を切った場合に問題が起きる可能性があるかどうかを調べます。
両方の可能性がある場合、DDI_SUSPEND 要求を拒否する必要があります。Example 12–6 は、 attach(9E) ルーチンで ddi_removing_power(9F) を使用して、DDI_SUSPEND コマンドが問題を引き起こすかどうかを調べる方法を示しています。
ダンプ要求は尊重される必要があります。フレームワークではdump (9E) エントリポイントを使用して、メモリーの内容を格納した状態ファイルを書き出します。このエントリポイントの使用時にデバイスドライバに課せられる制限については、dump (9E) のマニュアルページを参照してください。
電源管理に対応したコンポーネントの detach(9E) エントリポイントを呼び出すとき、DDI_SUSPEND コマンドを使用すると、デバイスの電源が切れるときに状態が保存されます。ドライバは保留中のタイムアウトをキャンセルします。またドライバは、dump(9E) 要求を除いて、pm_raise_power(9F) の一切の呼び出しを抑制します。DDI_RESUME コマンドを使用した attach(9E) の呼び出しによってデバイスがレジュームされると、タイムアウトおよび pm_raise_power () の呼び出しもレジューム可能になります。このような可能性に適切に対処できるよう、ドライバはその状態の十分な追跡を継続する必要があります。次の例は、DDI_SUSPEND コマンドを実装した detach(9E) ルーチンを示しています。
使用例 12-6 DDI_SUSPEND を実装する detach (9E) ルーチンint
xxdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
struct xxstate *xsp;
int instance;
instance = ddi_get_instance(dip);
xsp = ddi_get_soft_state(statep, instance);
switch (cmd) {
case DDI_DETACH:
/* ... */
case DDI_SUSPEND:
/*
* We do not allow DDI_SUSPEND if power will be removed and
* we have a device that damages tape when power is removed
* We do support DDI_SUSPEND for Device Reconfiguration.
*/
if (ddi_removing_power(dip) && xxdamages_tape(dip))
return (DDI_FAILURE);
mutex_enter(&xsp->mu);
xsp->xx_suspended = 1; /* stop new operations */
/*
* Sleep waiting for all the commands to be completed
*
* If a callback is outstanding which cannot be cancelled
* then either wait for the callback to complete or fail the
* suspend request
*
* This section is only needed if the driver maintains a
* running timeout
*/
if (xsp->xx_timeout_id) {
timeout_id_t temp_timeout_id = xsp->xx_timeout_id;
xsp->xx_timeout_id = 0;
mutex_exit(&xsp->mu);
untimeout(temp_timeout_id);
mutex_enter(&xsp->mu);
}
if (!xsp->xx_state_saved) {
/*
* Save device register contents into
* xsp->xx_device_state
*/
}
mutex_exit(&xsp->mu);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
attach(9E) の構文は次のとおりです。
int attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
システムの電源が復旧すると、reg プロパティーを持つか、またはpm-hardware-state プロパティーの値が needs-suspend-resume である各デバイスの attach(9E) エントリポイントが、コマンド値 DDI_RESUME を使用して呼び出されます。システムのシャットダウンが中止された場合、電源が切れなかったにもかかわらず、サスペンドされた各ドライバがレジュームのために呼び出されます。したがって、attach (9E) のレジュームコードは、システムの電源が実際に切れたかどうかを想定しないものにする必要があります。
DDI_RESUME の時点で、電源管理フレームワークは部品の電源レベルを不明とみなします。デバイスの性質に応じて、ドライバの開発者には 2 つの選択肢があります。
部品の電源を入れなくても、レジスタを読み取るなどしてデバイス部品の実際の電源レベルをドライバで特定できる場合、ドライバで pm_power_has_changed(9F) を呼び出して、各部品の電源レベルをフレームワークに通知します。
ドライバで部品の電源レベルを特定できない場合、ドライバでは各部品の電源レベルを内部的に不明とマークし、各部品に最初にアクセスする前に pm_raise_power(9F) を呼び出します。
次の例は、DDI_RESUME コマンドを使用した attach(9E) ルーチンを示しています。
使用例 12-7 DDI_RESUME を実装する attach (9E) ルーチンint
xxattach(devinfo_t *dip, ddi_attach_cmd_t cmd)
{
struct xxstate *xsp;
int instance;
instance = ddi_get_instance(dip);
xsp = ddi_get_soft_state(statep, instance);
switch (cmd) {
case DDI_ATTACH:
/* ... */
case DDI_RESUME:
mutex_enter(&xsp->mu);
if (xsp->xx_pm_state_saved) {
/*
* Restore device register contents from
* xsp->xx_device_state
*/
}
/*
* This section is optional and only needed if the
* driver maintains a running timeout
*/
xsp->xx_timeout_id = timeout( /* ... */ );
xsp->xx_suspended = 0; /* allow new operations */
cv_broadcast(&xsp->xx_suspend_cv);
/* If it is possible to determine in a device-specific
* way what the power levels of components are without
* powering the components up,
* then the following code is recommended
*/
for (i = 0; i < num_components; i++) {
xsp->xx_power_level[i] = xx_get_power_level(dip, i);
if (xsp->xx_power_level[i] != XX_LEVEL_UNKNOWN)
(void) pm_power_has_changed(dip, i,
xsp->xx_power_level[i]);
}
mutex_exit(&xsp->mu);
return(DDI_SUCCESS);
default:
return(DDI_FAILURE);
}
}