Posts filed under 'OS のコンフィグレーション'
前回のエントリで、.NET CF アプリケーションから WNetAddConnection3() を呼び出す例を述べました。その例の説明で書いたように、WNetAddConnection3() は、Windows フィルサーバとの接続などを行う API です。
■Windows ファイルサーバに対するクライアント機能
Windows ファイルサーバ(CIFS サーバ)をアクセスする機能、つまり、Windows ファイルサーバに対するクライアント機能は、”Windows Networking API/Redirector” というコンポーネントになっており、SYSGEN 変数の SYSGEN_REDIR を設定することにより、OS イメージに組み込まれます。詳細については、リファレンスの次のページをご覧ください。
Windows Networking API/Redirector Reference (Windows Embedded Compact 7)
http://msdn.microsoft.com/en-us/library/ee495264.aspx
Windows Networking API/Redirector (Windows Embedded CE 6.0)
http://msdn.microsoft.com/en-US/library/ee494246(v=winembedded.60).aspx
Platform Builder のカタログ項目ビュー(Catalog Items View)の階層で言うと、次の項目を選択すると、SMB/CIFS クライアント機能が OS Design に組み込まれます(※WEC 7 の場合):
<OS Design 名>
Core OS
Windows Embedded Compact
Communication Services and Networking
Networking – General
★ Windows Networking API/Redirector (SMB/CIFS)
ここで、”Redirector” という Windows の用語に馴染みのない方は、たとえば、次の解説記事が参考になるのではないかと思います。
「基礎から学ぶWindowsネットワーク 第20回」
http://www.atmarkit.co.jp/fwin2k/network/baswinlan020/baswinlan020_03.html
「基礎から学ぶWindowsネットワーク」
http://www.atmarkit.co.jp/fwin2k/network/baswinlan002/baswinlan002_03.html
なお、CIFS クライアントだけでなく、CIFS サーバ機能も WEC/WinCE に付属しています。こちらは、カタログ項目ビューの階層の、次の場所にあります:
<OS Design 名>
Core OS
Windows Embedded Compact
Communication Services and Networking
Servers
File Server (SMB/CIFS)
CIFS サーバに関する記述は、なぜか WEC 7 のリファレンスに見当たりません。興味のある方は、WinCE 6.0 のリファレンスをご覧ください。
File Server (Windows Embedded CE 6.0)
http://msdn.microsoft.com/en-us/library/ee500573(v=winembedded.60).aspx
■SMB/CIFS クライアント機能を組み込む場合の注意点
さて、SMB/CIFS クライアント機能(Windows Networking API/Redirector)を組み込む場合、一つ注意しなければいけません。WEC/WinCE のリファレンスには、このコンポーネントが依存するのは、TCP/IP (SYSGEN_TCPIP) と Winsock (SYSGEN_WINSOCK)、および NDIS (SYSGEN_NDIS) とだけ書かれているのですが、それらのコンポーネントだけでは、SMB/CIFS サーバにアクセスすることは、できません。
CIFS サーバに接続する場合は、認証処理が必要ですが、認証処理に必要なコンポーネントは、SMB/CIFS クライアント機能を組み込んでも自動的に組み込まれないため、明示的に組み込む必要があるのです。認証処理に必要なコンポーネントは、WEC 7 ですと、カタログ項目ビューの階層の、次の場所にあります:
<OS Design 名>
Core OS
Windows Embedded Compact
Security
Authentication Services (SSPI)
★ Kerberos
★ NTLM
通常は、NTLM だけを選択しても CIFS サーバに接続できるでしょう。お手元の環境で、試してみて下さい。これらのコンポーネントについては、リファレンスの次のページで説明されています(※WEC 7 の場合)。
NTLM Security Support Provider (Windows Embedded Compact 7)
http://msdn.microsoft.com/en-us/library/ee498104.aspx
Kerberos Security Support Provider (Windows Embedded Compact 7)
http://msdn.microsoft.com/en-us/library/ee498711.aspx
Authentication Services (Windows Embedded Compact 7)
http://msdn.microsoft.com/en-us/library/ee498877.aspx
2012/05/07
koga
今回は、マネージドコードのアプリケーション、つまり、.NET CF のアプリケーションとカーネルランドを、Ethernet 経由で一緒にデバッグする方法を説明します。「一緒にデバッグする」というのは、”「同時にデバッグする」ことはできないが、アプリケーションデバッガとカーネルデバッガを各々動かすことにより、アプリケーションとカーネルランドを、それぞれのデバッガでデバッグする”という意味です。
皆さんご存じの通り、ネイティブコードのアプリケーションの場合には、アプリケーションもカーネルランドも、どちらもカーネルデバッガでデバッグできます。たとえば、アプリケーションからデバイスドライバを呼び出した場合、デバイスドライバのソースコードにブレークポイントを設定してブレークした時、アプリケーションからデバイスドライバまでのコールスタック(「呼び出し履歴」)を見ることができます。アプリケーションがデバイスドライバを呼び出す場合、システムコールを経由しますが、カーネルデバッガは、その経路を把握して、あたかも通常の関数呼び出しのように、コールスタックを表示してくれるのです。僕は、このように、一つのデバッガだけでアプリケーションとカーネルランドをデバッグすることを指して「同時にデバッグする」と表現しています。
一方、マネージドコード(.NET CF)のアプリケーションの場合には、一つのデバッガだけでアプリケーションとカーネルの両方をデバッグすることは、できません。アプリケーションのデバッガとカーネルデバッガを各々動かす必要があるのです。このことを指して、ここでは「一緒にデバッグする」と表現します。
■Ethernet 経由でのアプリケーションデバッガ接続
Ethernet 経由で WinCE/WEC のアプリケーションデバッガを接続する場合は、ActiveSync は使用しません。ActiveSync は、2005年にリリースされた 4.0 から TCP/IP 接続をサポートしていませんから、使えないのです。ActiveSync を使わずに、TCP/IP で WinCE/WEC に接続する手順は、Visual Studio のリファレンスのページで説明されています:
How to: Connect to Windows CE Device Without ActiveSync
http://msdn.microsoft.com/en-US/library/ms228708(v=vs.90).aspx
上のページは、Visual Studio 2008 のリファレンスです。日本語の説明を読みたい場合は、Visual Studio 2005 ですが、ユニダックス社の「CE6.0 技術情報」に日本語の説明があります:
11. アプリケーションのリモートデバッグ
http://www.unidux.co.jp/embedded/techinfo/ce6/000437.php
上のページで説明されているように、Visual Studio に付属している次の5つのファイルを、WinCE/WEC の /Windows ディレクトリへコピーして、Visual Studio のデバイス設定を行うことにより、TCP/IP でアプリケーションデバッガを接続できます。
Clientshutdown.exe
ConmanClient2.exe
CMaccept.exe
eDbgTL.dll
TcpConnectionA.dll
WEC 7 で、AMR9 コアの CPU のボードに接続する場合ですと、上記のファイルは、次の場所にあります。
C:\Program Files\Common Files\microsoft shared\CoreCon\1.0\Target\wce400\armv5\
これらのファイルを、WinCE/WEC が起動した後に、/Windows ディレクトリに USB メモリなどを使ってコピーするか、または、OS Design の project.bib ファイルを編集して OS イメージに組み込めば、使えます。
または、OS Design のカタログ項目で、”Target Control Support(Shell.exe)” か “Release Directory File System” を選択している場合は、Flat Release Directory(環境変数 _FLATRELEASEDIR が指すディレクトリ)に上記5つのファイルを置いても、使えます。Target Control Support を有効にしてカーネルデバッガを使っている場合には、これが一番お手軽でしょう。
■OS Design から作った SDK との関連づけ
マネージドコードのアプリケーションも、ネイティブコードのアプリケーションも、WinCE/WEC のアプリケーションは、Visual Studio の「スマートデバイス」プロジェクトで作成します。それらのアプリケーションをデバッグする場合は、上で紹介したページで説明されている手順で、WinCE/WEC と接続する必要があります。
Visual Studio 2008 日本語版と WEC 7 の場合ですと、以下のような手順になります。
- 上記の5つのファイルを、WEC 7 の /Windows ディレクトリへコピーする。
- WEC 7 を DHCP 有効で動かしている場合は、コマンドプロンプトで ipconfig を実行して、WEC 7 の IP アドレスを調べる。
- Visual Studio 2008 の [ツール] > [オプション...] メニューを選択し、「オプション」ダイアログを開く。
- 「オプション」ダイアログの左端にあるツリービューで、[デバイスツール」を展開し、その下にある「デバイス」を選択する。
- 「オプション」ダイアログの右端に表示された「デバイス」画面にある、「デバイス(V):」というリストボックスから、アプリケーションのビルドに使った SDK のデバイスを選択する。
- 「デバイス(V):」リストボックスの右側にある「プロパティ」ボタンをクリックして、選択したデバイスのプロパティダイアログを開く。
- プロパティダイアログの「トランスポート(R):」というドロップダウンリストから、「TCP 接続トランスポート」を選択する。
- 「トランスポート(R):」ドロップダウンリストの右側にある「構成(C)...」ボタンをクリックして、「TCP/IP トランスポートの構成」ダイアログを開く。
- 「TCP/IP トランスポートの構成」ダイアログにある二つのラジオボタンのうち、「特定の IP アドレスを使用(S):」を選択する。
- 「特定の IP アドレスを使用(S):」の下にあるエディットフィールド兼ドロップダウンリストに、WEC 7 の IP アドレスを入力して設定する。
- 「TCP/IP トランスポートの構成」ダイアログ、プロパティダイアログ、および「オプション」ダイアログの OK ボタンを順にクリックして、設定内容を確定する。
- WEC 7 で、ConmanClient2.exe と CMaccept.exe を順に実行する。
- Visual Studio 2008 の [ツール] > [デバイスへの接続...] メニューを選択する。
■ターゲットボードとホスト PC を直結する場合
さて、上の手順では、WinCE/WEC を DHCP 有効で動かしている場合(デフォルトでは、DHCP 有効です)、WinCE/WEC を起動するたびに IP アドレスを調べて Visual Studio の「TCP/IP トランスポート構成」の設定に反映しなければいけません。DHCP 有効ではなく、固定 IP アドレスで動かせば、WinCE/WEC を起動するたびに設定する必要がなくなり、少しだけ手間を減らせます。
あるいは、WinCE/WEC とホスト PC をハブ経由で接続せず、クロスの Ethernet ケーブル(*)で直接接続する場合は、DHCP サーバにアクセスできませんから、WinCE/WEC とホスト PC 共に、固定 IP アドレスを割り当てる必要があります。
※最近のネットワークインタフェースは、ストレートの Ethernet ケーブルで直接接続しても、自動認識してくれるものが多くなっています。従って、WinCE/WEC とホスト PC を Ethernet ケーブルで直接接続する際、クロスケーブルがなければ、ストレートケーブルで接続しても大丈夫な場合があります。手元にクロスケーブルがない場合は、試してみて下さい。
なお、Ethernet 経由でリモートデバッグする場合、WinCE/WEC とホスト PC をハブ経由で接続するよりも、直接接続する方が、一般的には通信速度が上がりますので、より快適にデバッグできます。
Visual Studio を動かすホスト PC、つまり、Windows 7 や Vista/WindowsXP に固定 IP アドレスを設定する手順は、皆さんご存じだと思いますので、ここでは、WinCE/WEC に固定 IP アドレスを設定する方法を述べます。
WEC 7 と WinCE 6.0 の IP アドレス設定のレジストリは、リファレンスの以下のページで説明されています:
TCP/IPv4 Configurable Registry Settings (Windows Embedded Compact 7)
http://msdn.microsoft.com/en-us/library/ee494881.aspx
TCP/IPv4 Configurable Registry Settings (Windows Embedded CE 6.0)
http://msdn.microsoft.com/en-US/library/ee494881(v=winembedded.60).aspx
上のページの中ほどにある、”Adapter-specific Values” という表を見て下さい。ここに、”EnableDHCP”, “DefaultGateway”, “IpAddress”, “Subnetmask” が載っています。これらのレジストリ項目を使って、WinCE/WEC に固定 IP アドレスを設定できます。レジストリキーは、これらのページに書かれている通り、
[HKEY_LOCAL_MACHINE\Comm\<アダプタ名>\Parms\Tcpip]
です。たとえば、弊社が提供している Armadillo-400 シリーズ用の BSP であれば、以下の行を .reg ファイルに書いて OS イメージをビルドすると、クラス C の固定 IP アドレス 192.168.0.50 が設定されます:
[HKEY_LOCAL_MACHINE\Comm\FEC1\Parms\Tcpip]
"EnableDHCP"=dword:0
"DefaultGateway"="192.168.0.1"
"IpAddress"="192.168.0.50"
"Subnetmask"="255.255.255.0"
ここまでの設定は、Visual Studio のスマートデバイスプロジェクトのアプリケーションを Ethernet 経由でデバッグするための手順です。次に、今回の本題である、マネージドコード(.NET CF)のアプリケーションをカーネルランドと一緒にデバッグする方法を説明します。
■カーネルデバッガも一緒に動かす方法
今回の冒頭で、「.NET CF のアプリケーションとカーネルランドを一緒にデバッグするとは、アプリケーションデバッガとカーネルデバッガを各々動かすことにより、それぞれをデバッグすること」だと述べました。アプリケーションデバッガとカーネルデバッガを各々動かすというのは、どういうことかと言えば、Visual Studio を二つ起動して、一方でアプリケーションデバッガ、もう一方でカーネルデバッガを動かす、ということです。
以下に、もう少し具体的な手順を述べます。以下のようにすれば、アプリケーションデバッガとカーネルデバッガを各々動かして、.NET CF のアプリケーションとカーネルランドを一緒にデバッグできます。
- Visual Studio を起動して、OS Design のプロジェクトを開く。
- OS Design のプロジェクトをビルドしてできた OS イメージを、ホスト PC と Ethernet 接続したターゲットボードに転送し、カーネルデバッガを有効にして WinCE/WEC を起動する。
- Visual Studio をもう一つ起動して、デバッグしたいアプリケーションのプロジェクトを開く。
- 上で述べた手順を使って、アプリケーションのプロジェクトを開いた Visual Studio から WinCE/WEC に接続する。
- アプリケーションのデバッグを開始する。
このようにすれば、二つ起動した Visual Studio のうち、OS Design のプロジェクトを開いた方でカーネルデバッガが動き、アプリケーションのプロジェクトを開いた方でアプリケーションデバッガが動きます。なお、アプリケーションデバッガで設定したブレークポイントでアプリケーションが停止した場合、OS(WinCE/WEC)は動作し続けていますが、カーネルデバッガで設定したブレークポイントで停止した場合は、OS 全体が停止しますので、アプリケーションの動作も停止します。これは、アプリケーションデバッガとカーネルデバッガで違う点です。
以上で、アプリケーションとカーネルランドを一緒にデバッグする方法の説明は、一応終わりです。ただし、いくつか補足が必要なので、次回に続けます。もし、上の手順を試してみたが、アプリケーションデバッガとカーネルデバッガを一緒に動かせない、という方は、次回の説明もあわせて読んでみて下さい。
2012/04/03
koga
Windows Embedded Compact 7 (WEC 7) で追加になった機能の中に、USB Audio のクラスドライバがあります。このドライバを組み込めば、オーディオ出力のハードウェアがないデバイスでも、USB Audio アダプタを使って音声出力できます。今回は、実際に動作確認してみた結果を紹介します。
■USB Audio アダプタ
今回動作確認に使ったのは、エアリア社製の「響音4」という製品です:
http://www.area-powers.jp/product/usb_product/product/kyo-on/u1sounds4.html
家電量販店の店頭で購入しましたが、¥2,000未満の価格で、お手頃です。購入して、Windows 7 の PC に接続してみたところ、パッケージの箱の説明どおり、付属ドライバのインストール無しで、動作しました。つまり、USB Audio の規格に合致しているので、Windows 7 の標準ドライバで動作します。Windpows 7 のデバイスマネージャでデバイス情報を見たところ、USB の Vendor ID が 0D8C ですから、台湾の C-Media Electronics 社のチップセットを使っているようです:
http://www.cmedia.com.tw/ProductsIndex.aspx
■USB Audio クラスドライバ
WEC 7 の USB Audio クラスドライバは、リファレンスに簡単な説明が載っています。
http://msdn.microsoft.com/en-us/library/gg158818.aspx (USB Host Driver Catalog Items and Sysgen Variables)
上のページを見ると、SYSGEN_USB_AUDIO という Sysgen 変数が定義されると組み込まれ、出力と入力をサポートしているようです。このドライバのソースコードは、WEC 7 のソースツリーで、以下の場所にあります。
%_WINCEROOT%/public/COMMON/oak/drivers/wavedev/wavedev2/usb/
このソースを見ると、wavemain.cpp にある Waveform Audio Driver の関数実装のうち、WAV_IOControl() において、入力デバイスに対するメッセージ(WIDM_*)の応答動作も実装されており、出力と入力をどちらもサポートしていることが分かります。Waveform Audio Driver については、リファレンスをご覧ください:
http://msdn.microsoft.com/en-us/library/ee485907.aspx (Waveform Audio Driver Reference)
■USB Audio 機能の組み込み
USB Audio クラスドライバを OS イメージに組み込むには、Platform Builder のカタログ項目ビュー(Catalog Items View)で、次のカタログ項目を選択して下さい:
<OS Design 名>
Core OS
Device Drivers
USB
USB Host
USB Class Drivers
★ USB Audio Class Driver
USB ポート(USB ホストのポート)を搭載した WEC 7 デバイスであれば、これにより、音声出力できるようになります。USB Audio クラスドライバのレジストリ設定ファイル(usbaudio.reg)を見ると分かりますが、Audio クラスを宣言する全ての USB デバイスに対して、このドライバを USB Host Client Driver として登録する設定になっており、USB Audio デバイスの Vendor ID や Product ID を意識する必要は、ありません。
弊社が BSP を提供している CPU ボードである、アットマークテクノ社の Armadillo-440 で試してみたところ、冒頭で紹介した USB Audio アダプタ(エアリア社の響音4)を使って、ヘッドフォンに音声出力できました。エクスプローラのディレクトリアイコンをダブルクリック(ダブルタップ)した際などに、効果音が出力されます。Armadillo-400 シリーズ用 BSP の、公開している評価版では、Armadillo-440 液晶モデルの音声出力インタフェースをサポートしていませんが、USB Audio アダプタと組み合わせれば、WEC 7 の USB Audio クラスドライバを使って音声出力できるというわけです。
さらに、DirectShow と Windows Music Player、および、MP3 と WMA のコーデックを OS イメージへ組み込むことにより、MP3 や WMA の音楽ファイルを再生できることも確認できました。
以上、WEC 7 で追加された USB Audio クラスドライバの紹介です。興味のある方は、ぜひ試してみて下さい。
2012/02/12
koga
WinCE 6.0/WEC 7 では、通常、OS 全体を ROM イメージファイル(nk.bin)に格納し、Flash メモリなどに書き込んで使います。Flash メモリに書き込んだ ROM イメージファイルは、電源投入後に、ブートローダによって RAM にロードされ、ブートローダがカーネルのスタートアップルーチンへ制御を移すことにより、WinCE/WEC が起動する、というわけです(NOR Flash の場合には、XIP; Execute-In-Place 用の ROM イメージファイルを Flash メモリへ書き込むことにより、イメージファイルを RAM にロードせず、Flash メモリから直接カーネルを実行することも可能です)。
ここで、OS 全体を ROM イメージファイルに格納するということは、デバイスドライバやアプリケーションを修正した場合、その修正を反映するには、ROM イメージファイルを作り直して上書きしなければならない、ということを意味します。ROM イメージファイルのサイズは、コンフィグレーション次第で変わりますが、数十MB、場合によっては 64MB を超えることもあります。PC ならばともかく、一般の組み込み機器では、アップデートのたびに ROM イメージファイル全体を書き換えなければならないというのは、大きなコストだと言えるでしょう。
ROM イメージファイル全体を書き変えずに、アップデートしたいアプリケーション(.exe)やデバイスドライバ(.dll)だけを置き換えることは、できないのでしょうか?それは可能です。アップデートしたいものだけを置き換える、つまり部分アップデートには、二通り、ないしは三通りの方法があります。WinCE 6.0/WEC 7 を動かすデバイスのハードウェア構成の違いや、デバイスのメンテナンス方策、および、カーネル移植レイヤとブートローダのカスタマイズが可能かどうかにより、どの方法が最も適しているかは変わりますので、それぞれについて、順に紹介したいと思います。
今回は、ファイルシステム用の永続記憶域を使った部分アップデートの方法を二通り紹介し、次回は、ROM イメージファイルを複数に分割する方式の部分アップデートの方法を紹介します。
■アップデート対象を OS イメージに含めない方法
部分アップデートの最も簡単な方法は、部分アップデートする対象(アプリケーションなど)を OS イメージ(ROM イメージファイル)に含めず、永続記憶域に配置することです。たとえば、WinCE/WEC に付属しない、そのデバイス専用に開発したアプリケーションなどを、OS イメージには含めずに、ハードディスクや microSD カード、USB メモリなどに格納する、という方策です。
この方法の利点は、次の通りです:
・WinCE/WEC に対するカスタマイズの必要がない。
・OS イメージを配置する記憶域と、部分アップデート対象を配置する記憶域を、それぞれ違うものにできる。
たとえば、NOR Flash と NAND Flash を搭載したデバイスの場合であれば、NOR Flash に OS イメージを配置して、NAND Flash 上に部分アップデート対象を配置する、という方法です。NAND Flash 上にファイルシステムが構築されていれば、この方策を使えます。あるいは、NOR Flash と、microSD/SD スロットや USB ポートを搭載したデバイスであれば、microSD/SD カードや USB メモリを常時装着しておき、それらに部分アップデート対象を配置する、という方法が使えます。
ところで、部分アップデートする対象のアプリケーションや DLL を、Platform Builder の OS Design サブプロジェクトにして開発する場合、それらの .exe や .dll は、Platform Builder で設定すれば、OS イメージに格納されません。従って、ビルドや、カーネルデバッガを使ったデバッグまでは、OS イメージに含めるものと同様、Platform Builder で統合して行うことが可能です。
■永続記憶域上にルートファイルシステムを構築する方法
部分アップデートの二番目に簡単な方法は、永続記憶域上のファイルシステムを、ルートファイルシステムとしてマウントすることです。デフォルトのコンフィグレーションでは、WinCE/WEC は、RAMFS (Object Store) をルートファイルシステムとしてマウントしますが、永続記憶域上のファイルシステムをルートファイルシステムにすることも出来るのです。
ルートファイルシステムの設定は、WinCE 6.0 のリファレンスにある、次のページで説明されています:
Mounting an Installable File System as the Root Directory
http://msdn.microsoft.com/en-us/library/ee490194(v=winembedded.60).aspx
WEC 7 のリファレンスでは、上のページの説明に比べると若干分かりづらいですが、次のページで説明されています:
RAM File System Registry Settings (Windows Embedded Compact 7)
http://msdn.microsoft.com/en-us/library/ee490789.aspx
Mount Settings (Windows Embedded Compact 7)
http://msdn.microsoft.com/en-us/library/ee490773.aspx
WinCE 6.0 の場合、上記のページの説明を見て頂くと分かるように、必要な設定は、レジストリ内容を永続化する場合と一部共通します。レジストリ内容を永続化するための設定と、その仕組みについては、2011/02/15 のエントリ(「レジストリ変更内容の永続化(1/2)」)と、2011/02/21 のエントリ(「レジストリ変更内容の永続化(2/2)」)をご覧ください。
WinCE 6.0 の場合に、永続記憶域をルートファイルシステムとしてマウントするためには、レジストリ内容を永続化する場合の手順に加え、以下の手順が必要です:
・Platform Builder のカタログ項目ビューで、次のカタログ項目を選択する:
コア OS
CEBASE
ファイルシステムおよびデータストア
ファイルシステム – 内部(1つ選択)
★ ROM のみに適用されるファイルシステム
・当該ファイルシステムに対するレジストリ設定で、MountAsRoot の値を 1 に設定する。
2011/02/21 のエントリに書いた例(Armadillo-440 の microSD カードを使う場合)だと、次のように設定して下さい。
[HKEY_LOCAL_MACHINE\System\StorageManager\Profiles\SDMemory\FATFS]
"MountPermanent"=dword:1
"MountAsBootable"=dword:1
"MountAsRoot"=dword:1
この方法の利点は、次の通りです:
・WinCE/WEC のカーネル移植レイヤ(OAL)とブートローダをカスタマイズする必要がない。
・OS イメージに含めたアプリケーションやデバイスドライバも、アップデートすることが可能(ただし、出来ないものもある)。
OS イメージに含めたアプリケーションやデバイスドライバをアップデートできるのは、ルートファイルシステムに配置されたファイルによる、”shadowing” の仕組みがあるからです。この仕組みは、WinCE 6.0/WEC 7 の、以下のソースファイルで実装されています。
%_WINCEROOT%/private/winceos/COREOS/storage/fsdmgr/virtroot.cpp
%_WINCEROOT%/private/winceos/COREOS/storage/fsdmgr/pathapi.cpp
virtroot.cpp と pathapi.cpp は、FSD Manager (fsdmgr.dll) のソースファイルの一つですが、CreateFile() によるファイルオープンや、Find{First,Next}File() によるファイル走査の際に、ルートファイルシステム配下のパスが指定された場合は、そのファイルがルートファイルシステム上に存在しなければ、ROM イメージのファイルシステムをアクセスするようになっています。この仕組みにより、同じパス名のファイルが、ルートファイルシステムと ROM イメージのファイルシステム(つまり、OS イメージ)の両方に存在すると、ルートファイルシステム上のファイルが優先される、というわけです。これが、shadowing です。
(※ただし、virtroot.cpp と pathapi.cpp による shadowing は、.exe と .dll 以外のファイルに対して行われるようです。これについては、次の項で述べます。)
永続記憶域上のファイルシステムをルートファイルシステムに設定している場合、アップデート手順は、上で述べた一番目の方法と同じくらい簡単です。たとえば、ディスプレイドライバをアップデートしたい場合は、ディスプレイドライバの新しい .dll を /Windows ディレクトリに上書きコピーして、リブートするだけです。/Windows ディレクトリに上書きコピーした .dll は、永続記憶域上のファイルシステムに書き込まれ、リブートした際に、OS イメージ内の古い .dll を隠します(shadowing)。つまり、OS イメージ(ROM イメージファイル)全体を書き換えることなく、目的のファイルだけをアップデートできる、というわけです。
ただし、この方法で全てのデバイスドライバや DLL をアップデートできるわけでは、ありません。アップデートできるのは、ブートの初期フェーズ以降にロードされるデバイスドライバや DLL だけです。たとえば、microSD カード上にルートファイルシステムを構築する場合、SD ホストコントローラのドライバは、この方法ではアップデートできません。SD ホストコントローラのドライバは、ルートファイルシステムをマウントするために必要なので、ルートファイルシステムがマウントされる前にロードされます。従って、たとえルートファイルシステムに、更新した SD ホストドライバの .dll を配置したとしても、shadowing は行われず、OS イメージ内の「古い」方の .dll が動作します。
■shadowing について、もう少し
ちなみに、ROM イメージのファイルシステムのドライバは、romfsd.dll です。このドライバのソースは、開示されていません。romfsd.dll は、FSD Manager の初期化処理でロードされ、”ROM” という名前のボリュームとしてマウントされます。FSD Manager が romfsd.dll をロードする処理は、
%_WINCEROOT%/private/winceos/COREOS/storage/fsdmgr/fsdmain.cpp
にある InitializeROMFileSystem() で実装されています。InitializeROMFileSystem() は、同じファイルで実装されている STOREMGR_Initialize() の中で、最後に呼び出される関数です。つまり、WinCE/WEC のストレージ機能の中核を初期化する処理の、最終段階で呼び出される関数です。
romfsd.dll のソースコードが開示されていないので、詳細は不明ですが、おそらく、romfsd.dll は、OS イメージ内の全てのファイルに対するファイルシステムではなく、OS イメージの FILES セクションに収録されたファイルに対するものだと思われます。OS イメージの MODULES セクションに収録されたファイル(.dll および .exe)は、カーネルのローダー機能がアクセスする仕組みになっているようです。カーネルのローダー機能のソースは、
%_WINCEROOT%/private/winceos/coreos/nk/kernel/loader.c
です。loader.cpp で実装されている OpenFileFromROM() が、OS イメージ/ROM イメージの MODULES セクションから .exe や .dll をロードする関数です。この関数は、OpenExecutable() から呼び出されるのですが、OpenExecutable() は、 まず OpenFileFromFilesys() を呼び出し、ファイルシステム上にファイルが見つからなければ、OpenFileFromROM() を呼び出します。従って、永続記憶域上のルートファイルシステムに .exe や .dll が存在すれば、そちらが優先されます(shadowing)。
なお、カーネルデバッガで追ってみると、.dll や .exe は、romfsd.dll に対するアクセスでは見つからず、必ず、OpenFileFromROM() が呼ばれるようです。上で、romfsd.dll は、OS イメージの FILES セクションに収録されたファイルに対するものだと思われると書いたのは、この観測結果からの推察です。
■耐障害性の強化に関する考察
さて、永続記憶域上にルートファイルシステムを構築することにより、OAL とブートローダのカスタマイズ無しで部分アップデートできることを説明しました。ただし、ここで一つ問題があります。それは、ルートファイルシステムが破損する可能性です。不意の電源断などにより、ルートファイルシステムが破損してしまうと、WinCE/WEC は、起動できなくなってしまいます。Flash メモリなどに格納された OS イメージが無事でも、永続記憶域上のルートファイルシステムが破損してしまうと起動できない理由については、2011/08/02 のエントリ(「レジストリの永続化~RAM-Based の場合」)をご覧ください。このエントリの、三種類のレジストリ永続化方式の長短を述べた個所で、「(a) Hive-Based のレジストリ」の短所で述べたことが、ここでも当てはまるのです:
短所:レジストリの Hive を配置するファイルシステムを Bootable に設定しなければならないため、そのファイルシステムが破損するなどしてマウントできなくなった場合、ブートの第2フェーズへ遷移できず、第1フェーズの完了待ちで WinCE カーネルの起動が停止してしまう。
この問題を回避する方策としては、次のようなものが考えられます。
・ルートファイルシステム破損時の復旧用に、もう一つ OS イメージを作成し、ブートローダの操作で、復旧用の OS イメージを起動できるようにする。復旧用の OS イメージでは、ルートファイルシステムを永続記憶域に割り当てず、RAMFS (Object Store) をルートファイルシステムに割り当てる。
←復旧用の OS イメージの起動後、破損したファイルシステムをフォーマットするなどの、復旧作業を行えるようにする。
OS イメージを格納するストレージ(Flash メモリやハードディスクなど)の容量が十分大きく、OS イメージを二つ格納できる場合は、この方策が向いているでしょう。
・ファイルシステムを AutoLoad 可能な永続記憶域上に、ルートファイルシステムを割り当て、かつ、ルートファイルシステムを Bootable に設定しない。ルートファイルシステムとして正しく動作できるように、ブートの第1フェーズでファイルシステムをロードさせる。ルートファイルシステムを Bootable に設定しないので、RAMFS (Object Store) が Bootable なファイルシステムとしてマウントされる結果、永続記憶域上のルートファイルシステムをマウントできなくても、WinCE/WEC が起動する。
←この方策は、microSD/SD カードや USB メモリに対しては、使えません。使えない理由は、2011/02/21 のエントリ(「レジストリ変更内容の永続化(2/2)」)をご覧ください。SD メモリカードのドライバや USB メモリカードのドライバは、クライアントドライバなので、ロードするブートフェーズを明示的に指定できないのです。ファイルシステムを AutoLoad 可能な場合は、ブートの第1フェーズでロードされるように、レジストリの BootPhase 値を設定しておけば、たとえ RAMFS が Bootable なファイルシステムとしてマウントされても、ブートの第2フェーズへ遷移する前に、ルートファイルシステムがマウントされる筈です。
この方策は、NAND Flash やハードディスクを搭載したデバイスの場合に使えるでしょう。この方策を使った場合、そのままでは、レジストリが永続化されないという問題が残りますが、Hive ではなく、RAM ベースのレジストリにして、ルートファイルシステムを割り当てるのとは異なるストレージ(NOR Flash など)にレジストリを配置する、といった解決策が考えられます。
2012/01/10
koga
前回のエントリで、WinCE/WEC の「リモートディスプレイアプリケーション」のソースディレクトリについて述べました。
%_WINCEROOT%/PUBLIC/COMMON/OAK/DRIVERS/CERDISP/
の配下には、前回のエントリで紹介した CERDisp と CERHost の他に、CERDRV というソースディレクトリがあるのです。今回は、前回触れなかった CERDRV について紹介します。
■CERDRV の組み込み
%_WINCEROOT%/PUBLIC/COMMON/OAK/DRIVERS/CERDISP/ の下にある CERDRV ディレクトリには、ddi_cer という、仮想フレームバッファ機能を提供するディスプレイドライバのソースファイルが収録されています。より正確には、CERDRV ディレクトリは、ddi_cer_lib.lib という static library のソースディレクトリであって、
%_WINCEROOT%/PUBLIC/COMMON/CESYSGEN/makefile
に記述された設定によって、ddi_cer.dll が生成されます。%_WINCEROOT%/PUBLIC/COMMON/ 配下のソースのビルドについては、2011/08/14 のエントリも、ご覧になってみて下さい。
ddi_cer を OS イメージに組み込むには、BSP_DISPLAY_CER という環境変数を設定して下さい。このことは、
%_WINCEROOT%/PUBLIC/COMMON/OAK/FILES/common.bib
に書かれています。common.bib の、CE_MODULES_DISPLAY に対する条件記述は、以下のようになっています。
; @CESYSGEN IF CE_MODULES_DISPLAY
; @CESYSGEN IF CE_MODULES_MULTIMON
multimon.dll $(_FLATRELEASEDIR)\multimon.dll NK SHK
; @CESYSGEN ENDIF CE_MODULES_MULTIMON
IF BSP_NODISPLAY !
;
; MGDI Display drivers
;
; To use driver set this env var
;
; ddi_flat.dll BSP_DISPLAY_FLAT
; ddi_3dr.dll BSP_DISPLAY_SMI3DR
; ddi_ragexl.dll BSP_DISPLAY_RAGEXL
; ddi_cer.dll BSP_DISPLAY_CER
; ddi_nop.dll BSP_DISPLAY_NOP
IF BSP_DISPLAY_NOP
ddi_nop.dll $(_FLATRELEASEDIR)\ddi_nop.dll NK SHK
ENDIF BSP_DISPLAY_NOP
IF BSP_DISPLAY_CER
ddi_cer.dll $(_FLATRELEASEDIR)\ddi_cer.dll NK SHK
ENDIF BSP_DISPLAY_CER
…(途中略)
ENDIF BSP_NODISPLAY !
; @CESYSGEN ENDIF CE_MODULES_DISPLAY
最後の方にある、IF BSP_DISPLAY_CER と ENDIF BSP_DISPLAY_CER で囲われた行を見て下さい。
また、
%_WINCEROOT%/PUBLIC/COMMON/OAK/FILES/common.reg
には、以下の行があります。
IF BSP_DISPLAY_CER
[HKEY_LOCAL_MACHINE\System\GDI\Drivers]
"Display"="ddi_cer.dll"
ENDIF
Platform Builder で環境変数 BSP_DISPLAY_CER を設定することにより、common.bib と common.reg の上記個所が有効になり、ddi_cer.dll が OS イメージに組み込まれる、というわけです。
なお、お使いの BSP にディスプレイドライバが付属している場合には、BSP 付属のディスプレイドライバを無効にして下さい。BSP 付属のディスプレイドライバを無効にする方法ですが、platform.reg の中に、レジストリキー [HKEY_LOCAL_MACHINE\System\GDI\Drivers] に対する設定行があれば、ddi_cer.dll を組み込む時は、その行をコメントアウトしたうえで、環境変数 BSP_DISPLAY_CER を設定して OS イメージをビルドして下さい。
ここで、platform.reg は、BSP 固有のレジストリ設定を記述するファイルであり、
%_WINCEROOT%/PLATFORM/<BSP 名>/FILES/
に配置されています。
■WinCE/WEC のディスプレイドライバ
ddi_cer について、「仮想フレームバッファ機能を提供するディスプレイドライバ」と書きましたが、これについて、もう少しだけ詳しく書きます。
まず、WinCE/WEC のディスプレイドライバの考え方については、WinCE 6.0 のリファレンスの、以下のページが参考になります:
Display Driver Development Concepts
http://msdn.microsoft.com/en-US/library/ee485877(v=WinEmbedded.60).aspx
WEC 7 のリファレンスのディスプレイドライバの章には、このページに相当する、考え方(concept)を説明したページは無いようです:
Display Drivers (Windows Embedded Compact 7)
http://msdn.microsoft.com/en-us/library/gg159569.aspx
WinCE 6.0 と WEC 7 では、ディスプレイドライバのアーキテクチャは変わっていません。ディスプレイドライバの役割は、おおざっぱにいえば、GWES の描画エンジン機能が生成したレンダリング結果を、フレームバッファ上で合成・蓄積し、ディスプレイへ出力することです。そして、仮想フレームバッファ機能というのは、RAM 上にフレームバッファ領域を割り当て、「GWES の描画エンジン機能が生成したレンダリング結果を、フレームバッファ上で合成・蓄積」するだけの機能のことです。
Linux の場合、同様の仮想フレームバッファ機能として、X Window System の Xvfb (X virtual framebuffer) や、Linux framebuffer (fbdev) があるようです:
Xvfb
http://en.wikipedia.org/wiki/Xvfb
Linux framebuffer
http://en.wikipedia.org/wiki/Linux_framebuffer
さて、ddi_cer による仮想フレームバッファ機能と、前回紹介した「リモートディスプレイアプリケーション」(CERDisp と CERHost)を組み合わせれば、ディスプレイを持たない「ヘッドレス」のデバイスで、GUI 画面によるリモート操作を行うことが可能です。ディスプレイ付きのデバイスの場合は、CERDisp と CERHost により、デバイスに接続されたディスプレイと PC のディスプレイの両方に、WinCE の画面が表示されます。これに対し、ヘッドレスのデバイスの場合(または、ディスプレイ付きのデバイスのディスプレイドライバを無効にして、ddi_cer を組み込んだ場合)は、PC のディスプレイにだけ WinCE の画面が表示される、というわけです。
x86 ベースのボード/デバイスを除く、組み込み機器用プロセッサを用いたデバイスの場合、フレームバッファ領域は、専用の VRAM ではなく、RAM 上に確保するのが一般的です。従って、それらのデバイスについて考えると、実は、ddi_cer と通常のディスプレイドライバの違いは、フレームバッファの内容をディスプレイへ出力するかどうかだけなのです。
ただし、ddi_cer には、CERDisp と同じ機能も実装されていますので、フレームバッファ内容をリモート表示するディスプレイドライバ、という見方もできます。WinCE 6.0 のリファレンスでは、ddi_cer は “Windows Embedded CE remote graphics adapter.” と説明されています:
http://msdn.microsoft.com/en-us/library/ee482180(v=winembedded.60).aspx (Common Windows Embedded CE Modules)
ddi_cer に実装されている CERDisp 相当の機能は、レジストリ設定でオフにすることができます。ddi_cer のレジストリ項目については、
%_WINCEROOT%/PUBLIC/COMMON/OAK/DRIVERS/CERDISP/CERDRV/
に入っている cerdrv.reg を見て下さい。cerdrv.reg には、二つのレジストリキーが記述されています。[HKEY_LOCAL_MACHINE\System\GDI\Drivers] と [HKEY_LOCAL_MACHINE\Drivers\Display\DDI_CER] です。このうち、[HKEY_LOCAL_MACHINE\System\GDI\Drivers] の方は、上述した common.reg に記述されているものと同じです。cerdrv.reg の内容は、ビルド時に OS イメージに反映されませんので、[HKEY_LOCAL_MACHINE\Drivers\Display\DDI_CER] キーの内容については、platform.reg か project.reg にコピーするのがよいでしょう。
ddi_cer の、CERDisp 相当の機能をオフにするには、[HKEY_LOCAL_MACHINE\Drivers\Display\DDI_CER] キーの NoServer サブキーを 1 に設定して下さい。ddi_cer は、NoServer の値が 1 に設定されていると、CERDisp と同様の動作を行うサーバースレッドを始動せず、仮想フレームバッファ動作のみを行います。デフォルトでは、ddi_cer の CERDisp 相当の機能が有効なので、CERDisp を実行せず、PC 上で CERHost を実行するだけで接続することができます。NoServer を 1 に設定した場合は、CERDisp も実行する必要があります。
ddi_cer の CERDsip 相当の機能と CERDisp との主な違いは、次の通りです:
・ddi_cer は、ディスプレイドライバとしてフレームバッファを自分が持っているため、画面内容をクライアントへ転送する際は、フレームバッファを直接アクセスする。
CERDisp は、BitBlt() を使って画面内容をキャプチャしたのち、キャプチャした内容をクライアントへ転送する。
・ddi_cer は、カーネルモードドライバなので、TCP/IP 通信処理は、CERDisp の場合とは異なり、カーネルモジュール用の WinSock DLL がロードされる。
ここで、ddi_cer と CERDisp は、WinSock ライブラリとして、どちらも winsock.lib をリンクしています。従って、CERDisp に対しては、winsock.dll がロードされ、ddi_cer に対しては、k.winsock.dll がロードされます。カーネルモジュール用の DLL については、2008/07/22 に書いたエントリ(「カーネルからWinSock」)も、ご覧になってみて下さい。
なお、2007/07/22 のエントリでは、WinSock の DLL は、ws2.dll と k.ws2.dll だと書いており、ddi_cer と CERDisp がロードする DLL とは違っています。実は、winsock.dll(および k.winsock.dll)の方は、古い版の WinSock で、ws2.dll は、WinCE .NET 4.2 から導入された新しい版です。真相は分かりませんが、ddi_cer が k.winsock.dll を使うようになっているのは、2007/07/22 のエントリに書いた、k.ws2.dll に関する制限を回避するためなのかも知れません。
(あるいは、単に、ddi_cer も CERDsip も、WinCE .NET 4.2 より前から付属していて、[k.]ws2.dll を使うように改訂されていない、ということなのかも知れませんが。)
■通常のディスプレイドライバとの比較
興味のある人は、ddi_cer と、WinCE 6.0 のデバイスエミュレータのディスプレイドライバのソースを読み比べてみると、面白いんじゃないかと思います。WinCE 6.0 のデバイスエミュレータは、Samsung の S3C2410 という、ARM9 コアのプロセッサのリファレンスボードをエミュレーションしています。S3C2410 は、LCD コントローラを内蔵していますので、デバイスエミュレータのディスプレイドライバは、S3C2410 の LCD コントローラを制御する処理が実装されています。このドライバのソースコードを収録したディレクトリは、
%_WINCEROOT%/PLATFORM/DEVICEEMULATOR/SRC/DRIVERS/DISPLAY/LCD
です。また、S3C2410 のデータシートは、
http://www.alldatasheet.com/view.jsp?Searchword=s3c2410
などからダウンロードできます。
ddi_cer と、WinCE 6.0 のデバイスエミュレータのディスプレイドライバの大きな違いは、次の通りです:
・ddi_cer は、フレームバッファ領域を確保する際、C++ の new 演算子を使って確保する。従って、連続した物理メモリ領域は、確保しない。
(※より正確には、GPE ライブラリの GPESurf クラスのインスタンスを生成し、GPESurf のコンストラクタによってバッファを確保します。)
デバイスエミュレータのディスプレイドライバは、config.bib の設定により、フレームバッファとして予約した領域を使用する。
・ddi_cer は、ディスプレイデバイスを制御しない。GWES の描画エンジン機能が生成したレンダリング結果を、フレームバッファ上で合成・蓄積するのみ。ただし、レジストリ設定で NoServer が 0 に設定されているか、または何も設定されていない場合は、クライアントに対してフレームバッファ内容を定期的に転送し、「リモートディスプレイ」動作を行う。
デバイスエミュレータのディスプレイドライバは、プロセッサ(S3C2410)の LCD コントローラのレジスタを、カーネル仮想アドレス空間にマップして、必要な制御動作を行う。LCD コントローラの制御動作の一部として、フレームバッファの物理アドレスを、DMA 転送対象領域として LCD コントローラに設定する。
つまり、異なるのは、ディスプレイ出力用のコントローラ(プロセッサ内蔵の LCD コントローラなど)を制御するかどうか、および、フレームバッファ領域として、連続した物理メモリ領域を確保するかどうかです。ディスプレイ出力用のコントローラを制御する、通常のディスプレイドライバの場合には、フレームバッファ全体を一括して DMA 転送できるように、フレームバッファを、連続した物理メモリ領域に確保する必要があります。
なお、デバイスエミュレータのディスプレイドライバでは、フレームバッファの物理アドレスを LCD コントローラに設定する処理は、行っていません。この処理は、OAL の初期化処理として、以下のソースファイルで実装されています:
%_WINCEROOT%/PLATFORM/DEVICEEMULATOR/src/oal/oallib/init.c
init.c の中にある、OEMInit() から呼び出される InitDisplay() で、LCD コントローラの初期化を行い、その際に、フレームバッファの物理アドレスを設定しています。
LCD コントローラの初期化動作を OAL で行う実装になっているのは、カーネル起動時にスタートアップスクリーンを表示するためのようです。ここで、LCD コントローラに設定するフレームバッファのアドレスは、物理アドレスですが、ディスプレイドライバがフレームバッファ領域としてアクセスするアドレスは、カーネル仮想アドレスであることに注意して下さい。RAM の同じ領域であっても、ハードウェア(LCD コントローラ)は、物理アドレスでしかアクセスできず、逆に、カーネルモジュールは、物理アドレスをカーネル仮想アドレス空間にマップしたカーネル仮想アドレスでしかアクセスできません。
余談になりますが、連続した物理メモリ領域をフレームバッファに割り当てる際に、必ずしも config.bib で予約する必要は、ありません。AllocPhysMem() を使って動的に割り当てて使用することも可能です。ただし、AllocPhysMem() では、割り当て先の物理アドレスを指定できないので、ブートローダや OAL がスタートアップスクリーンを表示する際に使う領域と同じ領域を割り当てることが、できません。従って、フレームバッファとして使用する領域のサイズを、仕様として固定してしまえるのであれば、config.bib の設定で割り当てる方が、どちらかといえば簡単になるでしょう。
2012/01/04
koga
前回 は、OS をカスタマイズせずに、レジストリ設定や API 呼び出しだけでタスクバーの非表示対応(アプリケーションのフルスクリーン表示対応)を行う方法について、説明しました。今回は、タスクバーを表示しないシェルを組み込む方法について説明します。
■Taskman Shell
WinCE 6.0(および WEC 7)には、タスクバーを表示しないカスタムシェルのソースコードが付属しており、WinCE 6.0 のリファレンスでも説明されています:
http://msdn.microsoft.com/en-US/library/ee502244(v=WinEmbedded.60).aspx (Including the Taskman Shell)
標準シェルを使用する必要がなく、専用のフルスクリーン表示アプリケーションを搭載するデバイスを開発する場合には、標準シェルの代わりに、この Taskman Shell を OS イメージに組み込むのが、簡単で良いのではないかと思います。
ただし、上のページに書かれている、Taskman Shell を組み込む方法は、実際には使えません。説明が間違っているように思われます。そこで、Taskman Shell を組み込むやり方について、以下に一例を述べます。
■Taskman Shell の組み込み
上記の、WinCE 6.0 のリファレンスページでは、Taskman Shell を OS イメージに組み込む場合、’__SYSGEN_TASKMAN’ という環境変数の値を 1 に設定すればよいと書かれています。しかし、その設定を行っても、Taskman Shell は OS イメージに組み込まれず、それどころか、Tasklman Shell のビルドすら行われません。WinCE のソースツリー配下のファイルを ‘__SYSGEN_TASKMAN’ で検索しても、この環境変数を参照している個所は見当たりませんし、さらに、Taskman Shell のソースコードの親ディレクトリ(%_WINCEROOT%/Public/Wceshellfe/Oak/)にある dir ファイルを見ると、Taskman Shell のディレクトリ(taskman)が記載されておらず、ビルド対象から外れていることが分かります。
では、どうすれば、OS Design をビルドした時に、Taskman Shell が OS イメージへ組み込まれるようになるのでしょうか?以下に、Taskman Shell を OS イメージへ組み込む手順の一例を述べます。
1.) taskman/ ディレクトリを、ビルド対象に含める。
VS 2005/Platform Builder の「ソリューションエクスプローラー」で、TASKMAN のアイコンを右クリックしてポップアップメニューを開き、[ビルドに含める] を選択します。TASKMAN は、ソリューションエクスプローラーで、次の階層にあります:
C:/WINCE600
PUBLIC
wceshellfe
OAK
TASKMAN ★
注:確認していませんが、この(1)(「ビルドに含める」)は、必要ないかも知れません。
2.) %_WINCEROOT%/PUBLIC/CEBASE/OAK/MISC/ にある wceshellfe.bat を編集して、次の行を追加する:
REM for Taskamn shell
if "%__NO_SYSGEN_TASKMAN%"=="1" goto noTaskmanShell
rem Turn off StandardShell
set __SYSGEN_EXPLORER=
rem add TaskmanShell to WCESHELL module
set WCESHELLFE_MODULES=%WCESHELLFE_MODULES% taskman
:noTaskmanShell
上の行を追加する位置は、wceshellfe.bat の440行付近にある、以下の行の直前にして下さい。
goto :EOF
:Not_Pass1
3.) OS Design をリビルドする。
上記の手順により、OS イメージ(nk.bin)に taskman.exe が追加され、さらに、レジストリの [HKEY_LOCAL_MACHINE\init] キーの Launch50 の値が "taskman.exe" になります。標準シェル(explorer.exe)は OS イメージには収録されず、標準シェルを Taskman Shell が置き換えられます。ここで、標準シェルが OS イメージに収録されないのは、上の (2) で wceshellfe.bat に追加した行の中の
set __SYSGEN_EXPLORER=
という行の働きによるものです。一方、Taskman Shell が OS イメージに収録されるのは、
set WCESHELLFE_MODULES=%WCESHELLFE_MODULES% taskman
という行の働きによるものです。この行の意味については、2011/08/14 に書いた「CESYSGEN 条件文での ‘Component’ 名」を併せ読んでみて下さい。この行の働きにより、taskman がビルドターゲットの依存ファイル/ターゲットの一部となり、ビルドされるようになるというわけです。さらに、 %_WINCEROOT%/Public/Wceshellfe/Oak/FILES/ にある wceshellfe.bib と wceshellfe.reg の中の taskman.exe に関する記述が、上の行の設定により有効になる、というわけです。以下に、wceshellfe.bib と wceshellfe.reg の当該個所を引用します:
wceshellfe.bib
; @CESYSGEN IF WCESHELLFE_MODULES_TASKMAN
taskman.exe $(_FLATRELEASEDIR)\taskman.exe NK SH
; @CESYSGEN ENDIF
wceshellfe.reg
; Choose only *one* of the taskman or explorer components in your cesysgen.bat
; @CESYSGEN IF WCESHELLFE_MODULES_TASKMAN
[HKEY_LOCAL_MACHINE\init]
"Launch50"="taskman.exe"
"Depend50"=hex:14,00, 1e,00
; @CESYSGEN ENDIF
Taskman Shell を OS イメージに収録せず、標準シェルに戻す場合は、%_WINCEROOT%/PUBLIC/CEBASE/OAK/MISC/wceshellfe.bat に追加した行を削って元に戻すか、または、'__NO_SYSGEN_TASKMAN' という環境変数の値を 1 に設定して OS Design をビルドして下さい(*)。
* 上述した例では、'__NO_SYSGEN_TASKMAN' という環境変数を設定しない場合、Taskman Shell が組み込まれます。つまり、デフォルトで Taskman Shell が組み込まれる設定になってしまいます。どちらかといえば、デフォルトでは標準シェルが組み込まれるように設定できるのが良いと思います。つまり、冒頭で述べた WinCE 6.0 のリファレンスページに書かれているように、'__SYSGEN_TASKMAN' という環境変数を 1 に設定した時にだけ、Taskman Shell が組み込まれるようにできる方が良いでしょう。しかし、試してみたところ、wceshellfe.bat に追加する行の中で '__SYSGEN_TASKMAN' という環境変数を参照して条件分岐するようにしても、期待通りに動作しませんでした。簡単に動きを追ってみたところ、Platform Builder の環境変数設定で __SYSGEN_TASKMAN の値を設定しても、wceshellfe.bat の当該個所が実行される時点で、__SYSGEN_TASKMAN の値が空になってしまっていることが原因でした。詳細は不明ですが、WinCE のビルドシステムにおいて、'__SYSGEN' という接頭辞の環境変数に対しては、ビルド処理を開始する前に、その値を空にする内部処理を実行しているのかも知れません。
おそらく、今回述べた手順例よりも、もっと良いやり方があるのではないかと思います。WinCE のビルドシステムに対し、ビルド内容をカスタマイズする「フック」の仕組みが提供されていますから、その仕組みを使うことで、もっとエレガントな対応ができるのかも知れません。興味のある方は、ご自分で調べて、追ってみて下さい。
2011/08/18
koga
WinCE のビルドシステムでは、.bib ファイルや .reg ファイルなどで、@CESYSGEN IF … @CESYSGEN ENDIF という構文を使って、条件指定を記述できます。たとえば、WinCE のカタログ項目で「SD バス ドライバー」が選択されたら、BSP 固有の SD ホストコントローラのドライバが OS Design へ組み込まれるようにするには、BSP の platform.bib において、次のように記述します:
; @CESYSGEN IF CE_MODULES_SDBUS2
<SD ホストドライバ名>.dll $(_FLATRELEASEDIR)\<SD ホストドライバ名>.dll NK SHK
; @CESYSGEN ENDIF CE_MODULES_SDBUS2
ここで、条件指定に使っている ‘CE_MODULES_SDBUS2′ という変数名は、どうやって決まるのでしょうか?カタログ項目「SD バス ドライバー」の Sysgen 変数(システム生成変数)は ‘SYSGEN_SDBUS’ ですから、Sysgen 変数の名前を使っているわけでは、ありません。今回は、この条件指定に使う変数名について調べてみて分かったことを記します。
まず、WinCE のリファレンスの CESYSG 条件文に対する説明は、次のページです:
http://msdn.microsoft.com/en-US/library/ee478869(v=WinEmbedded.60).aspx (Preprocessing Using Cesysgen Conditionals)
このページでは、CESYSGEN 条件文で指定する変数名(’Component’ 名)について、次のように説明しています。
・対応するものが module の場合は、<OS tree name>_MODULES_<module_name>。
・対応するものが component の場合は、<module_name>_<component_name>。
ただし、この説明では、’module’ と ‘component’ について具体的な説明がなく、また、<OS tree name>, <module_name>, <component_name> について、説明されていません。これらについては、%_WINCEROOT%/PUBLIC/ 配下の .bat ファイルに記述された、ビルド用の変数設定を見るのが確実だと思います。
たとえば、カタログ項目「SD バス ドライバー」の例でいえば、プロパティの「モジュール」が sdbus2.dll ですが、’sdbus2′ をキーにして %_WINCEROOT%/PUBLIC/ 配下の .bat ファイルを検索すると、%_WINCEROOT%/PUBLIC/CEBASE/OAK/MISC/winceos.bat がヒットします。ヒットするのは、以下の行です:
set CE_MODULES=%CE_MODULES% sdbus2
ここで、CE_MODULES というビルド変数は、%_WINCEROOT%/PUBLIC/common/CESYSGEN/makefile で参照されており、ターゲット ‘preproc’ の依存ファイルリストの一部として記述されています。つまり、ビルドの preproc ステージで、CE_MODULES にセットされたターゲット群が、依存ファイルとしてビルドされるというわけです。
さて、上で述べた、CESYSGEN 条件文で指定する Component 名の ‘module’/'component’ は、このビルド変数の名前の末尾が ‘_MODULES’ と ‘_COMPONENTS’ のどちらなのか、に対応するようです。sdbus2 の場合は、CE_MODULES というビルド変数の値の一部としてセットされますので、’module’ です。そして、Component 名は、ビルド変数の名前に ‘_’ と、.dll/.exe の名前(つまり、当該 module/component のビルドターゲット名)を大文字にしたものを連結した形式となるようです。sdbus2 の場合は、CE_MODULES_SDBUS2 ですね。
Component 名が ‘component’ の場合の例は、pmif です。pmif は、%_WINCEROOT%/PUBLIC/CEBASE/OAK/MISC/winceos.bat の中で、次のように DEVICE_COMPONENTS というビルド変数の値の一部としてセットされます。
set DEVICE_COMPONENTS=%DEVICE_COMPONENTS% pmif
pmif は、対応するカタログ項目が選択された場合に、DeviceManager (devmgr.dll) にリンクされるライブラリ(pmif.lib)に対するビルドターゲットです。CESYSGEN 条件文の変数名は、Component 名が ‘component’ の場合、ビルド変数の名前から末尾の ‘_COMPONENTS’ を削って、’_’ とビルドターゲット名を大文字にしたものを連結した形式となるようです。つまり、pmif に対しては、DEVICE_PMIF となります。DEVICE_PMIF は、たとえば %_WINCEROOT%/PUBLIC/common/OAK/FILES/common.bib で参照されており、common.bib には次の行があります:
; @CESYSGEN IF DEVICE_PMIF
pm.dll $(_FLATRELEASEDIR)\pm.dll NK SHMK
; @CESYSGEN ENDIF
ちなみに、pmif に対応するカタログ項目は、「電源管理 (完全)」(Sysgen 変数:SYSGEN_PM)と「電源管理 (最小)」(Sysgen 変数:SYSGEN_PMSTUBS)です。これらのカタログ項目が、どちらも選択されていない場合は、pmif.lib が devmgr.dll にリンクされません。pmif が DEVICE_COMPONENTS の値の一部にセットされることにより、devmgr.dll のリンクライブラリに pmif.lib が追加される様子は、%_WINCEROOT%/PUBLIC/common/CESYSGEN/makefile をご覧ください。
さらに補足すると、pmif.lib のソースは、WinCE のカーネルソースの一部となっており、%_WINCEROOT%/PRIVATE/winceos/COREOS/device/pmif/ に配置されています。
さて、CESYSGEN 条件文で指定する Component 名の命名規約の説明における、’module’ と ‘component’ の違いは、上で述べた実例を振り返ると、おそらく、次のような使い分けなのだと思います:
・module
ビルドターゲットが、.dll または .exe、つまり、executable であるもの。
・component
ビルドターゲットが、.lib などの、ビルドの中間生成ファイルであるもの。
また、module については、Platform Builder のカタログ項目ビューで対応する項目のプロパティを見ると、「モジュール」という項目があり、component については、そうではない、ということも言えそうです。
module と component の概念については、WinCE のリファレンスの、次のページにも記述があります:
http://msdn.microsoft.com/en-US/library/ee482113(v=WinEmbedded.60).aspx (Windows Embedded CE Modules and Components)
このページの説明には、component は、module の構成要素であり、module によっては、(コンフィグレーションにより組込むかどうかを切り替えできる)オプションの component を含む、と書かれています。上で述べた解釈は、この説明にも合っていると思います。
蛇足ですが、.bat ファイルにおける、OS Design に module を含める記述の例(ビルド変数 XXX_MODULES の設定例)が、WinCE のリファレンスの次のページに載っています:
http://msdn.microsoft.com/en-US/library/ee478860(v=WinEmbedded.60).aspx (Cesysgen Batch File)
2011/08/14
koga
先日書いた「レジストリの永続化~RAM-Based の場合」では、RAM-Based のレジストリを永続化した場合の注意点として、次のことを書きました:
レジストリの変更内容を永続記憶域に保存するためには、RegFlushKey() の呼び出しが必要です。Hive-Based のレジストリとは異なり、RAM-Based のレジストリでは、RegFlushKey() を呼び出さない限り、レジストリの変更内容は保存されません。
これに加えて、もう一つ注意すべきことがあります。それは、OS イメージを更新する場合です。
OS イメージを更新して、NOR Flash などに新しいイメージを書き込む際、新しいイメージに収録されたレジストリの内容が、置き換え前のイメージに収録されたものと異なっていた場合は、Hive-Based のレジストリと RAM-Based のレジストリで振る舞いが異なります。Hive-Based のレジストリでは、新しいイメージに収録されたレジストリ内容が優先されるのに対し、RAM-Based のレジストリでは、永続記憶域に保存されているレジストリ内容が優先されるのです。
■永続化されたレジストリ内容の扱い方の違い
以下に、OS イメージが置き換えられた際に、永続記憶域に保存されているレジストリ内容がどう扱われるのか、Hive-Based の場合と RAM-Based の場合のそれぞれについて、順に述べます。
(1)Hive-Based の場合
Hive-Based レジストリの場合に、OS イメージが置き換えられた直後のブートにおいて、OS イメージに収録されたレジストリの内容が優先される様子は、リファレンスの次のページの説明を見て下さい:
http://msdn.microsoft.com/en-US/library/ee489764(v=WinEmbedded.60).aspx (Hive-based Registry Initialization)
このページの、Filesys.dll による Hive-Based レジストリの初期化手順の 15 には、次の記述があります:
15. If a clean registry is not required, the system registry hive file (System.hv) is loaded from the file system that contains it. A signature in the system hive file is checked against a signature in the ROM portion of the system registry. If the signatures do not match, a clean registry is required, even if this is not indicated by IOCTL_HAL_GET_HIVE_CLEAN_FLAG.
つまり、永続記憶域に保存されているレジストリファイル(System.hv)の signature と、OS イメージに収録されている System.hv の signature を照合し、signature が等しくない場合は、永続記憶域に保存されているレジストリファイルを捨てて初期化する、というわけです。
(2)RAM-Based の場合
RAM-Based レジストリを永続化した場合に、OS イメージが置き換えられても、永続記憶域に保存されているレジストリ内容が優先される様子は、リファレンスの次のページの説明を見て下さい:
http://msdn.microsoft.com/en-US/library/ee490800(v=WinEmbedded.60).aspx (OAL Initialization of RAM-based Registry)
このページの、OAL での対応により RAM-Based のレジストリを永続化した場合の、Filesys.dll によるレジストリの初期化手順の 2 には、次の記述があります:
2. If the boot process is a cold boot, Filesys.dll performs the following steps:
a. Filesys.dll loads the registry data stored in the Default.fdf file from ROM.
b. If pReadRegistryFromOEM is implemented in the OAL and registry data is available, Filesys.dll deletes the registry data that was restored from Default.fdf. Filesys.dll calls pReadRegistryFromOEM until all of the data is returned. If the data retrieval fails, Filesys.dll cleans the registry and restores Default.fdf.
つまり、OAL が ReadRegistryFromOEM() を実装しており、それを呼び出してレジストリ内容を永続記憶域からロードできた場合は、OS イメージから取り出したレジストリ内容を捨てて、ReadRegistryFromOEM() でロードした内容を使う、ということです。
■RAM-Based レジストリの場合の注意
上で述べたように、RAM-Based のレジストリを永続化した場合は、OS イメージを置き換えても、永続記憶域に保存されたレジストリ内容が優先され、新しい OS イメージに収録されたレジストリ内容は、参照されません。このことは、あるレジストリ設定を変更したくて OS イメージを作りなおした場合に、OS イメージを置き換えても、意図したレジストリ設定の変更が行われない、ということを意味します。作り直した OS イメージに収録したレジストリ内容を有効にするには、永続記憶域に保存されたレジストリを消去して、OS イメージに収録されたレジストリ内容が永続記憶域に保存されるようにしなければいけません。
このことは、OS のバージョンアップを行う際、つまり、WinCE 本体に対する QFE の適用や、あるいは、OS イメージに含める独自デバイスドライバの修正などを行う際に、レジストリ内容の更新が必要な場合に問題となります。そのような場合を考慮して、RAM-Basded レジストリを永続化する対応を行う時は、永続記憶域に保存されたレジストリを消去する機能も実装しておく必要があります。
たとえば、OS イメージの置き換えを行う際にレジストリを消去できるように、イメージ置き換え機能を実装する、という方策が考えられます。あるいは、ブートローダにレジストリ初期化機能を追加し、WinCE カーネルを起動する前にレジストリを消去できるようにする、という方策も考えられます。
2011/08/13
koga
今回は、前回提示したレジストリの設定内容を、レジストリが永続化される仕組みとともに説明します。弊社が提供している WinCE 6.0 の BSP(Lilas-am440-6; Armadillo-440 用の BSP)以外の BSP をお使いの方は、今回の説明を参考にして下さい。
・基本原理
レジストリを永続化する仕組みの基本原理は、次の通りです:
ブートの初期フェーズにおいて、レジストリが初期化される前に、レジストリのファイル(hive)を配置する永続記憶域用のデバイスドライバとファイルシステムをロードして初期化する。その後、レジストリのファイル(hive)が永続記憶域からロードされて、レジストリの初期化が完了する。
これは、前回示したリファレンスのページにも書かれています:
http://msdn.microsoft.com/en-us/library/ee490032.aspx (Implementing the Hive-Based Registry)
以下では、この仕組みを、詳しく見ていきます。
・三つのレジストリファイル
WinCE は、レジストリの配置形式として、RAM-Based と Hive-Based の二種類をサポートしています:
http://msdn.microsoft.com/en-us/library/ee490801.aspx (Registry Types)
前回示した手順では、OS Design で Hive-Based のレジストリを指定しました(カタログ項目の「Hive ベースのレジストリ」を選択)。上のページに書かれているように、WinCE 6.0 では、Hive-Based のレジストリがデフォルトです。上のページの末尾の方にある表で示されていますが、Hive-Based のレジストリでは、レジストリは、次の三つのファイルで構成されます:
- Boot.hv
このファイルは、必ず OS イメージの中にのみ配置され、永続記憶域に配置することは、できません。
- System.hv
- User.hv
これら二つのファイルは、Boot.hv 内での設定により、指定した永続記憶域、つまり OS イメージの外に配置できます。
Boot.hv の内容は、ブートの初期フェーズにのみ参照されます。bootable なファイルシステムがマウントされて、そのファイルシステム上に System.hv (および User.hv)が構築された時点で、破棄されます。その際、Boot.hv の内容に変更が加わっていれば、その変更は、System.hv に引き継がれます。Boot.hv は、ブートの初期フェーズにのみ参照されますので、初期フェーズで必要となる内容だけが収録されます。
Boot.hv の内容と、他の二つのファイルの初期状態の内容は、OS イメージのファイルを Platform Builder で開いてみれば分かります。Platform Builder の [ファイル] → [開く] → [ファイル...] メニューでオープンダイアログを開き、OS イメージのファイル(.bin ファイル)を指定してオープンしてみて下さい。OS イメージの内容を示すツリービューが表示されて、次の階層に、レジストリ内容が表示されます:
<OS イメージのファイル名>(※デフォルトは NK.bin)
NK
* ブートレジストリ
* レジストリ
(すべてのファイル)
「ブートレジストリ」配下が Boot.hv の内容、そして、「レジストリ」配下が System.hv と User.hv の内容です。注意深くみると、「ブートレジストリ」配下の内容と同じ項目が、「レジストリ」配下にもあることに気づくでしょう。これは、OS イメージがビルドされる際に、Boot.hv の内容が System.hv にマージされることによるものです。つまり、Boot.hv の内容は、System.hv の内容のうち、ブートの初期フェーズにだけ必要なサブセットになっているのです。
前回示したレジストリの設定内容は、先頭行が ‘; HIVE BOOT SECTION’ で、末尾行が ‘; END HIVE BOOT SECTION’ でした。.reg ファイルに、この二つの行で囲った記述があると、囲われた部分の内容は、OS イメージがビルドされる際に、Boot.hv に収録されます(結果として、System.hv にも収録されます)。WinCE は、ブートの初期フェーズにおいて Boot.hv を参照し、bootable なファイルシステムをマウントした後、そのファイルシステム上に System.hv を構築します。この時、ファイルシステム上に System.hv が存在していない場合(または、存在していても、その内容が有効でないと判定された場合)は、OS イメージ中の System.hv を、ファイルシステム上の指定されたディレクトリへコピーします。
デフォルトの設定では、RAMFS (Object Store) が bootable なファイルシステムとしてマウントされます。初期状態の RAMFS には、System.hv と User.hv は存在しませんので、コールドブートのたびに、OS イメージ内のものが RAMFS に展開される、というわけです。これが、前回の冒頭で述べた動作です。
参考:Platform Builder の、OS イメージの内容を閲覧する機能は、リファレンスの次のページで説明されています:
http://msdn.microsoft.com/en-us/library/ee482393.aspx (Run-Time Image Viewer)
注意:この節の冒頭で、WinCE は RAM-Based と Hive-Based の二種類のレジストリ配置形式をサポートしていると述べました。RAM-Based のレジストリに関するリファレンスを読むと分かりますが、実は、RAM-Based のレジストリも、永続化することが可能です。ただし、そのためには、BSP のカーネル移植レイヤ(OAL)での対応が必要となります:
http://msdn.microsoft.com/en-us/library/ee489958.aspx (Persisting Data with the RAM-Based Registry)
http://msdn.microsoft.com/en-us/library/ee490769.aspx (Data Persistence with the RAM-based Registry Using the OAL)
弊社が提供している WinCE 6.0 の BSP (Lilas-am440-6) の無償評価版では、現状、RAM-Based のレジストリの永続化には対応していません。
・Storage Manager と Device Manager の起動タイミング
これまでの説明で、「ブートの初期フェーズ」という表現を使いましたが、より正確には、「ブートの第1フェーズ」です。ブートのフェーズには、0, 1, 2 の三つがあります:
http://msdn.microsoft.com/en-us/library/ee490010.aspx (Boot Phases)
フェーズ0は、Boot.hv の内容がロードされる前の状態、フェーズ1は、Boot.hv の内容がロードされて、System.hv と User.hv が構築される前の状態です。System.hv と User.hv が構築されて、レジストリの初期化が完了した後、ブートが完了するまでの間がフェーズ2となります。
さて、前回示したレジストリの設定内容では、先頭の ‘Boot Phases’ 行の直後に、以下の行がありました:
[HKEY_LOCAL_MACHINE\init\BootVars]
"SystemHive"="REG\\system.hv"
"ProfileDir"="REG"
"Flags"=dword:3
"RegistryFlags"=dword:1
この BootVars キーの Flags の値の意味は、前回示したリファレンスのページで説明されています:
http://msdn.microsoft.com/en-us/library/ee490554.aspx (Hive-based Registry Setup)
Flags の値が3というのは、このページの後半にある表で、ビット 0×00000001 と 0×00000002 を共にセットした状態、つまり、Storage Manager と Device Manager を共に、ブートのフェーズ1で起動するという指定です。
BootVars キーの他のレジストリ値についても、上のページで説明されています。SystemHive は、System.hv のファイルパスであり(※この値で、System.hv 以外の名前に設定することが可能です)、ProfileDir は、User.hv を配置するディレクトリです。
(これを書いている時点では、上のページの ProfileDir の説明は、Systgem.hv の配置先と書かれており、正しくないように思われます。)
RegistryFlags については、上のページでは説明されていませんが、この値を1にすると、RegCloseKey() が呼ばれるたびに、レジストリの更新内容が保存されます:
http://msdn.microsoft.com/en-us/library/ee489941.aspx (Flush-On-Close Registry Flushing)
もし、パフォーマンスが問題となる場合には、RegistryFlags を1に設定せず(※デフォルトでは0になります)、必要な時点で、アプリケーションから RegFlushKey() を呼ぶようにすればよいでしょう。
・ストレージ用のデバイスドライバ
冒頭の「基本原理」の節で、”ブートの初期フェーズにおいて、レジストリが初期化される前に、レジストリのファイル(hive)を配置する永続記憶域用のデバイスドライバとファイルシステムをロードして初期化する。”と述べました。前回提示したレジストリ設定内容で、これに対応するのが、’[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\SDBusDriver]‘ 以降の部分です。そこでは、以下の設定を行っています:
- レジストリのファイルを配置する永続記憶域用のデバイスドライバのレジストリ設定を Boot.hv に収録し、かつ、ブートの第1フェーズでロードするよう指定。
- レジストリのファイルを配置するファイルシステムのレジストリ設定を Boot.hv に収録し、かつ、それを bootable に指定。
レジストリ設定を Boot.hv に収録する指定は、設定内容を ‘; HIVE BOOT SECTION’ と ‘; END HIVE BOOT SECTION’ で囲むことによって達成されます。通常、デバイスドライバのレジストリ設定は、System.hv のみに収録されますが、同じ設定内容を ‘; HIVE BOOT SECTION’ と ‘; END HIVE BOOT SECTION’ で囲って追記することにより、Boot.hv にも収録できます。
永続記憶域用のデバイスドライバを、ブートの第1フェーズでロードするのは、各デバイスドライバに対するレジストリの Flags の値で指定しています。具体的には、次の二つのキーの Flags の値に、dword:1000 を指定しています。
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\SDBusDriver]
...
"Flags"=dword:1000
...
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\ESDHC1_MX25]
...
"Flags"=dword:1000
...
これらは、それぞれ WinCE の SD バスドライバと、Armadillo-440 の SD ホストコントローラのドライバに対するキーです。Flags の値の意味は、リファレンスの次のページで説明されています:
http://msdn.microsoft.com/en-us/library/ee484469.aspx (ActivateDeviceEx)
dword:1000 は、DEVFLAGS_BOOTPHASE_1 であり、そのドライバをブートの第1フェーズでロードするという指定です。デフォルト(DEVFLAGS_BOOTPHASE_1 が指定されていない場合)では、たとえそのレジストリキーが Boot.hv に収録されていても、デバイスドライバは、ブートの第2フェーズ、つまり System.hv の構築が完了した後でロードされます。その逆に、Flags に DEVFLAGS_BOOTPHASE_1 が指定されていても、その設定内容が Boot.hv に収録されていなければ、ブートの第2フェーズでロードされます。
さて、前回示したレジストリ設定内容は、Armadillo-440 に装着した microSD カードを永続記憶域として使う設定でしたが、microSD のメモリカード用のドライバは、SD バスドライバと SD ホストコントローラのドライバに加え、SD メモリカードのドライバがあります。このドライバのキーは、
[HKEY_LOCAL_MACHINE\Drivers\SDCARD\ClientDrivers\Class\SDMemory_Class]
ですが、このキーには、Flags が指定されていません。指定してはいけないのです。SD メモリカードのドライバに対して、”Flags”=dword:1000 を指定すると、指定どおり、ブートの第1フェーズで順にロードされますが、それだとエラーが発生し、microSD のメモリカードを認識できなくなります。SD メモリカードのドライバは、SD カードのクライアントドライバ(クラスドライバ)であり、SD ホストコントローラを通じて microSD のメモリカードが認識された後、SD バスドライバによってロードされます。その際、SD メモリカードのドライバに対して “Flags”=dword:1000 が指定されていると、SD バスドライバがロードする時点で、既に Device Manager によって、SD メモリカードのドライバがロードされてしまっているため、ロードに失敗してしまうのです。バスドライバとクライアントドライバの関係は、他のバス、たとえば USB メモリの場合も、同様です。
Device Manager による、ブートの第1フェーズと第2フェーズでのドライバのロードの詳細に興味のある方は、WinCE のカーネルのソースツリーにある、Device Manager のソースをご覧になってみて下さい。以下が、関連するソースファイルです。
- <WINCE600_Root>/PRIVATE/WINCEOS/COREOS/DEVICE/DEVCORE/devcore.c
- <WINCE600_Root>/PRIVATE/WINCEOS/COREOS/DEVICE/DEVCORE/devload.c
devcore.c にある StartDeviceManager() で、ブートの第1フェーズと第2フェーズの各々での、ドライバのロード処理を行っています。devload.c には、第1フェーズでのロードを行う DevloadInit() と、第2フェーズでのロードに使われる InitDevices() の実装があります。また、ActivateDeviceEx() の実体である I_ActivateDeviceEx() も、devload.c で実装されています。
永続記憶域用のデバイスドライバを、ブートの第1フェーズでロードするよう指定する設定についての説明は、以上です。次に、ファイルシステムに対する設定です。ファイルシステムに対する設定は、Storage Manager に対する設定であり、前回示したレジストリ設定内容では、次の二つのキーです。
[HKEY_LOCAL_MACHINE\SYSTEM\StorageManager\Profiles\SDMemory]
"Folder"="SDMemory"
"Name"="SD Memory Card"
"AutoMount"=dword:1
"DefaultFileSystem"="FATFS"
"PartitionDriver"="mspart.dll"
[HKEY_LOCAL_MACHINE\System\StorageManager\Profiles\SDMemory\FATFS]
"MountPermanent"=dword:1
"MountAsBootable"=dword:1
このうち、一番目の SDMemory キーの内容は、もともと System.hv だけに収録される設定になっている内容を、コピーしたものです。Platform Builder で OS イメージのファイルを開き、「レジストリ」の配下からコピーしたいキーのパスを辿り、そのキーを右クリックして、ポップアップメニューの [テキストで表示] を選択すると、テキストエディタ(メモ帳)が開いて、キーの内容が表示されます。その機能を使ってコピーするのが便利でしょう。
二番目の SDMemory\FATFS キーの内容は、ここでは、必要となる追加の設定のみ行っています。これらのキーに対する説明は、リファレンスの次のページをご覧ください:
http://msdn.microsoft.com/en-us/library/ee490773.aspx (Mount Settings)
MountPermanent は、指定しなくても動作します。これは、マウントする際に、リムーバブルかどうかを指定するフラグ値ですが、レジストリファイルを配置しますので、リムーバブルではないことを示す1を指定しています。MountAsBootable は、bootable であることを示す1を指定しています。MountAsBootable については、次の節で、もう少し詳しく述べます。
・MountAsBootable
このフラグ値は、当該ファイルシステムが bootable かどうかを示す値ですが、レジストリファイルを配置するファイルシステム(今回の設定例では、FATFS)に対して、このフラグ値に1を指定するだけでは、期待通りに動作しません。その理由は、デフォルトでは、bootable と指定されたファイルシステムが他にあり、そちらが先にマウントされるためです。レジストリファイルが配置されるファイルシステムは、bootable と指定されたもののうち、最初にマウントされたものになります。
さて、注意深い方は、ここまでの説明の中で、前回示したレジストリ設定のうち、まだ説明されていない行があることにお気づきだと思います。設定内容のうち、二番目に書かれている次のキーです。
[HKEY_LOCAL_MACHINE\System\StorageManager\AutoLoad\ObjectStore]
"MountAsBootable"=dword:0
このキーが、レジストリファイルを配置するファイルシステムに対するキーに加えて、追加で必要な設定です。「三つのレジストリファイル」の節の末尾の方で、”デフォルトの設定では、RAMFS (Object Store) が bootable なファイルシステムとしてマウントされます。”と述べました。その設定を無効にするのが、上の ObjectStore キーに対する設定なのです。この設定により、RAMFS (Object Store) が bootable でないファイルシステムとしてマウントされますので、レジストリファイルを配置したいファイルシステム(今回の設定例では、FATFS)が、bootable なファイルシステムとしてマウントされる最初のものとなり、無事に、レジストリファイルの配置先として選択される、というわけです。
前節で述べた、Device Manager によるブートの第1フェーズでのドライバのロード処理の後、第2フェーズへ遷移する前に、bootable なファイルシステムがマウントされて、レジストリの初期化が完了するのを、Event オブジェクトを用いて待ち合わせる仕組みになっています。このため、第1フェーズから第2フェーズへの遷移は、次のようになります:
0.) Boot.hv での設定により(BootVars キー)、Device Manager と Storage Manager がブートの第1フェーズで(つまり、Boot.hv の内容のロード後に)起動される。
1.) Device Manager が、ブートの第2フェーズへ遷移する前に、Boot.hv で Flags の値の DEVFLAGS_BOOTPHASE_1 ビットが設定されているドライバをロードする。
→今回の設定例では、SD バスドライバと SD ホストコントローラが、ロードされる。
2.) Device Manager は、ブートの第1フェースでのロードを指定されたドライバ群をロードし終えた後、レジストリの初期化が完了するのを待つ。
3.) SD ホストコントローラが microSD カードを検出し、SD バスドライバへ通知することにより、SD バスドライバが(Boot.hv の設定内容により)SD メモリカードのドライバをロードする。
(※もし、デフォルト設定のまま、ObjectStore が bootable に指定されていると、microSD カードが検出される前に、RAMFS (Object Store) がマウントされるので、RAMFS 上に System.hv と User.hv が構築されて、以降の動作を待たずに、Device Manager が第2フェーズへ遷移してしまう。)
4.) SD メモリカードのドライバが、microSD メモリカードのパーティションを検出して、Storage Manager へ通知することにより、microSD メモリカードのファイルシステムがマウントされる。
5.) Storage Manager が(Boot.hv の設定内容により)microSD カードのファイルシステムを bootable としてマウントし、それがレジストリ機能に通知される結果、microSD カード上に System.hv と Use.hv が構築されて、レジストリの初期化が完了する。
6.) レジストリの初期化完了が、Event オブジェクトを通じて、(2) で待ち動作を行っている DeviceManager に通知されて、待ち動作が解除される。
7.) Device Manager は、ブートの第2フェーズへ遷移して、System.hv にレジストリ設定内容が収録されている、残りのデバイスドライバ(※起動時の自動ロードが指定されているもの)をロードする。
bootable なファイルシステムのうち、最初にマウントされたものだけが bootable として記録される様子は、WinCE のカーネルのソースツリーにある、File System Disk Manager (FSDMGR) のソースを見ると分かります。興味のある方は、次のソースをご覧になってみて下さい。
<WINCE600_Root>/PRIVATE/WINCEOS/COREOS/STORAGE/FSDMGR/mounttable.cpp
mounttable.cpp にある MountTable_t::RegisterVolume() で、MountAsBootable の指定により bootable と設定されたファイルシステムのうち、最初にマウントされたものだけを bootable なボリュームとして記録するようになっています。
・まとめ
以上で、Hive ベースのレジストリを永続化する手順の説明は終わりです。まとめとして、以下に、手順をおさらいしてみます。
1.) OS Design に、「Hive ベースのレジストリ」を追加する。
デフォルトでは「Hive ベースのレジストリ」が選択されますが、念のために指定しておくのがよいでしょう。
2.) <WINCE600_Root>/PLATFORM/AM440/FILES/platform.reg の末尾に、’; HIVE BOOT SECTION’ と ‘; END HIVE BOOT SECTION’ の2行を追加して、追加した2行の間に、以下で述べる内容を挿入する。
3.) [HKEY_LOCAL_MACHINE\init\BootVars] キーの設定。
レジストリファイルのパスと、Flags、および、RegistryFlags の設定を追加する。
4.) [HKEY_LOCAL_MACHINE\System\StorageManager\AutoLoad\ObjectStore] キーの設定。
RAMFS (Object Store) が bootable としてマウントされないように、MountAsBootable に0を指定する。
5.) レジストリを配置する永続記憶域のデバイスドライバ(バスドライバとホストコントローラのドライバ、およびクラスドライバ)の設定。
各々、System.hv に収録されている内容をコピーする。バスドライバとホストコントローラのドライバに対しては、ブートの第1フェーズでロードされるように、Flags の値に 0×1000 (DEVFLAGS_BOOTPHASE_1) を指定する。
6.) レジストリを配置する永続記憶域のデバイスに対する StorageManager の Profiles 配下のキーを設定する。
<プロファイル名> キーの内容は、System.hv に収録されている内容をコピーする。<プロファイル名>\<ファイルシステム名> キー(または、<プロファイル名>\<ファイルシステム名>\<パーティション名> キー)に対しては、MountAsBootable を1に設定する(他は省略可能)。
前回の設定例で対象にした、弊社提供の WinCE 6.0 BSP 以外の BSP をお使いの場合や、また、弊社提供の WinCE 6.0 BSP で、microSD カード以外の永続記憶域(USB メモリ)をお使いの場合には、上のまとめを参考にして下さい。永続記憶域のデバイスドライバに対するレジストリキーが分からない場合は、BSP の提供元に問い合わせしてみるのがよいでしょう。
・おまけ
今回の説明で、「ブートの初期フェーズ」という表現を使いましたが、それは主に、ブートのフェーズ1でした。フェーズ0、つまり、Boot.hv がロードされる前の処理、特にカーネル本体の初期化が完了するまでのブートシーケンスについては、全く触れていません。カーネル本体の初期化が完了するまでのシーケンスに興味のある方は、リファレンスの次のページが、取っ掛かりとして参考になるでしょう:
http://msdn.microsoft.com/en-us/library/ee482950.aspx (Nk.exe Boot Process)
ところで、Windows CE 5.0 までは、WinCE はマイクロカーネル構造を採用しており、マイクロカーネル本体と、Device Manager や Storage Manager は、それぞれ異なるプロセスとして動作する仕組みでした(Device Manager は Device.exe のプロセスに所属、Storage Manager は Filesys.exe のプロセスに所属)。Windows Embedded CE 6.0 において、それらは、カーネルにロードされる DLL となりました(Device Manager は Device.dll に所属、Storage Manager は Filesys.dll に所属)。つまり、純粋なマイクロカーネル構造から、モノリシックカーネルに近い構造に変わったのですが、モジュール分割の構造は、大きくは変わっていません。上のページで説明されているのは、Windows Embedded CE 6.0 のカーネル本体の初期化シーケンスですが、これは、Windows CE 5.0 までのマイクロカーネル本体のブートシーケンスに該当するものとなります。
Windows CE 5.0 と Windows Embedded CE 6.0 の違いは、@IT MONOist の次の記事が参考になるでしょう:
http://monoist.atmarkit.co.jp/fembedded/winembedded/ce6_01/ce02.html (「Windows CE 6で刷新されたアーキテクチャ」)
2011/02/21
koga
・はじめに
IP アドレスの設定など、各種設定項目は、WinCE の OS イメージを生成する際に、レジストリに格納されます。デフォルトでは、レジストリは OS イメージ内に配置され、起動時に RAMFS (ObjectStore) に展開されます。このため、コントロールパネルで IP アドレスなどの設定を変更しても、その変更内容は、電源を切った時点で失われてしまいます。つまり、コールドブートに対して、レジストリの変更内容は永続的(persistent)ではありません。なお、WinCE の RAMFS (ObjectStore) について興味のある方は、リファレンスページをご覧ください:
http://msdn.microsoft.com/en-us/library/ee490183.aspx (RAM (Object Store) File System)
さて、レジストリの変更内容を永続的にすることは可能です。レジストリを永続化するには、ハードディスクなどの永続記憶域にファイルとして配置すればよいのです。この手順については、以下のリファレンスページが参考になるでしょう。ただし、以下のページに書かれていることだけでは詳細な手順が分からないため、この後で、具体的な手順を述べます。
http://msdn.microsoft.com/en-us/library/ee490554.aspx (Hive-based Registry Setup)
http://msdn.microsoft.com/en-us/library/ee490032.aspx (Implementing the Hive-Based Registry)
・具体的な手順
レジストリを永続化するための手順の例として、弊社が提供している WinCE 6.0 の BSP(Lilas-am440-6; Armadillo-440 用の BSP)の場合の手順を述べます。BSP に固有な点は明記しますので、他の BSP をお使いの場合でも、参考にして頂けるのではないかと思います。以下が、その手順です。ここでは、永続記憶域として、Armadillo-440 に microSD カードを装着して使います。
1.) OS Design に、「Hive ベースのレジストリ」を追加する。
PlatformBuilder のカタログ項目ビューで、次のカタログ項目を選択して下さい
コア OS
CEBASE
ファイルシステムおよびデータストア
レジストリ記憶域(1つ選択)
* Hive ベースのレジストリ
2.) <WINCE600_Root>/PLATFORM/AM440/FILES/platform.reg の末尾に、次の行を追加する。
以下の内容は、最初の二つのレジストリキーが共通な設定で、残りは、BSP に固有な設定です。BSP に固有な設定は、レジストリのファイル(hive)を配置する永続記憶域として何を使うか、および、使用する永続記憶域のデバイスドライバのレジストリ設定によって変わります。
; HIVE BOOT SECTION
[HKEY_LOCAL_MACHINE\init\BootVars]
"SystemHive"="REG\\system.hv"
"ProfileDir"="REG"
"Flags"=dword:3
"RegistryFlags"=dword:1
[HKEY_LOCAL_MACHINE\System\StorageManager\AutoLoad\ObjectStore]
"MountAsBootable"=dword:0
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\SDBusDriver]
"Dll"="SDBus.dll"
"Flags"=dword:1000
"IClass"=multi_sz:"{20FA98A8-B298-4b32-8D72-C716AEE2FA84}=%b","{6F40791D-300E-44E4-BC38-E0E63CA8375C}=%b"
"Order"=dword:15
"Prefix"="SDC"
"RequestListDepth"=dword:30
"ThreadPriority"=dword:64
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\ESDHC1_MX25]
"DisableDMA"=dword:0
"Dll"="esdhc.dll"
"Flags"=dword:1000
"Index"=dword:1
"MaximumClockFrequency"=dword:3197500
"Order"=dword:21
"Prefix"="SHC"
[HKEY_LOCAL_MACHINE\Drivers\SDCARD\ClientDrivers\Class\SDMemory_Class]
"BlockTransferSize"=dword:40
"Dll"="SDMemory.dll"
"IClass"=multi_sz:"{A4E7EDDA-E575-4252-9D6B-4195D48BB865}","{8DD679CE-8AB4-43c8-A14A-EA4963FAA715}"
"Prefix"="DSK"
"Profile"="SDMemory"
[HKEY_LOCAL_MACHINE\SYSTEM\StorageManager\Profiles\SDMemory]
"Folder"="SDMemory"
"Name"="SD Memory Card"
"AutoMount"=dword:1
"DefaultFileSystem"="FATFS"
"PartitionDriver"="mspart.dll"
[HKEY_LOCAL_MACHINE\System\StorageManager\Profiles\SDMemory\FATFS]
"MountPermanent"=dword:1
"MountAsBootable"=dword:1
; END HIVE BOOT SECTION
3.) OS イメージを作りなおす。
OS Design のソリューションを、再ビルドして下さい。
以上で、レジストリを永続化した OS イメージが出来上がります。実際に動かしてみて、コントロールパネルで IP アドレスの設定を変更してみて下さい。変更した後、電源を入れ直してコールドブートしても、設定内容が失われず、永続化されているはずです。
上で示した、platform.reg で設定する内容については、次回、レジストリが永続化される仕組みとともに説明します。
2011/02/15
koga
Previous Posts