Subversion Repositories Projects

Rev

Blame | Last modification | View Log | RSS feed

/* 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.print(c,BYTE);
  }
  return;
}