input モジュールによる外部キーボードの接続


トラップ <CXL00145_AT_nifty.com>
2003.12.24 Create
2003.12.28 Modify


対応確認機種:SL-C7x0/860

はじめに

以前、筆者が“ぱたぱたキーボード”をつないだ時は、 特別な専用ソフト(でも無いが)ser_code_ksim を使って実現しました。 でも、Linux には本来、様々な入力機器を扱う仕組みが用意されています。 ・・・と、いうわけで、 今回は input モジュールを使った真っ当な外部キーボード接続を試してみます。


kernel 2.4以降での入力デバイスの扱い

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 ではどうなの?

LinuxZaurus でも、キー入力の基本的な流れは一緒です。 input モジュール群をコンパイルして作ってあげれば正常に動作します。 しかし、もともと外部キーボードをつなげない機種なので、 内蔵キーボードに特化されている部分があり、 それが故に困った現象が起きています。

標準コードへのマッピングがない

本来であれば内蔵キーボードのスキャンコードを、 linux/input.h に合うようにしていてくれれば ・・・内部で、PC用linuxと同様のキーコードで処理してくれてれば何の面倒もなかったんです。 しかしながら、内蔵キーボード固定であることからか、 そうした変換は行われておらず sharp_keycode.h で定義されたコードを出してきます。

これの何が問題になるかというと、 USBキーボード・ドライバや、前述の Stowaway, Newton ドライバ など、 linuxのカーネルソースに入っているドライバとは “出力されるコードがまるっきり違う”ために、 そのまま組み込んでも、 キートップとは異なる文字がデタラメに入力されて使い物にならないのです(--;。

#実際にはそれだけでなく、押したキーによっては、もっと不可解な挙動を示します。

Qt はキーを独自処理している

『それじゃ、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. キーボードの生のスキャンコード
  2. OS用のスキャンコード
  3. 文字コード

まず問題の1つは、1→2 の変換がないためカーネルソースにあるキーボードドライバがそのまま使えないことです。 まぁ、これはドライバ側のどこかのマップテーブルをどうにかすれば何とかなるでしょう。

次の問題は、2→3 の変換でコンソールと Qt の 2種類の異なる方式が存在することです。 このため、キーバインドを調整する場合はその両方に対して行わねばならないのですが・・・先刻書いたとおり、 Qt側を調整する手段がないようで、そのままではGUIでは使えないという悲しいことになってしまいます。

更に細かい事を言えば、外部キーボードと内蔵キーボードのキー配列は異なるので、 仮に外部キーボードに合わせて変更してしまったら、 内蔵キーボードからの入力がめちゃくちゃになってしまうという問題もあります。


では、外部キーボードをどう扱うか?

とりあえず、次の2つを考えてみました。

  1. 外部キーボードを内蔵キーボードに合わせて 1対1 にマップし、後は OS にまかせる
  2. ser_code_ksimのように、内蔵キーボードの入力シーケンスをエミュレートする

前者のメリットは、SHIFT キーやキーリピートの処理など、一切合切を OS にまかせてしまえることです。 しかし、内蔵キーボードがそのまま外に出たようなイメージになるので、 例えば / (スラッシュ) を入力するのに SHIFT + , (カンマ) しなければならないなど、 外部キーボードのメリット(PCと同じように記号を入力できる)を活かせません。 本当に、『内蔵キーボードのキーをそのまま外部に引き出した』ような感じになります。

後者のメリットは、/ は / で押せるようになり、 内蔵キーボードでは入力できない記号も楽に入力できるようになることです。 しかし、1対1のキー対応ではないため、スイッチ的に扱いたい場合(ゲームなど)は使えず、 キーリピートも、SHIFT を含む全ての修飾キー処理も、自前で行わないといけないなど 少々処理が複雑になります。

さて、どうするかなぁ・・・とあれこれ悩みましたが、 後者は ser_code_ksim で既にやっているということもありますし、 他のキーボード・ドライバとの組み合わせも考えて、 まずはお手軽なところで『外部キーボードは、内蔵キーボードと 1対1 対応』の方法を採ることにしました。

# それじゃ使いづらいよ・・という人は、自分で作って公開しましょう :-)


パッチを当てるべきinputモジュール

ここまで来たら話は簡単で、変更すべきは 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 の流れはそのままに何とか使えるようになったはずです。

[keybdev.c, input.c へのパッチの当て方]

※仮に、/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

[make menuconfig での指定例]

Input core support
m Input core support
m Keyboard support
m Mouse support
m Joystick support
m Event interface support

stowaway keyboard に必要なモジュール

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

[make menuconfig での指定例]

Character devices
 Joysticks
m  Serial port device support
m   Serial port input line discipline
m  Newton Keyboard
m  Stowaway Keyboard

[inputattach の作成]

※↓アーカイブにバイナリも入ってますが、一応コンパイル手順を記しておきます。

# tar xzvf inputattach.tar.gz
# arm-linux-gcc -o inputattach inputattach.c
# arm-linux-strip inputattach

stowaway keyboard の使用方法

下記手順に従ってモジュールをロードし、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キーボード に必要なモジュール

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)を下記に置いておきます。


USBキーボード の使用方法

REX-CFU1 カードが挿されており、USBホスト機能が動いているという前提です。 なお、rmmod する場合は外付けキーボードから実行すると、 Enter が離される前にモジュールがアンロードされてしまい、 Enter が押しっぱなしになるため、 内蔵キーボードから行ってください。

[起動手順]

# insmod input
# insmod hid
# insmod keybdev

※補足: # insmod keybdev debug=1 のようにして起動すると、押したキーのコード情報が dmesg に出力されます。

[終了手順]

# rmmod keybdev
# rmmod hid
# rmmod input

usbkbd-slc-0.2.patch でのキーバインド

SL-C7x0 のキーボードが日本語配列である為、日本語キーボードの使用を前提に調整しています。

なお、現在の 0.2 では、一部の記号を普通に入力できません。 これらの記号を入力するには、SL-C7x0 の内蔵キーボードと同様に Fn キーとの組み合わせを使ってください。 具体的には、Alt + E で = 、Alt + R で + が入力可能です。 テンキー部は NumLock は効きませんが、ほぼ文字通りの入力ができるはずです。

JP109キーボードSL-C7x0キーボード
ESCCancel
F1カレンダー
F2アドレス帳
F3メール
F4HOME
F5* 定義機能
F6
F7
F8カナひら
F9メニュー
F10
F11
F12OK
JP109キーボードSL-C7x0キーボード
半角/全角漢字(全/半)
TabTAB
CapsLockCtrl
左SHIFTSHIFT
左CtrlCtrl
左WindowsHOME
左AltFn
無変換Cancel
スペーススペース
変換OK
カタカナカナ/ひら
PageUpPageUp
PageDownPageDown
JP109キーボードSL-C7x0キーボード
右AltFn
右WindowsHOME
右MenuMenu
右CtrlCtrl
BackSpaceBS
EnterEnter
右SHIFTSHIFT
PrintScreenHOME
ScrollLockMenu
Pause/Break^C
Home^A
DeleteDel
End^E

※Insert, NumLock, その他拡張キーは未割当です。


気になる点 ( known problem )

かくして、stowaway keyboard も USB keyboard も input モジュールで使えるようになりました。 しかし、使ってみて幾つか気になった点があります。

1つは、やはりキーボードの配列問題。 本来の流れからすれば、 キーバインドを調整するのは loadkeys やその他の手段であって、 これはこれで正解ではありますが・・・やっぱり気になりますね。

次に、時折発生する無限リピート問題。 どうも何かのタイミングで、キーを離したことが伝わってない場合があるようです。 筆者自身は、外部キーボードから rmmod keybdev などとした場合に、 Enter が押しっぱなしになる(離されたのを感知する前にモジュール終らせてるから)のを見たくらいで、 他では特に問題ないのですが、Ctrlキーや Fnキーが押しっぱなしになると非常に面倒なこと (キー入力を受け付けず、再起動するはめになる)になります。

ドライバで変更したのはキーマップの部分だけなので、パッチのせいというだけじゃないでしょうが、 一応ご注意ください。


あとがき

以上で、Linux Zaurus でもハード工作なしで手軽に外部キーボードを使えるようになりました。

実際の運用としては多少困る点(CFスロットが潰されてしまうため通信時に使えない等)もあるので、 シリアル接続のキーボードも捨てがたいのですが、手軽に使えるようになったというのはいい事です。

キーバインドの問題もまだ残ってはいますが、 そこはそれ GPL で公開されているのですから好きに直して使ってください ;-)


補足

カーネルモジュールを新規に入れた場合は、root権限で depmod -a を実行しとくのを忘れずに。 モジュールの依存関係を調べてくれますので。


download

usbkbd-slc-0.2.patch.gz
keybdev, input 用のパッチ
usbkbd-slc-0.2.tar.gz
keybdev, input 用のバイナリ
( input/input.o input/keybdev.o )
stowaway-newton.patch.gz
stowaway, newton キーボード用ドライバのパッチ
stowaway-newton.tar.gz
stowaway, newton キーボード用ドライバのバイナリ
( char/joystick/newton.o char/joystick/stowaway.o char/joystick/serio.o char/joystick/serport.o )
inputattach.tar.gz
inputattach をとりあえず palm用stowaway keyboard で使えるようにしたもの
( inputattach inputattach.c )

旧版 (old)

usbkbd-slc-0.1.patch.gz
keybdev, input 用のパッチ

参考文献

  • ザウルス宝箱Pro (SLシリーズの技術サポート情報ページに、I/Oポート仕様書やカーネルソース類があります)
  • 4つ折りキーボード接続計画 for E1
  • 4つ折りキーボード接続計画
  • 他機種用キーボードについての補足
  • Linux serial keyboards
  • Interfacing the Palm Stowaway Keyboard to the serial port
  • Zaurus用REX-CFU1ドライバ配布ページ (humorumさん)

  • [INDEX]