Пример #1
0
int main(int argc, const char *argv[]) {
	int retVal = 0;
	struct USBDevice *device = NULL;
	const char *error = NULL;
	const char *vp;
	USBStatus uStatus;
	if ( argc != 2 ) {
		fprintf(stderr, "Synopsis: %s <VID:PID>\n", argv[0]);
		FAIL(1, cleanup);
	}
	vp = argv[1];
	uStatus = usbInitialise(0, &error);
	CHECK_STATUS(uStatus, 2, cleanup);
	uStatus = usbOpenDevice(vp, 1, 0, 0, &device, &error);
	CHECK_STATUS(uStatus, 3, cleanup);
	uStatus = usbPrintConfiguration(device, stdout, &error);
	CHECK_STATUS(uStatus, 4, cleanup);
cleanup:
	if ( device ) {
		usbCloseDevice(device, 0);
	}
	if ( error ) {
		fprintf(stderr, "%s: %s\n", argv[0], error);
		errFree(error);
	}
	return retVal;
}
Пример #2
0
// Load custom firmware (.hex) into the FX2's RAM
DLLEXPORT(FLStatus) flLoadCustomFirmware(
	const char *curVidPid, const char *fwFile, const char **error)
{
	FLStatus retVal = FL_SUCCESS;
	struct Buffer fwBuf = {0,};
	BufferStatus bStatus;
	FX2Status fxStatus;
	struct USBDevice *device = NULL;
	USBStatus uStatus;
	const char *const ext = fwFile + strlen(fwFile) - 4;
	const bool isHex = (strcmp(".hex", ext) == 0) || (strcmp(".ihx", ext) == 0);
	CHECK_STATUS(
		!isHex, FL_FILE_ERR, cleanup,
		"flLoadCustomFirmware(): Filename should have .hex or .ihx extension");
	uStatus = usbOpenDevice(curVidPid, 1, 0, 0, &device, error);
	CHECK_STATUS(uStatus, FL_USB_ERR, cleanup, "flLoadCustomFirmware()");
	bStatus = bufInitialise(&fwBuf, 8192, 0x00, error);
	CHECK_STATUS(bStatus, FL_ALLOC_ERR, cleanup, "flLoadCustomFirmware()");
	bStatus = bufReadFromIntelHexFile(&fwBuf, NULL, fwFile, error);
	CHECK_STATUS(bStatus, FL_FILE_ERR, cleanup, "flLoadCustomFirmware()");
	fxStatus = fx2WriteRAM(device, fwBuf.data, (uint32)fwBuf.length, error);
	CHECK_STATUS(fxStatus, FL_FX2_ERR, cleanup, "flLoadCustomFirmware()");
cleanup:
	bufDestroy(&fwBuf);
	if ( device ) {
		usbCloseDevice(device, 0);
	}
	return retVal;
}
Пример #3
0
// Load the standard FPGALink firmware into the FX2 at currentVid/currentPid.
DLLEXPORT(FLStatus) flLoadStandardFirmware(
	const char *curVidPid, const char *newVidPid, const char **error)
{
	FLStatus flStatus, retVal = FL_SUCCESS;
	struct Buffer ramBuf = {0,};
	BufferStatus bStatus;
	FX2Status fxStatus;
	struct USBDevice *device = NULL;
	USBStatus uStatus;
	uint16 newVid, newPid, newDid;
	CHECK_STATUS(
		!usbValidateVidPid(newVidPid), FL_USB_ERR, cleanup,
		"flLoadStandardFirmware(): The supplied VID:PID:DID \"%s\" is invalid; it should look like 1D50:602B or 1D50:602B:0001",
		newVidPid);
	newVid = (uint16)strtoul(newVidPid, NULL, 16);
	newPid = (uint16)strtoul(newVidPid+5, NULL, 16);
	newDid = (uint16)((strlen(newVidPid) == 14) ? strtoul(newVidPid+10, NULL, 16) : 0x0000);
	uStatus = usbOpenDevice(curVidPid, 1, 0, 0, &device, error);
	CHECK_STATUS(uStatus, FL_USB_ERR, cleanup, "flLoadStandardFirmware()");
	bStatus = bufInitialise(&ramBuf, 0x4000, 0x00, error);
	CHECK_STATUS(bStatus, FL_ALLOC_ERR, cleanup, "flLoadStandardFirmware()");
	flStatus = copyFirmwareAndRewriteIDs(
		&ramFirmware, newVid, newPid, newDid,
		&ramBuf, error);
	CHECK_STATUS(flStatus, flStatus, cleanup, "flLoadStandardFirmware()");
	fxStatus = fx2WriteRAM(device, ramBuf.data, (uint32)ramBuf.length, error);
	CHECK_STATUS(fxStatus, FL_FX2_ERR, cleanup, "flLoadStandardFirmware()");
cleanup:
	bufDestroy(&ramBuf);
	if ( device ) {
		usbCloseDevice(device, 0);
	}
	return retVal;
}
Пример #4
0
// Disconnect and cleanup, if necessary.
//
DLLEXPORT(void) flClose(struct FLContext *handle) {
	if ( handle ) {
		usbCloseDevice(handle->device, 0);
		if ( handle->writeBuffer.data ) {
			bufDestroy(&handle->writeBuffer);
		}
		free((void*)handle);
	}
}
// Open a connection, get device status & sanity-check it.
//
DLLEXPORT(FLStatus) flOpen(const char *vp, struct FLContext **handle, const char **error) {
	FLStatus retVal = FL_SUCCESS, fStatus;
	USBStatus uStatus;
	uint8 statusBuffer[16];
	struct FLContext *newCxt = (struct FLContext *)calloc(sizeof(struct FLContext), 1);
	uint8 progEndpoints, commEndpoints;
	CHECK_STATUS(!newCxt, FL_ALLOC_ERR, cleanup, "flOpen()");
	uStatus = usbOpenDevice(vp, 1, 0, 0, &newCxt->device, error);
	CHECK_STATUS(uStatus, FL_USB_ERR, cleanup, "flOpen()");
	fStatus = getStatus(newCxt, statusBuffer, error);
	CHECK_STATUS(fStatus, fStatus, cleanup, "flOpen()");
	CHECK_STATUS(
		statusBuffer[0] != 'N' || statusBuffer[1] != 'E' ||
		statusBuffer[2] != 'M' || statusBuffer[3] != 'I',
		FL_PROTOCOL_ERR, cleanup,
		"flOpen(): Device at %s not recognised", vp);
	CHECK_STATUS(
		!statusBuffer[6] && !statusBuffer[7], FL_PROTOCOL_ERR, cleanup,
		"flOpen(): Device at %s supports neither NeroProg nor CommFPGA", vp);
	progEndpoints = statusBuffer[6];
	commEndpoints = statusBuffer[7];
	if ( progEndpoints ) {
		newCxt->isNeroCapable = true;
		newCxt->progOutEP = (progEndpoints >> 4);
		newCxt->progInEP = (progEndpoints & 0x0F);
	}
	if ( commEndpoints ) {
		newCxt->isCommCapable = true;
		newCxt->commOutEP = (commEndpoints >> 4);
		newCxt->commInEP = (commEndpoints & 0x0F);
	}
	newCxt->firmwareID = (uint16)(
		(statusBuffer[8] << 8) |
		statusBuffer[9]
	);
	newCxt->firmwareVersion = (uint32)(
		(statusBuffer[10] << 24) |
		(statusBuffer[11] << 16) |
		(statusBuffer[12] << 8)  |
		statusBuffer[13]
	);
	newCxt->chunkSize = 0x10000;  // default maximum libusbwrap chunk size
	*handle = newCxt;
	return retVal;
cleanup:
	if ( newCxt ) {
		if ( newCxt->device ) {
			usbCloseDevice(newCxt->device, 0);
		}
		free((void*)newCxt);
	}
	*handle = NULL;
	return retVal;
}
// Disconnect and cleanup, if necessary.
//
DLLEXPORT(void) flClose(struct FLContext *handle) {
	if ( handle ) {
		USBStatus uStatus;
		struct CompletionReport completionReport;
		FLStatus fStatus = flFlushAsyncWrites(handle, NULL);
		size_t queueDepth = usbNumOutstandingRequests(handle->device);
		while ( queueDepth-- ) {
			uStatus = usbBulkAwaitCompletion(handle->device, &completionReport, NULL);
		}
		usbCloseDevice(handle->device, 0);
		free((void*)handle);
		(void)fStatus;
		(void)uStatus;
	}
}
Пример #7
0
static int uploadData(char *dataBuffer)
{
usbDevice_t *dev = NULL;
int         err = 0, len, mask, pageSize, deviceSize, i;
union{
    char            bytes[1];
    deviceInfo_t    info;
    deviceData_t    data;
}           buffer;

    if((err = usbOpenDevice(&dev, IDENT_VENDOR_NUM, IDENT_VENDOR_STRING, IDENT_PRODUCT_NUM, IDENT_PRODUCT_STRING, 1)) != 0){
        fprintf(stderr, "Error opening HIDBoot device: %s\n", usbErrorMessage(err));
        goto errorOccurred;
    }
    len = sizeof(buffer);
    if(endAddress[addressIndex] > startAddress[0]){    // we need to upload data
        if((err = usbGetReport(dev, USB_HID_REPORT_TYPE_FEATURE, 1, buffer.bytes, &len)) != 0){
            fprintf(stderr, "Error reading page size: %s\n", usbErrorMessage(err));
            goto errorOccurred;
        }
        if(len < sizeof(buffer.info)){
            fprintf(stderr, "Not enough bytes in device info report (%d instead of %d)\n", len, (int)sizeof(buffer.info));
            err = -1;
            goto errorOccurred;
        }
        pageSize = getUsbInt(buffer.info.pageSize, 2);
        deviceSize = getUsbInt(buffer.info.flashSize, 4);
        printf("Page size   = %d (0x%x)\n", pageSize, pageSize);
        printf("Device size = %d (0x%x); %d bytes remaining\n", deviceSize, deviceSize, deviceSize - 2048);
        if(endAddress[addressIndex] > deviceSize - 2048){
            fprintf(stderr, "Data (%d bytes) exceeds remaining flash size!\n", endAddress[addressIndex]);
            err = -1;
            goto errorOccurred;
        }
        if(pageSize < 128){
            mask = 127;
        }else{
            mask = pageSize - 1;
        }
        for (i = 0; i <= addressIndex; i++)
        {
           startAddress[i] &= ~mask;                  /* round down */
           endAddress[i] = (endAddress[i] + mask) & ~mask;  /* round up */
           printf("Uploading %d (0x%x) bytes starting at %d (0x%x)\n", endAddress[i] - startAddress[i], endAddress[i] - startAddress[i], startAddress[i], startAddress[i]);
           while(startAddress[i] < endAddress[i]){
               buffer.data.reportId = 2;
               memcpy(buffer.data.data, dataBuffer + startAddress[i], 128);
               setUsbInt(buffer.data.address, startAddress[i], 3);
               printf("\r0x%05x ... 0x%05x", startAddress[i], startAddress[i] + (int)sizeof(buffer.data.data));
               fflush(stdout);
               if((err = usbSetReport(dev, USB_HID_REPORT_TYPE_FEATURE, buffer.bytes, sizeof(buffer.data))) != 0){
                   //fprintf(stderr, "Error uploading data block: %s\n", usbErrorMessage(err));
                   continue;
                   goto errorOccurred;
               }
               startAddress[i] += sizeof(buffer.data.data);
           }
           printf("\n");
        }
    }
    if(leaveBootLoader){
        /* and now leave boot loader: */
        buffer.info.reportId = 1;
        usbSetReport(dev, USB_HID_REPORT_TYPE_FEATURE, buffer.bytes, sizeof(buffer.info));
        /* Ignore errors here. If the device reboots before we poll the response,
         * this request fails.
         */
    }
errorOccurred:
    if(dev != NULL)
        usbCloseDevice(dev);
    return err;
}
Пример #8
0
static void avrdoper_close(union filedescriptor *fdp)
{
    usbCloseDevice(fdp);
}
Пример #9
0
// Load the standard FPGALink firmware into the FX2 at currentVid/currentPid.
DLLEXPORT(FLStatus) flLoadStandardFirmware(
	const char *curVidPid, const char *newVidPid, const char *jtagPort, const char **error)
{
	FLStatus flStatus, returnCode;
	struct Buffer ramBuf = {0,};
	BufferStatus bStatus;
	FX2Status fxStatus;
	struct USBDevice *device = NULL;
	int uStatus;
	uint16 newVid, newPid, newDid;
	uint8 port, tdoBit, tdiBit, tmsBit, tckBit;
	if ( !usbValidateVidPid(newVidPid) ) {
		errRender(error, "flLoadStandardFirmware(): The supplied VID:PID:DID \"%s\" is invalid; it should look like 1D50:602B or 1D50:602B:0001", newVidPid);
		FAIL(FL_USB_ERR);
	}
	newVid = (uint16)strtoul(newVidPid, NULL, 16);
	newPid = (uint16)strtoul(newVidPid+5, NULL, 16);
	newDid = (strlen(newVidPid) == 14) ? (uint16)strtoul(newVidPid+10, NULL, 16) : 0x0000;
	if ( strlen(jtagPort) != 5 ) {
		errRender(error, "flLoadStandardFirmware(): JTAG port specification must be <C|D><tdoBit><tdiBit><tmsBit><tckBit>");
		FAIL(FL_FX2_ERR);
	}
	if ( (jtagPort[0] & 0xDF) == 'A' ) {
		port = 0;
	} else if ( (jtagPort[0] & 0xDF) == 'C' ) {
		port = 2;
	} else if ( (jtagPort[0] & 0xDF) == 'D' ) {
		port = 3;
	} else {
		errRender(error, "flLoadStandardFirmware(): JTAG port specification must be <A|C|D><tdoBit><tdiBit><tmsBit><tckBit>");
		FAIL(FL_FX2_ERR);
	}
	if  (jtagPort[1] < '0' || jtagPort[1] > '7' || jtagPort[2] < '0' || jtagPort[2] > '7' || jtagPort[3] < '0' || jtagPort[3] > '7' || jtagPort[4] < '0' || jtagPort[4] > '7' ) {
		errRender(error, "flLoadStandardFirmware(): JTAG port specification must be <A|C|D><tdoBit><tdiBit><tmsBit><tckBit>");
		FAIL(FL_FX2_ERR);
	}
	tdoBit = jtagPort[1] - '0';
	tdiBit = jtagPort[2] - '0';
	tmsBit = jtagPort[3] - '0';
	tckBit = jtagPort[4] - '0';
	if (
		port == 0 &&
		(isInvalidPortABit(tdoBit) || isInvalidPortABit(tdiBit) ||
		 isInvalidPortABit(tmsBit) || isInvalidPortABit(tckBit))
	) {
		errRender(error, "flFlashStandardFirmware(): Only bits 0, 1, 3 & 7 are available for JTAG use on port A");
		FAIL(FL_FX2_ERR);
	}		
	uStatus = usbOpenDevice(curVidPid, 1, 0, 0, &device, error);
	CHECK_STATUS(uStatus, "flLoadStandardFirmware()", FL_USB_ERR);
	bStatus = bufInitialise(&ramBuf, 0x4000, 0x00, error);
	CHECK_STATUS(bStatus, "flLoadStandardFirmware()", FL_ALLOC_ERR);
	flStatus = copyFirmwareAndRewriteIDs(
		&ramFirmware, newVid, newPid, newDid,
		port, tdoBit, tdiBit, tmsBit, tckBit,
		&ramBuf, error);
	CHECK_STATUS(flStatus, "flLoadStandardFirmware()", flStatus);
	fxStatus = fx2WriteRAM(device, ramBuf.data, ramBuf.length, error);
	CHECK_STATUS(fxStatus, "flLoadStandardFirmware()", FL_FX2_ERR);
	returnCode = FL_SUCCESS;
cleanup:
	bufDestroy(&ramBuf);
	if ( device ) {
		usbCloseDevice(device, 0);
	}
	return returnCode;
}
Пример #10
0
int bufferRead(void) {
	int retVal = 0;
	USBStatus uStatus;
	struct USBDevice *deviceHandle = NULL;
	const char *error = NULL;
	uint8 *ptr;
	const uint8 *buf;
	struct CompletionReport completionReport;

	// Init library
	uStatus = usbInitialise(0, &error);
	CHECK_STATUS(uStatus, 1, cleanup);

	// Open device
	uStatus = usbOpenDevice("1d50:602b", 1, 0, 0, &deviceHandle, &error);
	CHECK_STATUS(uStatus, 2, cleanup);

	// Select CommFPGA conduit (FX2 slave FIFOs = 0x0001)
	uStatus = usbControlWrite(deviceHandle, 0x80, 0x0000, 0x0001, NULL, 0, 1000, &error);
	CHECK_STATUS(uStatus, 3, cleanup);

	// Get the next available 64KiB write buffer
	uStatus = usbBulkWriteAsyncPrepare(deviceHandle, &ptr, &error);  // Write request command
	CHECK_STATUS(uStatus, 4, cleanup);
	buf = ptr;

	// Populate the buffer with a couple of FPGA write commands and one FPGA read command
   *ptr++ = 0x00; // write ch0
	*ptr++ = (uint8)(CHUNK_SIZE >> 24);
	*ptr++ = (uint8)(CHUNK_SIZE >> 16);
	*ptr++ = (uint8)(CHUNK_SIZE >> 8);
	*ptr++ = (uint8)(CHUNK_SIZE & 0xFF);
	memcpy(ptr, randomData, CHUNK_SIZE);
	ptr += CHUNK_SIZE;

   *ptr++ = 0x00; // write ch0
	*ptr++ = (uint8)(CHUNK_SIZE >> 24);
	*ptr++ = (uint8)(CHUNK_SIZE >> 16);
	*ptr++ = (uint8)(CHUNK_SIZE >> 8);
	*ptr++ = (uint8)(CHUNK_SIZE & 0xFF);
	memcpy(ptr, randomData+CHUNK_SIZE, CHUNK_SIZE);
	ptr += CHUNK_SIZE;

   *ptr++ = 0x80; // read ch0
	*ptr++ = (uint8)(CHUNK_SIZE >> 24);
	*ptr++ = (uint8)(CHUNK_SIZE >> 16);
	*ptr++ = (uint8)(CHUNK_SIZE >> 8);
	*ptr++ = (uint8)(CHUNK_SIZE & 0xFF);
	
	// Submit the write
	uStatus = usbBulkWriteAsyncSubmit(deviceHandle, 2, (uint32)(ptr-buf), 1000, &error);
	CHECK_STATUS(uStatus, 5, cleanup);

	// Submit the read
	uStatus = usbBulkReadAsync(deviceHandle, 6, NULL, CHUNK_SIZE, 9000, &error);  // Read response data
	CHECK_STATUS(uStatus, 6, cleanup);

	// Wait for them to be serviced
	uStatus = usbBulkAwaitCompletion(deviceHandle, &completionReport, &error);
	CHECK_STATUS(uStatus, 7, cleanup);
	printCompletionReport(&completionReport);
	uStatus = usbBulkAwaitCompletion(deviceHandle, &completionReport, &error);
	CHECK_STATUS(uStatus, 8, cleanup);
	printCompletionReport(&completionReport);
cleanup:
	usbCloseDevice(deviceHandle, 0);
	if ( error ) {
		fprintf(stderr, "%s\n", error);
		usbFreeError(error);
	}
	return retVal;
}
Пример #11
0
int multiRead(uint32 reqCount, uint32 reqSize, double *speed) {
	int retVal = 0;
	USBStatus uStatus;
	struct USBDevice *deviceHandle = NULL;
	const char *error = NULL;
	uint8 buf[5];
	struct CompletionReport completionReport;
	uint32 numBytes;
	double totalTime;
	#ifdef WIN32
		LARGE_INTEGER tvStart, tvEnd, freq;
		DWORD_PTR mask = 1;
		SetThreadAffinityMask(GetCurrentThread(), mask);
		QueryPerformanceFrequency(&freq);
	#else
		struct timeval tvStart, tvEnd;
		long long startTime, endTime;
	#endif

	// We can make one FPGA request message and re-use it
	buf[0] = 0x80;  // write ch0
	buf[1] = (uint8)(reqSize >> 24);
	buf[2] = (uint8)(reqSize >> 16);
	buf[3] = (uint8)(reqSize >> 8);
	buf[4] = (uint8)(reqSize & 0xFF);

	// Init library
	uStatus = usbInitialise(0, &error);
	CHECK_STATUS(uStatus, 1, cleanup);

	// Open device
	uStatus = usbOpenDevice("1d50:602b", 1, 0, 0, &deviceHandle, &error);
	CHECK_STATUS(uStatus, 2, cleanup);

	// Select CommFPGA conduit (FX2 slave FIFOs = 0x0001)
	uStatus = usbControlWrite(deviceHandle, 0x80, 0x0000, 0x0001, NULL, 0, 1000, &error);
	CHECK_STATUS(uStatus, 3, cleanup);

	// Record start time
	#ifdef WIN32
		QueryPerformanceCounter(&tvStart);
	#else
		gettimeofday(&tvStart, NULL);
	#endif

	// Send a couple of read commands to the FPGA
	uStatus = usbBulkWriteAsync(deviceHandle, 2, buf, 5, 9000, &error);  // Write request command
	CHECK_STATUS(uStatus, 4, cleanup);
	uStatus = usbBulkReadAsync(deviceHandle, 6, NULL, reqSize, 9000, &error);  // Read response data
	CHECK_STATUS(uStatus, 5, cleanup);

	uStatus = usbBulkWriteAsync(deviceHandle, 2, buf, 5, 9000, &error);  // Write request command
	CHECK_STATUS(uStatus, 6, cleanup);
	uStatus = usbBulkReadAsync(deviceHandle, 6, NULL, reqSize, 9000, &error);  // Read response data
	CHECK_STATUS(uStatus, 7, cleanup);

	// On each iteration, await completion and send a new read command
	numBytes = (reqCount+2)*reqSize;
	while ( reqCount-- ) {
		uStatus = usbBulkAwaitCompletion(deviceHandle, &completionReport, &error);
		CHECK_STATUS(uStatus, 8, cleanup);
		printCompletionReport(&completionReport);
		uStatus = usbBulkAwaitCompletion(deviceHandle, &completionReport, &error);
		CHECK_STATUS(uStatus, 9, cleanup);
		printCompletionReport(&completionReport);
		
		uStatus = usbBulkWriteAsync(deviceHandle, 2, buf, 5, 9000, &error);  // Write request command
		CHECK_STATUS(uStatus, 10, cleanup);
		uStatus = usbBulkReadAsync(deviceHandle, 6, NULL, reqSize, 9000, &error);  // Read response data
		CHECK_STATUS(uStatus, 11, cleanup);
	}

	// Wait for the stragglers...
	uStatus = usbBulkAwaitCompletion(deviceHandle, &completionReport, &error);
	CHECK_STATUS(uStatus, 12, cleanup);
	printCompletionReport(&completionReport);
	uStatus = usbBulkAwaitCompletion(deviceHandle, &completionReport, &error);
	CHECK_STATUS(uStatus, 13, cleanup);
	printCompletionReport(&completionReport);

	uStatus = usbBulkAwaitCompletion(deviceHandle, &completionReport, &error);
	CHECK_STATUS(uStatus, 14, cleanup);
	printCompletionReport(&completionReport);
	uStatus = usbBulkAwaitCompletion(deviceHandle, &completionReport, &error);
	CHECK_STATUS(uStatus, 15, cleanup);
	printCompletionReport(&completionReport);

	// Record stop time
	#ifdef WIN32
		QueryPerformanceCounter(&tvEnd);
		totalTime = (double)(tvEnd.QuadPart - tvStart.QuadPart);
		totalTime /= freq.QuadPart;
		*speed = (double)numBytes / (1024*1024*totalTime);
	#else
		gettimeofday(&tvEnd, NULL);
		startTime = tvStart.tv_sec;
		startTime *= 1000000;
		startTime += tvStart.tv_usec;
		endTime = tvEnd.tv_sec;
		endTime *= 1000000;
		endTime += tvEnd.tv_usec;
		totalTime = (double)(endTime - startTime);
		totalTime /= 1000000;  // convert from uS to S.
		*speed = (double)numBytes / (1024*1024*totalTime);
	#endif

cleanup:
	usbCloseDevice(deviceHandle, 0);
	if ( error ) {
		fprintf(stderr, "%s\n", error);
		usbFreeError(error);
	}
	return retVal;
}
Пример #12
0
int main(int argc, char *argv[]) {
	struct arg_str *vpOpt    = arg_str1("v", "vidpid", "<VID:PID>", " vendor ID and product ID (e.g 04B4:8613)");
	struct arg_uint *toOpt = arg_uint0("t", "timeout", "<millis>", " timeout in milliseconds");
	struct arg_int  *epOpt   = arg_int0("e", "endpoint", "<epNum>", " endpoint to write to");
	struct arg_lit  *benOpt  = arg_lit0("b", "benchmark", "        benchmark the operation");
	struct arg_lit  *chkOpt  = arg_lit0("c", "checksum", "         print 16-bit checksum");
	struct arg_lit  *helpOpt = arg_lit0("h", "help", "             print this help and exit\n");
	struct arg_file *fileOpt = arg_file1(NULL, NULL, "<fileName>", "             the data to send");
	struct arg_end  *endOpt  = arg_end(20);
	void* argTable[] = {vpOpt, toOpt, epOpt, benOpt, chkOpt, helpOpt, fileOpt, endOpt};
	const char *progName = "bulk";
	int numErrors;
	uint8 epNum = 0x06;
	FILE *inFile = NULL;
	uint8 *buffer = NULL;
	uint32 fileLen;
	struct USBDevice *deviceHandle = NULL;
	USBStatus uStatus;
	int retVal = 0;
	const char *error = NULL;
	double totalTime, speed;
	uint16 checksum = 0x0000;
	uint32 timeout = 5000;
	uint32 i;
	#ifdef WIN32
		LARGE_INTEGER tvStart, tvEnd, freq;
		DWORD_PTR mask = 1;
		SetThreadAffinityMask(GetCurrentThread(), mask);
		QueryPerformanceFrequency(&freq);
	#else
		struct timeval tvStart, tvEnd;
		long long startTime, endTime;
	#endif

	if ( arg_nullcheck(argTable) != 0 ) {
		printf("%s: insufficient memory\n", progName);
		FAIL(1, cleanup);
	}

	numErrors = arg_parse(argc, argv, argTable);
	if ( helpOpt->count > 0 ) {
		printf("Bulk Write Tool Copyright (C) 2009-2011 Chris McClelland\n\nUsage: %s", progName);
		arg_print_syntax(stdout, argTable, "\n");
		printf("\nWrite data to a bulk endpoint.\n\n");
		arg_print_glossary(stdout, argTable,"  %-10s %s\n");
		FAIL(0, cleanup);
	}

	if ( numErrors > 0 ) {
		arg_print_errors(stdout, endOpt, progName);
		printf("Try '%s --help' for more information.\n", progName);
		FAIL(2, cleanup);
	}

	if ( toOpt->count ) {
		timeout = toOpt->ival[0];
	}

	inFile = fopen(fileOpt->filename[0], "rb");
	if ( !inFile ) {
		fprintf(stderr, "Unable to open file %s\n", fileOpt->filename[0]);
		FAIL(3, cleanup);
	}

	fseek(inFile, 0, SEEK_END);
	fileLen = (uint32)ftell(inFile);
	fseek(inFile, 0, SEEK_SET);

	buffer = (uint8 *)malloc(fileLen);
	if ( !buffer ) {
		fprintf(stderr, "Unable to allocate memory for file %s\n", fileOpt->filename[0]);
		FAIL(4, cleanup);
	}

	if ( fread(buffer, 1, fileLen, inFile) != fileLen ) {
		fprintf(stderr, "Unable to read file %s\n", fileOpt->filename[0]);
		FAIL(5, cleanup);
	}

	if ( chkOpt->count ) {
		for ( i = 0; i < fileLen; i++  ) {
			checksum = (uint16)(checksum + buffer[i]);
		}
		printf("Checksum: 0x%04X\n", checksum);
	}

	if ( epOpt->count ) {
		epNum = (uint8)epOpt->ival[0];
	}

	uStatus = usbInitialise(0, &error);
	CHECK_STATUS(uStatus, 6, cleanup);
	uStatus = usbOpenDevice(vpOpt->sval[0], 1, 0, 0, &deviceHandle, &error);
	CHECK_STATUS(uStatus, 7, cleanup);

	#ifdef WIN32
		QueryPerformanceCounter(&tvStart);
		uStatus = usbBulkWrite(deviceHandle, epNum, buffer, fileLen, timeout, &error);
		QueryPerformanceCounter(&tvEnd);
		CHECK_STATUS(uStatus, 8, cleanup);
		totalTime = (double)(tvEnd.QuadPart - tvStart.QuadPart);
		totalTime /= freq.QuadPart;
		printf("Time: %fms\n", totalTime/1000.0);
		speed = (double)fileLen / (1024*1024*totalTime);
	#else
		gettimeofday(&tvStart, NULL);
		uStatus = usbBulkWrite(deviceHandle, epNum, buffer, fileLen, timeout, &error);
		gettimeofday(&tvEnd, NULL);
		CHECK_STATUS(uStatus, 8, cleanup);
		startTime = tvStart.tv_sec;
		startTime *= 1000000;
		startTime += tvStart.tv_usec;
		endTime = tvEnd.tv_sec;
		endTime *= 1000000;
		endTime += tvEnd.tv_usec;
		totalTime = (double)(endTime - startTime);
		totalTime /= 1000000;  // convert from uS to S.
		speed = (double)fileLen / (1024*1024*totalTime);
	#endif
	if ( benOpt->count ) {
		printf("Speed: %f MB/s\n", speed);
	}

cleanup:
	if ( buffer ) {
		free(buffer);
	}
	if ( inFile ) {
		fclose(inFile);
	}
	if ( deviceHandle ) {
		usbCloseDevice(deviceHandle, 0);
	}
	if ( error ) {
		fprintf(stderr, "%s\n", error);
		usbFreeError(error);
	}
	arg_freetable(argTable, sizeof(argTable)/sizeof(argTable[0]));
	return retVal;
}