USB の仕様は、
にあるのだが ... 正直、読む気がしない。なにかとっかかりが欲しいと思っていた。
すこし前になるが、Linux に デバイス側のドライバーがあることを知った。場所は、drivers/usb/gadget 。Mass Storage Class をはじめとして、シリアルや オーディオ、MIDI などのクラスのドライバーもある。さらに USB イーサネットまで。
電子工作で USB を扱うために、これらのドライバーを見ていこうと思う。基本的に、どのようにディスクリプターを定義して、どのようなコマンドがあって、それぞれについてどういう処理をしているかが解れば良いわけで、読むのに Linux の知識はほとんど必要ない。極端な話 処理の中身が解らなければそこで仕様書を紐解けばよいのだ。
まず読んでみたいのは、Mass Storage Class 。SCSI やら ATAPI やら 面倒そうな感じなのだが、どれぐらい面倒なものなのか知りたいと思っていた。このドライバーは、file_storage.c 。 読む Linux のバージョンは、最新の 2.6.31 ということにする。
さて、file_storage.c を開いてみると 4269 行もある。すごく長いのだが、SCSI や ATAPI あと FD 用の UFI など 6 種類のプロトコルの処理が入っていて、転送方法も BBB(Bulk-Only) , CB(Control-Bulk), CBI(Control-Bulk-Interrupt) の 3種類に対応しているようなので仕方がないのかも知れない。
...とか思ってみたのだが、どうも プロトコル毎の差がほとんどない。
プロトコルとしては、
#define USB_SC_RBC 0x01 // Reduced Block Commands (flash)
#define USB_SC_8020 0x02 // SFF-8020i, MMC-2, ATAPI (CD-ROM)
#define USB_SC_QIC 0x03 // QIC-157 (tape)
#define USB_SC_UFI 0x04 // UFI (floppy)
#define USB_SC_8070 0x05 // SFF-8070i (removable)
#define USB_SC_SCSI 0x06 // Transparent SCSI
という風に define されているのだが、USB_SC を サーチして みrると処理自体が変わるところは、2 ヶ所ぐらいしかない。しかも、CONFIG_USB_FILE_STORAGE_TEST が define されている時だけ有効。どうも SCSI かつ Bulk-Only がデフォルトみたい。それ以外は実際は動かないのではないか? という気もしてきた。
define されている SCSI コマンドのうち、なにかしら処理をしているのは、以下のもの。
0x12 INQUIRY
0x15 MODE_SELECT_6
0x55 MODE_SELECT_10
0x1a MODE_SENSE_6
0x5a MODE_SENSE_10
0x1e PREVENT_ALLOW_MEDIUM_REMOVAL
0x08 READ_6
0x28 READ_10
0xa8 READ_12
0x25 READ_CAPACITY
0x44 READ_HEADER
0x43 READ_TOC
0x23 READ_FORMAT_CAPACITIES
0x03 REQUEST_SENSE
0x1b START_STOP_UNIT
0x35 SYNCHRONIZE_CACHE
0x00 TEST_UNIT_READY
0x2f VERIFY_10
0x0a WRITE_6
0x2a WRITE_10
0xaa WRITE_12
特定のプロトコル用に作る場合、たぶん全部は実装する必要はなくて、いくつかは省けるのだろう。
ちなみに、SCSI コマンドの解析実行は、do_scsi_command() という関数で行っている。これを実行しているのは、カーネルスレッド。AVR でいうと、メインループで パケットを受け取り 解析しているような感じか。
ここで フロッピーのUFI(
pdf) をみてみることにする。観点はどれぐらい簡略化されているのかということ。
共通のもの
o 0x12 INQUIRY
o 0x55 MODE_SELECT_10
o 0x5a MODE_SENSE_10
o 0x1e PREVENT_ALLOW_MEDIUM_REMOVAL
o 0x28 READ_10
o 0x25 READ_CAPACITY
o 0x23 READ_FORMAT_CAPACITIES
o 0x03 REQUEST_SENSE
o 0x1b START_STOP_UNIT
o 0x00 TEST_UNIT_READY
o 0x2f VERIFY_10
o 0x2a WRITE_10
o 0xaa WRITE_12
処理を入れなくて良さそうなもの
- 0x04 FORMAT_UNIT
- 0x1d SEND_DIAGNOSTIC
UFI では使わないもの
x 0x15 MODE_SELECT_6
x 0x1a MODE_SENSE_6
x 0x08 READ_6
x 0xa8 READ_12
x 0x44 READ_HEADER
x 0x43 READ_TOC
x 0x35 SYNCHRONIZE_CACHE
x 0x0a WRITE_6
上記以外で UFI で定義されているもの
0x01 REZERO_UNIT
0x2b SEEK_10
0x2e WRITE_AND_VERIFY_10
少々コマンドが減ったが、UFI だから簡単かというとあまりそういう感じはしない。ちょっと期待はずれ。
まぁこの程度なら UFI を作って見ることは出来そうな気がしてきた。やっているうちに理解も深まるだろう。
あと、Renesas の SH7216のアプリケーションノートを発見。この
pdfを見てみると ... 0x06 Transparent SCSI は、次の 11 個のコマンドで実装できているようだ。
o 0x12 INQUIRY
o 0x1a MODE_SENSE_6
o 0x1e PREVENT_ALLOW_MEDIUM_REMOVAL
o 0x28 READ_10
o 0x25 READ_CAPACITY
o 0x23 READ_FORMAT_CAPACITIES
o 0x03 REQUEST_SENSE
o 0x1b START_STOP_UNIT
o 0x00 TEST_UNIT_READY
o 0x2f VERIFY_10
o 0x2a WRITE_10
Linux の file_storage.c をみながら、この 11 個のコマンドを実装してみることにしよう。転送方法は、BBB(Bulk-Only)。他は考えない。
ところで、ATAPI とは何か。実は知らなかったのだが、
Serial ATA (SATA) 開発者向け資料というページを見てだいぶ解ってきた。要するに SCSI プロトコルがベースになっていて、 READ_HEADER、READ_TOC あたりが ATAPI 特有のコマンドなのだろう。
Mass Storage Class で なにをしたいかもともと、SD カードのファイルを1枚のフロッピーみたいに扱って LCD とボタンでフロッピーの入れ替えをサポートする仮想フロッピー装置を作ってみたかったのだ。
それはいずれ頑張るとして... シリアルFLASH を読み書きするのに転用できないかと思ったのが調べだしたきっかけ。
Atmelのアプリケーションノートに AVR916: Upgrading the Flash using a U-Disk というのを発見。これをちょっと見てみると .. scsi_decoder.c で SCSI の処理をしている。かなり参考になりそう。ちなみに、対応しているコマンドは ...
0x06 Transparent SCSI
o 0x12 INQUIRY
o 0x1a MODE_SENSE_6
o 0x5a MODE_SENSE_10
o 0x1e PREVENT_ALLOW_MEDIUM_REMOVAL
o 0x28 READ_10
o 0x25 READ_CAPACITY
o 0x03 REQUEST_SENSE
o 0x00 TEST_UNIT_READY
o 0x2f VERIFY_10
o 0x2a WRITE_10
x 0x23 READ_FORMAT_CAPACITIES
x 0x1b START_STOP_UNIT
MODE_SENSE_10 が増えているが、全部で10個。実際の USB AVR の処理も含まれているし、こっちの方が参考になるかも。
作るだけなら出来そうな気もしなくはないのだが、4KB 単位で Write しないと いけないとか変な仕様になりそう。セクターサイズを 4KB にするとかも出来そうだけど OS が対応していないような気がするし↑の変な仕様のほうがまだマシかも知れない。
追記1:
usb162-0.1 を使って、シリアルFLASH のデバッグをして、やはり USB シリアル(CDC-ACM)が、デバッグに使えるのは便利だと実感したのだが ... Mass Storage Class のデバッグにもUSB シリアルが使えないかと思ってしまった。実用としても便利そうだしなんとかしたい。
最初の壁は、エンドポイントが 5つしかないこと。Mass Storage Class では、2 つのエンドポイントが必要だが、usb162-0.1 では 4 つ使っている。内訳は、デフォルトエンドポイント、CDC 、CDC-DATA(2つ)。
実をいうと、CDC のエンドポイントは割り当てているものの、なにも処理していない。こいつをどうにかすれば 3つに減らせる。
で、CDCの仕様書をはじめて見てみた。CDC には、1つのエンドポイントが必要と書いてあるが、一方でデフォルトエンドポイントを使うという記述もある。
じゃぁ いらないのかと思って、CDC のエンドポイント を定義しないようにしてみると ... エラーにはならないものの通信がまったくできない。
ここではまったのだが、結局CDC のエンドポイントをデフォルトエンドポイントにしたら動いた。これで良いのか?とも思うのだが動いたから OK としよう。
これで、Mass Storage Class のインターフェイスを追加できるようになった。
次の問題は、インターフェイスを追加するだけで良いのか?ということ。まず SETUP の クラスリクエストは、どのクラスのリクエストなのかという情報がない。どうもCDC と Mass Storage Class とでは 重なっていないようでなんとかなりそう。
後は OS が対応しているかどうか。... 複合デバイスでググってみたら、
複合デバイスの作りかたなんてページを発見。なんでも IAD -- USB Interface Association Descriptor というディスクリプタを記述するらしい。普通のでさえ良くわからないのに面倒な話だ。... といっても処理自体は変えなくて良さそうなので、普通に作った後問題だったら対処することにしよう。
さて、Mass Storage Class では、いくつかの SCSI コマンドを用意すれば良いのは分かった。他になにをすれば良いのか?
まず、ディスクリプタについて。
どうも インターフェイスと エンドポイント2つ(BULK IN,OUT)を定義するだけで良いらしい。インターフェイスのクラスは、0x08, サブクラス 0x06 (SCSI)、プロトコル 0x50 (Bulk-Only)。
SETUP は、GET_MAXLUN(0xfe)、RESET(0xff) という 2つのクラスリクエストに対応すればよい。
後は、Bulk-Only がどういうものか分かれば、作り出せる。
これは、SH7216のアプリケーションノートの
pdfに解説がある。
コマンドトランスポート(OUT) - データトランスポート(IN or OUT) - ステータストランスポート(IN) の3ステージで通信する規約らしい。
コマンドトランスポートでは、31 バイトの Commannd Block Wrapper(CBW) パケットが送られてきて、 コマンドによって データトランスポートを行い、最後に 13 バイトの Commannd Status Wrapper(CSW) パケットを送る。
少なくとも Bulk-Only では、非同期の処理はなく、コマンドを順番に処理すれば良い。
なんか簡単そうな感じまでするが、メモリが使えないのでデータトランスポートをバッファレスで処理しないといけない。このあたりは、AVR916を参考にしようと思う。
追記2:実装について
AT90USB162 は、エンドポイントのバッファから、1バイトづつ取り出す仕様になっていてメモリのようにランダムアクセスは出来ない。CDC の場合は、xxx_getc という API を作りエンドポイントのバッファから取り出している。(送る場合も同様で xxx_putc を使う)。で、バッファーの面倒を見るのが xxx_poll 。
さて、Mass Storage Class の場合もエンドポイントの処理は同じ。xxx_getc/xxx_putc をコピーして対象のエンドポイントを変更したものを yyy_getc/yyy_putc として作成し、xxx_poll の中の処理をコピーして、 Mass Storage Class のエンドポイントを対象にしたものを xxx_poll に追加する。
次に yyy_getc/yyy_putc を使って、Mass Storage Class 処理を行う関数 zzz_poll を 作る。こいつは、上記3ステージに対応した状態変数で処理を変えるステートマシンとして作る。
コマンドトランスポートでは、バッファーに 31 バイト以上あれば、処理を開始してコマンドを解析・処理をして、次の状態に移る。
データトランスポートでは、何の処理をどのオフセットから何バイト行うかを状態変数として持ち、バッファー分だけ(たぶん 32バイト)の処理をしてポインターを進める。最後まで行けば 次の状態に遷移。途中でエラーになった場合でも最後まで処理をしないといけない(はず)。
ステータストランスポートでも同じ感じ。何を返却するか覚えていて、バッファーが空けばそれを送り 元の状態に戻る。
重要なのは、中で待ち合わせをしないこと。すぐに処理できないのであれば、関数から抜ける (リターンする)。
理由は2つある。ひとつは、他の処理をできるようにするため。zzz_poll の関数名を付ける所以。もうひとつは、RESET がきたら確実に初期状態にするため。
さて、悩んでいるのは、31 バイトの CBW を一旦メモリにコピーするかどうか。31 バイトでも貴重なのだ。
ちなみに CBW のうち後ろ16バイトは、SCSI コマンド。前の15 バイトのうち 4 バイトを使うエリアが3つある。ひとつは シグネチャ 。CBW であることを確認するためのもので、文字列で 'USBC' 。次がタグ(シーケンス番号)。値を覚えていて同じ値を CSW に入れなければならない。最後が データトランスポートの転送長 。そのまま状態変数に使える。あとの3バイトは、転送方向、LUN、SCSI コマンドのバイト数。
ついでに CSW も記しておこう。13 バイトのうち、3つは 4バイトのエリア。最初はシグネチャで、'USBS'。次はタグ。最後が 処理しなかった 転送バイト数。最後1バイトがステータス。0 が成功、1がコマンドのエラー、2 がフェーズエラー。
CBW の 転送バイト数を状態変数として使うとすれば最後に 0 になるから、ちょっと書き換えれば CSW になる。
頭の15 バイトはメモリに置いた方が良さそうだ。SCSI コマンドは微妙。読みながら解析するのもアリかも知れない。
追記3:SCSI コマンドの実装
ちょっと AVR916 と UFI の仕様書をを見ながら実装してみている.
最初に INQUIRY を見てみた。データトランスポート で 36 バイトのデータを返すらしい。データ自体は const なので FLASH に置けば良さそう。パラメータエラーの場合、エラーコードを 覚えておいて、REQUEST_SENSE が来たときにその値を返す。
REQUEST_SENSE も データトランスポート で 18 バイトを返すが、sense_key と sense_asc の 2 つのデータが 18 バイトの中に埋まっている。コードの量を減らすために どういう工夫ができるのか悩み中。
よく見ると 0 が多い。 4 バイトのデータで 下位 1 バイトしか使わないケースもいくつかある。
結局 1 バイトを 書き込む yyy_putc 以外に、上位が 0 で 2 バイト書き込む yyy_putc2 とか 4 バイト書き込む yyy_putc4 とかを作る ことにした。
あと、頭が痛いのが MODE_SENSE 。_6 以外に _10 があって微妙に違う。送るデータも 共通データ以外に 2 種類の PAGE_CODE があり、設定によって切り分け 4 通りのデータになる。_6/_10 の組み合わせを入れると 8 通り。あぁ面倒。
ちょっと、Linux の file_storage.c を見てみたら、対応している PAGE_CODE は、CACHING_MODE_PAGE のみ... そしてそれは上記の 2 種類とは別。... ということは 特にサポートしなくて良いのかも。
さて、いろんなコマンドに対してデータトランスポートで返却しているようだ。処理するときに switch 文とかで処理するのは、いかにも効率が悪いし コードの量も増える。関数ポインタに処理関数を登録しておいて、それを実行するようにしたほうが良さそうに思えてきた。ちなみに、AVR916 では、コマンド処理の延長で 返却データを 書き込んでいる。そのほうが簡単なのだが... 最初の想定の通りステートマシンにしようと思う。
追記4:Linux (Vine-5.0)につないでみた。
とりあえず エンドポイントを 0 にマップしたやつを つないでみると ...
usb 3-1: config 1 interface 0 altsetting 0 has an invalid endpoint with address 0x80, skipping
usb 3-1: configuration #1 chosen from 1 choice
cdc_acm 3-1:1.0: ttyACM0: USB ACM device
なんか嫌なメッセージを出しつつも、初期化はできた。
で、minicom でつないでみると ...
minicom: cannot open /dev/ttyACM0: Input/output error
ダメじゃん。どうしよう。
さらに、適当に作った Mass Storage Class をくっつけたのを Linux につないでみた。
scsi scan: INQUIRY result too short (5), using 36
scsi 2:0:0:0: Direct-Access PQ: 0 ANSI: 0
sd 2:0:0:0: [sdb] Sector size 0 reported, assuming 512.
sd 2:0:0:0: [sdb] 1 512-byte hardware sectors (0 MB)
sd 2:0:0:0: [sdb] Write Protect is off
sd 2:0:0:0: [sdb] Mode Sense: 00 00 00 00
sd 2:0:0:0: [sdb] Assuming drive cache: write through
sd 2:0:0:0: [sdb] Sector size 0 reported, assuming 512.
sd 2:0:0:0: [sdb] 1 512-byte hardware sectors (0 MB)
sd 2:0:0:0: [sdb] Write Protect is off
まだまだダメそうなのだが、とりあえず Linux は scsi デバイスを認識しようとしてくれているみたい。
CDC の エンドポイントは問題だが、Mass Storage Class はテストできそうな感じ。
ちなみに、Windows XP につなぐと ... CDC は使える。Mass Storage Class をくっつけても それは認識されず CDC だけは使える状態。
とりあえずは、Linux で Mass Storage Class を テストできるようにしよう。
追記5:Linux (Vine-5.0)でなんか動いた。
まずエンドポイントの問題。これは、core の部分で 0 番をはじいているため。なんでもいいかも とか思って、(存在しない) 5 番のエンドポイントを指定したら OK だった。
ちょっといろいろあったのだが、最終的に次のメッセージが出るようになった。
usb 3-1: new full speed USB device using uhci_hcd and address 22
usb 3-1: configuration #1 chosen from 1 choice
cdc_acm 3-1:1.0: ttyACM0: USB ACM device
scsi20 : SCSI emulation for USB Mass Storage devices
usb-storage: device found at 22
usb-storage: waiting for device to settle before scanning
usb-storage: device scan complete
scsi 20:0:0:0: Direct-Access ATMEL AT90USB128 M S 0.00 PQ: 0 ANSI: 3
sd 20:0:0:0: [sdb] 1025 512-byte hardware sectors (1 MB)
sd 20:0:0:0: [sdb] Write Protect is off
sd 20:0:0:0: [sdb] Mode Sense: 0c 00 00 00
sd 20:0:0:0: [sdb] Assuming drive cache: write through
sd 20:0:0:0: [sdb] 1025 512-byte hardware sectors (1 MB)
sd 20:0:0:0: [sdb] Write Protect is off
sd 20:0:0:0: [sdb] Mode Sense: 0c 00 00 00
sd 20:0:0:0: [sdb] Assuming drive cache: write through
sdb: unknown partition table
sd 20:0:0:0: [sdb] Attached SCSI removable disk
ちなみに、いちばんはまったのは、送信のタイミング。0 バイト送信とかしないように 明示的にに flush するようにしたら、きれいに動くようになった。
で、ログをメモリにとって、シリアルを接続して確認したのだが、次のシーケンスで通信している。
FE (setup 0xff -- RESET )
FE (setup 0xff -- RESET )
FC (setup 0xfe -- GET_MAXLUN )
C0 12 24 00 SCSI INQUIRY - trans 0x24 bytes - OK
C0 00 00 00 SCSI TEST_UNIT_READY - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 1A C0 3F 00 SCSI MODE_SENSE_6 (ALL) - trans 0xC0 bytes - OK
C0 00 00 00 SCSI TEST_UNIT_READY - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 1A C0 3F 00 SCSI MODE_SENSE_6 (ALL) - trans 0xC0 bytes - OK
C0 28 00 00 SCSI READ_10 - trans 0xXXXXX00 - OK
C0 1E 00 00 SCSI PREVENT_ALLOW_MEDIUM_REMOVAL - OK
C0 00 00 00 SCSI TEST_UNIT_READY - OK
C0 1E 00 00 SCSI PREVENT_ALLOW_MEDIUM_REMOVAL - OK
:
実際に READ/WRITE してみる。(ちなみに 先にデバイスはない。 -- /dev/null, /dev/zero のようなもの )
# dd if=/dev/sdb of=123 bs=512
1025+0 records in
1025+0 records out
524800 bytes (525 kB) copied, 3.83482 s, 137 kB/s
# dd of=/dev/sdb if=123 bs=512
1025+0 records in
1025+0 records out
524800 bytes (525 kB) copied, 3.70997 s, 141 kB/s
一応エラーは出ていないようだ。
1025 セクタあるのは、READ_CAPACITY で返却するのは 最後のセクター番号のため。-1 しないといけない。
動いた記念に ここに書いた 状態のものを
usb162-0.2-wk2.tar.gz に置いておく。
ちなみに、Windows では、ディスクドライブとしてちゃんと認識されたものの ... CDC-ACM が認識されない。inf ファイルを作らないといけなさそうだが、作り方がわからない。あるいは、IAD -- USB Interface Association Descriptor を記述しなければならないのか?
それはともかく、次の段階は、本物の シリアルFLASHをつなげてみるつもり。で、4096B セクター でもいけるかどうかのテスト。
全部うまくいったら、API を整理。
追記5:READ/WRITE できるようになった。
一応 4KB セクターと 512B セクターを切り替えられるように作れた。
Read:
1048576 bytes (1.0 MB) copied, 17.6026 s, 59.6 kB/s
Write:
1048576 bytes (1.0 MB) copied, 63.263 s, 16.6 kB/s
えらく遅くなってしまった。Write が遅いのは、仕方がない。4KB の ERASE に 0.3 秒かかるはずなので、ERASE だけで Typ. 76.8 秒。
Read が遅いのは、ソフト SPI になってしまっているのも原因。そもそも FLASH なしでも 137 kB/s しか出ていない。バッファのサイズを大きくするか ダブルバッファにすれば .. 多少は速くなるのだが ...
値はちゃんと書き込めているし、2 回 READ して コンペアしても同じ値。
問題はあると思う。512B セクターで 4KB の 先頭しか Write しなければ、残りは 0xff で埋まる。先頭を外して書き込めば、AND になる。4KB セクターなら そのような問題はないが、今度は OS が対応しているかどうかが問題。これからテストしてみるつもり。
ここに書いた 状態のものは、
usb162-0.2-wk3.tar.gz。
gcc3 で ビルドすると 6146 バイト。シリアルでのモニターとか デバッグコード入れたままなので、チューニングすれば多少小さくなるはず。
追記6: Windows XP で複合デバイスをつかうには?
これ自体は簡単みたい。
- 装置のUSBコンフィグレーションに 8 バイトの IAD をインターフェイスの前に追加する。
- inf ファイルは、VIDとPID のところに、&MI_00 を追加。
これで、CDC-ACM 一個でも複合デバイス。
ただし、Windows XP SP3 以降でしかダメ。
ところで、いろいろ実験しているとドライバの設定が変に残って困る。
USBDeviewを使ってアンインストールするのが、便利で良いらしい。
あと、変にCOMポート番号が増えてしまう。
”USB シリアルポート番号を増やさないようにする"という記事が参考になった。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\COM Name Arbiter の ComDB 。これが使用中のビットマップになっている。
さて、結局CDC のエンドポイントをデフォルトエンドポイント(0 番)にしたら動いた件。
CDC-ACM 一個の複合デバイスでも OK だった。ただし、存在しない 5 番を割り当てるのは NG 。
そして、MSC を追加したときも NG 。困った。
ここで書いている NG の現象は、COMポートが割り当てられてターミナルも開けるのだが、通信が出来ない。Linux でも MSC がダメダメだったときは、あおりをくらって通信できなかったから、MSC が Windows の期待どおりに動けばあるいは通信もできるかも知れない。
追記7: Windows XP でなんか動いた。
EEPROM にログをとって調べたところ、MODE_SENSE_6 で おかしくなっていたことが判った。そうなると、Windows は MSC に対して RESET を出す。まだまだ変なのだが、CDC-ACM 経由で生きている状態で ログを取れるようになった。
以下シーケンス。
FE (setup 0xff -- RESET )
FE (setup 0xff -- RESET )
FC (setup 0xfe -- GET_MAXLUN )
C0 12 24 00 SCSI INQUIRY - trans 0x24 bytes - OK
C0 23 FC 05 SCSI READ_FORMAT_CAPACITY - trans 0xFC bytes - ERR
C0 03 12 00 SCSI REQUEST_SENSE - trans 0x12 bytes - OK
C0 23 FC 05 SCSI READ_FORMAT_CAPACITY - trans 0xFC bytes - ERR
C0 03 12 00 SCSI REQUEST_SENSE - trans 0x12 bytes - OK
C0 23 FC 05 SCSI READ_FORMAT_CAPACITY - trans 0xFC bytes - ERR
C0 03 12 00 SCSI REQUEST_SENSE - trans 0x12 bytes - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 28 00 00 SCSI READ_10 - trans 0xXXXXX00 - OK
C0 1A C0 1C 00 SCSI MODE_SENSE_6 (EXCPT) - trans 0xC0 bytes - OK
C0 1A C0 3F 00 SCSI MODE_SENSE_6 (ALL) - trans 0xC0 bytes - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 28 00 00 SCSI READ_10 - trans 0xXXXXX00 - OK
C0 28 00 00 SCSI READ_10 - trans 0xXXXXX00 - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 28 00 00 SCSI READ_10 - trans 0xXXXXX00 - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 00 00 00 SCSI TEST_UNIT_READY - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 28 00 00 SCSI READ_10 - trans 0xXXXXX00 - OK
C0 28 00 00 SCSI READ_10 - trans 0xXXXXX00 - OK
C0 28 00 00 SCSI READ_10 - trans 0xXXXXX00 - OK
C0 1A 0C 3F 00 SCSI MODE_SENSE_6 (ALL) - trans 12 bytes - OK
C0 00 00 00 SCSI TEST_UNIT_READY - OK
C0 00 00 00 SCSI TEST_UNIT_READY - OK
C0 00 00 00 SCSI TEST_UNIT_READY - OK
:
:
READ_FORMAT_CAPACITY が来ているがエラーにしている。AVR916 でも実装されていないので、これで良いのだろう。
2 つめの SCSI MODE_SENSE_6 (ALL) が サイズ 12 バイトで来ている。あと MODE_SENSE_6 (EXCPT) というのも。AVR916 に習って実装しなおした。
これで、RESET が来なくなった模様。
CDC-ACM は、なんか反応が悪い。
-- 反応が悪いのは、EEPROM の書き込みでヘマをしてて、コマンドがくるたびに、EEPROM に書いてしまっていたため。EEPROM に書かなくしたらまともになった。
さて、ディスクが見えたので、フォーマットしてみた。1MB あったのだが、フォーマット後は 400KB 弱。とりあえず 30KB のファイルを 3 つばかりコピー。
いったん、取り外して 再度接続すると ... さらに容量が減った。ファイルもちゃんと読めたのは 2つのみ。
これは、4KB 単位でしか書き換えできないのが理由。じゃぁ、セクタサイズを 4KB にしてみると ... Windowsは、フォーマットを完了できませんでした。.. と言われた。たぶん、容量的な問題。
まぁこんなものでも、役に立つのだ。ファイルシステムを作るのに無理があるだけの話。
とりあえずでも動いた記念に ここに書いた 状態のものを
usb162-0.2-wk5.tar.gz に置いておく。
追記8: 泥沼化
とりあえず動いたので、チューニングやらなにやらしていたのだが、なにか変になってきた。で、シリアルFLASH の シグネチャーを COMポート経由で READ してみると ... ちゃんと読めない。
壊れた?とか思ってリトライ入れたり、なんだかんだしているうちに、よくわからなくなってきた。
思い立って アクセスランプを付けてみたら、ずっと激しく点滅。どうも Windows が、ずっとアクセスしているらしい。
COMポート経由での アクセスとぶつかると、当然のように正しく動かないが、それが高い確率で起きていたのだった。
とりあえず一安心なのだが ... COMポートの通信ができなかったり段々変になってきた。
もうよく分からないので、いったん wk5 に戻って やりなおそうと思う。
とりあえず Windows がずっとアクセスしている原因の追究:
ログは以下のもの
C0 1A 0C 3F 00 SCSI MODE_SENSE_6 (ALL) - trans 0xC0 bytes - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 28 00 00 SCSI READ_10 - trans 0xXXXXX00 - OK
C0 1E 00 00 SCSI PREVENT_ALLOW_MEDIUM_REMOVAL - OK
C0 00 00 00 SCSI TEST_UNIT_READY - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 28 00 00 SCSI READ_10 - trans 0xXXXXX00 - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 28 00 00 SCSI READ_10 - trans 0xXXXXX00 - OK
C0 25 08 00 SCSI READ_CAPACITY - trans 0x08 bytes - OK
C0 28 00 00 SCSI READ_10 - trans 0xXXXXX00 - OK
C0 28 00 00 SCSI READ_10 - trans 0xXXXXX00 - OK
C0 28 00 00 SCSI READ_10 - trans 0xXXXXX00 - OK
C0 00 00 00 SCSI TEST_UNIT_READY - OK
C0 1E 00 00 SCSI PREVENT_ALLOW_MEDIUM_REMOVAL - OK
C0 1A 0C 3F 00 SCSI MODE_SENSE_6 (ALL) - trans 0xC0 bytes - OK
12 バイトの MODE_SENSE_6 (ALL)の内容が気に入らないのか、はたまた READ したデータの内容が気に入らないのか。
... MODE_SENSE_6 でどういう情報を返すのか?とか よく分かっていないので、SCSI の解説がないかと思ってググったら、こんなのを見つけた。
ハギワラシスコムの 製品の SCSI MODEL Flash Drive の SCSI の仕様書だそうだ。日本語だし 知りたいことが過不足なく書いてあるし ... これはいいものだ。
ちょっとチェックしてみる。
- 0x12 INQUIRY
AVR916 を元にしたのだが、今は ANSI Ver 3 になっている。だが、Linux の file_storage.c もこれも Ver. 2 。2 にしたほうが無難か。あと、VENDER_ID の直前のバイトが 0x10 。Sync と書いてある。内部にキャッシュがあって Sync が必要なのか?
- 0x1a MODE_SENSE_6
DBD というフラグがあって、これが 0 のときは、Block Descriptor を付けるらしい。Block Descriptor は、8 バイトで READ CAPACITY のようなデータ。サポートしないときは、エラーにするのか?
あとページコード。0x1C の EXPT はサポートしていない。そのかわり、0x08 CACHING はサポートしている。あと 0x04 GEOMETRY も。
- 0x1e PREVENT_ALLOW_MEDIUM_REMOVAL
これは、ロックする機能で、REMOVAL でないからサポートしていないようだ。内蔵FLASH は、実際REMOVAL でないし、おなじようにすればあるいは Windows からのアクセスが止まるのかも知れない。
- UNIT ATTENSION
あーそういうものなのか。Windows は、これを待っていたのか。
ちょっと適当なのだが、TEST_UNIT_READYのみ、UNIT ATTENSION に対応したところ、Windows からのアクセスが減った。-- 定常状態では、TEST_UNIT_READYだけが秒間 2 回ぐらい発行されている。
あと、フォーマットのとき、VERIFY_10 を発行するらしく、中身がないものを追加。書かなかったが、512Bセクタでも、フォーマットを完了できませんでした状態になっていたのが直った。
コードが枝分かれしてしまっているので、ここに書いた 状態のものは
usb162-0.2-wk5a.tar.gz 。あくまでテスト用。