0,0 → 1,720 |
/* 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; |
} |