前面一篇介绍了如何获得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)))[0] #define HIBYTE(x) ((char*)(&(x)))[1] #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\nVendor ID:\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_TAG 0xf0 //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/