So-net無料ブログ作成
検索選択

USBプロジェクト - ファームウェアに立ち返る (3) [USB]このエントリーを含むはてなブックマーク#

1516092

正しいUSBデバイスの続きです。 デスクリプタを整備していきます。

デバイス・デスクリプタを整備する

USBデバイスが、自身の素性をホストに知らせるのが、デスクリプタと呼ばれるデータです。 まずは、デバイス・デスクリプタから。

//----------------------------------------------------------------------------
//  Standard Device Descriptor
//  according to USB2.0 spec page 262
//----------------------------------------------------------------------------
typedef struct {
    byte        bLength;            // Size of this Descriptor in Bytes
    byte        bDescriptorType;    // Descriptor Type (=1)
    iword       bcdUSB;             // USB Spec Release Number in BCD
    byte        bDeviceClass;       // Device Class Code
    byte        bDeviceSubClass;    // Device Subclass Code
    byte        bDeviceProtocol;    // Device Protocol Code
    byte        bMaxPacketSize0;    // Maximum Packet Size for EP0
    iword       idVendor;           // Vendor ID
    iword       idProduct;          // Product ID
    iword       bcdDevice;          // Device Release Number in BCD
    byte        iManufacturer;      // Index of String Desc for Manufacturer
    byte        iProduct;           // Index of String Desc for Product
    byte        iSerialNumber;      // Index of String Desc for SerNo
    byte        bNumConfigurations; // Number of possible Configurations
}   DeviceDescriptor;

デバイス・デスクリプタは、固定長のデータです。 そのため、structで宣言する構造体で表現します。 構造体の内部構成は、USB2.0の仕様書の262ページに書いてあります。 仕様書は、 USB.orgここに あります。

でも、待てよ。 ファームウェアからデスクリプタに記述した内容を参照することなどは、まずありえない。 もし、必要なら、別の場所で定義した定数を使えば良いのだから。 従って、構造体など使わずに、ベタに8ビット符号無し整数列で書いても十分なんじゃないかな。

構造体の宣言で使用されているiwordは、"Intel Word"の意味です。 USBでは、LSBが先に配置されます。 そのため、モトローラ由来のアセンブラ・コンパイラとは相性が悪いため、iwordという構造体を定義して使っているというわけです。

//----------------------------------------------------------------------------
//  Intel type WORD description.
//----------------------------------------------------------------------------
typedef struct {                    // Data Type "Intel Word"
	  byte        lo;                 // (High/Low Byte swapped)
	  byte        hi;
}   iword;

デバイス・デスクリプタのデータは、ホストの要求に従って、送出するだけで、加工などは行いません。 そのため、ROMに配置します。

//----------------------------------------------------------------------------
//  Device Descriptor Declaration
//----------------------------------------------------------------------------
const DeviceDescriptor deviceDesc = {
                    // Size of this Descriptor in Bytes
  sizeof(DeviceDescriptor),
  DT_DEVICE,        // Descriptor Type (=1)
  {0x00, 0x02},     // USB Spec Release Number in BCD = 2.00
  0,                // Device Class Code (none)
  0,                // Device Subclass Code	(none)
  0,                // Device Protocol Code (none)
  8,                // Maximum Packet Size for EP0 
  {0x00, 0x01},     // Vendor ID = 0x0100
  {0x00, 0x00},     // Product ID = 0
  {0x00, 0x00},     // Device Release Number in BCD
  1,                // Index of String Desc for Manufacturer
  2,                // Index of String Desc for Product
  0,                // Index of String Desc for SerNo
  1                 // Number of possible Configurations
};                  // end of DeviceDesc

ベンダIDとプロダクトIDは、仮の値が入れてあります。 ベンダIDを入手しない限り、このデバイスは正式なデバイスには成りえないのね。

ストリング・デスクリプタを整備する

デバイス・デスクリプタの中にストリング・デスクリプタのインデックスを入れるフィールドがあります。 ストリング・デスクリプタは、文字列を保持するためのデスクリプタで、ホストから指示されたインデックスにより特定の文字列をホストに返します。 ここでは、言語コードを持たせるインデックス0のデータの他に 二種類の文字列を用意しました。

//----------------------------------------------------------------------------
// String Descriptors
//   0: Language code
//   1: Manufacturer
//   2: Product
//----------------------------------------------------------------------------

// Language IDs
const byte strDescLanguage[4] = {
  sizeof(strDescLanguage), DT_STRING,	      // Size, Type
  0x09, 0x04	                              // LangID Codes
};

// Manufacturer String
const byte strDescManufacturer [24] = {
  sizeof(strDescManufacturer), DT_STRING,   // Size, Type
  'n',0,'o',0,'r',0,'i',0,'t',0,
  'a',0,'n',0,'.',0,'o',0,'r',0,
  'g',0
};

// Product String
const byte strDescProduct [24] = {
  sizeof(strDescProduct), DT_STRING,        // Size, Type
  'L',0,'C',0,'D',0,' ',0,'D',0,
  'i',0,'s',0,'p',0,'l',0,'a',0,
  'y',0,
};

// Table of String Descriptors
const byte * const strDescTable[] = {
  strDescLanguage,
  strDescManufacturer,
  strDescProduct
};

ストリング・デスクリプタで厄介なのは、ユニ・コードが使用されていることです。 このため、ASCII文字列も16ビットのデータとして記述しなければなりません。 逆に言うと、日本語フォントも使えるはずですが、今は見送りとしています。

コンフィグレーション・デスクリプタを整備する

コンフィグレーション・デスクリプタは、複数のデスクリプタの集合です。 それぞれのデスクリプタは、structで宣言して構造体としています。

//----------------------------------------------------------------------------
//  Standard Configuration Descriptor
//  according to USB2.0 spec page 265
//----------------------------------------------------------------------------
typedef struct {
    byte        bLength;            // Size of this Descriptor in Bytes
    byte        bDescriptorType;    // Descriptor Type (=2)
    iword       wTotalLength;       // Total Length of Data for this Conf
    byte        bNumInterfaces;     // No of Interfaces supported by this Conf
    byte        bConfigurationValue;  // Designator Value for *this* Configuration
    byte        iConfiguration;     // Index of String Desc for this Conf
    byte        bmAttributes;       // Configuration Characteristics (see below)
    byte        bMaxPower;          // Max. Power Consumption in this Conf (*2mA)
}   ConfigurationDescriptor;

コンフィグレーション・デスクリプタの最初のデスクリプタは、コンフィグレーション・タイプです。

//----------------------------------------------------------------------------
//  Standard Interface Descriptor
//  according to USB2.0 spec page 268
//----------------------------------------------------------------------------
typedef struct {
    byte        bLength;            // Size of this Descriptor in Bytes
    byte        bDescriptorType;    // Descriptor Type (=4)
    byte        bInterfaceNumber;   // Number of *this* Interface (0..)
    byte        bAlternateSetting;  // Alternative for this Interface (if any)
    byte        bNumEndpoints;      // No of EPs used by this IF (excl. EP0)
    byte        bInterfaceClass;    // Interface Class Code
    byte        bInterfaceSubClass; // Interface Subclass Code
    byte        bInterfaceProtocol; // Interface Protocol Code
    byte        iInterface;         // Index of String Desc for this Interface
}   InterfaceDescriptor;

続いて、インターフェース・デスクリプタ構造体の宣言です。

//----------------------------------------------------------------------------
//  Standard HID Descriptor
//  according to HID1.11 spec page 22
//----------------------------------------------------------------------------
typedef struct {
    byte        bLength;            // Size of this Descriptor in Bytes
    byte        bDescriptorType;    // Descriptor Type (=5)
    iword       bcdHID;             // HID Class specification release number
    byte        bCountryCode;       // Hardware target country
    byte        bNumDesriptors;     // Number of HID class descriptors to follow
    byte        bRepDescriptorType; // Report descriptor type
    iword       wDescriptorLength;  // Total length of Report descriptor
}   HidDescriptor;

HIDデスクリプタは、HIDデバイスに特有のデスクリプタです。

//----------------------------------------------------------------------------
//  Standard Endpoint Descriptor
//  according to USB2.0 spec page 269
//----------------------------------------------------------------------------
typedef struct {
    byte        bLength;            // Size of this Descriptor in Bytes
    byte        bDescriptorType;    // Descriptor Type (=5)
    byte        bEndpointAddress;   // Endpoint Address (Number + Direction)
    byte        bmAttributes;       // Endpoint Attributes (Transfer Type)
    iword       wMaxPacketSize;     // Max. Endpoint Packet Size
    byte        bInterval;          // Polling Interval (Interrupt) in ms
}   EndpointDescriptor;

さらに、エンドポイント・デスクリプタが続きます。

これら、デスクリプタの配置順番は、HID仕様書の決められています。

//----------------------------------------------------------------------------
//  Configuration descriptors
//  according to HID1.11 spec page 48
//----------------------------------------------------------------------------
const ConfigurationDescriptors configurationDescriptors = {

//----------------------------------------------------------------------------
//  Configuration Descriptor
//----------------------------------------------------------------------------
  {	            // Size of this Descriptor in Bytes
    sizeof(ConfigurationDescriptor),
    DT_CONFIGURATION,   // Descriptor Type (=2)
    {
      sizeof(ConfigurationDescriptor) +
      sizeof(InterfaceDescriptor) +
      sizeof(HidDescriptor) +
      sizeof(EndpointDescriptor) * 2,
      0x00},    // Total Length of Data for this Conf
    1,          // No of Interfaces supported by this Conf
    1,          // Designator Value for *this* Configuration
    0,          // Index of String Desc for this Conf
    0xc0,       // Self-powered, no Remote-Wakeup
    1           // Max. Power Consumption in this Conf (*2mA)
  },            // end of ConfigDesc

//----------------------------------------------------------------------------
//  Interface Descriptor
//----------------------------------------------------------------------------
  {             // Size of this Descriptor in Bytes
    sizeof(InterfaceDescriptor),
    DT_INTERFACE,   // Descriptor Type (=4)
    0,          // Number of *this* Interface (0..)
    0,          // Alternative for this Interface (if any)
    2,          // No of EPs used by this IF (excl. EP0)
    0x3,        // IF Class Code (0x03 - HID)
    0,          // No Interface Subclass Code
    0,				  // IF Protocol Code  (0 = None)
    0           // Index of String Desc for this Interface
  },            // end of InterfaceDesc

//----------------------------------------------------------------------------
//  HID Descriptor
//----------------------------------------------------------------------------
  {	            // Size of this Descriptor in Bytes
    sizeof(HidDescriptor),
    DT_HID,    // Descriptor Type (=5)
    {0x01,0x01},    // HID class spec release 1.01
    0x00,      // Country code
    0x01,      // Number of descriptors
    DT_REPORT, // Descriptor type...?
               // Total size of report descriptor
    {sizeof(reportDesc),0}
  },           // end of __hidDesc
  
//----------------------------------------------------------------------------
//  Endpoint #1 Descriptor
//----------------------------------------------------------------------------
  {             // Size of this Descriptor in Bytes
    sizeof(EndpointDescriptor),
    DT_ENDPOINT,    // Descriptor Type (=5)
    0x81,       // Endpoint Address (EP1, IN)
    0x03,       // Interrupt transfer
    {0x08, 0x00},   // Max. Endpoint Packet Size
    10          // Polling Interval (Interrupt) in ms
  },            // end of __endpoint1Desc
  
//----------------------------------------------------------------------------
//  Endpoint #2 Descriptor
//----------------------------------------------------------------------------
  {             // Size of this Descriptor in Bytes
    sizeof(EndpointDescriptor),
    DT_ENDPOINT,    // Descriptor Type (=5)
    0x02,       // Endpoint Address (EP2, OUT)
    0x03,       // Interrupt transfer
    {0x08, 0x00},   // Max. Endpoint Packet Size
    10          // Polling Interval (Interrupt) in ms
  }             // end of endpoint2Desc

};

この構造体の宣言とデスクリプタ値の配置も、HIDのレポートデスクリプタのように、機械にやらせたいな。

付録 : USBプロジェクト索引

参考文献

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

  • 作者: インターフェース編集部
  • 出版社/メーカー: CQ出版
  • 発売日: 2006/07
  • メディア: 単行本

nice!(0)  コメント(1)  トラックバック(1)  このエントリーを含むはてなブックマーク#

nice! 0

コメント 1

Tsuneo

>ファームウェアからデスクリプタに記述した内容を参照することなどは、まずありえない。 もし、必要なら、別の場所で定義した定数を使えば良いのだから。 従って、構造体など使わずに、ベタに8ビット符号無し整数列で書いても十分なんじゃないかな。

それはそうなんですが、構造体で書いておくことをお勧めします。
構造体で書いておくと、バイト長の間違い、余計なフィールドなど、コンパイラがエラーをチェックしてくれます。また、面倒なデスクリプタのバイト数の計算など、なるべくコンパイラにやらせるようコードしましょう。構造体の宣言など結構なコード量になりますが、一度書いておけば基本的にどのプロジェクトにも、また他のMCUにも使い回しができます。また、サンプルから起こしてくれば、気にいらない部分だけ直せば良いですし。デスクリプタの書き方は、皆さんそれぞれに工夫をこらしているので、このMCUに限らずいろいろなUSB MCUのサンプルをダウンロードして、鑑賞してみてください。

ちなみに、私は最近はこんな風に書いています。これは、3xHIDのコンポジット・
デバイスです。SiLabsの C8051F32x/34x 用のものですが、デスクリプタや標準デバイスリクエストの処理はほとんど MC68HC908JB16 とそっくりなのが見て取れるでしょう。

"USB composite device" on SiLabs Forum
http://www.cygnal.org/ubb/Forum9/HTML/001050.html
USB_HID_composite_01.zip

これは、NXP LPC214x (ARM7) 用のLPCUSB です。
http://sourceforge.net/projects/lpcusb

Atmel AT91 (ARM7)
http://www.atmel.com/dyn/resources/prod_documents/AT91USBFramework-Core1.02+HID1.01.zip

Atmel AVR32
http://www.atmel.com/dyn/resources/prod_documents/AT32UC3B-SoftwareFramework-1.1.1.zip

ST micro
http://www.st.com/mcu/familiesdocs-19.html


>ベンダIDを入手しない限り、このデバイスは正式なデバイスには成りえないのね。

正式、非正式というよりも、VID/PID が他のデバイスと重なるとコンフリクトを起こす点が問題です。ユニークなVID/PID を手に入れるには、

1) いくつかのメーカーが自分のベンダーID(VID)のもとで、プロダクトID(PID)を販促用にただで配っています。もちろんそのメーカーのチップに使うのが本筋です。
- マイクロチップ
http://ww1.microchip.com/downloads/en/Market_Communication/APPLICATION%20FOR%20SUBLICENSE%20TO%20USB%20VID%2020071029.pdf

- SiLabs "Obtaining a Unique PID"
http://portal.knowledgebase.net/article.asp?article=87271&p=4120

2) 自分の VID のもとで、ある範囲の PID を売っているお店があります。
VOTI: http://www.voti.nl/shop/catalog.html?USB-PID-10

3) 正式に VID を USB.org から入手するには、
1. USB-IF のメンバーになる。年会費 US$4000.
2. US$2000 で2年間USBのロゴライセンスを取る
3. VID を1つ US$2000. で買う
http://www.usb.org/developers/vendor/


なお、単に1台のWindows PCでコンフリクトを防ぐなら、レジストリエディタで、
HKEY_LOCAL_MACHINE\SYSTEM\CurretControlSet\Enum\USB
を覗けばこれまで接続したUSBデバイスのVID/PID のリストがあるので、それを避ければよいでしょう。

Tsuneo
by Tsuneo (2008-03-22 07:22) 

コメントを書く

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

トラックバック 1

この記事のトラックバックURL:
※言及リンクのないトラックバックは受信されません。