USB キーボードと kbdmouse.dllアプリケーションからリブート(または電源 OFF)~その2(2/2)

アプリケーションからリブート(または電源 OFF)~その1(1/2)

2009/07/20 koga

「WinCE では、Linux のように、リブートやシャットダウンを行うコマンドは無いのですか?」
先日、こんな質問を某所でもらいました。「シェルのコマンドとしては、存在していないはずですが、リブートしたりホールト(halt)するコマンドプログラムは、作れると思います。必要な API などを調べてみますので、少し時間を下さい。」そう回答して帰ってきたので、調べてみました。

なお、回答した際には、「ただし、PC Linux のように自動で電源 OFF する機能は、ハードウェアの支援が必要なので、デバイスによっては、シャットダウンしても電源は切れず、電源を切れる状態になるだけです。」と言い添えてあります。

調べた結果、OS デザインに Power Management が組み込まれている場合、リブートするには、SetSystemPowerState() を使うのが最も簡単だと分かりました。SetSystemPowerState() については、WinCE のリファレンスをご覧ください:
 http://msdn.microsoft.com/en-us/library/aa929708.aspx
SetSystemPowerState() の第二引数に POWER_STATE_RESET を渡せば、WinCE がリブートします。また、POWER_STATE_RESET ではなく、POWER_STATE_SUSPEND を渡すと、サスペンド状態となりますので、安全に電源を切ることができます。

では、OS デザインに Power Management を組み込んでいない場合には、どうすればよいのでしょうか?以下に、リブート動作とサスペンド動作について、もう少し詳しく書きます。ただし、以下で述べるのは、Windows Embedded CE 6.0(WinCE 6.0)に対するものです。WinCE 5.0 以前のバージョンについては、確認していませんが、以下で述べることがそのまま当てはまるわけではないと考えて下さい。
(※サスペンド動作については、次回に書くことにします。今回は、少々長くなるので、二回に分けて書くことにしました。)

■リブート動作
リブート動作の詳細は、ボードや CPU ごとに異なります。従って、その部分は、OAL で実装されます。具体的には、OALIoCtlHalReboot() が、リブート動作の実装です:
 http://msdn.microsoft.com/en-us/library/aa910452.aspx
デバイスエミュレータの場合、OALIoCtlHalReboot() のソースは、
 <WINCE600>/PLATFORM/DEVICEEMULATOR/SRC/OAL/OALLIB/reboot.c
です。この reboot.c を見ると、デバイスエミュレータの CPU(S3C2410) のウォッチドッグ・タイマの発火時間を短い値にセットして、CPU にリセットがかかるようにしているのが分かります。

さて、単純に CPU にリセットをかけ、OS をリブートした場合、レジストリやファイルシステムに不整合が生じる可能性があります。レジストリやファイルシステムに対する更新が、メモリ上のキャッシュに対してのみ行われ、永続記憶域に反映されていない状態でリセットがかかれば、更新内容が失われてしまうからです。従って、OALIoCtlHalReboot() の呼び出しを行う前に、レジストリやファイルシステムのフラッシュ動作を行わなければいけません。

また、OALIoCtlHalReboot() を呼び出せるのは、カーネルだけです。つまり、アプリケーションから呼び出すことは、できません。アプリケーションからデバイスをリブートする場合は、カーネルに対して OALIoCtlHalReboot() の呼び出しを要求し、さらに、OALIoCtlHalReboot() が呼び出される前に、レジストリやファイルシステムのフラッシュ動作を発火させなければいけません。これらの処理は、SetSystemPowerState() を使えるのであれば、意識する必要は、ありません。Power Management 機能が、必要な処理を行ってくれるからです。

では、Power Management を OS デザインに組み込んでおらず、使えない場合は、どうすればよいのでしょうか。その場合は、カーネルに対して、リブート動作を要求します。制御コードに IOCTL_HAL_REBOOT を指定して KernelIoControl() を呼び出すことにより、カーネルにリブート動作を行わせることが出来ます。

ただし、KernelIoControl() で IOCTL_HAL_REBOOT を指定することは、アプリケーションからは出来ません。OAL 側でサポートを追加しない限り、アプリケーションによる KernelIoControl() の呼び出しでは、IOCTL_HAL_REBOOT は未サポートの制御コードとして扱われます。

アプリケーションから 、つまり、ユーザモードから KernelIoControl() を呼び出した場合、その応答処理は、
 <WINCE600>/PUBLIC/COMMON/OAK/OALIOCTL/oalioctl.cpp
で実装されている IOControl() で処理されます。ソース(oalioctl.cpp)を見れば分かるように、IOControl() では、以下の制御コードのみに対して応答動作を行い、それ以外の制御コードに対しては、ERROR_NOT_SUPPORTED エラーとして処理するのです。

IOCTL_HAL_GET_CACHE_INFO
IOCTL_HAL_GET_DEVICE_INFO
IOCTL_HAL_GET_DEVICEID
IOCTL_HAL_GET_UUID
IOCTL_PROCESSOR_INFORMATION

なお、oalioctl.cpp のコメントに書かれていますが、OEM、つまり WinCE デバイスを実装する側では、oalioctl.cpp を編集して、IOControl() が応答する制御コードを追加/削除することができます。上で、「OAL 側でサポートを追加しない限り」と書いたのは、IOControl() が応答する制御コードとして IOCTL_HAL_REBOOT が追加されていれば、アプリケーションから KernelIoControl() を呼び出してリブートできる、という意味なのです。

OAL 側でサポートを追加していない場合は、アプリケーションから KernelIoControl() を呼び出して IOCTL_HAL_REBOOT を指定しても、エラーになってしまうので、リブートできません。その場合は、専用のデバイスドライバを作り、デバイスドライバから KernelIoControl() を呼び出せば、IOCTL_HAL_REBOOT を指定できます。上記の oalioctl.cpp が関与するのは、ユーザモードから KernelIoControl() が呼び出された場合であり、カーネルモードからの呼び出しでは、全ての制御コードが処理されます。従って、カーネルモードのデバイスドライバから KernelIoControl() を呼び出せばよい、というわけです。

ところで、リブートする際に、KernelIoControl() を呼び出すだけでよいのでしょうか?IOCTL_HAL_REBOOT を渡して KernelIoControl() を呼び出せば、カーネルによって OALIoCtlHalReboot() が呼び出されます。しかし、リブートの際には、OALIoCtlHalReboot() の呼び出しを行う前に、レジストリやファイルシステムのフラッシュ動作を行わなければいけないと書きました。

実は、デバイスドライバから呼び出す場合は、KernelIoControl() を呼び出すだけで、必要な処理が行われます。ただし、ここで述べるのはドキュメント化された情報ではなく、WinCE 6.0 のカーネルのソースを追跡して判断したことですので、ご注意下さい。念のため、アプリケーションから呼び出す場合のように(※これについては、後で述べます)、KernelIoControl() の前に FileSystemPowerFunction() を呼び出す方が良いでしょう。

カーネルモードから KernelIoControl() を呼び出した場合ですが、k.coredll.dll を経由して、カーネル内部の同じ名前の関数が呼び出されます。カーネル内部の KernelIoControl() は、
 <WINCE600>/PRIVATE/WINCEOS/COREOS/NK/KERNEL/kwin32.c
で実装されています。このソースを見れば、IOCTL_HAL_REBOOT が渡された場合、KernelIoControl() から CallOEMIoControl() を経由して、NKReboot() が呼び出されることが分かります。NKReboot() は、IOCTL_HAL_REBOOT を渡して OEMIoControl() を呼び出すことにより、OALIoCtlHalReboot() の呼び出しを行うのですが、OEMIoControl() の前に、FSNotifyMountedFS() というカーネル内部関数を呼び出します。この FSNotifyMountedFS() によって、レジストリやファイルシステムのフラッシュ動作が実行されるのです。

一方、OAL 側でサポートが追加されていて、アプリケーションから KernelIoControl() に IOCTL_HAL_REBOOT を渡した場合、上述した oalioctl.cpp の IOControl() では、そのまま OEMIoControl() を呼び出します。そのため、レジストリやファイルシステムのキャッシュ動作は、行われません。従って、アプリケーション自身が、レジストリやファイルシステムのキャッシュ動作を発火させる必要があります。具体的には、FileSystemPowerFunction() の引数に FSNOTIFY_POWER_OFF を渡して呼び出します。FileSystemPowerFunction() のリファレンスは、
 http://msdn.microsoft.com/en-us/library/aa914779.aspx
です。

次は、「サスペンド動作」と、「電源 OFF について」という項を書くつもりでしたが、少々長くなってしまいました。この二つについては、次回に書くことにします。

Entry Filed under: OS のコンフィグレーション, OS の内部動作

1 Comment Add your own

  • 1. 鏑木肆星  |  9月 16th, 2009 at 09:46

    はじめまして、鏑木と申します。
    私は現在Windows CE 6.0のVC++のアプリケーションを開発している者なのですが、アプリケーションの開発にて電源オフ監視をするにはどのようにすればよいのか(どのようなAPIを使えばよいのか)がわからず、ネットを探していましたところこちらに有益な情報があり、コメントさせて頂きました。
    今回の内容では、CEのサスペンドや電源オフの動作をさせる処理に関してでしたが、電源オフ動作を検知するにはどのようにすればよいのでしょうか?
    もし宜しければ、ご教授頂きたいと思い厚かましいながら質問させて頂きました。
    使用しているOSはWindows CE 6.0、BSPはGeode LX(x86)のもので、開発環境はVisual Studio 2005です。

Leave a Comment

Required

Required, hidden

Some HTML allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Trackback this post  |  Subscribe to the comments via RSS Feed


Categories

Links

Posts by Authors

Recent Posts

Calendar

2009年7月
« 12月   8月 »
 1234
567891011
12131415161718
19202122232425
262728293031  

Posts by Month

Posts by Category

Meta