最終更新: 2000.9.20
本稿では、BeOSのジョイスティックドライバの書き方について、筆者が調査した結果を解説します。 BeOSのデバイスドライバ(カーネルドライバおよびモジュール)の知識を前提とします。
Beは、マイナーなジャンルのデバイスドライバ開発に関しては、 ほとんど情報を公開していません。 ジョイスティック/ゲームポートもその例にもれず、 sonic_vibesドライバのソースコードと Be BookのBJoystickの解説、 Be Developer Newsletterの記事 が間接的に参考になる程度です。 そこで、ジョイスティックドライバの書き方について調査した結果、 どうにか動くものが書ける程度の情報が得られたので、以下に解説します。
本稿の内容は筆者が調査したもので、 一部を除いてBeが公表・保証したものではなく、 必ずしも正しいという保証はありません。 また、未だ不明な部分も多く残っています。
まず、BeOSにおけるジョイスティックのAPI(BJoystickクラス)について簡単に見ていきます。詳細は Be Bookの解説と Be Developer Newsletterの記事を参照してください。
BJoystickはstandard modeとenhanced modeの2つの動作モードがあります。 standard modeはBeBoxのゲームポートに由来する古いもので、 ジョイスティックのモデルに関係なく2軸2ボタンに限定されており、あまり使いません。 enhanced modeはR4で追加されたもので、ジョイスティックの機能をフルに利用できます。 また、Joysticks preferenceの設定はenhanced modeでのみ有効です。 通常はenhanced modeを使うことになると思います。 standard modeからenhanced modeに移行することができますが、 デフォルトではゲームポートのオープンと同時にenhanced modeに移行します。 standard modeを使うには、enhanced modeに移行しないよう特に指定する必要があります。
アプリケーションからのジョイスティックのアクセス手順は以下のようになります (関数名はBJoystickクラスのメンバ関数です)。 なお、enhanced modeを使うためには、あらかじめJoysticks preferenceでジョイスティックの設定をしておく必要があります。
BeOSのジョイスティックサポートの枠組は以下のようになっています。
applications Joysticks preference------------+ | | | | |BJoystick joystick definition files | | | | operating system----------------+ | | |kernel driver API | | gameport drivers--------+ | | | |module API | | | joystick modules | | gameport hardware | | joystick hardware
主な構成要素の機能は以下の通りです。
ゲームポートを操作してジョイスティックのデータを入力するカーネルドライバです。 ゲームポートのハードウェア(ISAサウンドカード、PCIサウンドカード、 USB、独自インタフェースなど)に依存します。
ドライバは/dev/joystick/下にデバイスファイルをpublishします。 アプリがBJoystick::Update()を呼ぶと、このデバイスファイルのリードが発生し、 ドライバのread()が呼ばれます。 これに対してドライバはジョイスティックのデータを入力・解釈して返します。 返すデータの形式は、standard modeではjoystick構造体、 enhanced modeではextended_joystick構造体 (ともにjoystick_driver.hで定義されている)です。
standard modeではゲームポートドライバ自身がデータの解釈も行いますが、 enhanced modeでは、ジョイスティックのモデルによってデータの解釈が異なる可能性を想定し、 ゲームポートドライバは生データの入力のみを行い、 解釈は適切なジョイスティックモジュールに依頼します。 これにより、ゲームポートのハードウェアに依存する部分と ジョイスティックのモデルに依存する部分を分離することができます。
ただし、両者の役割分担の詳細は必ずしも明らかではありません。 ゲームポートの場合、他のバスやポートのようにきれいに分離できるとは限らず、 またその必要性も比較的低いので、 enhanced modeでは生データの入力を含むほとんどの処理を ジョイスティックモジュールで行わせる実装も考えられます。 本稿では生データの入力は常にゲームポートドライバの役割と仮定していますので、 ジョイスティックモジュールで行う場合は若干の相違点が生じます。 なお、USBの場合はUSBバスマネージャとのインタフェースが複雑になるため、 データ入力は常にゲームポートドライバで行うべきでしょう。
また、ジョイスティックがフォースフィードバック機能を持つ場合、 enhanced modeにおいてデバイスファイルにextended_joystick構造体をライトすると、 フォースフィードバックを発生することになっています。ただし詳細は不明です。
ゲームポートドライバが入力した生データを解釈し、 カーネルが理解できる形式(extended_joystick構造体)に変換するモジュールです。 ジョイスティックのモデルに依存します。 APIはjoystick_driver.hのjoystick_module構造体で定義されています。 enhanced modeにおいてゲームポートドライバから呼ばれます。 OSはジョイスティックモジュールを呼ばない(std_ops()を除く)ことに注意してください。
ジョイスティックのモデルごとに、 諸元(製品名、軸やボタンの数・名称、ジョイスティックモジュール名など)を記述したテキストファイルです。 Joysticks preferenceでは定義ファイルから得られた製品名一覧を表示し、 ユーザーはその中からゲームポートに接続されているものを選びます。 この情報(joystick_module_info構造体)は、 ゲームポートがenhanced modeに移行するときに、 OSからゲームポートドライバに通知されます。 ゲームポートドライバは、これをもとに適切なジョイスティックモジュールを選択します。
なお、Joysticks preferenceでの選択は、~/config/settings/joysticks/下に保存されます。 ここには、ドライバ名/ポート番号というシンボリックリンクがあり、 選択されたジョイスティック定義ファイルを指しています。
各ファイルの所在は以下の通りです。
| ゲームポートドライバ | /system/add-ons/kernel/drivers/dev/joystick/* |
| ジョイスティックモジュール | /system/add-ons/kernel/media/joy/* |
| ジョイスティック定義ファイル | /etc/joysticks/* |
| ゲームポートデバイスファイル | /dev/joysticks/*/* |
これ以外に、generic gameport moduleというものが存在しており (/system/add-ons/kernel/generic/gameport、 joystick_driver.hのgeneric_gameport_module構造体)、 sonic_vibesドライバでも使用していますが、これの役割と使用法は明らかではありません。 使わなくてもドライバは書けるようなので、ここでは無視します。
ジョイスティックのドライバを開発するということは、 ゲームポートドライバを開発すること、 ジョイスティックモジュールを開発すること、 そしてジョイスティック定義ファイルを書くことを意味します。
ゲームポートドライバのAPIで実装すべき内容を解説します。 構造体などについては、sonic_vibesドライバのjoystick_driver.hを参照してください。
ゲームポートがPCIやISAの場合は、ここで検出や初期化をすることができます。 ただし、init_hardware()の後すぐにドライバはアンロードされてしまうので、 ここで得た情報をグローバル変数に格納しても意味がありません。 情報を保存するには、areaを確保してそこに格納するなどします。 PCIの場合、デバイスは一般に物理メモリ空間にマップされるので、 areaはいずれにせよ必要になります。
ドライバの変数の初期化などをします。 また、ゲームポートの検出や初期化をinit_hardware()の代わりに ここで毎回行うこともできます。 USBの場合はここではバスマネージャの取得とドライバの登録を行い、 実際の処理はdevice_added()/device_removed()で行います。
ドライバの後処理をします。 USBの場合はここでドライバの解除とバスマネージャの解放を行います。
デバイス名をpublishします。デバイス名は通常 「joystick/ドライバ名/ポート番号」の形式をとります (joystick/gameport/201、joystick/ymf724/1など)。 USBの場合はジョイスティックの抜き挿しによって動的に変化します。 とくに変わったところはありません。
デバイスフックを返します。これも特に変わったところはありません。
デバイス名が該当するものであれば、そこからポート番号を取得します。 ポートを調べ、ジョイスティックが接続されていれば、cookieの確保と初期化をします。 cookieは以下のような情報を含みます。
cookieからポート番号を得て、ゲームポートからデータを入力します。 standard modeの場合は、入力データを解釈し、joystick構造体に変換して返します。 enhanced modeの場合は、ジョイスティックモジュールのread()を呼んで、 入力データをextended_joystick構造体に変換してもらい、これを返します。
enhanced modeの場合、ライトデータ(extended_joystick構造体)を ジョイスティックモジュールのforce()に渡し、フォースフィードバックの処理をします。 具体的な内容は不明です。
joystick_driver.hに定義してあるioctl(B_JOYSTICK_*)を処理します。 B_JOYSTICK_SET_DEVICE_MODULEとB_JOYSTICK_GET_DEVICE_MODULEは必須です。
B_JOYSTICK_SET_DEVICE_MODULEは、 アプリケーションからの指示でenhanced modeに移行するときに呼ばれ、 Joystick preferenceで設定したジョイスティックの情報が渡されます。 引数はjoystick_module_info構造体で、ジョイスティックモジュール名、 製品名、軸やボタンの数などが含まれます。 与えられた設定情報が実際のジョイスティックに適合する場合は、 joystick_module_infoを保存し、 ジョイスティックモジュールを取得(get_module())し、 モジュールのconfigure()を呼んで初期化をさせ、 enhanced modeに移行します。 なお、Joystick preferenceでProbeを実行すると、 すべてのジョイスティック定義ファイルについてこのioctlを呼び、 正常終了したものが接続されている(可能性がある)と判断されます。
B_JOYSTICK_GET_DEVICE_MODULEは、 B_JOYSTICK_SET_DEVICE_MODULEで与えられたjoystick_module_info構造体を返すだけです。
処理はありません。
cookieを解放します。 enhanced modeの場合は、ジョイスティックモジュールのcrumble()を呼んで 後処理をさせ、モジュールを解放(put_module())します。
ジョイスティックモジュールのAPIで実装すべき内容を解説します。 ジョイスティックモジュールについては、ゲームポートドライバと比べて不明な部分が多いです。
与えられたポート番号とjoystick_module_info構造体に従って、 cookieの確保と初期化をします。 このcookieは、ゲームポートドライバのcookie(カーネルとゲームポートドライバの通信に使われる)とは別物で、 ジョイスティックモジュールとゲームポートドライバの通信に使うものです。 内容は、ゲームポートドライバが入力した生データなどになるでしょう。
なお、ジョイスティックのモデルを自動検出できる場合には、 検出した情報でjoystick_module_info構造体を書き換える場合があります。 とくに、USBの場合は、joystick_module_info構造体 (およびその元となるジョイスティック定義ファイル)に頼らなくても、 必要な情報はすべてジョイスティックから得られますので、 完全に自動検出とすることが望ましいでしょう。 すなわち、USBジョイスティックについては、 (半ばダミーの)ジョイスティック定義ファイルを1つだけ用意(例えば"Generic USB HID Joystick")しておき、 Joystick preferenceではモデルに関わらずこれを選択するだけで済むようにすべきでしょう。
ゲームポートドライバのread()から呼ばれます。 ドライバが入力した生データを解釈して、extended_joystick構造体に格納して返します。
ゲームポートドライバのwrite()から呼ばれます。 与えられたextended_joystick構造体にもとづいてフォースフィードバック処理を行います。
cookieを解放します。
B_MODULE_INITとB_MODULE_UNINITが呼ばれますが、 とくに処理は必要ありません。
joystick_module構造体をpublishします。
拙作のサンプルソースを参考にしてください。
不明な点はまだたくさんあります。