2014.3.9

前へ
次へ
ホームページトップへ戻る

CPLD+SIMMを使ってUSBプロトコルの解析を!
VHDLを速習! XC95144XL+16MB・SIMMを使ってUSBプロトコルアナライザを作ってしまいました!
主目的は差し迫った事情からUSBプロトコルの解析をすることだったのですが、その手段として選んだのがコレ!


[第25回]


●STRING DESCRIPTOR

USB機器をパソコンに接続すると、最初にEnumerationという手続きが開始されます。
これはなかなかに面倒でわかりにくい手続きです。
ネット上でもそれこそいやになってしまうくらいあれこれ調べましたし、もちろん「トランジスタ技術」の過去記事も繰り返し読みました。
それでもいまひとつはっきりしないところがありました。
自分ではわかったつもりでいても、やはり理解しきれていないところが残っていたようです。

すでに書きましたように、唐突にCPLD+SIMMなんてものを持ち出してUSB信号の解析をやりはじめましたのは、ND80ZV(ND80Z3.5)をご購入いただいた方のうち何人かの方から「USBで接続しようとすると不明なデバイスと表示されて接続できません」というメールをいただいたことに端を発しています。
しかし私の所有するWindows98からWindows7までのパソコンの全てでND80ZV(ND80Z3.5)をUSB接続すると正しく認識されてしまうために、これまでどうすることもできませんでした。
たまたま葛Z術少年出版の吉崎様がお持ちのパソコンでも同様のことが発生したとのメールをいただきましたので、それをお借りして徹底的に調べてみることにしました。

いろいろ調べていくうちに、どうやらそれがUSB3.0という規格のせいであるらしいということがわかってきました。
ND80ZV(ND80Z3.5)に搭載のPIC18F14K50に仕込んであるプログラムはHIDデバイスというクラスのもので、USB2.0(厳密に言えばUSB1.1)規格のものです。
USB3.0は2.0も1.1も接続できるのですが、どうやら完全に互換ではなくて、それなりに機能アップされたもののようです。
調べてみてわかってきたことのひとつがアクセスタイムがやたら速いということでした。
私はそんな規格は全く知らなかったものですから、当然そのような速度に応えるプログラムにはなっていませんでした。
そのために吉崎様からお借りしたパソコンではUSB接続でこけてしまっていたのでした。
以上の経緯については、今までの回で説明をいたしました。

実はそのほかにも副産物といいますか、当初どうにも信じられなかったちょっと納得できかねるルールがあることにも気が付きました。
それが今回のテーマのSTRING DESCRIPTORについての扱いです。

STRING DESCRIPTORは装置のメーカー名やデバイス名などを文字情報としてホストに伝えるためのディスクリプタです。
このSTRING DESCRIPTORは必須ではなくて省略可能です。
余計なものをつければそれだけ面倒なことになる度合いも増えますので、ND80ZV(ND80Z3.5)に搭載のPIC18F14K50のプログラムではSTRING DESCRIPTORは省略していたのですが、USB接続で認識できないという問題を解決していくなかで、ひょっとしたらそのこと(STRING DESCRIPTORを省略していること)が認識されない要因かとも考えて、それも追加してテストをしていくなかでSTRING DESCRIPTORについてのホストからのちょいと不思議な要求がだんだんと明らかになってきました。

このように書いていましても、実は今でも内心「ほんまかいな」とまだいささか腑に落ちていないところがあります。
上に書きましたように、STRING DESCRIPTORは省略可能ですので、本当はこんなことは深く追求しなくてもよいことなのかもしれませんが、納得できないままにしておくのも面白くありませんし、この機会に将来のためにできれば追求してはっきりさせておこうと思ったのです。

STRING DESCRIPTORは英語以外の言語にも対応するためだと思いますが、16ビットで1文字を示すルールになっています。
これは実際にプログラムでアルファベットコード(ASCIIコード)を使って記述する場合にはなかなか手間がかかるルールです。
とりあえず以上のことを予備知識としていただいて、なにはともあれ実際のデータのやりとりの様子を見ていただくことにいたしましょう。

下は吉崎様からお借りしたWindowsXPパソコン(USB3.0)に秋月のPICWRITERを接続したときのUSB信号をCPLD+SIMM回路で記録して、そのデータを自作ソフトで解読したものの一部です。
解析作業を繰り返し行なっていくうちに、CPLD+SIMMに仕込むVHDLプログラムも、それから解読のための8086プログラムもだんだんと精度のよいものになってきました。
下のリストなどは、はじめのころに見ていただいておりましたものに比べますと、ちょいと自画自賛でありますが、なかなか見事なできばえだと思います。
ここまでできればほとんど完璧といってもよろしいのではありませんでしょうか。

(D6PX6CK.TXT)
0025900 SOF FNO=73D
0025910 SETUP ADRS=04 ENDP=00 
        DATA0  80 06 03 03 09 04 FF 00   GET_DESCRIPTOR 
        ACK 
0025912 IN ADRS=04 ENDP=00 
        NAK 
0025913 IN ADRS=04 ENDP=00 
        NAK 
0025914 IN ADRS=04 ENDP=00 
        NAK 
0025916 IN ADRS=04 ENDP=00 
        DATA1  10 03 4F 00 6C 00 48 00   
        ACK 
0025918 IN ADRS=04 ENDP=00 
        NAK 
0025918 IN ADRS=04 ENDP=00 
        NAK 
0025919 IN ADRS=04 ENDP=00 
        NAK 
0025920 IN ADRS=04 ENDP=00 
        NAK 
0025922 IN ADRS=04 ENDP=00 
        DATA0  6F 00 73 00 73 00 00 00   
        ACK 
0025922 IN ADRS=04 ENDP=00 
        NAK 
0025924 IN ADRS=04 ENDP=00 
        DATA1  
        ACK 
0025926 OUT ADRS=04 ENDP=00 
        DATA1  
        ACK 
0025960 SETUP ADRS=04 ENDP=00 
        DATA0  80 06 00 02 00 00 FF 00   GET_DESCRIPTOR CONFIG
        ACK 

このリストでは、ホストから送出されたGET DESCRIPTORコマンドと、それに応えてPICWRITERからSTRING DESCRIPTORが送出されたところが記録されています。

ここで簡単にコマンドについて説明をしておきましょう。
Enumeration手続きでは、ホストから送られるコマンドも、装置から返されるデータもともに一度に送るのは8バイト以内というルールがあります。
リストの最初にある 80 06 03 03 09 04 FF 00 がホストから送られたコマンドです。
最初の2バイト 80 06 がディスクリプタ送信要求です。
次の 03 03 は、前の03はINDEXで、後ろの03がDESCRIPTORの種類(03はSTRING DESCRIPTOR)を示しています。
STRING DESCRIPTORはINDEX0(基本情報)とメーカー名、製品名、製品番号(シリアルナンバー)に分けてそれぞれを区別するINDEXをつけて示します。
それぞれのINDEXをどういう番号にするかは、最初に送るDEVICE DESCRIPTORに記述します。
PICWRITERの場合INDEX=03はシリアルナンバーのようです。
次の 09 04 はSTRING DESCRIPTORを記述する言語の指定です。
0409は英語(アルファベット)で表記することを示しています。
Enumerationでは16ビットのデータは最初に下位バイト、次に上位バイトの順に並べます。

最後の FF 00 は送信すべきDESCRIPTORの長さ(バイト数)の指定です。
ここでの長さの指定は16ビットですが、実際にディスクリプタを送信するときにはその先頭でもディスクリプタの長さを指定します。
ところがディスクリプタの先頭に置く長さ指定のためのエリアは1バイトという決まりになっていますので、実際には最長でも255バイト(FFH)の長さしか記述ことはできません。
ですからここが00FFであるのは、ホストからは長さを指定していないことを示しています。

STRING DESCRIPTOR送信要求コマンドが送出されて、PICWRITERがそれを受け取ってACKを返すとホストからはほとんど間髪を入れずにINパケットが連続して送出されています。
まるで「はよォ送らんかい。われ、なにもたもたしとんねん」とでも言われているようです。
前にもちょっと説明しましたが行の先頭に表示されている7桁の数字は、USBデータを16進数ダンプ表示したときの行番号に相当しています。
その1行は16バイト(128ビット)のデータです。
上のリストで見ますとこのときのINパケットはほとんど毎行発行されていますから、その間隔は、USB信号のクロックが12MHzなので
128/12≒11μSほどと計算されます。

その速さではさすがにPICWRITERも即答は無理のようで、準備ができるまでNAKを返しています。
25916行のINパケットになって、やっとSTRING DESCRIPTORの最初の8バイトを送っています。
この8バイトの意味ですが、先頭の1バイトはディスクリプタの長さを示しています。
10Hですからこのディスクリプタ(シリアルナンバー)は全体で16バイトであることを示しています(この数値を覚えておいてください)。
次の03はこのディスクリプタがSTRING DESCRIPTORであることを示しています。
ディスクリプタの先頭の1バイトはそのディスクリプタの長さ(バイト数)で、次の1バイトはそのディスクリプタの種別を示します。
上でも書きましたように、STRING DESCRIPTORの種別コードは03です。
3バイト目からはディスクリプタのデータそのものの記述が続きます。
その部分は 4F 00 6C 00 48 00 ですから、”OlH”です。
その次に送出された8バイトは 6F 00 73 00 73 00 00 00 ですから”oss”です。
続けると ”OlHoss”になります。
意味不明な文字列です。
何でしょう?
ここはシリアルナンバーのはずなのですが…。

それはともかくとしまして、このリストに表示されているコマンドとデータのやり取りにはおかしいところがあるのですが、それはどこだかおわかりになりますでしょうか。
おかしいというよりも、理屈に合わないと言うべきでしょうか。

あ。
もう少し説明を加えてから考えていただいたほうがよいかもしれませんね。
コマンドやデータは送受信の途中でノイズなどの影響で落ちてしまって途中のやり取りが欠落してしまう可能性を考慮したためでしょうか、先頭にDATA0かDATA1パケットをつけて送られます。
DATA0とDATA1は交互に送ります。
ホストから送られるコマンドはやり取りの開始ですから必ずDATA0です。
それに対する装置からの返答はDATA1から始めます。
装置からの応答が8バイト以上の場合は、8バイトごとに区切って、最初にDATA1、次にDATA0、その次はDATA1というように交互に送ります。

ところでこれものちほどまた説明しますが、所定のバイト数のデータをホストが受け取ると、ホストはそれを受け取ったしるしとして中身の無いDATA1を装置に返します(このときは装置側がDATA1で終わったときでも必ずDATA1が送出されます)。

さてそれだけのことを頭に入れていただいた上でもう一度上のリストのやり取りを見てください。
ほら。
おかしいことに気がつきませんでしょうか?

本日は時間がなくなってしまいました。
この続きは次回にいたします。

CPLD+SIMMを使ってUSBプロトコルの解析を![第25回]
2014.3.9upload

前へ
次へ
ホームページトップへ戻る