//-------------------------------------------------------------------
//	tokencrc.cpp
//
//	This program's output is intended to offer a tutorial on the 
//	steps by which a USB token-packet's 5-bit CRC field could be 
//	calculated by hand, then confirmed by comparison with output 
//	displayed by a USB Protocol Analyzer such as the Beagle 480.   
//	
//	We hope to clear up various ambiguities in published work on
//	this topic arising from the USB bitstream order, byte-order, 
//	and the number of bits to retain when representing the CRC-5 
//	polynomial in binary, as well as the number of bits that get
//	'flipped' in the 'leading-zero fix' and 'trailing-zero fix.'  
//	
//		to compile:  $ g++ tokencrc.cpp -o tokencrc
//		to execute:  $ ./tokencrc <DevID> <EndPt>
//
//	REFERENCE: 
//	"USB Specification, Revision 2.0," (April 2000), page 198.
//
//	programmer: ALLAN CRUSE
//	written on: 14 JUN 2011
//-------------------------------------------------------------------

#include <stdio.h>	// for printf() 
#include <stdlib.h>	// for atoi()
#include <string.h>	// for strcat()


unsigned int bit_reflect( unsigned int parm, int numbits )
{
	unsigned int	temp = 0;
	for (int i = 0; i < numbits; i++)
		{
		temp <<= 1;
		if ( (parm >> i) & 1 ) temp |= 1;
		}
	return	temp;
}


int main( int argc, char **argv )
{
	if ( argc < 3 )	// fewer than two command-line arguments
		{
		fprintf( stderr, "usage: $ %s <devID> <endpt> \n", argv[0] );
		exit(1); 
		}

	int	devID = atoi( argv[1] ) & 0x7F;
	int	endpt = atoi( argv[2] ) & 0x0F;

	printf( "\n\n Developing a USB token-packet's data-bytes for" );
	printf( " deviceID=%u, endpoint=%u \n\n", devID, endpt );

	unsigned short	packet = (endpt << 7)|(devID << 0);
	printf( "\n   packet-bits (lsb-to-msb):  " );
	for (int i = 0; i < 16; i++)
		{
		char	bit = (packet >> (15 -i)) & 1;
		if (( i == 5 )||( i == 9)) printf( "- " );
		printf( "%X ", bit );
		}
	printf( "\n" );


	unsigned short	stream = bit_reflect( packet, 11 );
	printf( "\n   packet-bits (msb-to-lsb):  " );
	for (int i = 0; i < 16; i++)
		{
		char	bit = (packet >> i) & 1;
		if (( i == 7 )||( i == 11 )) printf( "- " );
		printf( "%X ", bit );
		}
	printf( "\n" );

	//===========================================================
	// Detailed display of the polynomial long-division in GF(2) 
	//===========================================================	

	printf( "\n\n Now perform the 3-step CRC-5 computation: \n" );
	char	dividend[ 17 ] = { 0 };;
	for (int i = 0; i < 16; i++)
		dividend[i] = (( packet >> i )&1 ) ? '1' : '0';
	
	// the so-called 'leading-zeros fix'
	printf( "\n   STEP 1: complement the five leading bits \n\n" );
	printf( "           %s \n", dividend );
	printf( "    xor    11111 \n" );
	printf( "          ------------------ \n" );
	for (int i = 0; i < 5; i++) dividend[i] ^= 1;
	printf( "           %s \n", dividend );

	// the long-division of polynomials over the Galois Field GF(2)
	printf( "\n   STEP 2: perform the polynomial long-division \n" );
	char	poly5[ 7 ] = "100101";
	printf( "\n         ___________________ \n" );
	printf( "  %s ) %s \n", poly5, dividend );
	char	indent[ 60 ] = { 0 };
	strcat( indent, "          " );
	for (int i = 0; i < 11; i++)
		{
		printf( "%s", indent );
		unsigned int	top = dividend[i] & 1;
		switch ( top )	
			{
			case 1: printf( " %s \n", poly5 ); break;
			case 0: printf( " %s \n", "000000" ); break;
			}
		printf( "%s-------- \n", indent );

		if ( top ) for (int j = 0; j < 6; j++)
			dividend[ i+j ] ^= (poly5[ j ] & 1);
		dividend[i] = ' ';
		printf( "           %s \n", dividend );
		strcat( indent, " " );
		}

	// the so-called 'trailing-zeros fix'
	printf( "\n STEP 3: complement the five remainder-bits \n" );
	printf( "\n" );
	printf( "           %s \n", dividend );
	printf( "                 xor  %s \n", "11111" );
	printf( "%s------- \n", indent );
	for (int j = 0; j < 5; j++) dividend[ 11+j ] ^= 1;
	printf( "           %s \n", dividend );
	
	//=======================================================

	// convert the residual dividend-string into an integer	
	unsigned int	remainder = 0;
	for (int i = 0; i < 5; i++)
		{
		remainder <<= 1;
		if ( dividend[ 11+i ] & 1 ) remainder |= 1;
		}

	// show the final bitstream-reordering and byte-reordering 
	printf( "\n\n Now we merge this CRC with the token-packet's data:\n" );
	unsigned int	token = packet | (bit_reflect( remainder, 5 ) << 11);

	printf( "\n   packet-bits (lsb-to-msb):  " );
	for (int i = 0; i < 16; i++)
		{
		char	bit = (token >> i) & 1;
		if (( i == 7 )||( i == 11 )) printf( "- " );
		printf( "%X ", bit );
		}
	printf( "\n" );

	printf( "\n   packet-bits (msb-to-lsb):  " );
	for (int i = 0; i < 16; i++)
		{
		char	bit = (token >> (15 -i)) & 1;
		if (( i == 5 )||( i == 9)) printf( "- " );
		printf( "%X ", bit );
		}
	printf( "\n\n" );

	// show the resulting token-packet's data+CRC in hexadecimal
	printf( "\n The token-packet's data-bytes " );
	printf( "(with 5-bit CRC included):  0x%04X \n", token );
	unsigned char	*ptr = (unsigned char *)&token;
	printf( "\n The token-packet data-bytes" );
	printf( " in little-endian byte-order: " );
	printf( "%02X %02X \n\n", ptr[0], ptr[1] );
}