トラップ <CXL00145_AT_nifty.com>
2003.12.24 Create
2003.12.28 Modify
以前、筆者が“ぱたぱたキーボード”をつないだ時は、 特別な専用ソフト(でも無いが)ser_code_ksim を使って実現しました。 でも、Linux には本来、様々な入力機器を扱う仕組みが用意されています。 ・・・と、いうわけで、 今回は input モジュールを使った真っ当な外部キーボード接続を試してみます。
Linux kernel 2.4 以降には、シリアルキーボードや USBキーボード、 マウスやジョイパッドといった、 いわゆる入力デバイスを統一して扱う機構があります。 実際、PC 上の Linux では、ごく普通に USBキーボードや USBマウスを使っていますよね。 更には、Stowaway keyboard や Newton keyboard などのシリアルキーボードに対しても、 カーネルソースへのパッチの形でドライバが存在しています。
では、前回は何故それを使わなかったのか?ですが、実は以下の理由から利用を控えていました。
足りないモジュールはカーネルソースから作成すれば済みますが、 自動でモジュールのロードをしてくれるわけでもなく面倒だなぁ・・・というのが1つ。 そして一番厄介なのは、それらのドライバが想定するキーコードと LinuxZaurus が内部で扱うコードとが全く異なることです。 このため、そのままでは使い物になりません。
こうして、前述の ser_code_ksim の作成に至ったわけですが、 ここに来て、SL-6000へのUSBホスト機能搭載、 humorumさんの Zaurus用REX-CFU1ドライバの配布 により、色々な入力機器を接続できる可能性が出て来ました。 そろそろ input モジュールも食べ頃でしょう。
普段、使う分には気にする必要はないのですが、 まずはキーボードから文字が入力されるまでを、順を追って見ていきたいと思います (話の都合上、PC 上の Linux を例にします)。
所詮、キーボードはスイッチの集まりでしかありません。 人がキーを押すとスイッチが入り、離すと切れるだけの話です。 これを、キーボードに搭載されたマイコンが検知して PC に送信しています。 ちなみに、携帯機器の内蔵キーボードでは、 わざわざ専用マイコンを使うまでもないので、 直接 CPU が I/O ラインをチェックすることが多いようです。
ドライバの役割には、キーボードとの通信と、スキャンコードの処理、 その他細々とした処理があります。 実際には、通信に使う経路によって更に他のモジュール (USBならusbcoreとかその他)が絡んできますので、 ここではスキャンコードの処理に話を絞ります。
さて、キーボードから何が押されてるか(スキャンコード)を得ることができました。 しかし、このコードをそのまま OS に渡すわけにはいきません。 なぜなら、キーボードによってスキャンコードが全然違うからです (同じ Enterキーでも、 US101 Keyboard と stowaway keyboard とではコードが異なります)。 これでは、キーボードを統一して扱うことなんてできませんよね?
そこで、あらかじめ OS の標準コード(linux/input.h で定義されています)を決めておき、 それぞれのキーボード・ドライバで、 独自コードから標準コードへのマッピングを行うことで違いを吸収しています。
ここまでは、キーを単なるスイッチとして捉えているに過ぎません。 しかし実際にキーボードとして使う場合には、SHIFT キーなどの処理も必要ですし、 キーボードに合うように文字の割り当てを調整しなくてはなりません。
PC 上の Linux では、loadkeys を実行して、 指定した配列(jp106だとかus101だとか)になるようキー割り当てを変更できます。 これは、ちょっとした記号の位置変更というだけではなく、 qwerty 配列を dvorak 配列にするような大掛かりな変更も可能です。 これでようやく、意図したとおりの文字を入力できるわけです。
※ X Window System 上でも、XkbDisable 設定で使用することで、 loadkeys で指定したとおりのキー配列で利用できます。
LinuxZaurus でも、キー入力の基本的な流れは一緒です。 input モジュール群をコンパイルして作ってあげれば正常に動作します。 しかし、もともと外部キーボードをつなげない機種なので、 内蔵キーボードに特化されている部分があり、 それが故に困った現象が起きています。
本来であれば内蔵キーボードのスキャンコードを、 linux/input.h に合うようにしていてくれれば ・・・内部で、PC用linuxと同様のキーコードで処理してくれてれば何の面倒もなかったんです。 しかしながら、内蔵キーボード固定であることからか、 そうした変換は行われておらず sharp_keycode.h で定義されたコードを出してきます。
これの何が問題になるかというと、 USBキーボード・ドライバや、前述の Stowaway, Newton ドライバ など、 linuxのカーネルソースに入っているドライバとは “出力されるコードがまるっきり違う”ために、 そのまま組み込んでも、 キートップとは異なる文字がデタラメに入力されて使い物にならないのです(--;。
#実際にはそれだけでなく、押したキーによっては、もっと不可解な挙動を示します。
『それじゃ、loadkeys で外部キーボードに合うようにキーバインドを変更すれば良いのでは?』
確かに loadkeys で割り当てを変更すれば、 (一部の問題を除けば)コンソール画面での問題は解決するでしょう。 しかし、Qt は loadkeys の仕組みを使わず独自にキーの処理を行っているため、 これだけでは GUI画面からは満足に使えないことになってしまいます。
X のように、XkbDisable で loadkeys の仕組みを使う・・・というのであれば楽だったのですが、 どうもそうした手段がないようで・・・うむむ・・・。
※SL-6000, SL-C3000 以降では、/opt/QtPalmtop/etc/keycode.tbl がキー割り当てを替えられますが、 内蔵キーボードを含め、全体に影響するので注意のこと。
とりあえず、次の 3種類のコードがあり、 1. から 2. へはキーボードドライバ側で変換を、 2. から 3. へは loadkeys や Qt の仕組みで変換しているということがわかりました。
まず問題の1つは、1→2 の変換がないためカーネルソースにあるキーボードドライバがそのまま使えないことです。 まぁ、これはドライバ側のどこかのマップテーブルをどうにかすれば何とかなるでしょう。
次の問題は、2→3 の変換でコンソールと Qt の 2種類の異なる方式が存在することです。 このため、キーバインドを調整する場合はその両方に対して行わねばならないのですが・・・先刻書いたとおり、 Qt側を調整する手段がないようで、そのままではGUIでは使えないという悲しいことになってしまいます。
更に細かい事を言えば、外部キーボードと内蔵キーボードのキー配列は異なるので、 仮に外部キーボードに合わせて変更してしまったら、 内蔵キーボードからの入力がめちゃくちゃになってしまうという問題もあります。
とりあえず、次の2つを考えてみました。
前者のメリットは、SHIFT キーやキーリピートの処理など、一切合切を OS にまかせてしまえることです。 しかし、内蔵キーボードがそのまま外に出たようなイメージになるので、 例えば / (スラッシュ) を入力するのに SHIFT + , (カンマ) しなければならないなど、 外部キーボードのメリット(PCと同じように記号を入力できる)を活かせません。 本当に、『内蔵キーボードのキーをそのまま外部に引き出した』ような感じになります。
後者のメリットは、/ は / で押せるようになり、 内蔵キーボードでは入力できない記号も楽に入力できるようになることです。 しかし、1対1のキー対応ではないため、スイッチ的に扱いたい場合(ゲームなど)は使えず、 キーリピートも、SHIFT を含む全ての修飾キー処理も、自前で行わないといけないなど 少々処理が複雑になります。
さて、どうするかなぁ・・・とあれこれ悩みましたが、 後者は ser_code_ksim で既にやっているということもありますし、 他のキーボード・ドライバとの組み合わせも考えて、 まずはお手軽なところで『外部キーボードは、内蔵キーボードと 1対1 対応』の方法を採ることにしました。
# それじゃ使いづらいよ・・という人は、自分で作って公開しましょう :-)
ここまで来たら話は簡単で、変更すべきは input モジュールのソース keybdev.c, input.c です。
keybdev.c は、他のキーボード・ドライバから渡されたコードを適切に処理した上で OS に渡します。 ですので、この中で Linux Zaurus に合うようにテーブル(x86_keycodes[])の内容を書き換えてあげれば良いでしょう。 なお、PC/ATキーボード特有の処理(PrtScとかSysRqとかの)も書かれていますが、 これを残しておくと意図しないコードが送られて困るのでコメントアウトします。
それからもう1つ、input.c の修正です。 実際に使ってみたところ、キーリピートの速度が速すぎて Linux Zaurus 側で処理しきれないため、 REP_DELAY と REP_PERIOD を、それぞれ HZ/2 , HZ/15 に変更しました。
ま、平たく言えばキーマップテーブルを書き換えただけなんですが、 これで input の流れはそのままに何とか使えるようになったはずです。
※仮に、/linux にカーネルソースが展開され、/patch にパッチファイルがある場合の例 # cd /linux # zcat /patch/usbkbd-slc-0.2.patch.gz | patch -p1
drivers/input/input drivers/input/keybdev drivers/usb/hid drivers/usb/usbkbd
Input core support m Input core support m Keyboard support m Mouse support m Joystick support m Event interface support
Linux serial keyboardsから、 patch-ipaq と inputattach.c を入手します。 カーネルに patch-ipaq を当て、必要なモジュール群 (serial, stowaway, keybdev, serport ) と inputattach の実行バイナリを作り、下記のように起動すれば一丁上がり!
・・・のはずだったのですが、 どうも stowaway.c のキーマップがずれているようなのと、 SL-C7x0 のカーネルソースにはそのままパッチが当てられなかったので、 一部キーマップを修正し、別途 Linux Zaurus 用にカーネルパッチを作成してみました。
なお、inputattach については、Visor用のであればそのまま使えたのですが、 Palm用のもの(RTSの制御が必要なもの)では動かないので、最初にちょっとだけRTS制御を入れてみました。 もっとも、これだけだと数分放置するとキーボードがスリープに入るので、 参考リンクのようにハードウェアで RTS の下げ上げを行う回路を挟んだ方が良いとは思います。
※仮に、/linux にカーネルソースが展開され、/patch にパッチファイルがある環境での例 # cd /linux # zcat /patch/stowaway-newton.patch.gz | patch -p1
drivers/char/joystick/serio drivers/char/joystick/serport drivers/char/joystick/newtonkbd drivers/char/joystick/stowaway
Character devices Joysticks m Serial port device support m Serial port input line discipline m Newton Keyboard m Stowaway Keyboard
※↓アーカイブにバイナリも入ってますが、一応コンパイル手順を記しておきます。 # tar xzvf inputattach.tar.gz # arm-linux-gcc -o inputattach inputattach.c # arm-linux-strip inputattach
下記手順に従ってモジュールをロードし、inputattach で割り当ててあげれば使えるはずです。 なお、rmmod する場合は外付けキーボードから実行すると、 Enter が離される前にモジュールがアンロードされてしまい、Enter が押しっぱなしになるため、 内蔵キーボードから行ってください。
# insmod input # insmod serio # insmod serport # insmod stowaway # insmod keybdev # inputattach --stowaway /dev/ttyS0 &
# kill `ps -o pid= -C inputattach` # rmmod keybdev # rmmod stowaway # rmmod serport # rmmod serio # rmmod input
USBホストI/Fとそのドライバについては、 humorumさんによる Zaurus用REX-CFU1ドライバ配布ページ をご参照ください。 humorumさんの偉大なる試み無くしては、USB周辺機器を使うことはできず、この文書も存在しなかったでしょう。
前述のように、そのままではキー割り当てがめちゃめちゃで使い物になりませんので、 input.o と keybdev.o のみパッチを当てています。 パッチの当て方、コンパイルすべきモジュールについては、 上の 『パッチを当てるべきinputモジュール』 をご覧ください。
※input/input.o, input/keybdev.o のみまとめたバイナリ(usbkbd-slc-0.2.tar.gz)を下記に置いておきます。
REX-CFU1 カードが挿されており、USBホスト機能が動いているという前提です。 なお、rmmod する場合は外付けキーボードから実行すると、 Enter が離される前にモジュールがアンロードされてしまい、 Enter が押しっぱなしになるため、 内蔵キーボードから行ってください。
# insmod input # insmod hid # insmod keybdev
※補足: # insmod keybdev debug=1 のようにして起動すると、押したキーのコード情報が dmesg に出力されます。
# rmmod keybdev # rmmod hid # rmmod input
SL-C7x0 のキーボードが日本語配列である為、日本語キーボードの使用を前提に調整しています。
なお、現在の 0.2 では、一部の記号を普通に入力できません。 これらの記号を入力するには、SL-C7x0 の内蔵キーボードと同様に Fn キーとの組み合わせを使ってください。 具体的には、Alt + E で = 、Alt + R で + が入力可能です。 テンキー部は NumLock は効きませんが、ほぼ文字通りの入力ができるはずです。
|
|
|
※Insert, NumLock, その他拡張キーは未割当です。
かくして、stowaway keyboard も USB keyboard も input モジュールで使えるようになりました。 しかし、使ってみて幾つか気になった点があります。
1つは、やはりキーボードの配列問題。 本来の流れからすれば、 キーバインドを調整するのは loadkeys やその他の手段であって、 これはこれで正解ではありますが・・・やっぱり気になりますね。
次に、時折発生する無限リピート問題。
どうも何かのタイミングで、キーを離したことが伝わってない場合があるようです。
筆者自身は、外部キーボードから rmmod keybdev
ドライバで変更したのはキーマップの部分だけなので、パッチのせいというだけじゃないでしょうが、 一応ご注意ください。
以上で、Linux Zaurus でもハード工作なしで手軽に外部キーボードを使えるようになりました。
実際の運用としては多少困る点(CFスロットが潰されてしまうため通信時に使えない等)もあるので、 シリアル接続のキーボードも捨てがたいのですが、手軽に使えるようになったというのはいい事です。
キーバインドの問題もまだ残ってはいますが、 そこはそれ GPL で公開されているのですから好きに直して使ってください ;-)
カーネルモジュールを新規に入れた場合は、root権限で depmod -a を実行しとくのを忘れずに。 モジュールの依存関係を調べてくれますので。