$Id: aztcd,v 1.2 2002/10/12 04:23:01 mdk Exp $
          Readme-File /usr/src/Documentation/cdrom/aztcd

	     AZTECH CD-ROM CDA268-01A, ORCHID CD-3110,
      OKANO/WEARNES CDD110, CONRAD TXC, CyCDROM CR520, CR540
                           CD-ROM ドライブ
                       Version 2.6 以降用
                (他のドライブに関しては 6.〜 8.参照)

注意：このドライバは、ここにあげた CD-ROM ドライブで動作します。この
      ドライブには、専用のインタフェースがあります（サウンドカード、
      あるいは ISA-AT バスカードに実装されています）。
      Aztech CDA269-031SE といったような、＊ＩＤＥ＊インタフェースでは、
      絶対に動作しません（唯一分かっている例外は、CyCDROM CR520ie の
      ような「疑似」 IDE ドライブです。これについては、一定の条件下で、
      aztcd で動作します。7. 参照）。ATAPI 互換とも呼ばれる、IDE インタ
      フェース付きの CD-ROMドライブを使うつもりなら、ide-cd.c ドライバ
      を是非使って下さい。このドライバは Mark Lord と Scott Snyder が
      書いたものです。標準のカーネル 1.2.x では、IDE-CDROM-DRIVES を
      サポートしていますので、新しいカーネルをコンパイルする場合は、
      make config の HARDDISK SECTION を参照してください。
----------------------------------------------------------------------------
目次                     1. 注意
                         2. インストール
                         3. カーネルの構成
                         4. カーネルの再コンパイル
                         4.1   AZTCD を実行時のローダブルモジュールにする
                         4.2   サウンドカードに接続された CDROM
                         5. 既知の問題と今後の開発
                         5.1   マルチセッションのサポート
                         5.2   ステータスの認識
                         5.3   DOSEMU の CDROM サポート
                         6. バグ報告
                         7. その他のドライブ
                         8. うまくいかない場合 ... デバッグ
                         9. ドライバの技術面での履歴
                        10. 謝意
                        11. アドオンプログラムの作成: CDPLAY.C
                        付録： cdplay.c のソースコード
----------------------------------------------------------------------------

このソフトウェアは、アルファテストとベータテストでうまく動作しており、
1994年 12月から、カーネル 1.1.8x 以降の標準カーネルの一部になっています。
このドライバは、AZTECH CDA268-01A, ORCHID CDS-3110, ORCHID/WEARNES CDD110 
および CONRAD TXC (Nr.99 31 23 -シリーズ 04) で動作し、カーネル 1.0.9 以降
で安定していることが証明されました。しかし、どのようなソフトウェアにも、
依然としてバグがあるかもしれません。ですから問題に遭遇している場合は、
このソフトウェアの改善に手を貸して欲しいと、請われているわけです。

ですから、詳細なバグ報告を是非送って下さい（バグ報告の章参照）。また、
サポートしているドライブ数を増やすお手伝いもしていただければと思います。

是非 REAME ファイルは念入りに読んでください。また何かがうまく行かないときに
リブートするためにも、古いカーネルは常にコピーを取っておくようにして下さい。

2. インストール
ドライバは 'aztcd.h' というヘッダーファイルと、'aztcd.c' というソースコード
で構成されています。前者は普通 /usr/src/linux/drivers/cdrom にあり、後者も
通常は同じ場所にあります。このドライバでは /dev/aztcd （ディストリビューション
によっては /dev/aztcd0）を用います。このデバイスは、メジャー番号が 29 に
なっており、/dev ディレクトリにある、有効なブロックデバイスでなければなりま
せん。CD-ROM をマウントする場合、カーネルに ISO9660 ファイルシステムサポート
を組み込んでおく必要があります。

要注意：aztcd.c は Linux カーネルと並行して開発されたもので、下位互換性の
無い重要な変更や細かい変更が数多くなされましたし、今でもそうです。ですから、
1.3.33 よりも古いカーネルでは、バージョン 1.80 以降の aztcd.c は絶対に動作
しません。ですから、是非適切なカーネルと共に、最新版の aztcd.c を常に使う
ようにしてください。

3. カーネルの構成
すでにお使いのカーネルで AZTECH ドライバが使えるように構成してあれば、Linux
のブート中に以下のようなメッセージが出ているのが分かるはずです。
    Aztech CD-ROM Init: DriverVersion=<version number> BaseAddress=<baseaddress>
    Aztech CD-ROM Init: FirmwareVersion=<firmware version id of your I/O-card>>>
    Aztech CD-ROM Init: <drive type> detected
    Aztech CD-ROM Init: End

違うメッセージが出ているけれども、持っているのが確かにサポートしているドライブ
だとすれば、ベースアドレスが違うのかもしれません。Aztech ドライバは、コンパイル
時に aztcd.h に指定したベースアドレスで、CD-ROM ドライブを探します。この
アドレスは、ブートパラメータ aztcd=.... で上書きも可能です。リブートしたら、
このブートパラメータ aztcd=<ベースアドレス> をつけて、Linuxをスタートさせて
ください。例えば、aztcd=0x320 とします。ベースアドレスが分からない場合は、
DOS で PC を立ち上げて、CD-ROM の DOS ドライバが出すブートメッセージを見て
ください。これでも分からない場合は、ブートパラメータに aztcd=<ベースアドレス>,0x79
を使って下さい。これは aztcd にちょっと難しいことを指示します。
おそらく、aztcd を再コンパイルして、ベースアドレスの自動探査を使うように設定
されると思います（第四章参照）。

メッセージが正しいようなら、'root' ユーザーで次のようにしてドライブを
マウントできるはずですし、

          mount -t iso9660 -r /dev/aztcd0 /mnt
他のファイルシステムと同じように使えるはずです（動作しない場合は、
/dev/aztcd0 と /mnt がほんとうに存在するかどうか確かめてください。
必要なら以下のようにして作成して下さい）。
      mknod /dev/aztcd0 b 29 0
      mkdir /mnt                       

これでも、Linuxのブート中に違うメッセージが出ていたり、CD-ROM をマウント
しようとすると、カーネルが ISO9660 ファイルシステムをサポートしていないと
いうメッセージが出る場合は、カーネルの再コンパイルする必要があります。

Aztech/Orchid/Okano/Wearnes/TXC のドライブは持ってないので、Linux のブート
中に行なうドライブの検出を迂回したい場合は、aztcd=0 というブートパラメータ
を指定して Linux をスタートさせて下さい。

最近ではたいていのディストリビューションには、aztcd を含むブートディスク
イメージが入っています。ですから是非忘れないで下さい。このドライバは
IDE/ATAPI ドライブでは動作しません。こういったドライブには、代わりに ide-cd.c
を使わなければなりません。

4. カーネルの再コンパイル
カーネルがまだ、AZTECH ドライバと ISO9660 ファイルシステムをサポートするような
構成になっていない場合、カーネルを再コンパイルする必要があります。

- aztcd.h を編集して、I/O アドレスを、お使いの I/O ベースアドレス 
  (AZT_BASE_ADDR) に設定して下さい。
  ドライバは割り込みも DMA も使いませんから、AZTECH CD268, an ORCHID CD-3110
  あるいは ORCHID/WEARNES CDD110 を使うつもりなら、設定する必要があるのは
  この項目だけです。サウンドカードが付いている場合は、4.2 章を読んで下さい。
  他のドライブを使っている方は、このファイルの「その他のドライブ」の章を読む方が
  いいでしょう。このアドレスは、カーネルのブートパラメータに、aztcd=... と
  いうように設定することもできます。

- AZT_BASE_ADDR を -1 に設定すれば、ベースアドレスの自動探査を使うように、
  aztcd が設定されると思います。この場合、aztcd は AZT_BASE_AUTO にリスト
  されているアドレスを探査します。でも是非覚えておいて下さい。自動探査は、
  他のハードウェア要素に悪い影響を与えることもあります。

- 他にも、設定してかまわない点がいくつかあります。例えば、ドライブのアンマウント
  時の CD の自動イジェクトとか、トレイのロック等々です。詳細は aztcd.h を
  参照して下さい。

- 2.1.0 より前のバージョンのカーネルを使っている場合、aztcd.h の
  '#define AZT_KERNEL_PRIOR_2_1' 行のコメントを外して下さい。

- 新しいカーネルを構築します。（aztcd をカーネルに組み込みたい場合は）
  'Aztech/Orchid/Okano/Wearnes support'  を有効にするように構成して下さい。
  aztcd を実行時のローダブルモジュールで使いたい場合は、'Aztech... support'
  を有効にするような構成をしてはいけません。ともあれ、ISO9660 ファイルシステム
  はカーネルに含ませるようにしなければなりません。

- 新しいカーネルを有効にします。普通は LILO を実行すればそうなります
  （その前に LILO の設定を忘れないで下さい。またうまくいかない場合のために、
  古いカーネルはコピーを取っておいて下さい）。

- リブートして下さい。

- aztcd をカーネルに組み込んだ場合、ブート中に次のようなメッセージが出る
  のが分かるはずです。
    Aztech CD-ROM Init: DriverVersion=<version number> BaseAddress=<baseaddress>
    Aztech CD-ROM Init: FirmwareVersion=<firmware version id of your I/O-card>
    Aztech CD-ROM Init: <drive type> detected
    Aztech CD-ROM Init: End

- aztcd をカーネルに組み込まず、実行時のローダブルモジュールでロードしたい
  場合は、4.1 を参照して下さい。

- メッセージが正しいようなら、ユーザー 'root' で、以下のようにしてドライブを
  マウントできるはずですし、
          mount -t iso9660 -r /dev/aztcd0 /mnt

  他のファイルシステムと同じように使えるはずです。（動作しない場合は、
  /dev/aztcd0 と /mnt が実際に存在するかどうかを調べて下さい。そして
  必要なら、以下のようにして作成して下さい）
      mknod /dev/aztcd0 b 29 0
      mkdir /mnt                       
- これでもだめなら、「その他のドライブ」の章と「デバッグ」の章を参照して下さい。

4.1 AZTCD を実行時のローダブルモジュールにする
aztcd を常駐させる必要がなければ、insmod と rmmod を使って、実行時に
ドライバをロードしたり削除することもできます。aztcd をローダブルモジュール
で構築するには、AZTECH モジュールサポートを有効にするように、カーネルを
構成しなければなりません（カーネルの構成時に 'm' と答えて下さい）。
どのみち、ブートカーネルのバージョンとソースカーネルのバージョンが違ったまま、
モジュールを作ると、問題に遭遇するかもしれません。ですから、必要なら
カーネルを再構築して下さい。

次は、/usr/src/linux/drivers/cdrom/aztcd.h にある AZTECH インタフェース
カードのベースアドレスを、適切な値の変更します。AZT_BASE_ADDR を '-1' に
設定して、ベースアドレスの自動探査を使うように設定してもかまいません。
その場合は、aztcd は AZT_BASE_AUTO にリストしてあるアドレスを探査します。
しかしこれは覚えておいて下さい。自動探査は、常に他のハードウェア要素に、悪い
影響を与えることもあります。他にも、設定しても良いような、特徴的なことが
いくつかあります。例えば、ドライブのアンマウント時に CD を自動イジェクトする
等々です。詳細は aztcd.h を参照してください。それからディレクトリを
/usr/src/linux に変えて、以下のようにします。
                    make modules  
	            make modules_install
その後、以下のようにしてドライバを実行時にロードできるようになりますし、
                    insmod /lib/modules/X.X.X/misc/aztcd.o
rmmod aztcd として、削除できるようにもなります。

aztcdh. に正しいベースアドレスを設定しなかった場合は、以下のようにして、
ドライバのロード時にベースアドレスを与えてもかまいません。
                    insmod /lib/modules/X.X.X/misc/aztcd.o aztcd=<base address>

繰り返しになりますが、aztcd=-1 と指定すれば、自動探査を行ないます。
ブートカーネルに iso9660 ファイルシステムを組み込んでいない場合は、
CDROM のマウント前にそのモジュールもロードしなければなりません。
                    insmod /lib/modules/X.X.X/fs/isofs.o

マウントの手順は、先の 4. で説明したように行ないます。
（すべてのコマンドにおいて、'X.X.X' は現在の Linux カーネルのバージョン番号
になります。詳細は、/usr/src/linux/Documentation の modules.txt ファイルを
参照して下さい。）

4.2 サウンドカードに接続された CDROM
ほとんどのサウンドカードには、実際 CD-ROM ドライブに対するバスインタフェース
があります。多くの場合、このサウンドカードは設定する必要があり、そうしないと
CDROM は使えるようになりません。この設定手順の内容は、ある種の初期化データを
サウンドカードのレジスタに書くことです。現時点で AZTECH-CDROM ドライバが
実際にサポートしているサウンドカードは一種類（SoundWave32）だけです。
他のサウンドカードを使用している方は、最初に DOS でブートし、DOS のドライバ
にそのサウンドカードと CDROM を初期化させて、それから PC ウォームブートで
（あるいは loadlin を使ってブートし）、Linux をスタートさせてみて下さい。

AZTECH ドライバは、直接 SoundWave32 サウンドカードの CD-ROM インタフェース
を実装しています。linux/drivers/cdrom/aztdc.h を編集して '#define AZT_SW32'
のコメントを外し、AZT_BASE_ADDR と AZT_SW32_BASE_ADDR に適切な値を設定して
ください。このサポートは、SoundWave32 に Orchid CDS-3110 を接続してテスト
しました。
自分のサウンドカードをサポートしたい場合は、どういう設定が必要なのかを
見つけ出して、適切な情報を筆者（6. 参照）にメールして下さい。

5. 既知の問題と今後の開発
5.1 マルチセッションのサポート
CD でマルチセッションをサポートするというのは、まだ夢物語です。筆者は
XA CD でマルチセッションの基本的なサポートを実装しテストしました。でも、
厳密なテストを行なうには、CD やアプリケーションの数が足りません。そこで、
手を貸していただけるなら、是非連絡を下さい（電子メールアドレスは以下を参照
してください）。バージョン 1.4 以降については、AZT_MULTISESSION を 1 に設定
すれば、aztcd.h 内でマルチセッションを有効にできます。こうすると、ISO9660
ファイルシステムでマルチセッション CD を扱うことになります。つまり、直前の
セッションからの、テーブル・オブ・コンテンツ (TOC) 情報へのリダイレクト要求
を扱うわけです。この情報には前回のセッションすべての情報、等々が入っています。
AZT_MULTISESSION を 0 に設定しても、とにかくマルチセッション CD は使えます。
その場合、ドライブのファームウェアは自動的にリダイレクトを行ないます。
ISO9660 ファイルシステムに対しては、マルチセッション CD は「普通の」シングル
セッション CD のように見えます。しかし、それでも全セッションのデータは閲覧
できるし、アクセスできます。ですから、現実的には、実世界のアプリケーションでは、
すべてその差異には気づかないでしょう。でも将来のアプリケーションは、もっと
進んだマルチセッションの機能を利用するかもしれませんから、ioctl の
CDROMMULTISESSION を使って、ISO9660 のマルチセッションインタフェース用の
インタフェースの実装を開始しました。


5.2 ステータスの認識
ドライブのステータス認識は、あらゆるケースで正常に動作しません。ドライブが
既にマウントされている状態で、ディスクを交換したり、ドアを開けると、Aztech
ドライバはそれを検出します。にもかかわらず、それによって、ISO9660 ファイル
システムドライバの別々のレイヤーが複数の読み込みをしようとして、結局タイム
アウトになってしまいます。ですからちょっぴり待っていただかなくてはなりません。
...でも、いずれにしても、ドライブをマウントしている状態で、ディスクを交換する
というのは、やり方がまずいんじゃないの？！

ドライバは、ドライブのハンドシェーク（STEN_LOW マクロと、DTEN_LOW マクロ）
に対して、ほとんどのケースでビジーウェイトを用います。筆者は、486/DX2、66MHz
のマシンと、ペンティアムの 60MHz、同じく 90MHz でテストしました。
これよりずっと高速のマシンでは、いつでもタイムアウトメッセージが出そうです。
その場合は、aztcd.h を編集して、タイムアウト値 AZT_TIMEOUT を増やして下さい。

いくつかの「低速な」ドライブのコマンドについては、タイマー・ウェイトキュー
（STEN_LOW_WAIT マクロ）を使ったウィエトを実装しました。このタイムアウト
メッセージが出るようなら、 aztcd.h を編集して、タイムアウト値 AZT_STATUS_DELAY 
も増やしてかまいません。カーネルパニックのメッセージが出るようなら、aztcd.c を
編集して、STEN_LOW_WAIT を STEN_LOW に置き換えて下さい。STEN_LOW を使った
ウェイトはもっと安定してますが、CPU オーバーヘッドの原因にもなります。

5.3 DOSEMU の CDROM サポート
リリース 1.20 で、aztcd は dosemu-0.60.0 下で動作中、CD-ROM へのアクセスが
できるように修正しました。1.20 より前のバージョンの aztcd では、dosemu 下で
CD-ROM をアクセスすると、Linuxがもっともクラッシュしやすいです。この問題は
部分的には修正しましたが、最初にディレクトリをアクセスすると、まだ 30 秒ほど
ハングするかもしれません。そういうわけですから、dosemu の CD-ROM サポートを
aztcd と組み合わせて使う場合は、我慢して下さい。:-)！
この問題は、1995年 7月現在、dosemu の CD-ROM ドライバの修正で直りました。
新しいバージョンは dosemu-0.60.2 になりました。dosemu のREADME.CDROM を参照
してください。

6. バグ報告
詳細なバグ報告とその修正内容を、電子メールで是非以下宛てに送って下さい。

        Werner.Zimmermann@fht-esslingen.de

その際、同封して欲しいのは、お使いの CD-ROMドライブの種類とインタフェース
カードの説明、Linux ブート時の正確なファームウェアメッセージ、AZTECH CD-ROM
ドライバのバージョン番号、および Linux カーネルのバージョンです。それに、
お使いのシステムの、他のハードウェアの説明も重要になることがあります。特に、
マイクロプロセッサの種類、クロック周波数、サウンドカードやイーサネット
アダプタ、ゲームカード等々といった、他のインタフェースカードです。

筆者は報告を収集し、時々必要な修正をしようと努めています。直接バグ報告者に
バグの修正内容を送って、引き続きテストとデバッグをお願いするかもしれません。

CD-ROM の作者の皆さんには、CD-ROM のコーポレートコピーをボランティアに
送っていただくようお願いします。彼らは Linux 用の CD-ROM サポートを提供
してくれています。そういったものに関する、本文書の作者の通常郵便の住所は
以下のとおりです。
           Prof. Dr. W. Zimmermann
           Fachhochschule fuer Technik Esslingen
           Fachbereich IT
           Flandernstrasse 101
           D-73732 Esslingen
           Germany

7. その他のデバイス
ORCHID CDS3110 や OKANO CDD110、 WEARNES CDD110 それに Conrad TXC Nr. 993123
シリーズ 04 といったドライブは、AZTECH CDA268-01A とほとんど同じもののようです。
特に、同じコマンドコードを使用しているようです。ですから、これらのドライブで
AZTECH ドライバを動作させるのは、きわめて簡単でした。

ですが、あいにく筆者は利用できるこういったドライブはどれも持っていません。
ですから自分ではテストできそうにありません。使用前には DOS のドライバで
そのドライブを初期化（特にサウンドカードと組み合わせている場合）して、それから
ウォームブート（CTRL-ALT-RESET）か、DOS から Linux をスタート（例えば 
'loadlin' を使って）させる必要がありそうです。

うまくいかない場合は、デバッグの章を読んで下さい。よろしく！

お手数かけて申し訳ありませんが、テストに利用できるハードウェアが無い場合は、
開発が難しいのです。ですから、できれば是非、手を貸して下さい。

CyCDROM CR520ie を実際お使いなら、Hilmar Berger の協力に感謝です。
良い機会です。このドライブは aztcd で動作します。CR520ie は IDE ドライブと
して販売されていますし、本当に IDE インタフェースに接続します（プライマリ
は 0x1F0、セカンダリは 0x170で、マスターではなく、スレーブで設定します）。
にもかかわらず、ATAPI 互換ではないのです。しかし依然として Aztech のコマンド
コードを用いています。


8. デバッグ：うまくいかない場合、以下のことを試して下さい。
-完全な README ファイルを、再度読む。

-お使いのドライブが以下のハードウェア設定になっていることを確認する。
    transfer mode: polled
    IRQ:           not used
    DMA:           not used
    Base Address:  something like 300, 320 ...

お使いのドライブに付いてきた DOS のドライバをスタートさせれば、このことは
確認できます。ドライブと DOS のドライバを正しく設定すれば、自分のドライブ
が DOS の下で、本当に正しくこのモードで動作しているかどうかが確認できます。
もし DOS 下で動作しなければ、Linux でも動作しません。

ドライブのベースアドレスが 0x170 やあるいは 0x1F0 といったようなものなら
（そして、そのドライブが CyCDROM CR520ie でも CR 940ie でもなければ）、
お持ちのドライブは、おそらく IDE/ATAPI 互換のドライブです。これは aztcd.c
ではサポートしていません。代わりに、ide-cd.c を使って下さい。
ベースアドレスが aztcd.h で正しく設定してあることを確認し、/dev/aztcd0 が
正しいメジャー番号で存在していることも確認して下さい（Aztech のドライブに
関しては、この内容と /usr/include/linux/major.h ファイルにあるエントリを
比較して下さい）

-CD-ROM を入れて、トレイを閉じて下さい。

-PC をコールドブートしてください（つまり、電源スイッチ投入かリセットボタン
 を使ってください）。

-DOSから（例えば loadlin を使って）Linux を起動する場合は、
 その CD-ROM ドライブ用の DOS ドライバがロードされないことを確認して
 ください（DOS の config.sys でドライバを呼び出している行をコメントに
 してください）。

-Linux の init 中に出る init メッセージから、aztcd を探して、正確に
 そのメッセージを書き留めておいてください。

-root でログインし、mount -t iso9660 /dev/aztcd0 /mnt を実行してください。

-一回でうまくいかなくても、何回か試してみて下さい。トレイを開いたり閉じたり
 もしてみてください。そうしてから、またマウントしてください。タイプした
 コマンドや表示された aztcd のメッセージはすべて、注意深く書き留めて下さい。

-'Aztech CD-ROM init: aborted' というメッセージが出たら、バージョン文字列
 に関する下記注意を読んで下さい。

これでだめなら、以下のように変えて、同じことをして下さい。

-CD-ROM 用の DOS ドライバを DOS 下で確実にロードしてから、DOS を
 スタートさせて下さい（つまり、再度 config.sys の当該部分のコメント
 を外します）。

-PC をウォームブートして下さい（つまり、CTRL-ALT-DEL を使います）。
 loadlin があれば、それを使ってスタートさせることもできます（両方試して
 みてください）。
 ...
 もう一度、実行したすべてのコマンドと、aztcd のメッセージを書き留めて
 ください。

STEN_LOW か STEN_LOW_WAIT のエラーメッセージが出る場合は、タイムアウト値
を増やして下さい。

これでも、まだだめなら、

-aztcd.c の次の行を覗いてみて、 #if 0
                                #define AZT_TEST1
                                ...
                                #endif

'#if 0' を '#if 1' に置き換えて下さい。

-カーネルを再コンパイルして、上記二つの手順を繰り返して下さい。今度は、ドライバ
 からデバッグメッセージの塊が出てきます。再度、実行したコマンドと適切なメッセージ
 を書き留めて下さい。syslogd を動かしている場合は、このメッセージは syslogd の
 カーネルログファイルでも見つかるかも知れません。それでも、インストールの仕方に
 よっては、init() を呼ぶ時でもまだ、syslogd は動いていないことがあります。
 したがって、ログインプロンプトが現れるまでの、init の間で aztcd のメッセージを
 探して下さい。
 それから、aztcd.c を覗いてみて、何が起こったのか見つけ出して下さい。
 通常の呼びだし順序は、Linux のブート手順 init() の間は aztcd_init() で、
 'mount -t iso9660 /dev/aztcd0 /mnt' を行なった後は、通常、次のようになります。

     aztcd_open()    -> CDROM、ないしオーディオ CD を入れて、コールドブート後 Status 2c
                     -> CDROM を入れてウォームブート後 Status 8
                     -> ディスクを入れず、トレイを閉じて、コールドブート後 Status 2e
                     -> ドアを開けたままマウントして、コールドブート後 Status 6e
     aztUpdateToc()
     aztGetDiskInfo()
     aztGetQChannelInfo()   数回繰り返し

     aztGetToc()
     aztGetQChannelInfo()   数回繰り返し
     トラック情報リスト
     do_aztcd_request()  }  
     azt_transfer()    } 数回繰り返し
     azt_poll          }

この呼びだし順序が違っていたりステータスフラグが違ってないかどうか調べて下さい。

 他にもメッセージはたくさんあります。例えば、ACMD コマンドコード（aztcd.h で
定義してあります）、getAztStatus コマンドからのステータス情報、azt_poll() にある、
有限状態マシンの状態シーケンスです。一番大切なのはステータスメッセージです。
定義の仕方を見て、そのメッセージが現れたところの内容が意味を成していれば、
それを理解するようにして下さい。CD-ROM を入れた状態では、aztcd_open() を除けば、
ステータスは常に 8 になるはずです。トレイを開けて、オーディオ ディスクを入れたり、
何も入れなかったり、再度 CD-ROM を入れたりして、それに応じてステータスビットが
変わるかどうか調べて下さい。ステータスビットは、ドライブの製造業者が実装の変更を
行なう、一番可能性がありそうな場所です。

これでも、まだだめなら、足掛かりは aztcd.c にある aztcd_init 関数を見てみる
ことです。ここで、init 中にドライブを検出するはずです。以下のようにしてみて
ください。

-'aztcd=<ベースアドレス>,0x79' というブートパラメータを付けて、システムをリブート
 してください。0x79 というパラメータを付けると、ほとんどのドライブで、バージョン
の検出を迂回します。その後、init の間に先頭と終りに空白が付いた、バージョン文字列
が表示されるのが分かるはずです。
では、表示されたメッセージの最初の 3 文字か 4 文字に正確に合うように、
      if ((result[1]=='A')&&(result[2]=='Z' ...)
aztcd_init() にある上記のステートメントを調整して下さい。

別の足掛かりは、aztcd_init() にある、「スマート」カードの検出機能です。通常、
aztcd_init が バージョン文字列を読もうとする時は、CD-ROM ドライブはレディ状態
になっており、時間がかかる ACMD_SOFT_RESET を使わずに済ませられます。これは、
AFL_OP_OK を正しく読みとれているかどうかを見れば、わかります。例えば、ウォーム
スタート前にエラーが出たとか、最初に DOS 下で動かしたというようなことで、
CD-ROM が何らかの状態でハングする場合、バージョン文字列は正しいかも知れませんが、
次のコマンドは正しく動作しません。そういう時は、ACMD_SOFT_RESET がいつでも
実行されるように、コードを変えて下さい。if 文の 'if ( ...=AFL_OP_OK)' を 'if (1)'
に置き換えます。

成功したら、お使いのドライブの正確なバージョン文字列とコードの修正内容に、
簡単な説明を付けて、是非筆者にメールして下さい。うまくいかない場合でも、
デバッグメッセージの出力をメールしていただいてかまいません。でも忘れないで
くださいね。そういったものが役に立つのは、情報が正確かつ完全であり、
ハードウェアのセットアップと行なったこと（コールドブート／ウォームブート、
DOS 有り／DOS無し、DOS ドライバを動かした／動かさなかった、実行した Linux
のコマンド、等々）が詳細にわたって説明されている場合だけなのです。

9. ドライバの技術面での履歴
AZTECH ドライバは Mitsumi のドライバに手を加えたものです。加工しなくてはならない
重要な項目は 4 つありました。

a) Mitsumi のドライブは、各コマンドの確認をしている完全なステータス情報を、
実際に送出していますが、Aztech のドライブでは、コマンドを処理したという
信号しか出ていません。ですから、完全なステータス情報が必要なると、いつも
ACMD_GET_STATUS コマンドを実行しています。このコマンドのハンドシェーク手順
は、aztSendCmd() 関数と sendAztCmd() 関数、それに getAztStatus() 関数の中
で見つかります。

b) Aztech のドライブには ACMD_GET_DISK_INFO コマンドがありません。ですから、
トラック数（第一トラック、最終トラック）やディスク長の情報が必要になったら、
トラックの先頭にある TOC を読みとらざるを得ませんでした（aztGetDiskInfo() 
関数参照）。

c) ドライブからデータを読みとる場合、Mitsumi は常に不定数 (0xffffff) のセクター
を読みとるコマンドで始めています。適当な数のセクターを読みとると、ドライブは
ACMD_STOP コマンドで停止します。でも、これは Aztech のドライブでは動作しません。
停止させる方法は見つかりませんでした。停止と一時停止コマンドが実際に動作する
のは、AUDIO モードだけで、DATA モードでは動作しないのです。したがって、
azt_poll 関数にある「有限状態マシン」を修正して、一定数のセクターだけを
読みとり、その後要求に応じて新しい読み取りを始めるようにしなければなりません
でした。Mitsumi のドライバで実装している、バッファ／キャッシュ手法を完全に
理解していなかったので、すべてのケースを正しくカバーしているかどうかは、
確信が持てません。タイムアウトメッセージが出る場合は、常に function azt_poll()
や switch(cmd) 周辺とか case ACD_S_DATA に、もっともバグがありそうです。

d) ドライブモードの変更に関する情報は入手していませんでしたから、
azt_poll() 関数の case AZT_S_MODE 周辺が動作するかどうか疑わしいです。
自分でテストした時は、raw モードでの読みとりに切替えることができませんでした。
raw モードで読みとるため、Aztech では用意されたモードではなく、別のコマンド
を使っています。これは ISO9660 で使っているセクションではなく、ioctl セクション
で実装しただけのものです。

このドライバは、Intel 486/DX2, 8MB メモリ, 340MB IDE ハードディスクが付いた 
AST PC と、インテルのペンティアム 60MHz, 16MB メモリ, 520MB IDE が付いて、
LST 1.8 ディストリビューションの 1.0.9 バージョンの Linux カーネルが動いている
AST PC で開発しました。カーネルは、gcc 2.5.8 を使ってコンパイルしました。
使っている CD-ROM ドライブは、Aztech CDA268-01A です。ドライブからの情報によれば、
ファームウェアバージョンは AZT26801A1.3 となっています。これには ISA バス
インタフェースカードが付いており、DMA も割り込みも使わずに polled I/O で動作
します。その他のドライブのコードはすべて、インターネット上の数多くのボランティアに、
「リモートで」テストとデバッグをしてもらいました。

問題がありそうだと感じた点や、ドライブの動作などを完全には理解していない点
についてはすべて、ソースコード中で /*???*/ という印を付けています。
Mitsumi のドライバでも、コードを完全には理解していない部分がいくつかありました。


10. 謝辞
Aztech 社の P. Bush と GWDG のE.Moenkeberg、この二人の助けがなかったら、
この作業は不可能だったと思います。P. Bush は Aztech ドライブの技術情報
を伝えてくれましたし、E.Moenkeberg は 様々な CD-ROM ドライブに共通な構造
を分析するという、すごい仕事をしてくれました。彼は 'kernel ready' という
ソフトウェアの作成において大きな助けにもなってくれましたし、ニュースグループ
で CDROM 関連の質問の多くにも答えてくれました。彼こそが本物の Linux CD-ROM
エキスパートです。インターネット上の仲間たち全員にも感謝します。彼らは、
CDROM に関して価値ある情報を集めてくれました。

筆者の最初の試作品はずっと低速だったのですが、Joe Nardone (joe@access.digex.net) は、
それに対しても、我慢強くテストし、コードの改善のため助言をしてくれました。
特に、azt_poll() の「有限状態マシン」は、筆者が mcd.c からコピーしたものでしたが、
Joe はそれを書き直して、きれいな C のコードにしてくれたうえに、見苦しい 'goto' 文
を除去してくれました。

Robby Schirmer (schirmer@fmi.uni-passau.de) は、オーディオ関係 (ioctl) をテストして、
それに関するたくさんのパッチを提案してくれました。

Joseph Piskor と Peter Nugent は ORCHID CD3110 で使ってくれた、最初の
ユーザーで、問題が起こっても大変我慢してくれました。

Reinhard Max は SoundWave32 サウンドカードの CDROM インタフェースに関する
情報を伝えてくれました。

Jochen Kunz と Olaf Kaluza は Conrad の TXC ドライブに関する情報を伝えて
くれました。

Hilmar Berger は CyCDROM CR520ie をサポートするパッチを渡してくれました。

これまで述べてきた事柄に興味がある方なら、どなたでも 'ftp.gwdg.de' の
'pub/linux/cdrom' ディレクトリ、そして 'ftp.cdrom.com' の 'pub/cdrom'
ディレクトリを覗いてみて下さい

11. アドオンプログラムの作成：cdplay.c
aztcd.c で使っている ioctl 関数は、ご自分のプログラムでも使えます。
この使い方の例として、オーディオ CD 用の小さな CD プレーヤ 'cdplay.c' を
紹介します。このプログラムで、オーディオ CD の再生ができるようになります。
指定したトラックの再生、一時停止、レジューム、トラックのスキップ、早送り、
早戻しができます。ドライブを止めずにプログラムを終了すると、再生は継続します。
cdplay を（誤）使用して、データディスクを読み取り、16進ダンプを取ることも
できます。このファイルの付録に、コードがあります。エディタでその部分だけ
取り込んで、'cdplay.c' という別ファイルにしてもかまいません。コンパイルして
実行可能にするには、次のようにします。
  gcc -s -Wall -O2 -L/usr/lib cdplay.c -o /usr/local/bin/cdplay # コンパイルする
  chmod +755 /usr/local/bin/cdplay                              # 実行可能にする
  ln -s /dev/aztcd0 /dev/cdrom                                  # リンクを作る
   （/usr/lib のついては、インクルードファイルがあるトップディレクトリに置き換えて
   ください。また、/usr/local/bin ディレクトリは、できた実行可能なバイナリを置いて
   おきたい場所にしてください）

このプログラムを使うには、cdplay、「および」 /dev/mcd0 ないし /dev/aztcd0 に
正しいパーミッションを設定しなければなりません。オーディオ CD を再生している
時は、/dev/cdrom をマウントしないようにするのを忘れないで下さい。

このプログラムは、aztcd.c の ioctl 関数をテストするために、ハックしたものに
すぎません。筆者はこのプログラムのメンテナンスは行ないません。ですから、
問題に遭遇した場合はこのプログラムを捨てるか、ソースコード 'cdplay.c' を見て
ください。プログラムには最低限のユーザー保護と入力エラー検出しか入っていません。
誤った順序でコマンドを使ったり、CD の間違った場所を読みとろうとすると、
エラーメッセージが出たり、マシンがハングすることさえあるかもしれません。
cdplay の使用中に、STEN_LOW, STEN_LOW_WAIT あるいはセグメント侵犯のエラー
メッセージが出てしまうと、その後システムはそれ以上安定しなくなるかもしれません。
リブートする方がいいでしょう。ioctl 関数はカーネルモードで動作するので、
もっとも普通に見られる、Linux のマルチタスキング保護機能は動作しません。
初期化していない「見当違いの」ポインタ等々を使うと、他のユーザーのデータや
プログラム領域に書きこんだり、カーネルのテーブルを破壊するのは簡単です。
ですから、システムプログラミングをしたりカーネルをハックする際、ioctl を実験
する場合は、常にシステムのコピーを安全な場所にバックアップして下さい（そして
最初にバックアップコピーのリストアを試して下さい）。

'cdtester.c' という、手を加えて改善したバージョンには、CDROM ドライブのテスト用に、
さらに多くの機能があります。これは E.Moenkeberg が書いたもので、
/usr/src/linux/Documentation/cdrom/sbpcd にあります。

Werner Zimmermann
Fachhochschule fuer Technik Esslingen
(EMail: Werner.Zimmermann@fht-esslingen.de)
October, 1997

---------------------------------------------------------------------------
APPENDIX: cdplay.c のソースコード

/* Tiny Audio CD Player

   Copyright 1994, 1995, 1996 Werner Zimmermann (Werner.Zimmermann@fht-esslingen.de)

This program originally was written to test the audio functions of the
AZTECH.CDROM-driver, but it should work with every CD-ROM drive. Before 
using it, you should set a symlink from /dev/cdrom to your real CDROM
device.

The GNU General Public License applies to this program.

History:  V0.1  W.Zimmermann: First release. Nov. 8, 1994
          V0.2  W.Zimmermann: Enhanced functionality. Nov. 9, 1994
          V0.3  W.Zimmermann: Additional functions. Nov. 28, 1994          
          V0.4  W.Zimmermann: fixed some bugs. Dec. 17, 1994
          V0.5  W.Zimmermann: clean 'scanf' commands without compiler warnings
                              Jan. 6, 1995
          V0.6  W.Zimmermann: volume control (still experimental). Jan. 24, 1995
          V0.7  W.Zimmermann: read raw modified. July 26, 95
*/

#include <stdio.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/cdrom.h>
#include <linux/../../drivers/cdrom/aztcd.h>

void help(void)
{ printf("Available Commands:  STOP         s      EJECT/CLOSE  e       QUIT         q\n");
  printf("                     PLAY TRACK   t      PAUSE        p       RESUME       r\n");
  printf("                     NEXT TRACK   n      REPEAT LAST  l       HELP         h\n");
  printf("                     SUB CHANNEL  c      TRACK INFO   i       PLAY AT      a\n");
  printf("                     READ         d      READ RAW     w       VOLUME       v\n");
}

int main(void)
{ int handle;
  unsigned char command=' ', ini=0, first=1, last=1;
  unsigned int cmd, i,j,k, arg1,arg2,arg3;
  struct cdrom_ti       ti;
  struct cdrom_tochdr   tocHdr;
  struct cdrom_subchnl  subchnl;
  struct cdrom_tocentry entry;
  struct cdrom_msf      msf;
  union  { struct cdrom_msf msf;
           unsigned char buf[CD_FRAMESIZE_RAW];
         } azt;
  struct cdrom_volctrl  volctrl;

  printf("\nMini-Audio CD-Player V0.72   (C) 1994,1995,1996  W.Zimmermann\n");
  handle=open("/dev/cdrom",O_RDWR);
  ioctl(handle,CDROMRESUME);
  
  if (handle<=0) 
    { printf("Drive Error: already playing, no audio disk, door open\n");
      printf("             or no permission (you must be ROOT in order to use this program)\n");
    }
  else
    { help();
      while (1)
        { printf("Type command (h = help):  ");
          scanf("%s",&command); 
          switch (command)
            { case 'e':   cmd=CDROMEJECT;
                          ioctl(handle,cmd);
                          break;  
              case 'p':   if (!ini)
                             { printf("Command not allowed - play track first\n");
                             }
                          else
                             { cmd=CDROMPAUSE;
                               if (ioctl(handle,cmd)) printf("Drive Error\n");
                             }
                          break;
              case 'r':   if (!ini)
                             { printf("Command not allowed - play track first\n");
                             }
                          else
                             { cmd=CDROMRESUME;
                               if (ioctl(handle,cmd)) printf("Drive Error\n");
                             }
                          break;
              case 's':   cmd=CDROMPAUSE;
                          if (ioctl(handle,cmd)) printf("Drive error or already stopped\n");
                          cmd=CDROMSTOP;
                          if (ioctl(handle,cmd)) printf("Drive error\n");
                          break;
              case 't':   cmd=CDROMREADTOCHDR;
                          if (ioctl(handle,cmd,&tocHdr)) printf("Drive Error\n");
                          first=tocHdr.cdth_trk0;
                          last= tocHdr.cdth_trk1;
                          if ((first==0)||(first>last))
                            { printf ("--could not read TOC\n");
                            }
                          else
                            { printf("--first track: %d   --last track: %d   --enter track number: ",first,last);
                              cmd=CDROMPLAYTRKIND;
                              scanf("%i",&arg1);
                              ti.cdti_trk0=arg1;
                              if (ti.cdti_trk0<first) ti.cdti_trk0=first;
                              if (ti.cdti_trk0>last)  ti.cdti_trk0=last;
                              ti.cdti_ind0=0;
                              ti.cdti_trk1=last;
                              ti.cdti_ind1=0;
                              if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
                              ini=1;
                            } 
                          break;
              case 'n':   if (!ini++) 
                            { if (ioctl(handle,CDROMREADTOCHDR,&tocHdr)) printf("Drive Error\n");
                              first=tocHdr.cdth_trk0;
                              last= tocHdr.cdth_trk1;
                              ti.cdti_trk0=first-1;
                            }
                          if ((first==0)||(first>last))
                            { printf ("--could not read TOC\n");
                            }
                          else
                            { cmd=CDROMPLAYTRKIND;
                              if (++ti.cdti_trk0 > last)  ti.cdti_trk0=last;
                              ti.cdti_ind0=0;
                              ti.cdti_trk1=last;
                              ti.cdti_ind1=0;
                              if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
                              ini=1;
                            }
                          break;
              case 'l':   if (!ini++)
                            { if (ioctl(handle,CDROMREADTOCHDR,&tocHdr)) printf("Drive Error\n");
                              first=tocHdr.cdth_trk0;
                              last= tocHdr.cdth_trk1;
                              ti.cdti_trk0=first+1;
                            }
                          if ((first==0)||(first>last))
                            { printf ("--could not read TOC\n");
                            }
                          else
                            { cmd=CDROMPLAYTRKIND;
                              if (--ti.cdti_trk0 < first) ti.cdti_trk0=first;
                              ti.cdti_ind0=0;
                              ti.cdti_trk1=last;
                              ti.cdti_ind1=0;
                              if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
                              ini=1;
                            }  
                          break;
              case 'c':   subchnl.cdsc_format=CDROM_MSF;
                          if (ioctl(handle,CDROMSUBCHNL,&subchnl)) 
                            printf("Drive Error\n");
                          else
                            { printf("AudioStatus:%s   Track:%d  Mode:%d   MSF=%d:%d:%d\n", \
                              subchnl.cdsc_audiostatus==CDROM_AUDIO_PLAY ? "PLAYING":"NOT PLAYING",\
                              subchnl.cdsc_trk,subchnl.cdsc_adr, \
                              subchnl.cdsc_absaddr.msf.minute, subchnl.cdsc_absaddr.msf.second, \
                              subchnl.cdsc_absaddr.msf.frame);
                            }
                          break;              
              case 'i':   if (!ini)
                            { printf("Command not allowed - play track first\n");
                            }
                          else
                            { cmd=CDROMREADTOCENTRY;
                              printf("Track No.: ");
                              scanf("%d",&arg1);
                              entry.cdte_track=arg1;
                              if (entry.cdte_track<first) entry.cdte_track=first;
                              if (entry.cdte_track>last)  entry.cdte_track=last;
			      entry.cdte_format=CDROM_MSF;
                              if (ioctl(handle,cmd,&entry)) 
                               { printf("Drive error or invalid track no.\n");
                               }
                              else
                               { printf("Mode %d Track, starts at %d:%d:%d\n", \
                               entry.cdte_adr,entry.cdte_addr.msf.minute, \
                               entry.cdte_addr.msf.second,entry.cdte_addr.msf.frame);
                               }
                            }
                          break;
              case 'a':   cmd=CDROMPLAYMSF;
                          printf("Address (min:sec:frame)  ");
                          scanf("%d:%d:%d",&arg1,&arg2,&arg3);
                          msf.cdmsf_min0  =arg1;
                          msf.cdmsf_sec0  =arg2;
                          msf.cdmsf_frame0=arg3;
                          if (msf.cdmsf_sec0  > 59) msf.cdmsf_sec0  =59;
                          if (msf.cdmsf_frame0> 74) msf.cdmsf_frame0=74;
                          msf.cdmsf_min1=60;
                          msf.cdmsf_sec1=00;
                          msf.cdmsf_frame1=00;
                          if (ioctl(handle,cmd,&msf)) 
                           { printf("Drive error or invalid address\n");
                           }
                          break;
#ifdef AZT_PRIVATE_IOCTLS /*not supported by every CDROM driver*/
              case 'd':   cmd=CDROMREADCOOKED;
                          printf("Address (min:sec:frame)  ");
                          scanf("%d:%d:%d",&arg1,&arg2,&arg3);
                          azt.msf.cdmsf_min0  =arg1;
                          azt.msf.cdmsf_sec0  =arg2;
                          azt.msf.cdmsf_frame0=arg3;
                          if (azt.msf.cdmsf_sec0  > 59) azt.msf.cdmsf_sec0  =59;
                          if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74;
                          if (ioctl(handle,cmd,&azt.msf)) 
                           { printf("Drive error, invalid address or unsupported command\n");
                           }
                          k=0;
                          getchar();
                          for (i=0;i<128;i++)
                           { printf("%4d:",i*16);
                             for (j=0;j<16;j++)
                               { printf("%2x ",azt.buf[i*16+j]);
                               }
                             for (j=0;j<16;j++)
                               { if (isalnum(azt.buf[i*16+j])) 
                                   printf("%c",azt.buf[i*16+j]);
                                 else
                                   printf(".");
                               }
                             printf("\n"); 
                             k++;
                             if (k>=20)
                              { printf("press ENTER to continue\n");
                                getchar();
                                k=0;
                              }
                           } 
                          break;
              case 'w':   cmd=CDROMREADRAW;
                          printf("Address (min:sec:frame)  ");
                          scanf("%d:%d:%d",&arg1,&arg2,&arg3);
                          azt.msf.cdmsf_min0  =arg1;
                          azt.msf.cdmsf_sec0  =arg2;
                          azt.msf.cdmsf_frame0=arg3;                          
                          if (azt.msf.cdmsf_sec0  > 59) azt.msf.cdmsf_sec0  =59;
                          if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74;
                          if (ioctl(handle,cmd,&azt)) 
                           { printf("Drive error, invalid address or unsupported command\n");
                           }
                          k=0;
                          for (i=0;i<147;i++)
                           { printf("%4d:",i*16);
                             for (j=0;j<16;j++)
                               { printf("%2x ",azt.buf[i*16+j]);
                               }
                             for (j=0;j<16;j++)
                               { if (isalnum(azt.buf[i*16+j])) 
                                   printf("%c",azt.buf[i*16+j]);
                                 else
                                   printf(".");
                               }
                             printf("\n"); 
                             k++;
                             if (k>=20)
                              { getchar();
                                k=0;
                              }
                           } 
                          break;
#endif
              case 'v':   cmd=CDROMVOLCTRL;
                          printf("--Channel 0 Left  (0-255): ");
                          scanf("%d",&arg1);
                          printf("--Channel 1 Right (0-255): ");
                          scanf("%d",&arg2);
                          volctrl.channel0=arg1;
                          volctrl.channel1=arg2;
                          volctrl.channel2=0;
                          volctrl.channel3=0;
                          if (ioctl(handle,cmd,&volctrl)) 
                           { printf("Drive error or unsupported command\n");
                           }
                          break;  
              case 'q':   if (close(handle)) printf("Drive Error: CLOSE\n");
                          exit(0);
              case 'h':   help();
                          break;
              default:    printf("unknown command\n");
                          break;
            }
       }
    }
  return 0;
}
==============================================================================
翻訳団体：JF プロジェクト <http://www.linux.or.jp/JF/>
翻訳者：芳賀靖史
校正協力：JF プロジェクト <http://www.linux.or.jp/JF/>

