zoologist 发表于 2015-7-30 13:45:39

Arduino 控制USB设备(4)解析描述符

前面一篇介绍了如何获得USB Descriptor,更麻烦的是这个数据的解读。在【参考1】给出了一个直接解析 Descriptor的例子。美中不足的是,这个例子只能在老版本的Arduino上工作(我估计是 0.22),在新版本 1.6.x 的IDE上会出现很多报错。

经过努力修改,终于可以编译通过(需要选择 Arduno Uno),程序如下:

/* MAX3421E USB Host controller configuration descriptor parser */
//#include "Spi.h"
#include "Max3421e.h"
#include "Usb.h"
#include "descriptor_parser.h"

#define LOBYTE(x) ((char*)(&(x)))
#define HIBYTE(x) ((char*)(&(x)))
#define BUFSIZE 256    //buffer size
#define DEVADDR 1

#define getReportDescr( addr, ep, nbytes, parse_func, nak_limit ) ctrlXfer( addr, ep, bmREQ_HIDREPORT, USB_REQUEST_GET_DESCRIPTOR, 0x00, HID_DESCRIPTOR_REPORT, 0x0000, nbytes, parse_func, nak_limit )
#define getReport( addr, ep, nbytes, interface, report_type, report_id, parse_func, nak_limit ) ctrlXfer( addr, ep, bmREQ_HIDIN, HID_REQUEST_GET_REPORT, report_id, report_type, interface, nbytes, parse_func, nak_limit )

/* Foeward declarations */
void setup();
void loop();
byte ctrlXfer( byte addr, byte ep, byte bmReqType, byte bRequest, byte wValLo, byte wValHi, unsigned int wInd, uint16_t nbytes, PARSE parse_func, uint16_t nak_limit );
void HIDreport_parse( uint8_t* buf, uint8_t* head, uint8_t* tail);

typedef struct {
uint8_t bDescriptorType;
uint16_t wDescriptorLength;
} HID_CLASS_DESCRIPTOR;


//typedef void (*PARSE)( int8_t*, int8_t*, int8_t );

MAX3421E Max;
USB Usb;

void setup()
{
Serial.begin( 115200 );
printProgStr(PSTR("\r\nStart"));
Max.powerOn();
delay( 200 );
}

void loop()
{
uint8_t rcode;
uint8_t tmpbyte = 0;

//PARSE pf = &HIDreport_parse;
/**/
Max.Task();
Usb.Task();
if( Usb.getUsbTaskState() >= USB_STATE_CONFIGURING ) {//state configuring or higher
/* printing device descriptor */
    printProgStr(PSTR("\r\nDevice addressed... "));
    printProgStr(PSTR("Requesting device descriptor."));
    tmpbyte = getdevdescr( DEVADDR );                           //number of configurations, 0 if error   
    if( tmpbyte == 0 ) {
      printProgStr(PSTR("\r\nDevice descriptor cannot be retrieved. Program Halted\r\n"));
      while( 1 );         //stop
   }//if( tmpbyte
   /* print configuration descriptors for all configurations */
   for( uint8_t i = 0; i < tmpbyte; i++ ) {
       getconfdescr( DEVADDR, i );
   }   
/* Stop */
      while( 1 );                        //stop
}   
}

/* Prints device descriptor. Returns number of configurations or zero if request error occured */
byte getdevdescr( byte addr )
{
USB_DEVICE_DESCRIPTOR buf;
byte rcode;
//Max.toggle( BPNT_0 );
rcode = Usb.getDevDescr( addr, 0, 0x12, ( char *)&buf );
if( rcode ) {
    printProgStr( rcode_error_msg );
    print_hex( rcode, 8 );
    return( 0 );
}
printProgStr(PSTR("\r\nDevice descriptor: \r\n"));
//Descriptor length
printProgStr( descr_len );
print_hex( buf.bLength, 8 );
//Descriptor type
//printProgStr( descr_type );
//print_hex( buf.bDescriptorType, 8 );
//printProgStr( descrtype_parse( buf.bDescriptorType ));
//USB Version
printProgStr(PSTR("\r\nUSB version:\t\t"));
Serial.print(( HIBYTE( buf.bcdUSB )), HEX );
Serial.print(".");
Serial.print(( LOBYTE( buf.bcdUSB )), HEX );
//Device class
printProgStr( class_str );
print_hex( buf.bDeviceClass, 8 );
printProgStr( classname_parse( buf.bDeviceClass ));
//Device Subclass
printProgStr( subclass_str );
print_hex( buf.bDeviceSubClass, 8 );
//Device Protocol
printProgStr( protocol_str );
print_hex( buf.bDeviceProtocol, 8 );
//Max.packet size
printProgStr( maxpktsize_str );
print_hex( buf.bMaxPacketSize0, 8 );
//VID
printProgStr(PSTR("\r\nVendorID:\t\t"));
print_hex( buf.idVendor, 16 );
//PID
printProgStr(PSTR("\r\nProduct ID:\t\t"));
print_hex( buf.idProduct, 16 );
//Revision
printProgStr(PSTR("\r\nRevision ID:\t\t"));
print_hex( buf.bcdDevice, 16 );
//Mfg.string
printProgStr (PSTR("\r\nMfg.string index:\t"));
print_hex( buf.iManufacturer, 8 );
getstrdescr( addr, buf.iManufacturer );
//Prod.string
printProgStr(PSTR("\r\nProd.string index:\t"));
print_hex( buf.iProduct, 8 );
//printProgStr( str_cont );
getstrdescr( addr, buf.iProduct );
//Serial number string
printProgStr(PSTR("\r\nSerial number index:\t"));
print_hex( buf.iSerialNumber, 8 );
//printProgStr( str_cont );
getstrdescr( addr, buf.iSerialNumber );
//Number of configurations
printProgStr(PSTR("\r\nNumber of conf.:\t"));
print_hex( buf.bNumConfigurations, 8 );
return( buf.bNumConfigurations );
}
/* Get string descriptor. Takes device address and string index */
byte getstrdescr( byte addr, byte idx )
{
char buf[ BUFSIZE ];
byte rcode;
byte length;
byte i;
unsigned int langid;
if( idx == 0 ) {//don't try to get index zero
    return( 0 );
}
rcode = Usb.getStrDescr( addr, 0, 1, 0, 0, buf );//get language table length
if( rcode ) {
    printProgStr(PSTR("\r\nError retrieving LangID table length"));
    return( rcode );
}
length = buf[ 0 ];      //length is the first byte
rcode = Usb.getStrDescr( addr, 0, length, 0, 0, buf );//get language table
if( rcode ) {
    printProgStr(PSTR("\r\nError retrieving LangID table"));
    return( rcode );
}
HIBYTE( langid ) = buf[ 3 ];                            //get first langid
LOBYTE( langid ) = buf[ 2 ];                            //bytes are swapped to account for endiannes
//printProgStr(PSTR("\r\nLanguage ID: "));
//print_hex( langid, 16 );
rcode = Usb.getStrDescr( addr, 0, 1, idx, langid, buf );
if( rcode ) {
    printProgStr(PSTR("\r\nError retrieving string length"));
    return( rcode );
}
length = ( buf[ 0 ] < 254 ? buf[ 0 ] : 254 );
printProgStr(PSTR(" Length: "));
Serial.print( length, DEC );
rcode = Usb.getStrDescr( addr, 0, length, idx, langid, buf );
if( rcode ) {
    printProgStr(PSTR("\r\nError retrieveing string"));
    return( rcode );
}
printProgStr(PSTR(" Contents: "));
for( i = 2; i < length; i+=2 ) {
    Serial.print( buf[ i ] );
}
return( idx );
}
/* Returns string to class name */
const char* classname_parse( byte class_number )
{
switch( class_number ) {
    case 0x00:
      return PSTR(" Use class information in the Interface Descriptor");
    case 0x01:
      return PSTR(" Audio");
    case 0x02:
      return PSTR(" Communications and CDC Control");
    case 0x03:
      return PSTR(" HID (Human Interface Device)");
    case 0x05:
      return PSTR(" Physical");
    case 0x06:
      return PSTR(" Image");
    case 0x07:
      return PSTR(" Printer");
    case 0x08:
      return PSTR(" Mass Storage");
    case 0x09:
      return PSTR(" Hub");
    case 0x0a:
      return PSTR(" CDC-Data");
    case 0x0b:
      return PSTR(" Smart Card");
    case 0x0d:
      return PSTR(" Content Security");
    case 0x0e:
      return PSTR(" Video");
    case 0x0f:
      return PSTR(" Personal Healthcare");
    case 0xdc:
      return PSTR("Diagnostic Device");
    case 0xe0:
      return PSTR(" Wireless Controller");
    case 0xef:
      return PSTR(" Miscellaneous");
    case 0xfe:
      return PSTR(" Application Specific");
    case 0xff:
      return PSTR(" Vendor Specific");
    default:
      return unk_msg;
}//switch( class_number
}            
/* Getting configuration descriptor */
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;
printProgStr(PSTR("\r\n\nConfiguration number "));
Serial.print( conf, HEX );
rcode = Usb.getConfDescr( addr, 0, 4, conf, buf );//get total length
if( rcode ) {
    printProgStr(PSTR("Error retrieving configuration length. Error code "));
    Serial.println( rcode, HEX );
    return( 0 );
}//if( rcode
LOBYTE( total_length ) = buf[ 2 ];
HIBYTE( total_length ) = buf[ 3 ];
printProgStr(PSTR("\r\nTotal configuration length: "));
Serial.print( total_length, DEC );
printProgStr(PSTR(" bytes"));
if( total_length > BUFSIZE ) {    //check if total length is larger than buffer
    printProgStr(PSTR("Total length truncated to "));
    Serial.print( BUFSIZE, DEC);
    printProgStr(PSTR("bytes"));
    total_length = BUFSIZE;
}
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;
      case( HID_DESCRIPTOR_HID ):
      printhid_descr( buf_ptr );
      break;
      default:
      printunkdescr( buf_ptr );
      break;
      }//switch( descr_type
    Serial.println("");   
    buf_ptr = ( buf_ptr + descr_length );    //advance buffer pointer
}//while( buf_ptr <=...
return( 0 );
}
/* function to print configuration descriptor */
void printconfdescr( char* descr_ptr )
{
USB_CONFIGURATION_DESCRIPTOR* conf_ptr = ( USB_CONFIGURATION_DESCRIPTOR* )descr_ptr;
uint8_t tmpbyte;
printProgStr(PSTR("\r\n\nConfiguration descriptor:"));
printProgStr(PSTR("\r\nTotal length:\t\t"));
print_hex( conf_ptr->wTotalLength, 16 );
printProgStr(PSTR("\r\nNumber of interfaces:\t"));
print_hex( conf_ptr->bNumInterfaces, 8 );
printProgStr(PSTR("\r\nConfiguration value:\t"));
print_hex( conf_ptr->bConfigurationValue, 8 );
printProgStr(PSTR("\r\nConfiguration string:\t"));
tmpbyte = conf_ptr->iConfiguration;
print_hex( tmpbyte, 8 );
getstrdescr( DEVADDR, tmpbyte );
printProgStr(PSTR("\r\nAttributes:\t\t"));
tmpbyte = conf_ptr->bmAttributes;
print_hex( tmpbyte, 8 );
if( tmpbyte & 0x40 ) {//D6
    printProgStr(PSTR(" Self-powered"));
}
if( tmpbyte & 0x20 ) { //D5
    printProgStr(PSTR(" Remote Wakeup"));
}
printProgStr(PSTR("\r\nMax.power:\t\t"));
tmpbyte = conf_ptr->bMaxPower;
print_hex( tmpbyte, 8 );
printProgStr(PSTR(" "));
Serial.print(( tmpbyte * 2 ), DEC);
printProgStr(PSTR("ma"));
return;
}
/* function to print interface descriptor */
void printintfdescr( char* descr_ptr )
{
USB_INTERFACE_DESCRIPTOR* intf_ptr = ( USB_INTERFACE_DESCRIPTOR* )descr_ptr;
uint8_t tmpbyte;
printProgStr(PSTR("\r\nInterface descriptor:"));
printProgStr(PSTR("\r\nInterface number:\t"));
print_hex( intf_ptr->bInterfaceNumber, 8 );
printProgStr(PSTR("\r\nAlternate setting:\t"));
print_hex( intf_ptr->bAlternateSetting, 8 );
printProgStr(PSTR("\r\nEndpoints:\t\t"));
print_hex( intf_ptr->bNumEndpoints, 8 );
printProgStr( class_str );
tmpbyte = intf_ptr->bInterfaceClass;
print_hex( tmpbyte, 8 );
printProgStr(classname_parse( tmpbyte ));
printProgStr( subclass_str );
print_hex( intf_ptr->bInterfaceSubClass, 8 );
printProgStr( protocol_str );
print_hex( intf_ptr->bInterfaceProtocol, 8 );
printProgStr(PSTR("\r\nInterface string:\t"));
tmpbyte = intf_ptr->iInterface;
print_hex( tmpbyte, 8 );
getstrdescr( DEVADDR, tmpbyte );
return;
}
/* function to print endpoint descriptor */
void printepdescr( char* descr_ptr )
{
USB_ENDPOINT_DESCRIPTOR* ep_ptr = ( USB_ENDPOINT_DESCRIPTOR* )descr_ptr;
uint8_t tmpbyte;
printProgStr(PSTR("\r\nEndpoint descriptor:"));
printProgStr(PSTR("\r\nEndpoint address:\t"));
tmpbyte = ep_ptr->bEndpointAddress;
print_hex( tmpbyte & 0x0f, 8 );
printProgStr(PSTR(" Direction: "));
( tmpbyte & 0x80 ) ? printProgStr(PSTR("IN")) : printProgStr(PSTR("OUT"));
printProgStr(PSTR("\r\nAttributes:\t\t"));
tmpbyte = ep_ptr->bmAttributes;
print_hex( tmpbyte, 8 );
printProgStr(PSTR(" Transfer type: "));
printProgStr((char*)pgm_read_word(&transfer_types[(tmpbyte & 0x03)]));
if(( tmpbyte & 0x03 ) == 1 ) {//Isochronous Transfer
    printProgStr(PSTR(", Sync Type: "));
    printProgStr((char*)pgm_read_word(&sync_types[(tmpbyte & 0x0c)]));
    printProgStr(PSTR(", Usage Type: "));
    printProgStr((char*)pgm_read_word(&usage_types[(tmpbyte & 0x30)]));
}//if( tmpbyte & 0x01
printProgStr( maxpktsize_str );
print_hex( ep_ptr->wMaxPacketSize, 16 );
printProgStr(PSTR("\r\nPolling interval:\t"));
tmpbyte = ep_ptr->bInterval;
print_hex( tmpbyte, 8 );
printProgStr(PSTR(" "));
Serial.print( tmpbyte, DEC );
printProgStr(PSTR(" ms"));
return;
}
/* function to print HID descriptor */
void printhid_descr( char* descr_ptr )
{
PARSE pf = &HIDreport_parse;
USB_HID_DESCRIPTOR* hid_ptr = ( USB_HID_DESCRIPTOR* )descr_ptr;
uint8_t tmpbyte;
/**/
printProgStr(PSTR("\r\nHID descriptor:"));
printProgStr(PSTR("\r\nDescriptor length:\t"));
tmpbyte = hid_ptr->bLength;
print_hex( tmpbyte, 8 );
printProgStr(PSTR(" "));
Serial.print( tmpbyte, DEC );
printProgStr(PSTR(" bytes"));
printProgStr(PSTR("\r\nHID version:\t\t"));
Serial.print(( HIBYTE( hid_ptr->bcdHID )), HEX );
Serial.print(".");
Serial.print(( LOBYTE( hid_ptr->bcdHID )), HEX );
tmpbyte = hid_ptr->bCountryCode;
printProgStr(PSTR("\r\nCountry Code:\t\t"));
Serial.print( tmpbyte, DEC );
printProgStr(PSTR(" "));
( tmpbyte > 35 ) ? printProgStr(PSTR("Reserved")) : printProgStr((char*)pgm_read_word(&HID_Country_Codes[ tmpbyte ]));
tmpbyte = hid_ptr->bNumDescriptors;
printProgStr(PSTR("\r\nClass Descriptors:\t"));
Serial.print( tmpbyte, DEC );
//Printing class descriptors
descr_ptr += 6; //advance buffer pointer
for( uint8_t i = 0; i < tmpbyte; i++ ) {
    uint8_t tmpdata;
    HID_CLASS_DESCRIPTOR* hidclass_ptr = ( HID_CLASS_DESCRIPTOR* )descr_ptr;
    tmpdata = hidclass_ptr->bDescriptorType;
    printProgStr(PSTR("\r\nClass Descriptor Type:\t"));
    Serial.print( tmpdata, HEX );
    if(( tmpdata < 0x21 ) || ( tmpdata > 0x2f )) {
   printProgStr(PSTR(" Invalid"));
    }
    switch( tmpdata ) {
      case 0x21:
      printProgStr(PSTR(" HID"));
      break;
      case 0x22:
      printProgStr(PSTR(" Report"));
      break;
      case 0x23:
      printProgStr(PSTR(" Physical"));
      break;
      default:
      printProgStr(PSTR(" Reserved"));
      break;
    }//switch( tmpdata
    printProgStr(PSTR("\r\nClass Descriptor Length:"));
    Serial.print( hidclass_ptr->wDescriptorLength );
    printProgStr(PSTR(" bytes"));
    printProgStr(PSTR("\r\n\nHID report descriptor:\r\n"));
    getReportDescr( DEVADDR, 0 , hidclass_ptr->wDescriptorLength, pf, USB_NAK_LIMIT );
    descr_ptr += 3; //advance to the next record
}//for( uint8_t i=...
return;
}
/*function to print unknown descriptor */
void printunkdescr( char* descr_ptr )
{
byte length = *descr_ptr;
byte i;
printProgStr(PSTR("\r\nUnknown descriptor:"));
printProgStr(PSTR("Length:\t\t"));
print_hex( *descr_ptr, 8 );
printProgStr(PSTR("\r\nType:\t\t"));
print_hex( *(descr_ptr + 1 ), 8 );
printProgStr(PSTR("\r\nContents:\t"));
descr_ptr += 2;
for( i = 0; i < length; i++ ) {
    print_hex( *descr_ptr, 8 );
    descr_ptr++;
}
}
/* Control-IN transfer with callback. Sets address, endpoint, fills control packet with necessary data, dispatches control packet, and initiates bulk IN transfer   */
/* Control, data, and setup stages combined from standard USB library to be able to read large data blocks. Restricted to control-IN transfers with data stage   */
/* data read and MAX3421E RECV FIFO buffer release shall be performed by parse_func callback */
/* return codes:                */
/* 00       =   success         */
/* 01-0f    =   non-zero HRSLT*/
byte ctrlXfer( byte addr, byte ep, byte bmReqType, byte bRequest, byte wValLo, byte wValHi, unsigned int wInd, uint16_t nbytes, PARSE parse_func, uint16_t nak_limit = USB_NAK_LIMIT )
{
byte rcode;   
SETUP_PKT sp;
EP_RECORD* ep_rec = Usb.getDevTableEntry( addr, ep );
byte pktsize;
byte maxpktsize = ep_rec->MaxPktSize;
unsigned int xfrlen = 0;
/**/
Max.regWr( rPERADDR, addr );                  //set peripheral address
/* fill in setup packet */
sp.ReqType_u.bmRequestType = bmReqType;
sp.bRequest = bRequest;
sp.wVal_u.wValueLo = wValLo;
sp.wVal_u.wValueHi = wValHi;
sp.wIndex = wInd;
sp.wLength = nbytes;
Max.bytesWr( rSUDFIFO, 8, ( char *)&sp );    //transfer to setup packet FIFO
rcode = Usb.dispatchPkt( tokSETUP, ep, nak_limit );            //dispatch packet
//Serial.println("Setup packet");   //DEBUG
if( rcode ) {                                 //return HRSLT if not zero
      printProgStr(PSTR("\r\nSetup packet error: "));
      Serial.print( rcode, HEX );                                          
      return( rcode );
}
/* Data stage */
//ep_rec->rcvToggle = bmRCVTOG1;
Max.regWr( rHCTL, bmRCVTOG1 );//set toggle
while( 1 ) {                  //exited by break
    /* request data */
    rcode = Usb.dispatchPkt( tokIN, ep, nak_limit );
    if( rcode ) {
      printProgStr(PSTR("\r\nData Stage Error: "));
      Serial.print( rcode, HEX );
      return( rcode );
    }
    /* check for RCVDAVIRQ and generate error if not present */
    /* the only case when absense of RCVDAVIRQ makes sense is when toggle error occured. Need to add handling for that */
    if(( Max.regRd( rHIRQ ) & bmRCVDAVIRQ ) == 0 ) {
      printProgStr(PSTR("\r\nData Toggle error."));
      return ( 0xf0 );                           
    }   
    pktsize = Max.regRd( rRCVBC );//get received bytes count
    parse_func( pktsize );          //call parse function. Parse is expected to read the FIFO completely
    Max.regWr( rHIRQ, bmRCVDAVIRQ );                  // Clear the IRQ & free the buffer
    xfrlen += pktsize;                              // add this packet's byte count to total transfer length
    /* The transfer is complete under two conditions:         */
    /* 1. The device sent a short packet (L.T. maxPacketSize)   */
    /* 2. 'nbytes' have been transferred.                     */
    if (( pktsize < maxpktsize ) || (xfrlen >= nbytes )) {      // have we transferred 'nbytes' bytes?
      break;
    }
}//while( 1 )
rcode = Usb.dispatchPkt( tokOUTHS, ep, nak_limit );
if( rcode ) {   //return error
    printProgStr(PSTR("Status packet error: "));
    Serial.print( rcode, HEX );                                          
}
return( rcode );
}
/* Parses bitfields in main items */
void print_mainbitfield( uint8_t byte_toparse )
{
( byte_toparse & 0x01 ) ? printProgStr(PSTR("Constant,")) : printProgStr(PSTR("Data,"));//bit 0
( byte_toparse & 0x02 ) ? printProgStr(PSTR("Variable,")) : printProgStr(PSTR("Array,"));//bit 1
( byte_toparse & 0x04 ) ? printProgStr(PSTR("Relative,")) : printProgStr(PSTR("Absolute,"));//...
( byte_toparse & 0x08 ) ? printProgStr(PSTR("Wrap,")) : printProgStr(PSTR("No Wrap,"));
( byte_toparse & 0x10 ) ? printProgStr(PSTR("Non Linear,")) : printProgStr(PSTR("Linear,"));
( byte_toparse & 0x20 ) ? printProgStr(PSTR("No preferred,")) : printProgStr(PSTR("Preferred State,"));
( byte_toparse & 0x40 ) ? printProgStr(PSTR("Null State,")) : printProgStr(PSTR("No Null Position,"));//bit 6
( byte_toparse & 0x40 ) ? printProgStr(PSTR("Volatile( ignore for Input),")) : printProgStr(PSTR("Non-volatile(Ignore for Input),"));//bit 7
}
/* HID Report Desriptor Parser Callback             */
/* called repeatedly from Control transfer function */
void HIDreport_parse( uint8_t pkt_size )
{
#define B_SIZE 0x03      //bSize bitmask
#define B_TYPE 0x0c      //bType bitmask
#define B_TAG0xf0      //bTag bitmask
/* parser states */
enum STATE { ITEM_START, DATA_PARSE };
static STATE state = ITEM_START;
static uint8_t databytes_left = 0;
static uint8_t prefix;            //item prefix - type and tag
uint8_t byte_toparse;
uint8_t bType;
uint8_t tmpbyte;
/**/
while( 1 ) {
   if( pkt_size ) {
       byte_toparse = Max.regRd( rRCVFIFO );//read a byte from FIFO
       pkt_size--;
   }
   else {
       return;                              //all bytes read
   }
   switch( state ) {
      case ITEM_START://start of the record
      prefix = byte_toparse >>2;      //store prefix for databyte parsing
      tmpbyte = byte_toparse & B_SIZE;
      /* get item length */
      ( tmpbyte == 0x03 ) ? databytes_left = 4 : databytes_left = tmpbyte;
         if( databytes_left ) {
         state = DATA_PARSE;    //read bytes after prefix
         }
         printProgStr(PSTR("\r\nLength: "));
         Serial.print( databytes_left, DEC );
         /* get item type */
         bType = ( byte_toparse & B_TYPE ) >>2;
         printProgStr(PSTR("Type: "));
         printProgStr((char*)pgm_read_word(&btypes[ bType ]));
         /* get item tag */
         printProgStr(PSTR("\t\tTag: "));
         tmpbyte = ( byte_toparse & B_TAG ) >>4 ;
         switch( bType ) {
         case 0://Main
             if( tmpbyte < 0x08 ) {
               printProgStr(PSTR("Invalid Tag"));
             }
             else if( tmpbyte > 0x0c ) {
               printProgStr( reserved_msg );
             }
             else {
               printProgStr((char*)pgm_read_word(&maintags[ tmpbyte - 8 /* & 0x03 */]));
               //Serial.print("Byte: ");
               //Serial.println( tmpbyte, HEX );
             }
             break;//case 0 Main
         case 1://Global
             ( tmpbyte > 0x0b ) ? printProgStr( reserved_msg ) : printProgStr((char*)pgm_read_word(&globaltags[ tmpbyte ]));
             break;//case 1 Global
         case 2://Local
             ( tmpbyte > 0x0a ) ? printProgStr( reserved_msg ) : printProgStr((char*)pgm_read_word(&localtags[ tmpbyte ]));
             break;//case 2 Local
         default:
             break;
         }//switch( bType...      
         break;//case ITEM_START
       case DATA_PARSE:
         switch( prefix ) {
         case 0x20://Main Input
         case 0x24://Main Output
         case 0x2c://Main Feature
             /* todo: add parsing 8th bit */
             print_mainbitfield( byte_toparse );
             break;
         case 0x28:    //Main Collection
             if(( byte_toparse > 0x06 ) && ( byte_toparse < 0x80 )) {
               printProgStr( reserved_msg );
             }
             else if(( byte_toparse > 0x7f ) && ( byte_toparse <= 0xff )) {
               printProgStr(PSTR("Vendor-defined"));
             }
             else {
               printProgStr((char*)pgm_read_word(&collections[ byte_toparse ]));
             }
             break;//case 0x28 Main Collection         
         //case 0x30: //Main End Collection
         case 0x01:    //Global Usage Page
             switch( byte_toparse ) {//see HID Usage Tables doc v.1.12 page 14
               case 0x00:            
               case 0x01:
               case 0x02:
               case 0x03:
               case 0x04:
               case 0x05:
               case 0x06:
               case 0x07:
               case 0x08:
               case 0x09:
               case 0x0a:
               case 0x0b:
               case 0x0c:
               case 0x0d:
               case 0x0e:
               case 0x0f:
               case 0x10:
               printProgStr((char*)pgm_read_word(&usage_pages[ byte_toparse ]));
               break;
               case 0x14:
               printProgStr(PSTR("Alphanumeric Display"));
               break;
               case 0x40:
               printProgStr(PSTR("Medical Instruments"));
               break;
               case 0x80:
               case 0x81:
               case 0x82:
               case 0x83:
               printProgStr(PSTR("Monitor page"));
               break;
               case 0x84:
               case 0x85:
               case 0x86:
               case 0x87:
               printProgStr(PSTR("Power page"));
               break;
               case 0x8c:
               printProgStr(PSTR("Bar Code Scanner page"));
               break;
               case 0x8d:
               printProgStr(PSTR("Scale page"));
               break;
               case 0x8e:
               printProgStr(PSTR("Magnetic Stripe Reading (MSR) Devices"));
               break;
               case 0x8f:
               printProgStr(PSTR("Reserved Point of Sale pages"));
               break;
               case 0x90:
               printProgStr(PSTR("Camera Control Page"));
               break;
               case 0x91:
               printProgStr(PSTR("Arcade Page"));
               break;               
             default:
//               printProgStr(PSTR("Data: "));
//               print_hex( byte_toparse, 8 );
               //databytes_left--;
               break;         
             }//switch case 0x01:    //Global Usage Page
         }//switch( prefix ...         
         printProgStr(PSTR("Data: "));
         print_hex( byte_toparse, 8 );
         databytes_left--;
         if( !databytes_left ) {
         state = ITEM_START;
         }
         break;
   }//switch( state...
   }//while( 1 ...
}
/* 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);
}

/* given a PROGMEM string, use Serial.print() to send it out       */
/* Some non-intuitive casting necessary:                           */
/* printProgStr(PSTR("Func.Mode:\t0x"));                           */
/* printProgStr((char*)pgm_read_word(&mtpopNames[(op & 0xFF)]));   */
void printProgStr(const char* str)
{
if(!str) {
    return;
}
char c;
while((c = pgm_read_byte(str++))) {
    Serial.write(c);
}
return;
}


工作的照片:

我用USB Shield挂接了一个USB小键盘,获得的结果如下:

Start
Device addressed... Requesting device descriptor.
Device descriptor:

Descriptor Length:        12
USB version:        1.10
Class:        00 Use class information in the Interface Descriptor
Subclass:        00
Protocol:        00
Max.packet size:        08
Vendor ID:        13BA
Product ID:        0001
Revision ID:        0100
Mfg.string index:        00
Prod.string index:        00
Serial number index:        00
Number of conf.:        01

Configuration number 0
Total configuration length: 34 bytes

Configuration descriptor:
Total length:        0022
Number of interfaces:        01
Configuration value:        01
Configuration string:        00
Attributes:        A0 Remote Wakeup
Max.power:        32 100ma

Interface descriptor:
Interface number:        00
Alternate setting:        00
Endpoints:        01
Class:        03 HID (Human Interface Device)
Subclass:        01
Protocol:        01
Interface string:        00

HID descriptor:
Descriptor length:        09 9 bytes
HID version:        1.10
Country Code:        0 Not Supported
Class Descriptors:        1
Class Descriptor Type:        22 Report
Class Descriptor Length:54 bytes

HID report descriptor:

Length: 1 Type: Global        Tag: Usage Page        Generic Desktop Controls Data: 01
Length: 1 Type: Local        Tag: Usage        Data: 06
Length: 1 Type: Main        Tag: Collection        Application (mouse, keyboard) Data: 01
Length: 1 Type: Global        Tag: Usage Page        LEDs Data: 08
Length: 1 Type: Local        Tag: Usage Minimum        Data: 01
Length: 1 Type: Local        Tag: Usage Maximum        Data: 03
Length: 1 Type: Global        Tag: Logical Minimum        Data: 00
Length: 1 Type: Global        Tag: Logical Maximum        Data: 01
Length: 1 Type: Global        Tag: Report Size        Data: 01
Length: 1 Type: Global        Tag: Report Count        Data: 03
Length: 1 Type: Main        Tag: Output        Data,Variable,Absolute,No Wrap,Linear,Preferred State,No Null Position,Non-volatile(Ignore for Input), Data: 02
Length: 1 Type: Global        Tag: Report Count        Data: 05
Length: 1 Type: Main        Tag: Output        Constant,Array,Absolute,No Wrap,Linear,Preferred State,No Null Position,Non-volatile(Ignore for Input), Data: 01
Length: 1 Type: Global        Tag: Usage Page        Keyboard/Keypad Data: 07
Length: 1 Type: Local        Tag: Usage Minimum        Data: E0
Length: 1 Type: Local        Tag: Usage Maximum        Data: E7
Length: 1 Type: Global        Tag: Report Count        Data: 08
Length: 1 Type: Main        Tag: Input        Data,Variable,Absolute,No Wrap,Linear,Preferred State,No Null Position,Non-volatile(Ignore for Input), Data: 02
Length: 1 Type: Global        Tag: Report Size        Data: 08
Length: 1 Type: Global        Tag: Report Count        Data: 01
Length: 1 Type: Main        Tag: Input        Constant,Array,Absolute,No Wrap,Linear,Preferred State,No Null Position,Non-volatile(Ignore for Input), Data: 01
Length: 1 Type: Local        Tag: Usage Minimum        Data: 00
Length: 1 Type: Local        Tag: Usage Maximum        Data: 91
Length: 2 Type: Global        Tag: Logical Maximum        Data: FF Data: 00
Length: 1 Type: Global        Tag: Report Count        Data: 06
Length: 1 Type: Main        Tag: Input        Data,Array,Absolute,No Wrap,Linear,Preferred State,No Null Position,Non-volatile(Ignore for Input), Data: 00
Length: 0 Type: Main        Tag: End Collection

Endpoint descriptor:
Endpoint address:        01 Direction: IN
Attributes:        03 Transfer type: Interrupt
Max.packet size:        0008
Polling interval:        0A 10 ms

修改后的可以正常编译的代码下载



参考:

1. https://github.com/felis/USB_Host_Shield/tree/master/examples/descriptor_parser USB_Host_Shield/examples/descriptor_parser/

zjhyhky 发表于 2015-7-30 16:16:46

很厉害啊,看起来很麻烦!

wetnt 发表于 2015-7-30 17:26:56

精华啊,正在找这个呢!
页: [1]
查看完整版本: Arduino 控制USB设备(4)解析描述符