Archive for 2009/07/10

USB キーボードと kbdmouse.dll

WinCE の OS イメージに USB キーボードのサポートを追加する場合、何が必要なのでしょうか?USB ホストコントローラのドライバと、USB HID キーボードのドライバを追加すれば OK、というのは、残念ながら間違いです。

USB マウスの場合は、USB ホストコントローラのドライバと USB HID マウスのドライバを OS イメージに追加すれば使えるようになりますが、キーボードの場合は、若干事情が異なります。その違いは、入力処理の複雑さにおける、マウスとキーボードの違いに由来しています。具体的には、キーボード配列(キーボードレイアウト)に対する処理が必要なために、同じ USB HID ドライバであっても、キーボードの方が、マウスよりもドライバの構成が複雑なのです。

両者の違いを知るには、ソースを見るのが手っ取り早いやり方です。USB HID のマウスとキーボードのドライバは、それぞれ、以下のディレクトリ配下にソースがあります:

 ・USB HID マウス
 <WINCE600>/PUBLIC/COMMON/OAK/DRIVERS/USB/CLASS/HID/CLIENTS/MOUHID/

 ・USB HID キーボード
 <WINCE600>/PUBLIC/COMMON/OAK/DRIVERS/USB/CLASS/HID/CLIENTS/KBDHID/

ドライバのバイナリは、マウスが mouhid.dll、キーボードが kbdhid.dll です。お手元に、USB のマウスとキーボードが使える OS イメージ(nk.bin)があれば、それを Platform Builder で開き、内容を見てみて下さい。mouhid.dll と kbdhid.dll が入っているはずです。

さて、mouhid.dll と kbdhid.dll を確認した際に、注意深い人なら、kbdmouse.dll という DLL があることに気付くでしょう。ファイル名からすると、キーボードとマウスの面倒を見るドライバのようですが、実は、このドライバが、キーボード配列に対する処理を行うドライバなのです。

kbdmouse.dll について見る前に、mouhid.dll と kbdhid.dll のソースに話しを戻しましょう。これら二つのドライバの複雑さの違いは、ソースのサイズを比べてみても分かります。MOUHID/ ディレクトリ配下のソースと KBDHID/ ディレクトリ配下のソースのサイズを比べれば、KBDHID/ ディレクトリ配下のソースの方が大きいですし、バイナリのサイズも、kbdhid.dll の方が mouhid.dll よりも大きくなっています。また、これらのドライバが DLL として export している関数を比べても、kbdhid.dll の方が多いことが分かります。MOUHID/mouhid.def と、KBDHID/kbdhid.def の内容を見比べてみて下さい。

mouhid.def の方は、export するシンボルとして、HID クライアントドライバとしての、次の二つの関数が書かれています:
 HIDDeviceAttach
 HIDDeviceNotifications

一方、kbdhid.def の方には、これら二つに加え、ストリームインタフェースドライバの六つの関数が書かれています:
 HIDDeviceAttach
 HIDDeviceNotifications
 KBD_Init
 KBD_PreDeinit
 KBD_Deinit
 KBD_Open
 KBD_Close
 KBD_IOControl

mouhid.dll と kbdhid.dll は、どちらも内部にスレッドを持ち、自身が制御する USB デバイス(マウスやキーボード)から、インタラプト転送でデータを読み出し、読み出したデータを処理して GWES に通知する、という構造です。全体の構造の大枠は違わないのですが、kbdhid.dll の方が、export している関数の個数を見ても分かるように、より複雑です。

さて、kbdhid.dll が、HID クライアントの関数に加え、ストリームインタフェースドライバの関数を export しているのは、実は、他のドライバから呼び出されるためです。その他のドライバというのが、上で出てきた、kbdmouse.dll なのです。この kbdmouse.dll のソースがどこにあるのかというのは、おそらく、WinCE のソースディレクトリを眺めてみても分からないでしょう。kbdhid.dll や mouhid.dll であれば、DLL の名前と同じディレクトリがありますから、比較的分かりやすいのですが、kbdmouse.dll は、対応するディレクトリが見当たりません。それもそのはず、この DLL は、OS イメージのビルド時に、他の DLL をコピーして作られるのです。

どうやって kbdmouse.dll が生成されるのかは、platform.bib を見て下さい。次のような記述が見つかるでしょう:

; @CESYSGEN IF CE_MODULES_KEYBD || CE_MODULES_POINTER
#if ! (defined BSP_NOKEYBD && defined BSP_NOMOUSE)
#if ! (defined IMGPPC || defined IMGTPC)
IF LOCALE=0411 !
IF LOCALE=0412 !
IF BSP_KEYBD_NOP
; @CESYSGEN IF CE_MODULES_NOPKEYBOARD
kbdmouse.dll        $(_FLATRELEASEDIR)\KbdNopUs.dll         NK SHK
; @CESYSGEN ENDIF CE_MODULES_NOPKEYBOARD
ENDIF   ; BSP_KEYBD_NOP

ENDIF   ; LOCALE != 0412
ENDIF   ; LOCALE != 0411
IF LOCALE=0411
IF BSP_KEYBD_JPN1
IF BSP_KEYBD_NOP
; @CESYSGEN IF CE_MODULES_NOPKEYBOARD
kbdmouse.dll        $(_FLATRELEASEDIR)\KbdNopJpn1.dll       NK SHK
; @CESYSGEN ENDIF CE_MODULES_NOPKEYBOARD
ENDIF   ; BSP_KEYBD_NOP

これは、デバイスエミュレータの platform.bib から抜き出したものですが、カタログ項目の選択により、KbdNopUs.dll や KbdNopJpn1.dll など、複数存在する DLL のうちのどれかをコピーして、kbdmouse.dll が生成されることが分かります。コピー元の DLL は、名前の末尾が ‘Us’ や ‘Jpn1′ など、言語/国を識別する文字列になっていますが、これらの DLL は、キーボードから入力された scan code を virtual key code に変換する処理を実装しています。言語設定ごとのキーボード配列を、個々の DLL に埋め込み、どの DLL を kbdmouse.dll にするかで、キーボード配列を切り替える、というわけです。

キーボード配列に関する仕組みについては、WinCE のリファレンスで、キーボードドライバの “Layout Manager” の項を参照して下さい:
 http://msdn.microsoft.com/en-us/library/aa927162.aspx

キーボードドライバについて、長々と書きましたが、最初の問題に戻りましょう。USB キーボードのサポートを追加するには、USB Host コントローラのドライバと、USB HID キーボードのドライバ(kbdhid.dll)を追加するだけでは不足。では、何が必要か、という問題です。正解は、kbdmouse.dll でした。では、kbdmouse.dll を追加しないと、何が起きるのでしょう?

kbdmouse.dll が OS イメージに含められていないと、USB キーボードを USB ポートに繋いでも、キーボードとして認識されるものの、キーボードをタイプしても、文字入力されません。USB キーボードから scan code が入力されても、kbdmouse.dll が存在しないために virtual key code に変換できず、GWES がキーイベントを発生させることが出来ないからです。

kbdhid.dll のソースを見ると、keybd_event() を呼び出している箇所があるのですが(kbdhid.cpp の KeyboardEvent() です)、keybd_event() の第一引数に渡す virtual key code の値は、MapVirtualKey() を呼び出して scan code から変換したものを使っています。ここで、kbdmouse.dll が存在しないと、MapVirtualKey() が呼び出された際、GWES が virtual key code への変換を行えず、MapVirtualKey() が 0 を返します。この結果、keybd_event() の第一引数が 0、つまり不正な値になるので(※virtual key code の値は、[1..254] の範囲の値でなければいけません)、キーイベントが発生しません。

キーボードドライバ(kbdmouse.dll)による MapVirtualKey() の機能の実装については、WinCE のリファレンスで、キーボードドライバの “Keyboard Driver MDD Functions” の項を参照して下さい。PFN_KEYBD_DRIVER_MAP_VIRTUAL_KEY が、MapVirtualKey() に対して、キーボードドライバの MDD(Model Device Driver) レイヤが実装する関数です:
 http://msdn.microsoft.com/en-us/library/aa927863.aspx

この関数を実装したソースファイルは、
 <WINCE600>/PUBLIC/COMMON/OAK/DRIVERS/KEYBD/LAYMGR/
にある laymgr.cpp です。お手元に、USB キーボードを使える WinCE 6.0 の開発ボードがあれば、カーネルデバッガで追跡してみるのも面白いでしょう。laymgr.cpp で実装されている KeybdDriverMapVirtualKey() に、カーネルデバッガでブレークポイントをセットして、USB キーボードのキーをタイプしてみて下さい。kbdhid.cpp の GenerateKeyInfo() が MapVirtualKey() を呼び出し、その結果、GWES を経由して KeybdDriverMapVirtualKey() が呼び出されるのが分かるでしょう。

ちなみに、KbdNopUs.dll が export するシンボルを定義しているのは、
 <WINCE600>/PUBLIC/COMMON/OAK/DRIVERS/KEYBD/DLL/KBDNOP/
にある kbdnop.def です。このファイルに、 KeybdDriverMapVirtualKey も書かれています。

だいぶ長くなってしまいましたが、最後に、もう一つおまけです。WinCE のリファレンスには、HID キーボードのドライバが、キーボードの LED の点灯制御を行うために必要な手順の説明があります(”Adding Keyboard LED Support to the HID Keyboard Driver”):
 http://msdn.microsoft.com/en-us/library/aa918138.aspx

ターゲットボードにキーボード相当のハードウェアが搭載されておらず、キーボードを使うには USB キーボードを繋ぐしかなく、そして、USB キーボードは一つしか繋がない、という場合には(※一般的には、それが普通でしょう)、KbdNopUs.dll などの、WinCE 付属のキーボードドライバを kbdmouse.dll として OS イメージに組み込めば十分です。

しかし、たとえば、ターゲットボードにキーパッドが搭載されており(※評価ボードの場合だと、そういうことがありますね)、キーパッドでも文字入力できて、Caps Lock キーや Num Lock キー機能も使える場合には、そのボードに USB キーボードを繋げた際、ボード搭載のキーパッドと外付けの USB キーボードとの間で、Caps Lock や Num Lock の状態を同期させなければいけません。そのような場合には、上で説明されている処理を実装する必要がある、というわけです。その場合は、WinCE 付属のキーボードドライバをそのまま使うのではなく、自前で実装しなければいけません。たぶん、
 <WINCE600>/PUBLIC/COMMON/OAK/DRIVERS/KEYBD/LAYMGR/
にある LayoutManager.lib のソースと、
 <WINCE600>/PUBLIC/COMMON/OAK/DRIVERS/KEYBD/HIDIOCTL/
にある hidioctl.{cpp,h} を別の場所にコピーしたうえで、laymgr.cpp を改変して使うことになるでしょう。そして、改変版の LayoutManager.lib を組み込んだ自前のキーボードドライバをビルドして、その DLL を kbdmouse.dll として OS イメージに組み込む、というわけです。

Add comment 2009/07/10 koga


Categories

Links

Posts by Authors

Recent Posts

Calendar

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

Posts by Month

Posts by Category

Meta