SSブログ

HIDのバッファはあてにならないのか? [USB]

hid_apli_result_001.pngまあ確かに中途半端な知識でこれをやっていると言うのは事実ですが、もう少し色々検証してみたいと思います。

そもそもHIDデバイスか、否か、と言うのはチップで決まる訳ではなく、ディスクリプタの中でPC側に、「この様に扱って下さいね!」と宣言しているからで、エンドポイントのバッファが、デバイス側で順番に入れたデータがPC側に到着した時、順番が入れ替わっています!なんて事は有り得ないでしょう。そんな事では危なくてバッファなんて使えません。

左の図は以下のプログラムを動かした結果です。「abcd・・・」を100行分メモ帳に書かせて、順番の入れ替わりが無いか確かめてみました。
/****************************************************************************/
/* HIDでHello World!                                                       */
/****************************************************************************/

//Simple HID Keyboard Demo Code For ElectronicLaden USB08 board
//The 3 keys on the demo board emulated up/down or left/right arrow keys
//Compiled under CW for HCS08 Ver 5.0
//Jay, MJ Labs
//Modify the usb_descriptors.c for USB device identifiers

#include  /* for EnableInterrupts macro */
#include 
#include "derivative.h" /* include peripheral declarations */
#include "usb_lowlevel.h"

#define _CPU_CLOCK_  (12000000UL)
#define _BUS_CLOCK_  (6000000UL)
#define PERIOD_10R0ms  ((_BUS_CLOCK_ / 1 / 100) - 1)

typedef struct
{
  unsigned char ascii;
  unsigned char shift;
  unsigned char code;
} KEY_CODE;

/****************************************************************************/
/* 変数定義                                                                 */
/****************************************************************************/
volatile char KbBuffer[ 8 ];
unsigned char LedBuffer;
volatile char int_flg;
extern const KEY_CODE tbl109A[];
unsigned long systime;

/****************************************************************************/
/* 関数プロトタイプ宣言                                                     */
/****************************************************************************/
int search_from_tbl( unsigned char *code, unsigned char *shift, unsigned char c );
int Str2Keycode( char *dst, const char *src );
void Str2PC( const char *str );

/****************************************************************************/
/* ソフトウエアwait                                                         */
/* インタラプト転送のタイミングでフラグが1になるので、それまで待つ。        */
/****************************************************************************/
static void wait( void )
{
  while( int_flg == 0 ) ;
  int_flg = 0;
}

/****************************************************************************/
/* rel_wait                                                                 */
/* 相対時間の待ちを行う。単位は10ms。                                       */
/* ※何時かはオーバーフローして正しく動かなくなるが、それは1年以上先。      */
/****************************************************************************/
void rel_wai( unsigned long reltim )
{
  unsigned long next = systime + reltim;

  while( systime <= next ) wait();
}

/****************************************************************************/
/* 文字列をキーコードに変換する。                                           */
/****************************************************************************/
int Str2Keycode( char *dst, const char *src )
{
  unsigned char code,shift;

  while( *src )
  {
    if( search_from_tbl( &code, &shift, *src++ ) == (-1) ) return (-1);
    *dst++ = code;
  }
  *dst = 0;

  return 0;
}

/****************************************************************************/
/* 文字列を送信する処理                                                     */
/* 文字列の最大長は40文字とする                                             */
/****************************************************************************/
void Str2PC( const char *str )
{
  int i;
  char *ptr;
  char dst[ 40 ];

  (void)Str2Keycode( dst, str );

  for( i = strlen( str ), ptr = dst; i > 0; i -= 6 )
  {
    wait();
    (void)memcpy( &KbBuffer[2], ptr, (i < 6) ? i : 6 );
    ptr += 6;

    wait();
    (void)memset( &KbBuffer[2], 0, 6 );
  }
}

/****************************************************************************/
/* main                                                                     */
/****************************************************************************/
void main( void )
{
  int i;
  static const char abc[] = "aaabbbcdefghijklmnopqrstuvwxyz\r";

  CONFIG = 0x21;	// USB Reset Disable, COP Disable
  T1SC = 0x00;	// clear TSTOP, Prescaler=0

  T1SC = 0x20;     /*0b0010 0000 タイマー停止、system clock / 1*/
  T1MOD = PERIOD_10R0ms;  /*周期設定*/
  T1SC_TOIE = 1;   /*オーバーフロー割込みの有効化*/
  T1SC_TSTOP = 0;  /*タイマースタート*/

  DDRD |= 0x20;		// init LEDs
  PTD |= 0x20;

  initUSB();		/*USBレジスタの初期化*/
  (void)memset( KbBuffer, 0, 8 );

  EnableInterrupts  /*割込み許可*/

  rel_wai( 200 );  /*2秒間の待ちを入れる*/

  KbBuffer[0] = 0x08;  /*左GUI、つまりWindowsキー*/

  wait();
  (void)memset( KbBuffer, 0, 8 );

  wait();
  KbBuffer[2] = 0x15;  /*Rのタイプ*/

  wait();
  (void)memset( KbBuffer, 0, 8 );

  wait();
  Str2PC( "notepad\r" );  /*メモ帳の起動*/

  wait();
  (void)memset( KbBuffer, 0, 8 );

  rel_wai( 100 );  /*1秒間の待ちを入れる*/

  for( i = 0; i< 100 ;i++ )
    Str2PC( abc );  /*文字の書き込み*/

  for( ; ; ) ;
}

/****************************************************************************/
/* タイマー1割込み                                                          */
/* 10ms周期                                                                 */
/****************************************************************************/
void interrupt VectorNumber_TIM1Ovr Int_TIM1_Ovr()
{
  T1SC_TOF = 0;	/*clear status*/
  systime++;  /*システムタイマーの更新*/
}

/****************************************************************************/
/* end of file                                                              */
/*                                                      designed by hamayan */
/****************************************************************************/

結果は、100行全て正しく記述できています。

面白いのは、一回の送信でなら、同じキーコードが連続した分、メモ帳にも書かれている点です。
先頭の”a”と”b"が連続しているのが判ります。"aaabbb"なので、この部分が丸ごと6byteのバッファに収められてPC側に送信された事になります。

もしこれが人間の入力なら、複数のキーを同時に(実際には時間差が有って当然)押した時の順番なんて判らないのですから、順不同が当然です。
しかし今回キーコードを生成しているのはマイコンですから、バッファにデータを詰める順番には十分意味が有ります。
そして、このデータを受ける側のPCもまたコンピュータなのですから、順番に入ってきたデータを、順不同で評価するなんて事もわざわざやらない限りは無いでしょう。

勿論これは結果を元にしたフィクションなのですが。

nice!(0)  コメント(6)  トラックバック(0) 

nice! 0

コメント 6

Tsuneo

>そして、このデータを受ける側のPCもまたコンピュータなのですから、順番に入ってきたデータを、順不同で評価するなんて事もわざわざやらない限りは無いでしょう。

前のコメントでスペックの該当ページまで書いてるんですから、是非そこだけは読んで下さい。:-)
USB HIDのスペックは次のようにキーボードの要求仕様を規定しています。

Device Class Definition for HID 1.11
http://www.usb.org/developers/devclass_docs/HID1_11.pdf

Appendix C: Keyboard Implementation (HID1_11.pdf p62)
"The following are the design requirements for USB keyboards:
...
- The order of keycodes in array fields has no significance. Order determination is done by the host software comparing the contents of the previous report to the current report. If two or more keys are reported in one report, their order is indeterminate."

(訳)キーコードの配列上での順序には特に意味はない。キーインの順序はホスト側で、直前のレポートと現在のレポートを比べることで決定される。2個以上のキーが1つのレポートに含まれるなら、そのキーインの順序は不定である。

従って、Windowsのとあるバージョンで所期の動作をしたとしても、他のOSで再現できるかどうかは不明です。また、Windows自体が将来にわたってこの動作を保証するかどうかも不明です。動作の再現性を重視するなら、
- USB スペックの規定にのみ依存したデザインとする。
- あるいは、動作するOSのバージョンを指定する。WinXP SP2 など。

Tsuneo
by Tsuneo (2008-03-14 06:15) 

hamayan

すいません。まだ読んでいませんでした。申し訳無い。

えー、今のところはWindowsの2KかXP限定で構わないのです。他のOSはせいぜい98SEしか持っていませんから。

今考えているのはキーボードを作る事ではなく、HIDのキーボードインタフェースを利用した一種の通信端末なので、Windows限定で構わないからもしバッファをフルに使う事で帯域を6倍にできるなら、それは一つのオプションとして用意できると考えています。

その為に、実装の見えないWindowsのHIDドライバーを外から色々やってみて、動作を調べています。

by hamayan (2008-03-14 09:11) 

Tsuneo

ところで、キーコードと{0}を交互に送っていますが、キーが異なれば{0}は必要ないのでは。前回の転送と今回とでキーコードが違っていれば、前回のキーはbreakしたと判断されるはずですが。

Tsuneo
by Tsuneo (2008-03-14 18:05) 

noritan

思いつきですが、

連続したレポートのバッファの互いに異なる位置に同じコードを入れたら、どうなるでしょうか。前回キーコードがあった場所に{0}が入っているから、前回のキーはbreakしたと判断しないかな?

結局は、オペレーティング・システムに依存するんだろうな。

by noritan (2008-03-14 18:40) 

hamayan

> ところで、キーコードと{0}を交互に送っていますが、キーが異なれば{0}
> は必要ないのでは。前回の転送と今回とでキーコードが違っていれば、
> 前回のキーはbreakしたと判断されるはずですが。

その処理方法も考えて見ましたが、その場合前回送った内容を記憶しておいて、0を入れるか、新しいコードを入れるかの判断が必要となり、煩雑になるので、一律に0を交互に入れています。

by hamayan (2008-03-14 18:57) 

hamayan

そうか、、、1byteずつ送る方式だったら、前回のコードと比較してもそれ程のペナルティにはならないな。

よし、今夜はそれにチャレンジだ!。
by hamayan (2008-03-14 19:03) 

コメントを書く

お名前:[必須]
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

※ブログオーナーが承認したコメントのみ表示されます。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。