//bw_meter.c 
//cypress FX2   68013   measure the bandwidth of the USB interface
//

//             (C) Marko Cebokli S57UUU  Aug 2006 
//		windows mod by S57RA
//             GNU/GPL licence:  www.gnu.org/licences/licences.html#GPL

//compile:  gcc -lusb bw_meter_v1.c -o bw_meter_v1

// uncoment here the platform you use
//#define win32 1
#define linux 1

#include <stdio.h>
#include <usb.h>

double MHZ;  // speed of PC in MHZ...
/***** time functions that run on wins and linux... ******/
// got this at www.cs.cm.edu ...
void access_counter
(unsigned *hi, unsigned *lo)
{
/* Get cycle counter */
asm("rdtsc; movl %%edx,%0; movl %%eax,%1"
: "=r" (*hi), "=r" (*lo)
: /* No input */
: "%edx", "%eax");
}


/* Keep track of most recent reading of cycle counter */
static unsigned cyc_hi = 0;
static unsigned cyc_lo = 0;
void start_counter()
{
/* Get current value of cycle counter */
access_counter(&cyc_hi, &cyc_lo);
}

double get_counter()
{
unsigned ncyc_hi, ncyc_lo;
unsigned hi, lo, borrow;
/* Get cycle counter */
access_counter(&ncyc_hi, &ncyc_lo);
/* Do double precision subtraction */
lo = ncyc_lo - cyc_lo;
borrow = lo > ncyc_lo;
hi = ncyc_hi - cyc_hi - borrow;
return (double) hi * (1 << 30) * 4 + lo;
}


void P()
{ int i;
for (i=0; i<25000; i++){
	printf(".");}
}

/**********end time functs********************************/

double calibrate(){ // returns computer speed in MHz
printf("Measuring speed of the computer, please wait a few seconds...");
//determine speed....
double Cycles;
double calcMHz;
// sleep na winsih mora bit 1000, na linuxu pa 10...
#ifdef win32 //billy$ box counts in thousands
int sleep_time = 1000;
#endif
#ifdef linux //linux box counts in pennys
int sleep_time = 1;
#endif
start_counter();
sleep(sleep_time);
Cycles = get_counter();
#ifdef linux
calcMHz = Cycles /(sleep_time * 1e6); //unix
#endif
#ifdef win32
calcMHz = Cycles /(sleep_time * 1e3); //wins
#endif
return calcMHz;
}


int main()
{
MHZ=calibrate();
printf("\ncomputer speed: %10.4f \n",MHZ);
struct usb_bus *bus;
struct usb_device *dev;
struct usb_device *current_device;
usb_dev_handle *current_handle;

unsigned char reset[2]={1,0};
unsigned char buffer[20000];
int er[11];
int endpoint=8;
int i,j,tlen;
FILE *f;
unsigned char s[1024];
int length;
int addr;
int type;
unsigned char data[256];
unsigned char checksum,a;
unsigned int b;

usb_init();
er[2]=usb_find_busses();
er[3]=usb_find_devices();

#ifdef linux
bus=usb_busses; //unix
#endif
#ifdef win32
bus=usb_get_busses();   // windows version
#endif

printf("\nSearching for USB devices... ");
current_device=NULL;
for (bus = usb_get_busses(); bus; bus = bus->next) {
    for (dev = bus->devices; dev; dev = dev->next) {
		printf("\nbus: %s   device: %s   ",bus->dirname,dev->filename);
		printf("vendor: %02x  product: %02x  ",dev->descriptor.idVendor, dev->descriptor.idProduct);
		if ((dev->descriptor.idVendor==0x4b4)&&(dev->descriptor.idProduct==0x8613))
			{printf(" --> Found CY7C68013");	
			 current_device=dev;}
		}
}

if (current_device==NULL) 
{printf ("\nNo CY7C68013 device present\n\n"); exit(1);}

current_handle=usb_open(current_device);

er[4]=usb_control_msg(current_handle, 0x40, 0xa0, 0xE600, 0, reset, 1, 1000);     //RESET

f=fopen("bw_meter_fw.ihx", "r");		//load firmware from intel hex file
if (f==NULL) 
{printf ("Could not open file:  bw_meter_fw.ihx\n\n"); exit(1);}

printf("\n");
while(!feof(f)){
	fgets(s, 1024, f); /* we should not use more than 263 bytes normally */
	sscanf(s+1, "%02x", &length);
	sscanf(s+3, "%04x", &addr);
	sscanf(s+7, "%02x", &type);
	if(type==0){
		printf("Programming %3d byte%s starting at 0x%04x", length, length==1?" ":"s", addr);
		a=length+(addr &0xff)+(addr>>8)+type;
		for(i=0;i<length;i++){
			sscanf(s+9+i*2,"%02x", &b);
			data[i]=b;
			a=a+data[i];}
		sscanf(s+9+length*2,"%02x", &b);
		checksum=b; if(((a+checksum)&0xff)!=0x00) printf("  ** Checksum bad");
		for(i=addr;i<addr+length;i+=16)
			{tlen=addr+length-i;
			if(tlen>16) tlen=16;
			er[5]=usb_control_msg(current_handle, 0x40, 0xa0, i, 0, data+(i-addr), tlen, 1000);}
		} else 
	if(type==0x01){
		printf("End of file\n");
		} else
	if(type==0x02){
		printf("Extended address?\n"); continue;
		}
	}
fclose(f);

er[6]=usb_control_msg(current_handle, 0x40, 0xa0, 0xE600, 0, reset+1, 1, 1000);   //UNRESET
//sleep(1000);
er[10]=usb_set_configuration(current_handle, 1);
er[7]=usb_claim_interface(current_handle, 0);
er[8]=usb_set_altinterface(current_handle, 1);


double tv1,tv2,tv3,tvx[200];		//measure transfer speed
int usec,k,stat[1000],chunk,count[200];
float mbps,wc;

//gettimeofday(&tv1,NULL);
start_counter();
chunk=8192;			//got cca 30MB/sec at 8192,  29 at 4096,   15 at 2048
j=0;
for (k=0;k<200;k++)
	{
	stat[k]=usb_bulk_read(current_handle, (0x80|endpoint), buffer, chunk, 1000);
	count[k]=buffer[0];
//	gettimeofday(&tv3,NULL);
//	tvx[k]=tv3;
	tvx[k]=get_counter(); 
	j=j+stat[k];
	}
tv2=get_counter();
//usec=(1E6*tv2.tv_sec+tv2.tv_usec)-(1E6*tv1.tv_sec+tv1.tv_usec);
usec=(int)(tv2/MHZ);
printf("\n\nHere are some values read from the Cypress FIFO\n");
for (i=0;i<512;i++) printf(" %02x", buffer[i]);
printf("\n\n");
printf("Block counts, should increment by chunk/512 = %3d\n",chunk/512);
for (i=0;i<200;i++) printf(" %4d", count[i]);
printf("\n\n");
printf("Status  should be equal to chunk = %5d\n",chunk);
for (i=0;i<200;i++) printf(" %4d", stat[i]);
printf("\n\n");
wc=1.0E9;
for (i=0;i<199;i++)
	{
	mbps=(stat[i]/((tvx[i+1]-tvx[i])/MHZ));
	if (wc>mbps) wc=mbps;
	printf("time: %4.4f, bytes: %d, xfer MBPS %6.2f \n",((tvx[i+1]-tvx[i])/MHZ), stat[i],mbps);
	}
printf("\n\n");
printf("  %d  usec,  %d  bytes",usec,j);
printf("\n  %3.2f Mbytes/sec  average",(float)j/(float)usec);
printf("\n  %3.2f  Mbytes/sec  worst case",wc);
printf("\n\n");



usb_release_interface(current_handle, 0);
usb_close(current_handle);
for (i=0;i<11;i++) printf("er[%d]=%d",i,er[i]);
printf("\n\n");
return 0;
}
