Arduino 控制USB设备(3)取得描述符

对于USB设备来说,最重要的莫过于描述符。这里介绍如何获得描述符,以及部分描述符的含义。

程序来自http://www.circuitsathome.com/mcu/arduino-usb-host-part-3-descriptors

/* MAX3421E USB Host controller get configuration descriptor */
#include <spi.h>
#include "max3421e.h"
#include "usb.h"
 
#define LOBYTE(x) ((char*)(&(x)))[0]
#define HIBYTE(x) ((char*)(&(x)))[1]
#define BUFSIZE 256    //buffer size
 
void setup();
void loop();
 
MAX3421E Max;
USB Usb;
 
void setup()
{
  byte tmpdata = 0;
  Serial.begin( 9600 );
  Serial.println("Start");
  Max.powerOn();
  delay( 200 );
}
 
void loop()
{
  byte rcode;
  Max.Task();
  Usb.Task();
  if( Usb.getUsbTaskState() >= 0x80 ) {  //state configuring or higher
    rcode = getconfdescr( 1, 0 );                 //get configuration descriptor
    if( rcode ) {
      Serial.println( rcode, HEX );
    }
    while( 1 );                          //stop
  }
}
 
byte getconfdescr( byte addr, byte conf )
{
  char buf[ BUFSIZE ];
  char* buf_ptr = buf;
  byte rcode;
  byte descr_length;
  byte descr_type;
  unsigned int total_length;
  rcode = Usb.getConfDescr( addr, 0, 4, conf, buf );  //get total length
  LOBYTE( total_length ) = buf[ 2 ];
  HIBYTE( total_length ) = buf[ 3 ];
  if( total_length > 256 ) {    //check if total length is larger than buffer
    Serial.println("Total length truncated to 256 bytes");
    total_length = 256;
  }
  rcode = Usb.getConfDescr( addr, 0, total_length, conf, buf ); //get the whole descriptor
  while( buf_ptr < buf + total_length ) {  //parsing descriptors
    descr_length = *( buf_ptr );
    descr_type = *( buf_ptr + 1 );
    switch( descr_type ) {
      case( USB_DESCRIPTOR_CONFIGURATION ):
        printconfdescr( buf_ptr );
        break;
      case( USB_DESCRIPTOR_INTERFACE ):
        printintfdescr( buf_ptr );
        break;
      case( USB_DESCRIPTOR_ENDPOINT ):
        printepdescr( buf_ptr );
        break;
      default:
        printunkdescr( buf_ptr );
        break;
        }//switch( descr_type
    buf_ptr = ( buf_ptr + descr_length );    //advance buffer pointer
  }//while( buf_ptr <=...
  return( 0 );
}
/* prints hex numbers with leading zeroes */
// copyright, Peter H Anderson, Baltimore, MD, Nov, '07
// source: http://www.phanderson.com/arduino/arduino_display.html
void print_hex(int v, int num_places)
{
  int mask=0, n, num_nibbles, digit;
 
  for (n=1; n<=num_places; n++) {
    mask = (mask << 1) | 0x0001;
  }
  v = v & mask; // truncate v to specified number of places
 
  num_nibbles = num_places / 4;
  if ((num_places % 4) != 0) {
    ++num_nibbles;
  }
  do {
    digit = ((v >> (num_nibbles-1) * 4)) & 0x0f;
    Serial.print(digit, HEX);
  }
  while(--num_nibbles);
}
/* function to print configuration descriptor */
void printconfdescr( char* descr_ptr )
{
 USB_CONFIGURATION_DESCRIPTOR* conf_ptr = ( USB_CONFIGURATION_DESCRIPTOR* )descr_ptr;
  Serial.println("Configuration descriptor:");
  Serial.print("Total length:\t");
  print_hex( conf_ptr->wTotalLength, 16 );
  Serial.print("\r\nNum.intf:\t\t");
  print_hex( conf_ptr->bNumInterfaces, 8 );
  Serial.print("\r\nConf.value:\t");
  print_hex( conf_ptr->bConfigurationValue, 8 );
  Serial.print("\r\nConf.string:\t");
  print_hex( conf_ptr->iConfiguration, 8 );
  Serial.print("\r\nAttr.:\t\t");
  print_hex( conf_ptr->bmAttributes, 8 );
  Serial.print("\r\nMax.pwr:\t\t");
  print_hex( conf_ptr->bMaxPower, 8 );
  return;
}
/* function to print interface descriptor */
void printintfdescr( char* descr_ptr )
{
 USB_INTERFACE_DESCRIPTOR* intf_ptr = ( USB_INTERFACE_DESCRIPTOR* )descr_ptr;
  Serial.println("\r\nInterface descriptor:");
  Serial.print("Intf.number:\t");
  print_hex( intf_ptr->bInterfaceNumber, 8 );
  Serial.print("\r\nAlt.:\t\t");
  print_hex( intf_ptr->bAlternateSetting, 8 );
  Serial.print("\r\nEndpoints:\t\t");
  print_hex( intf_ptr->bNumEndpoints, 8 );
  Serial.print("\r\nClass:\t\t");
  print_hex( intf_ptr->bInterfaceClass, 8 );
  Serial.print("\r\nSubclass:\t\t");
  print_hex( intf_ptr->bInterfaceSubClass, 8 );
  Serial.print("\r\nProtocol:\t\t");
  print_hex( intf_ptr->bInterfaceProtocol, 8 );
  Serial.print("\r\nIntf.string:\t");
  print_hex( intf_ptr->iInterface, 8 );
  return;
}
/* function to print endpoint descriptor */
void printepdescr( char* descr_ptr )
{
 USB_ENDPOINT_DESCRIPTOR* ep_ptr = ( USB_ENDPOINT_DESCRIPTOR* )descr_ptr;
  Serial.println("\r\nEndpoint descriptor:");
  Serial.print("Endpoint address:\t");
  print_hex( ep_ptr->bEndpointAddress, 8 );
  Serial.print("\r\nAttr.:\t\t");
  print_hex( ep_ptr->bmAttributes, 8 );
  Serial.print("\r\nMax.pkt size:\t");
  print_hex( ep_ptr->wMaxPacketSize, 16 );
  Serial.print("\r\nPolling interval:\t");
  print_hex( ep_ptr->bInterval, 8 );
  return;
}
/*function to print unknown descriptor */
void printunkdescr( char* descr_ptr )
{
  byte length = *descr_ptr;
  byte i;
  Serial.println("\r\nUnknown descriptor:");
  Serial. print("Length:\t\t");
  print_hex( *descr_ptr, 8 );
  Serial.print("\r\nType:\t\t");
  print_hex( *(descr_ptr + 1 ), 8 );
  Serial.print("\r\nContents:\t");
  descr_ptr += 2;
  for( i = 0; i < length; i++ ) {
    print_hex( *descr_ptr, 8 );
    descr_ptr++;
  }
}

 

运行结果如下:

Usbdesc

手工分析USB的描述符简直就是噩梦.......最好有USB逻辑分析仪【参考1】,抓包解析USB数据非常方便。

多说两句关于【参考1】背后的故事:当年,我在一家国内最大的PC组装企业工作。有一次,加拿大的客户抱怨我们卖出去的机器无法使用他们的一款USB条码枪。我们的机器是 AMD RS780系列的商用机,这个系列的特点是:价格便宜,可以支持很多显示器(如果没有记错的话,用上标准的扩展配件可以同时支持5台显示器的显示,这在当时是很牛x的技术)。只是这个芯片组经常有稀奇古怪的问题,卖出去两三年之后会有客户报告奇怪的问题(销售总是承诺售后保修很长的时间)。比如:台湾邮政的客户惊奇的发现,每天早晨这个机器开机进入桌面之后会出现黑屏闪一次的问题等等。

USB条码枪这个客诉先到前端看了差不多三个月都没有搞定,走流程到全球售后研发这边了。组里正好有一台USB逻辑分析仪,我就拿着实验了一下。最后抓取得结果是这个条码枪和芯片组有兼容性问题,在判断USB设备速度的时候,K J 握手有问题。外国人和中国人在对待问题上差别蛮大,前者通常要求你找到Root Cause,即使时间很长也可以等待,找到之后,如果不是我们的问题,他们也能接受;而后者的要求通常是不管你什么Root Cause, 马上给我个能用的方法就行。他们的逻辑简单得也令人发指,最常见的说法是,我在XXX的机器上看不到现象,那就一定是你们的问题。

只是没想到 Root Cause 找到了,客户也表示接受了,我这边反倒闯了祸。

当时我们刚换了一个Team的主管,BIOS工程师出身,管着下面八九个人。据他所知,在 RS780 芯片组USB配置空间上有个寄存器,可以显示当前某个USB口上是否有USB设备插入。这个寄存器是 RS780芯片组特有的。这个主管坚持非要我去检查这个位是否设置起来。我说,逻辑分析仪结果都抓出来了还看啥啊? 最主要是我太懒了,看问题不一定花时间,但是找机器走流程装系统之类的很麻烦。一番话下去,主管为了不再暴露他对USB的无知,没再说下去,只是脸色变得铁青。从技术的角度来说,我的解释没有办法反驳。好比用示波器看到了波形,知道是硬件问题,Onwer肯定要换成 HW 工程师.......

最后看没看我忘记了,只是后来我在这个主管下面日子不好过了。再后来没办法自己找了个其他部门跑路了,办理转部门的时候这个主管非常“遗憾”的拖了我很久才撒手.......

这个故事告诉我们:

1. AMD 的芯片组不稳定啊!

2. 谦虚谨慎根据情况装孙子很重要,特别是国企类型的企业

3. 懂一半的领导比什么都不懂的更麻烦。

就是这样。

参考:

1.https://www.lab-z.com/wp-content/uploads/2013/02/usbhs.pdf 关于 USB 高速(HighSpeed)和中速(FullSpeed)的区分

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注