もしかしたら開発室附属雑記帳

計算機の明後日を考える「もしかしたら開発室」の雑文(又は下書き)コーナー

技術ネタ

[ リスト | 詳細 ]

記事検索
検索

全3ページ

[1] [2] [3]

[ 次のページ ]

随分前になりますが、Windows8.1で「親指の友」が動かない件(原因と対策)という記事の中で、「Windows 8.1以降では旧来の親指シフトドライバで必要な通知が飛ばなくなった」という趣旨の記述をしていたのですが、別件を発端とする調査の結果、これが一部事実と異なることがわかりました。

既に当時調査を行った機体もWin10化してしまっているので、再度Win8.1で確認し直すことはできないのですが、少なくともWin10(1709)では、以下の挙動が確認できました。

  • x86版かつPS/2接続のキーボードに紐付くドライバ宛には、正しくIME状態の通知が行われるように見える
    これは、Windows付属KB211用ドライバ(親指シフト PS/2 キーボード)のモード同期処理が正しく動作することを意味します(ストアアプリ相手であってもOKでした)。同じ仕組みを利用する初代親指の友ドライバも正しく動作する可能性が高いです。

  • x64版ではIME状態の通知を受け取る部分の処理が消されているらしく、IME状態の通知を行っても何も起こらない
    つまり、Windows付属の親指シフトキーボードドライバ(FMV-KB211用)を入れても、キーボード側の入力モードがずれます。
    調査手法の都合上、そもそも通知を飛ばす機能自体が生きているのかどうかの確認は行えていません。

  • USB接続のキーボードに紐付くドライバ宛には、一切のIME状態通知が行われない。もしくは、キーボードクラスドライバのインスタンス番号が先頭以外のキーボードに紐付くドライバ宛には、一切のIME状態通知が行われない(この調査のみ、Win10 1803 x86)。
    調査手法・手持ち機材の都合上、どちらが正解なのか確認できていません(USB接続 かつ インスタンス番号先頭の場合や、PS/2接続 かつ インスタンス番号が先頭以外の場合が確認できていません)。

  • また、x86版側の挙動については、Win XP(SP3)環境でも相違ないことが確認できました。x64版XPは環境が無い為不明です。XPでの挙動から察するに、8.1でも上記と同様の動作だった可能性が高いと考えられます(勿論、一時的におかしくなっていたのが後に修正された可能性もゼロでは無いですが…)。

    もし、当時その記事(や、その続きの記事)をご覧になり、Win7からのアップグレードを控えられたり、或いはそれを機に(Windows標準ドライバが同じ機能に依存している)KB211等のキーボードを予備役にしてしまった方等がいらっしゃいましたら、大変申し訳ないです。お詫びして訂正します。少なくとも現行Win10のx86版(32bit版)をお使いであれば、PS/2接続など特定の条件下で通知に依存するドライバも正しく動作するようですので、お試しください。

    また、旧Oyayusbyに付属する説明書等には「WindowsはカナロックLEDの制御を行わない」と書いていましたが、これも誤りで、正確には「カナロックLEDの制御自体は今(Win10 1709/1803 x86/x64)でも行われるが、IMEの状態や実際の入力状況とは特に連動しておらず、IME OFF状態でカナロック操作(Ctrl + Shift + カタカナひらがな)を行った時にトグルする以外は挙動不審(IME OFFでLED点灯状態なのに入力は英字…なども普通に起こる)」というものでした。重ねて訂正いたします。尚、これはカナロックLEDが存在すると通知したUSB HIDキーボード(ようはOyayusby)での挙動なので、AXキーボードなど、AT / PS/2接続のものでも同じかどうかは不明です(自分が持つLEDの通知手段が無いので…)。

    以下、前者の件についての詳細を書きます。

    発覚までの経緯と確認内容

    掲示板の方でリクエストがあった、OyayusbyをKB211と組み合わせて使う為のカナロック制御アプリ(Oyayusby Utility)を開発するにあたって実験を行っていた時の話です。グローバルフックの実験が上手くいき、IMEの状態も取れたので、試しにPS/2接続用親指シフトキーボードドライバのモード同期を行う機能を組み込んでテストしようとしていました。

    ここで比較用として、このアプリを走らせない状態での挙動(x86)を確認したところ、意図に反してIME状態の同期が行われてしまったことから、今回の件が発覚しました。具体的には、Windows標準のKB211用親指シフトキーボードドライバ(親指シフト PS/2 キーボード)をインストールし、マウス操作でIMEの入力モードを変化させ(英小⇔かな)、そのモードで正しく入力できるかどうかを確認しました。かなに切り換えると正しく入力できない(対応するキーのJISかなが入力される)挙動を期待していたのですが、実際には正しく追随されてしまった…という訳です。

    原因特定の為に調査を進める過程で、機材の都合からx64版の環境を使用したところ、今度は一切IME状態の同期が行われなかったことから、x64版とx86版で動作が異なることが判明しました。上記実験アプリを使っても同期不可です(この件の詳細は後述します)。本当はJapanistの有無で挙動が異なるかを見ようとしたのですが、こちらは関係無かったようです(2003 / 10共に、あっても無くても挙動は変わりませんでした)。

    さらに、当時記事を書くにあたってテストした環境と近い状態で確認すべく、同一の機体(ASUS T100TA-DK32G。但し、OSはWin8.1ではなく、Win10 1709 x86)にて旧親指の友ドライバのかな入力時同時打鍵が効くか試したところ、こちらは当時と同じで正しく入力できませんでした(つまり、IME状態の通知が来ていない)。当該機体にPS/2ポートは無く、キーボードドックのキーボードはUSB HIDキーボードとして認識されている為、USBキーボード限定で通知が来ていなかった可能性が出てきました。

    そこで、PS/2ポートを持つ機体(Win10 1709 x86)にOyayusby + 初代親指の友ドライバを接続し、初代親指の友ドライバのレイアウトファイルが使用される状況下で同様の調査を行いましたが、やはりUSB HIDキーボード側ではかな入力時の同時打鍵操作が正しく行えませんでした。

    しかし、わざわざIME状態通知を飛ばす側でPS/2キーボードかUSB HIDキーボードかを見て、前者の場合だけ通知を飛ばす…みたいな器用なことをやってるのか甚だ疑問です(できなくはなさそうだが、かなり面倒そうに見える)。特に、T100TA-DK32Gは本体の音量調整ボタンなどもキーボードとして認識されており、キーボードクラスドライバインスタンスはキーボードドック取り付け時点で2つになっています。そこで、キーボードクラスドライバインスタンスの列挙順で先頭のものに決め打ちで通知を飛ばしているのでは? と仮説を立ててみました。キーボードクラスのインスタンス番号とキーボードデバイスの対応は以下のレジストリキー以下で確認できそうです。

    HKLM\SYSTEM\CurrentControlSet\Control\Class\{4d36e96b-e325-11ce-bfc1-08002be10318}

    これまで試して上手くいかなかったキーボードのインスタンス番号はいずれも0001となっており、先頭に他のデバイスが割り当てられている状態でした(PS/2ポートがある機体ではPS/2接続のキーボード。T100TA-DK32Gでは音量調整等のGPIOボタン)。T100TA-DK32G(但し、上述の調査から時間が空いてから実施した為、Win10 1803 x86環境に変更済)で確認する限り、先頭インスタンスのデバイスを削除・再起動することでこの順序の入れ換えができるようでしたので、この操作の後にキーボードドックの初代親指の友ドライバでかな入力が正しく行えるかを見てみました…が、ダメでした。

    改めて確認してみると、

  • 他にもkbdclassのインスタンス列挙に関係してそうなエントリがある

    HKLM\SYSTEM\CurrentControlSet\Services\kbdclass\Enum

    削除・再起動後にここを確認すると、前述のキーと異なり、順序の入れ換えが行えていません(GPIOボタン側が先頭のまま)。
    但し削除後・再起動前に見ると、先頭(0番)にUSBキーボードが来ます(一時的)。

  • 実際にキーボードクラスドライバへ通知を飛ばす際に必要なハンドルを取る手段として、レジストリを参照せずに行う古いやり方がある

    以下のパスをCreateFile()で直接開くことで、該当するキーボードドライバのハンドルを得ることが可能です。

    \\.\Device\KeyboardClass*
    ※(*は0〜)

    削除後・再起動前に、SysinternalsのWinObjで上記オブジェクトが存在するか見ると、KeyboardClass0側が消えていて、KeyboardClass1は残っています。

  • 先の列挙方法も含めた3つとも、列挙される順序は連動して無いように見える
    それぞれ番号がずれる場面があるので(削除後・再起動前)。

  • となっていました。また、GPIOボタン側の削除・再起動を繰り返しても同上でした。PS/2ポートを持つ機体については、そもそもPS/2キーボードを削除したときの挙動が見れなかった為(再起動するまで削除されない・再起動するとPnPで自動的に再インストールされる)、ハンドル側の挙動について上記に当てはまるか不明です。

    上記ハンドルを取る手段については他の方法(Setup API経由での列挙。順序は最初に紹介したレジストリキーと同一でした)よりお手軽ですし、Win2000より前にはSetup APIが存在しなかった為、IME状態通知の歴史(恐らく初期のNTから存在する)を考えると、このオブジェクトを使っている可能性が一番疑わしいのではと考えています。具体的に起こっている状況を想像すると、

    1. システム側のIME状態通知送信部の内部実装が\\.\Device\KeyboardClass0を決め打ちで開いている
    2. KeyboardClass*の割り当て順序にある程度の法則性がある
      例えば、脱着不可なレガシデバイス(PS/2)やルートバスに近い方(GPIOボタン等)から順にハンドル生成される等。

    なんてことになっていて、上記2つより、

    1. PS/2ポートを持つ機体では、このポートが常にKeyboardClass0になっていて、親指シフトキーボード用ドライバの動作に支障がないように働いている
    2. 脱着可能かつUSBポート経由のキーボードドックはGPIOボタンよりオブジェクト生成順序が後ろになり、IME状態通知が来ることはない

    みたいな状況になっている可能性は割と考えられます。このハンドルが具体的にどういう順序で生成されるのか、あるいは現状どのインスタンスと結びついてるかを手軽に見る方法があると良いのですが、前者はざっと調べた限り具体的な情報は見つかりませんでしたし、後者もkd使わないとダメそうなようなので(RS-232Cで2台直結…)、今回これ以上の深追いはあきらめました。

    尚、GPIOボタン側にダミーのフィルタドライバ(IME状態通知が来たときにログ出力等)を仕込めば切り分けは可能ですが、今回はそこまでは実施しませんでした(或いは、キーボードクラスドライバのインスタンスがUSB HIDキーボードしか存在しない機材が手元にあれば良かったのですが…)。

    なんで当時間違えたか

    ひとえに「実験環境の組み方が悪かった(ことに気づかなかった)」という点に尽きます。

    当該Win8.1機(ASUS T100TA-DK32G)は当時の手持ちで唯一となるWin8以降の環境(但し32bit)であり、かつ唯一PS/2ポートを持たない機体でした(これは今でもそうです)。前述の通り、当該機体のUSB HID経由のキーボードドライバには通知が来ないようなので、これを以て「通知が来ないよう変更された」と思ってしまった訳です。

    このドライバは元々PS/2ポートでの使用を前提としていて、複数インスタンスが実行されることは考慮しておらず(一応最低限の行儀は満たしていますので、複数動作させてワークを破壊したりすることは無いです)、この件以前に複数のキーボードクラスが登録された環境での動作や、USBキーボードへのアタッチ動作を確認したことはありませんでした(Oyayusby作る前はUSBキーボードが手元に無かったというのもありますが)。今冷静に考えると「じゃあ真っ先に疑うべきはUSB化 / 複数インスタンス化だったろ」という話なのですが、当時の状況を思い出せる限り確認したり再現実験を行ったりした限り、infの改造でUSBキーボードへのドライバ導入ができることを以て「フィルタドライバから見たキーボードの通信I/Fは同じ」と判断し、IME状態通知が一部のキーボード宛にしか来ないことについては想像が及ばなかったようです…。

    また、残りの環境をWin10化した時点では既にMk-2ドライバしか使用しておらず、旧作ドライバ等のテストも(Win8.1時代の結論より、ハナから使えないと思って)しなかったことや、Win7以前のx64版を常用する機会に恵まれなかったことなど、本件に気づくきっかけを悉く逃していたのも重なってしまいました。

    x64版でIME状態通知を受け付けない件の詳細

    無理やり投げてみても動かなかった…というのは上述の通りですが、じゃあなんで?という話です。

    Windows付属KB211用ドライバで、実際に通知を受けてキーボードへのコマンドを発行しているのは、Windows標準のPS/2ポートドライバ(i8042prt.sys)です。なんでそんなことがわかるのかというと、実は2003 Server頃の古いDDKには、こいつのソースがサンプルとして同封されていた為です(input\pnpi8042として収録)。現在のWDKサンプルには含まれていないようですが、本記事投稿時点ではまだ2003DDKの入手が可能のようですので、見てみたい方はこちらから。

    こいつを見ますと、IME状態通知関連の部分が、軒並み以下のようなifdefで切られています。

    #if defined(_X86_)

    昔読んだときは純粋に「Alpha / MIPS / PowerPC避け」だと解釈したのですが(多分書いた人の意図も同様と思います。i8042prtのPnP対応が行われたのは恐らくWin2000の時で、当時x64版はまだ存在しませんでした)、改めて今見ると、これじゃぁx64版も除外されてしまうのですよねぇ…(x64版は_AMD64_。x86の表記自体はx64を内包する使い方もあるのでマジ紛らわしい…)。

    そもそもx64版とx86版でこの部分の挙動を変える必然性がイマイチ謎ですし(Alpha等の除外については、これらを積んだFMVが無い為…と解釈できます)、x64版にも「親指シフト PS/2 キーボード」のinfエントリやら専用レイアウトファイル(f3ahvoas.dll)やらが含まれているのを考えると、x64版で使えないのは単なるミスで、中の人が誰も気づいてないだけなのでは?…という気がします。

    ついでに言うと、たとえ非IA-32機であってもPS/2ポートを持つ機体ならKB211を差すこともできたでしょうから(しかもこいつはスキャンコード3に対応してたので、これが必要なWSとかでも使えたはずなんですよね)、#if definedで切る必要すらないと思うのですが…やっぱしFMVのオプションという位置づけの対応なんですかねぇ…。

    尚、ソースが見れるのはi8042prt.sysの中身だけなので、送信側がどういう風になっているのかは不明です。現状送信されているのかどうかについても、ダミーのレイアウト / フィルタドライバを差さないと見れないので、こちらでは調べていません。

    あとがき

    思えばこれまでも、Win8対応富士通製ドライバのうち、x86版だけなぜかWin7までのドライバと同じバージョン表記のバイナリ(但しタイムスタンプやサイズは異なる)になっていたり、Win8.1や10にしつこく標準親指シフトドライバが残っているのを確認していたりしたので、そこでちゃんと深追いしておけばもっと早く気づけたのかなという気がしています…。思い込みと切り分けの甘さは今後注意していく所存です。

    しかしながら、このような「割と巨大なブラックボックスを相手にニッチな機能の正確な挙動を調べる」というのを一人の個人だけがやってるのは、どうやっても正確性の面で厳しいのでは? とも感じています。私が用意できる環境には限りがあるので、どうしてもその範囲内で観測できたことしか気づきませんし、その中での調査しかできません。ですので、もし他にも私が思いっきり間違えたことを書いてるような箇所がありましたら、遠慮なくご指摘頂けると幸いです。調査のきっかけは単なる好奇心でだったり必要に迫られたもの(親指絡みではこっちが多いかも)だったりしますが、折角調べたので自分以外の誰かも必要としているなら…と思って掲載しています。間違いがそのままになっているのは不本意ですので、お手元での追試結果が記載内容と違ったりしたら是非教えて頂けると有り難いです。

    開く コメント(2)

    以前、ちょっと実験で、PS/2接続の親指シフトキーボードを接続した環境から別のマシンにリモート接続を行い、そちらで動作するアプリケーション内で親指シフト入力を行えるか試したところ、少し弄る程度で上手くいってしまいました。そのための環境構築手順と、内部動作の詳細について記載してみたいと思います。

    ここでの記載内容は、Windows 10(1607又は1703) + 親指の友 Mk-2ドライバを使って確認した内容です。富士通謹製のキーボードドライバを使う場合も似たような方法でいけるのでは? と踏んでいますが、レイアウトファイルとして指定されるものとは別のDLLが同封されていたりと、動作が私のドライバと異なる可能性もありますので、上手くいかなかった場合はごめんなさい。また、PS/2タイプの親指シフトキーボードをWindows標準の親指シフトキーボードドライバで使う場合は、ここで書かれた内容相当のものがシステム標準で実装されていますので、改めてこの記事の内容を行う必要はありません(ただ、ホスト側・コンソール側のどちらかがWin8以降の場合、モードずれ対策の制御が効くかどうかは不明です)。

    尚、親指の友 Mk-2ドライバは未署名のため、x64環境下で使うのがそれなりに面倒なのですが、ホスト側(サーバ側・リモートでの接続を受け付ける側)をx64版・コンソール側(つまり、操作するキーボードが接続されている側)をx86版のシステムとすると、署名作業を行う必要がなくなります(ホスト側では当該ドライバのカーネルモードモジュールをインストールする必要がない為。レイアウトファイルはユーザモードDLLですので、署名が無くてもx64環境下でロードさせることは可能です)。

    超ざっくりした手順

    1. コンソール側に、使用したいキーボードドライバをインストールし、使用可能な状態にします。
    2. ホスト側に、コンソール側で使用されるものと同じキーボードレイアウトファイル(例えば、親指の友 Mk-2ドライバであれば、kbol_611.dllかkbol_oas.dll)を、ホスト側の%SystemRoot%\System32\以下にコピーします。ホスト側にも同じドライバがインストールされていれば、既にファイルがあるはずですが、そうでない場合(そうしたくない場合)は手動でコピーしてください。
    3. ホスト側の以下のレジストリ値を、使用したいレイアウトファイルの名前に書き換えます。

      HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\KeyboardType Mapping\JPN\00000002
      "00000002"はレイアウトファイルとして親指の友 Mk-2ドライバのものを使う場合です。富士通製ドライバでどの値が来るかは不明ですが、もし左記を試して上手くいかないようであれば、"00020002"、"00000502"、"00020502"辺りを試してみて下さい(値が無い場合は作成)。

    これで導入完了です。

    リモート接続時に行われていること

    リモートデスクトップのプロトコル上、端末間で転送されるキーコードは「コンソール側キーボードドライバから通知されたスキャンコード」です。つまり、スキャンコードを仮想キーコードや対応する文字として解釈する判断は、全てホスト側で行います(一応、コンソール側からUnicodeキャラクタを送ることもできるようなのですが、これ送ったらどういう扱いになるのだろう…。通常はこっちではなく、前者の方が使われているようです)。

    このため、ホスト・コンソール間で使用するキーボードレイアウトの同期が必要です。この情報(キーボードレイアウトの識別子)の転送はセッション開始時に行われます(この辺りの詳しい動作は後述します)。ホスト側はこの情報より、必要なキーボードレイアウトを自身のシステム上で探してロードします。これによって、コンソール側のキーボードドライバが受信・処理したスキャンコードを、ホスト側で正しく扱うことができるようになる…という訳です。

    これらの動作を、親指の友 Mk-2ドライバを使う場合の機能分担として示すと、親指シフトの同時打鍵処理(レイアウトファイルが理解できるキーシーケンスに変換する)やキー置換、零遅延モードの処理などは全てコンソール側で行われます。一方、スキャンコードから仮想キーコードへの変換(PF20や英字といった特殊なキーを含む)や、仮想キーコードから文字への変換(NICOLA配列のカナ等を含む)などはホスト側で処理されます。つまり、時間制約がシビアな処理については全てコンソール側で行われることになります

    このため、表示上のラグは感じるかもしれませんが、誤打鍵などの問題は特に発生しないはずです(普通にローカルで入力する場合と同程度)。また、ドライバの挙動はコンソール側で設定した内容がそのまま使えますので、コンソール側における普段使いの感覚で自然に使用できるのではないかと思います。

    レイアウトファイルが選ばれるまでの挙動

    システムが使用するキーボードレイアウトの情報は、全てレジストリ上に登録されています。この辺りの情報は「入力ロケール」として、一緒に使用するIME等と共にまとめて管理されているようです。

    システムにインストールされている入力ロケールは以下のレジストリキーに登録されています。
    HKEY_CURRENT_USER\Keyboard Layout\Preload\

    ここで指定する値に対応する入力ロケールは、以下のレジストリキーに登録されています。
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\

    但し、新しいI/Fを使うIME(IMM32ではなくTSFを使うもの)の場合は、IMEに関する情報はこの辺りには現れないようです。例えば、Japnist 10とMS-IMEのみがインストールされている環境では、データが"00000411"になっているエントリ1つだけが存在します。"00000411"は前述したKeyboard Layouts\を見て頂くと分かるとおり、日本語入力ロケールに対応しているエントリです(IMEに関する情報を含まない)。

    この入力ロケール毎キー配下に"Layout File"なる値があります。ここが、この入力ロケールで使用されるキーボードレイアウトファイル名です。ところが、試しにKeyboard Layouts\00000411\の値を見て頂くと分かると思いますが、ここには"kbdjpn.dll"が指定されています。お使いのキーボード配列がどうなっていようと、恐らく通常はこのような状態になっているはずです(US配列に設定していようが、富士通製ドライバを導入していようが、親指の友ドライバを使っていようが)。

    実は、kbdjpn.dllには他のキーボードレイアウトを参照して渡す処理が実装されています(一応、自分自身が持つデフォルトの日本語配列を返す機能も持っているようなのですが)。現在の言語が日本語かつリモート接続ではない場合、以下のレジストリエントリで指定されたレイアウトファイルを参照します。
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\i8042prt\Parameters\LayerDriver JPN

    これがかの有名な、日本語ロケールにおけるレイアウトファイルを変更する場合に触るレジストリ値の正体です(よくUS配列を使いたい場合とかに弄られている印象があります)。親指の友 Mk-2ドライバをインストールする際にも、ここを書き換えて同封のレイアウトファイルが読み込まれるようにすることで親指シフト配列を実現しています。

    一方、リモート接続された場合は少し挙動が変わります。リモートデスクトップのプロトコルでは、セッション開始時にコンソール側で使用しているキーボードレイアウトの識別子をホストへ伝達します(Client MCS Connect Initial PDU with GCC Conference Create Request時に送る、TS_UD_CS_COREのkeyboardType / keyboardSubType)。この識別子は、恐らくコンソール側で使用しているキーボードレイアウトファイル内に記載されているdwType / dwSubTypeの値を使うのですが、通常日本語配列のキーボードレイアウトでは、別途コンソール側のレジストリ設定で上書きしているようです。これは以下で指定します。
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\i8042prt\Parameters\OverrideKeyboardType 及び OverrideKeyboardSubtype

    通常の106キーボードであれば0x07(日本語) / 0x02(Microsoft / 106配列)になっているはずです。親指の友 Mk-2ドライバでは0x07 / 0x52(富士通 / FMV親指)にしています…が、レイアウトファイル内のNLS設定にて106キーボードを装うようにフラグを立てている為、Overrideの値をさらに上書きして、対外的には0x07 / 0x02として動作します(ややこしい…)。

    ホスト側では、コンソールから送信されたこのType / SubTypeを用いて、このセッションで使用するキーボードレイアウトファイルを検索します。この為のテーブルもレジストリ上に登録されていまして、以下のキーが対応します。
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\KeyboardType Mapping\JPN (Typeが0x07の場合…日本語なので)

    各値の名前は、SubTypeバイトの上位ニブルのみ4ビット左シフトしたもののようなのですが、テーブルを見ていただければ分かる通り、他にも謎のビットが使われていたりして、正直よくわかりません。唯一Windows SDK付属のkbd.hから伺える変則タイプの挙動として、富士通親指シフトキーボードをリモートデスクトップ経由で使う場合はSubTypeを0x00020002として扱うようです(なのでテーブル上でも、"00020002"のエントリはf3ahvoas.dllというWindows標準親指シフトドライバのレイアウトファイルが指定されています)。

    尚、前述の通り、親指の友 Mk-2ドライバのレイアウトファイルを使う場合は00000002の値を弄ればOKです。

    終わりに

    ひとまずこれで、親指シフトキーボード使用時にもリモート接続を活用しやすくなるのではないかと思います。また、レイアウトファイル周りの解析結果については、他のマイナー配列使いの方にとっても応用が効くかもしれません。

    尚、記載内容の多くは、関連するDLLの解析(主にinput.dll、kbdjpn.dll)や外部I/F操作による値変化の追跡などから得ていますので、将来に渡って通用するかどうかの保証はありません。例によって、記載内容を使用する場合は自己責任の下でお願いします。
    リモートデスクトッププロトコルについてはマトモな資料が存在しますので、詳しく知りたい方はそちらも参照してみてください。

    開く コメント(0)

    USB-DAC for STM32F4 Discovery

    ええと、前回分が長くなりすぎましたので、改めて本題です。

    早速軽いのを作ってみました。USB-DAC for STM32F4 Discoveryです。

    イメージ 1


    というのも、メイン機のSE-90PCIが絶不調(ボード差しているとよく死ぬ)になってしまいまして、急遽代替品が必要になったからです。たまたま、以前勤めていた会社から「自己啓発用」として渡されたボードが余っていたので、こいつを使ってサクっと(はいきませんでしたが…後述)作ってみました。

    一応、このボード上にはDAC(CS43L22)が乗っていまして、こいつを使って音声出力しています。データフォーマットはPCM 44.1KHz 32bit ステレオ固定です(但し、DAC側は24bitまでの対応なので、I2S送信時に最下位8bitは無視されます)。また、ストリームの転送にはアイソクロナス転送の非同期モードを使用します(DACをホストと独立したクロックで動かし、別途フィードバック通知用のエンドポイントを用いてホストからの送信データ長を調整します)。Windows Vista以降であれば標準ドライバで使用できるはずです。

    今回から、ブツのアップ先をgithubにしてみました。また、ファーム書き込みに開発環境が必要なので、生成済のバイナリは用意していません。試してみたい方は"System Workbench for STM32"を導入の上、リポジトリのプロジェクトをインポートしてビルド→デバッグ(書き込み)を行って下さい。他、動作中のLEDに関する説明や、micro-B側からのバスパワー化改造等については、リポジトリの方のreadmeを参照下さい。そもそも英語でまとまった文章を書いたのがほぼ初みたいなものなので、あんまり酷いところや意味不明な文などがありましたらご指摘頂けると幸いです。

    本当はサンプリングレート・ビットレート等を色々選べるようにしようと思ったのですが、チップのデータシートを見れば見るほど微妙な気がしてきたので、この石を使って全力で作るのは止めました。というのも、まずチップ内蔵のUSB Phyがフルスピードまでにしか対応していないため、データレートを上げるのは困難です(1023バイト/msまでしか転送できないため、176.4kHz 24bit ステレオがギリギリアウトとなる)。また、DACへのMCLK出力を有効化すると、一般的な帯域における実サンプリングレートでそこそこの誤差が出ます。CS43L22はMCLKを入力しないと使えないDACのため、止むなく44.1kHzのストリームを約44.108kHzで再生する実装としています。幸いフィードバック通知用のエンドポイントを使っていますので、こんな誤差持ちでもデータバッファが空になったりすることはありません(長時間演奏でもおかしくなることはないはずです)。

    また、プロジェクトの雛型自体はCubeMXで吐かせたものを使っているのですが、USB Audioスタックがそのままだとまるで使い物にならなかったので、それなりに手を加えております。他、USBコアやI2Sドライバにも手を入れています。

    特に、このチップのアイソクロナス転送機能は、送受信開始時に予め「奇数フレーム / 偶数フレームのどちらで転送すべきか」を適切に指定する必要があります。ドライバの素の実装では、送受信開始時点で最後に受信したSOFのフレームIDを見て、次のフレームで正しく転送が行われるようビットを設定しているのですが、そもそも次のフレームで転送が行われるかどうかはホスト次第ですし、再生停止中(ホストがSET_INTERFACEでalt = 0を選択している状態)はOUT転送が全く行われません。さらに、フィードバック用のエンドポイントはディスクリプタに記載された周期(今回は32ms毎)でしかIN転送リクエストが来ませんので、タイミングによってはフィードバック送信が全く行えなったり、初回のデータストリームを取りこぼす可能性があります。

    元のプロトコルスタックではこの辺りに対するケアが全く無く、このままだと全く使い物にならないのですが、データシートをよく見ると「アイソクロナス転送のincomplete割り込み」というのがありまして、SOF〜一定時刻までに予定したアイソクロナス転送が行われなかった場合に割り込みを起こすことができます(但し、素のプロトコルスタックでは、このコールバック関数の呼出パスが突然分断されてたりする謎仕様です)。この辺を補ったところ、EVEN/ODDの指定を間違えた場合にもこの割り込みが来ているようですので、今回はこれを使って転送をリトライするようにしました(この辺の挙動については、CubeMXの生成物に対するものではありませんが、海外フォーラムなどでは詳しい議論が行われていたりします。最初原因がわからずマジで頭抱えてました…先人達に多謝)。

    以上のように、USB-DACという使い方に対しては大変筋が悪い石のような気がしますので、わざわざこのボードを買ってきてまで作るようなもんではないと思います。が、もし手元にSTM32F4 Discoveryが使い道も無く余っているようでしたら、こういった形で復活させてみるのも良いと思います。

    あ、ちなみに音はそんなに悪いもんじゃないですよ。直接ミニフォーンジャックにヘッドホンを差して聞いても、特に目立ったノイズは感じません。自前のアンプ + SX-WD5KTに繋いでみても、今までのSE-90PCI(DACからの直出し)と比べて、一聴してわかるほどの悪化は感じられません(少なくとも、良くなった印象はありません)。まぁこんなもんか…という感じです。

    でもまぁ、そのうちハイスピード対応の石と差動出力対応のDACを使ってちゃんと作りたいですねぇ(秋月のラインナップだとNXPのLPC4337位しか無いなぁ…)。


    もしこのコードをベースに色々弄る場合は、以下にご留意下さい。

  • ディスクリプタを弄る場合、同一VID/PIDのデバイスであればWindowsが初回接続時にキャッシュした値を使ってしまうようです。一旦デバイスマネージャから削除した上で再接続して下さい。
  • ビルドオプションにて、最適化レベルを「デバッグ(-Og)」にしています。-O2(デフォルト)等では、ほぼCubeMX生成直後のレベルでも不審死が相次ぎました…。
  • 開く コメント(0)

    また暫く時間が空いてしまいました。

    この間、7月末に駆け込みでWindows7→10への無償アップグレードを済ませたのですが、Windows 95 日本語β版以来あの操作環境に慣れ切ってきた身としては、ビジュアル面が大きく変わったせいかあんまり表示が頭に入ってこない感じになってしまって、ちょっと使いづらくなってしまいました。

    無理やり以前の環境に近づけようとする場合、例えばExplorerの使い勝手については「秀丸ファイラーClassic」を使えばほぼXP相当にできますし、アイコンデザイン変更に伴う違和感(というか認知の遅れ)は、昔のデザインのものをWin95のインストールディスク等から回収してレジストリ上で差し替えてやることで解消できます(この2つをやるだけでも結構効果的です)。しかし、7まではあったUIのクラシック表示がWindows 8以降は廃止になってしまったため、そのままではこれ以上以前の使い勝手に戻すことができなくなってしまいました。

    どうにかならんものか…と色々調べてみたところ、英語圏のフォーラムではWindows 8登場時からそいういった模索が行われていたようで、少々ダーティですがクラシック表示を可能にする方法が確立されていました。しかし、どうもまだ日本語圏ではそういった情報が見られませんので、自分自身のメモがてら、まとめを書いてみたいと思います。

    尚、日本でもテーマを無理やり繕って「クラシック風」のデザインにする「クリーン」な話はちらほら見かけるのですが、そういったレベルの話ではありませんのでご安心を;-)




    ざっくり要約すると、テーマが有効になっている環境(Win10では必ず有効になります)では、GUIを持つアプリの起動時にwin32k.sysによってUxTheme.dllがユーザモードへ突っ込まれるのですが、この際にWinLogon.exeが作成した"ThemeSection"なる名前を持つハンドルを使って使用するテーマの情報を得るようです。その後、これを元にUxTheme.dllがUIの描画を担います。
    ここで、このハンドルを外部から無理やりクローズすることでテーマの参照ができないようにしてしまうと、UxTheme.dllによる描画を諦め、これまでと同じWin32k.sys内のクラシック表示用ルーチンを動作させることができます(というか、こっちのコードは残っていたんですねぇ…。もしかしたらフェイルセーフ用かもしれませんが)。これを利用することで、無理やりクラシック側のコードしか走らないようにしてしまうのが今回の主旨です。これなら今までと同等のビジュアルになるだけでなく、描画そのものをカーネルモードで行わせることになるので若干のパフォーマンス改善にも繋がるかもしれません。


    では実際にハンドルのクローズを行う方法ですが、コマンドラインからハンドル操作を行うソフトとしてSysInternalsのHandleがありますので、あとはこれを使って上記ハンドルを閉じるバッチファイルでも作ればOKです。

    参照元のページにはCamTron氏が投稿したバッチファイルが貼ってあるのですが、ログインしないと読めないようですので、他の方が転載したと思われる代物(ここ)を元に、正しく動くよう修正したものを以下に置いておきます。

    @echo off
    FOR /F "delims=^T" %%G IN ('Handle ThemeSection') do set output=%%G
    FOR /F "tokens=6" %%G IN ('echo %output%') DO set handleid=%%G
    FOR /F "tokens=3" %%G IN ('echo %output%') DO set pid=%%G
    echo %handleid%
    echo %pid%
    Handle -c %handleid% -p %pid% -y

    このバッチファイルをHandle.exeへパスを通した環境で管理者として実行すると、これ以降に起動したアプリは全てクラシック表示となります。
    ここで行っている操作はサインアウトすれば元に戻りますので、恒久的にクラシック表示にしてしまいたいのであれば、タスクスケジューラを使ってこのバッチファイルをログイン時に実行するよう登録しておくと良いです。尚、「最上位の特権で実行する」のチェックと、バッチファイルのカレントディレクトリは忘れず登録して下さい。


    ちなみに、無理やり動かしているだけあっていくつか不具合がありまして、

  • なぜかメニュー表示がフラットスタイルになります。
  • 前述の方法では、起動タイミングの都合上、Explorerに対してクラシックスタイルを適用することができません。
  • だからといってExplorerを一旦落として再起動させると、クラシックスタイルにはなりますが、タスクバーや右クリックメニュー等の表示が崩壊します(使い物になりません)。
  • コントロールパネルからテーマに関連する設定を開くことができなくなります。例えば「個人設定」や「コンピューターの簡単操作センター」など。
  • なぜかタスクマネージャが即死するようになります。但し、これについては、タスクマネージャをSysInternalsの「Process Explorer」に置き換えるなどしていれば影響を受けません。
  • ストアアプリが上手く動かなくなります(例えば、「設定」であれば、歯車が表示されてから先に進まなくなる)。
    ・ Windows8以降は電卓もストアアプリになってしまったので致命的なのですが、電卓だけなら、MS謹製の「Calculator Plus」を入れれば使い勝手が大きく変わることもなく回避可能です。
  • Win7であればAeroGlassが効いていたであろう領域が半透明になります(Presto時代のOpera(v12.18)のタイトルバー等)。

  • これらの現象が問題となる場合は、タスクスケジューラからバッチのログイン時実行を一時的に消し、一旦サインアウトしてしまえば良いでしょう。

    本当は、別のところで公開されている"ClassicTheme"というソフトを使った方が、上記クラシック表示に伴うトラブルを解決する為の処理を一部やってくれる為便利なのですが、困ったことにWindowsDefenderがマルウェア扱いしてくるので、ソースを読んで何をやっているか理解できる方以外にはおすすめしません。


    一応これでクラシック表示は実現できたのですが、Win10のデフォルトではタイトルバーの幅などがWindows 95以来のデフォルト値より大きく設定されています(テーマ適用時と共通の値が使われますので、そちらに合わせたサイズになっています)。こちらは以下のレジストリキー以下を弄ることで変更可能です。

    HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics

    座標系の単位はいずれもTwip(.netになる前のVB使いだった方にとっては鼻水が出る程懐かしいのでは…)のようでして、96dpiの環境では15で1ピクセルです。参考までに、こちらで設定した値を以下に載せておきます。
    Windows Registry Editor Version 5.00

    [HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics]
    "BorderWidth"="-15"
    "CaptionHeight"="-270"
    "CaptionWidth"="-270"
    "MenuHeight"="-270"
    "MenuWidth"="-270"
    "ScrollHeight"="-240"
    "ScrollWidth"="-240"
    "SmCaptionHeight"="-210"
    "SmCaptionWidth"="-210"
    "PaddedBorderWidth"="0"

    デフォルトへ戻したい場合は以下に設定して下さい。
    Windows Registry Editor Version 5.00

    [HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics]
    "BorderWidth"="-15"
    "CaptionHeight"="-330"
    "CaptionWidth"="-330"
    "MenuHeight"="-285"
    "MenuWidth"="-285"
    "ScrollHeight"="-255"
    "ScrollWidth"="-255"
    "SmCaptionHeight"="-330"
    "SmCaptionWidth"="-330"
    "PaddedBorderWidth"="-60"

    フォントについては同じキー配下にあるCaptionFont、IconFont等を弄れば変更できます。バイナリなので一瞬面食らいますが、GDIで使われるLOGFONT型(Unicode版)のメモリイメージのようです。詳しくはMSDN等を参照下さい。

    色についても、昔と同じレジストリキーから設定できます。お好みのカラーリングを設定してみて下さい。
    HKEY_CURRENT_USER\Control Panel\Colors


    ここまでやると、こんな表示に持っていくことができました。
    イメージ 1


    前述のような不具合もありますので100%実用的かと言えば微妙ですが、上手く切替ながら使えば、違和感を抑えつつ作業効率を上げることが可能ではないかと思います。尚、ここに記載の内容を使った結果、未知の不具合やトラブルに見舞われる可能性もありますので、くれぐれも自己責任の上、参考にして頂ければと思います。

    開く コメント(3)

    開く トラックバック(0)

    事の発端

    学生の頃にH8マイコン基板を使ってFMR(TOWNS)/OASYS用キーボードをPS/2に変換するアダプタを作って以来、そのリニューアル版にあたる「FMR to PS/2 キーボード変換器」に移行しつつも、長らくFMR60KB201(初代FMR-50/60/70系の親指シフトキーボード)とFACOM6140KA1(88年モデルからのOASYS-100系用親指シフトキーボード)をメインキーボードとして使ってきたのですが、一貫して一つの不満がありました。

    「なんとなく反応が鈍い(気がする)」

    全般的にどのキーに対しても言えることではあったのですが、特にカーソルキーを派手に使ったりした場合は露骨で、ギリギリ許容できる範囲でありつつ、確実にやる気を削ぐ絶妙な遅延がずっと気になっていました。

    カーソル操作といえば、FMR系のキーボードはこれらのキー操作に限って、なぜか無駄にインテリジェントな通知をして来ます。こういったことから、変換アダプタの方でキー解析に時間がかかっているせいでトロく感じるのではないか? と考え、今のFMR to PS/2 キーボード変換器に至るまで高速化を目指して改良してきた訳です。

    しかしながら、

  • カーソルキーの解析は200μsもかからず実行できている(実測)
  • キーコード2バイト目受信完了〜PS/2側送信完了までの実測は約2ms程度(PS/2側0xE0プリフィクス付きの場合)

  • という状況まで追い込んだところで件の遅延が体感上改善しないことから、

    「遅延を生み出している張本人はキーボードに乗っているマイコン / 通信プロトコルではなかろうか」

    という疑惑が生まれました。

    そもそも、キーボードからの通信仕様を眺めたり、実際に信号を観察したりすると、

  • ボーレートが9600bpsと、PS/2での最速通信速度(約16.6kbps)の約半分程度
  • 2バイト目のデータを受信しないとキーコードが得られない(1バイト目はフラグ類なので)
  • しかも、1バイト送信毎に約4.5ms程の間隔を空けて送って来る
  • 全キーのマトリクススキャン自体に16msかかっている

  • という素敵な設計になっており、既にこの時点で変換器よりも遥かに大きな遅延が生み出されています。

    でも、TOWNSに繋いで使っているときは、そんなにトロくは感じないんだよなぁ…。
    そもそもシステム全体がトロいから気にならないだけなのか、ソフト側のステップ数やハードの経路段数が少ない分、割り込み応答性がV機よりも良いのか…。

    プロトコルやキーボードマイコンの問題となると、さすがに外からはどうしようもないので途方に暮れていた訳ですが、親指の友 Mk-2ドライバに零遅延モードを追加する実験等を通して、益々キー操作の応答性を意識するようになったところで、さすがになんとかしたいと考えていた矢先、重要な存在を思い出しました。

    FACOM6130KF1

    イメージ 1


    こちらもOASYS-100系の親指シフトキーボードなのですが、FACOM6140KA1よりも前のモデルです。
    神田さんのところのカタログやビジネスOASYSの型番・製造時期を見る限り、86年の100F2や、87年モデルの100HX・100N辺りで使われたのではないかと推測されます。

    何が重要かと言うと、このキーボード、エンコード用のマイコンが乗っておらず、標準ロジックICで組まれた行列選択回路を直接外部から叩けるI/Fになっています。ようは「直接キーマトリクススキャンをしてください」ということですね。もし件の遅延がプロトコルやマイコンのファームに起因しているのであれば、このキーボード用の変換アダプタを作った際には遅延が発生しなくなるはずです。

    ということで、早速作ってみました。「OASYS(13ピン) to PS/2 キーボード変換器」です。

    イメージ 2


    本当は「FACOM6130KF1 to PS/2変換器」とすべきなのですが、OASYS-100系用キーボードのうち、他の型番を持つDIN13ピン(※)接続のものならキーテーブルさえ見直せば使えるんじゃないかと淡い期待を寄せた為、こういうネーミングになりました。尚、キーボード自体にファントムキー対策(ダイオード)が入っていますので、ソフト的な工夫は一切無くともnキーロールオーバ対応です。

    ※FACOM6130KF1入手当時に調べた際、「これはDIN13ピンと呼ぶらしい(呼ばれているらしい)」と判断したため、以降この名前で呼んできたのですが、実はDIN規格とは関係ないコネクタのようで(DIN規格は8ピンまでしかない)、正しくはホシデンの丸型多極コネクタ(13ピン)という代物のようです(コネクタにホシデンのロゴが入ってました…)。
    まぁ確かに外形はDINコネクタと同じ位ですし、一応世間的にもDIN13ピンで通っているようなので以降もこの名前で呼びますが、お買い求めの際等はお間違えの無いようご注意下さい。

    ただ、写真のブツは手持ち部品を使って作った関係上、キーボード基板側のフラットケーブルコネクタ(MIL規格 2.54ピッチ20ピン)から信号線を引き出しており、DIN13コネクタ側の結線については調べてません。ざっと調べてみた限り、こいつのメスコネクタは入手が難しそうだったので(オスの方なら秋葉原のヒロセテクニカルとかにもあったんですが…)、キーボードをOASYS実機に繋ぎかえる必要が無く、かつビジュアルを多少犠牲にしても良ければ、どこでも買えるフラットケーブルコネクタ経由にするのも手かと思います。

    さて、この変換アダプタを使った結果ですが、結論から言うと非常に良好でして、遅延は殆ど感じられず、丸5年を経てやっと普通のキーボード生活に戻れたような感じです。これならキーを押す度にやる気を削がれることもありません。

    以上の結果より、やはり遅延の主たる原因は、キーボードマイコン(か、通信プロトコル)にあったと言えそうです。

    話の続き

    個人的な問題として、とりあえずOASYS配列のキーボードについては、FACOM6140KA1に代わってFACOM6130KF1を使うことで遅延問題は解決しました。
    しかし、FMR系の配列を持つキーボードはいずれもマイコン内蔵になるため、こっちの配列で使えるキーボードが無くなってしまいます…。最悪、パターンカットして元々のキーボードマイコンを切り離し、マトリクススキャン版の自作アダプタを仕込む作戦が考えられますが、我が家ではFM-TOWNSが現役であり、キーボードをこちらに繋いで使う場面があるため、元のマイコンを使えなくするのは抵抗があります…。

    ということで、こんな仕組みを考えてみました。

    パラサイトキーボードマイコン

    ようは、マイコンからマトリクスに対する出力が衝突しなければ(Hi-Z固定にさえなってくれれば)、追加マイコン側からの走査出力が繋がっていても影響はない訳です(逆に、元マイコンに走査させる場合は、追加マイコン側の出力線を入力ポートに設定すればOKです)。

    ということで、2つのキースキャンマイコンを同居させ、接続状況に応じていずれか一方のマイコンだけがマトリクススキャンを行うような構成を組んでみました。元マイコン側が動作すれば今まで通りですし、追加マイコン側が動けば高速なPS/2キーボードに早変わり…という訳です。

    FMR/TOWNSシリーズのキーボードマイコンにはi8049(又はそのCMOS版であるi80C49)が使われており、マトリクススキャン用の線はこれのバスポート(DB0〜6又は7)に繋がっています。このポートは名前から推測できるように、丁度初期出力がHi-Zになっています。よって、追加マイコン側から元マイコンのリセットピンを制御すれば、元マイコンのキースキャンを制御できる…ということになります。

    接続先の判断についても、FMR側ではキーボードからのTx線がオープンコレクタ出力になっており、受信端でプルアップするようになっています。よって、プルアップの有無(予め高抵抗でプルダウンしておき、アイドル時電位のH/L)を判断すれば、FMR側が接続されているのかの判断は可能です。

    以上のことより、起動時にこの信号状態を見て、i8049側のリセットを制御する機能を「OASYS(13ピン) to PS/2 キーボード変換器」に追加してしまえば、当初の目的を達成することができます。というわけで、早速組み込んでみました。

    イメージ 3


    こちらを使ってみた結果ですが、やはり大変良好で、いかにタッチの好みを遅延で相殺していたか思い知りました。先月からブログの更新ペースが上がっているのですが、冗談抜きにこのアダプタを作ったことで遅延問題が解決したことが原因です。だって、よくよく考えたら超好みのキーボードだったはずなのに、今まで家帰ってから全然触る気がしなかったんだもんなぁ。今の反応であれば時間が許す限り触っていたいので、そりゃアウトプットも増えますよね。

    尚、こちらの機能も「OASYS(13ピン) to PS/2 キーボード変換器」の公開版に含まれていますが、あくまでもオマケ機能であり、デフォルトは無効にしています(ソース中のコメントを外してリビルドする必要があります)。また、(完成して暫く使ってから気付いたのですが)PAUSEキーやEXT1キー等が増えているタイプ(FMR60KB211)ではマトリクスの制御線が1本増えているため、これらのキーボードを使う場合はアダプタとの接続やファームの制御を一部変更する必要があります。

    しまいに

    さて、自分としては、大昔にFMR60KB101を触ったことで始まった「キーボード熱」が、紆余曲折を経てやっと満足いく着地点にたどり着いたかな…というところです。我ながら大変長い脱線だったと思いますが、これでようやっと、快適な環境で集中して「作りたいものを作る」作業に戻ることができます。

    まぁまたPS/2が絶滅する頃に一悶着やるのでしょうが…。でも、USB HIDが現役ならOyayusbyとコレをニコイチするだけですから、はっきりいって大した作業じゃないですねぇ。

    ということで、もし期待して頂いていた方が居られたら申し訳ないのですが、今後キーボード周りのハード製作やソフト・資料の更新を行う予定はありません。勿論、(今回のように)突発的に何か思いついて、急遽作ったりチューニングしたりということはあるかもしれませんが、基本的には新規で何か作ったりすることはないとお考えください。
    一応こちらの製作物は全てソースコードを同封してありますので、必要な機能が欲しい方は、是非ご自分で改造するなりして対応して下さい。

    この記事までで、とりあえずブログ用に書こうと思っていたネタも消費したので、今後は電脳文庫プロジェクトの開発を進めつつ、ブログの方にはそっちに関連するネタ(OS設計に関するネタ、関係するハードのネタとか)や作り事全般に関する話を書いていこうかと思います。

    オチ

    ノロいの、キーボードマイコン…。

    …うん、つまらん。

    まぁやる気削ぐって意味では立派な呪いでしたがね。

    開く コメント(2)

    開く トラックバック(0)

    全3ページ

    [1] [2] [3]

    [ 次のページ ]


    .


    プライバシー -  利用規約 -  メディアステートメント -  ガイドライン -  順守事項 -  ご意見・ご要望 -  ヘルプ・お問い合わせ

    Copyright (C) 2019 Yahoo Japan Corporation. All Rights Reserved.

    みんなの更新記事