Files
EF3-Interface/PcDmis/ThirdParty/UsbSupport/libusb-win32-src-1.2.4.0/examples/benchmark.c
T
2013-05-09 20:29:54 +08:00

1621 lines
48 KiB
C

/* USB Benchmark for libusb-win32
Copyright © 2010 Travis Robinson. <libusbdotnet@gmail.com>
website: http://sourceforge.net/projects/libusb-win32
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, please visit www.gnu.org.
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include "usb.h"
#define _BENCHMARK_VER_ONLY
#include "benchmark_rc.rc"
#define MAX_OUTSTANDING_TRANSFERS 10
// This is used only in VerifyData() for display information
// about data validation mismatches.
#define CONVDAT(format,...) printf("[data-mismatch] " format,__VA_ARGS__)
// All output is directed through these macros.
//
#define LOG(LogTypeString,format,...) printf("%s[" __FUNCTION__ "] "format, LogTypeString, __VA_ARGS__)
#define CONERR(format,...) LOG("Error:",format,__VA_ARGS__)
#define CONMSG(format,...) LOG("",format,__VA_ARGS__)
#define CONWRN(format,...) LOG("Warn:",format,__VA_ARGS__)
#define CONDBG(format,...) LOG("",format,__VA_ARGS__)
#define CONERR0(message) CONERR("%s", message)
#define CONMSG0(message) CONMSG("%s", message)
#define CONWRN0(message) CONWRN("%s", message)
#define CONDBG0(message) CONDBG("%s", message)
// This is the libusb-win32 return code for a transfer that timed out.
#define TRANSFER_TIMEDOUT -116
// Custom vendor requests that must be implemented in the benchmark firmware.
// Test selection can be bypassed with the "notestselect" argument.
//
enum BENCHMARK_DEVICE_COMMANDS
{
SET_TEST = 0x0E,
GET_TEST = 0x0F,
};
// Tests supported by the official benchmark firmware.
//
enum BENCHMARK_DEVICE_TEST_TYPE
{
TestTypeNone = 0x00,
TestTypeRead = 0x01,
TestTypeWrite = 0x02,
TestTypeLoop = TestTypeRead|TestTypeWrite,
};
// This software was mainly created for testing the libusb-win32 kernel & user driver.
enum BENCHMARK_TRANSFER_MODE
{
// Tests for the libusb-win32 sync transfer function.
TRANSFER_MODE_SYNC,
// Test for async function, iso transfers, and queued transfers
TRANSFER_MODE_ASYNC,
};
// Holds all of the information about a test.
struct BENCHMARK_TEST_PARAM
{
// User configurable value set from the command line.
//
INT Vid; // Vendor ID
INT Pid; // Porduct ID
INT Intf; // Interface number
INT Altf; // Alt Interface number
INT Ep; // Endpoint number (1-15)
INT Refresh; // Refresh interval (ms)
INT Timeout; // Transfer timeout (ms)
INT Retry; // Number for times to retry a timed out transfer before aborting
INT BufferSize; // Number of bytes to transfer
INT BufferCount; // Number of outstanding asynchronous transfers
BOOL NoTestSelect; // If true, don't send control message to select the test type.
BOOL UseList; // Show the user a device list and let them choose a benchmark device.
INT IsoPacketSize; // Isochronous packet size (defaults to the endpoints max packet size)
INT Priority; // Priority to run this thread at.
BOOL Verify; // Only for loop and read test. If true, verifies data integrity.
BOOL VerifyDetails; // If true, prints detailed information for each invalid byte.
enum BENCHMARK_DEVICE_TEST_TYPE TestType; // The benchmark test type.
enum BENCHMARK_TRANSFER_MODE TransferMode; // Sync or Async
// Internal value use during the test.
//
usb_dev_handle* DeviceHandle;
struct usb_device* Device;
BOOL IsCancelled;
BOOL IsUserAborted;
BYTE* VerifyBuffer; // Stores the verify test pattern for 1 packet.
WORD VerifyBufferSize; // Size of VerifyBuffer
};
// The benchmark transfer context used for asynchronous transfers. see TransferAsync().
struct BENCHMARK_TRANSFER_HANDLE
{
VOID* Context;
BOOL InUse;
CHAR* Data;
INT DataMaxLength;
INT ReturnCode;
};
// Holds all of the information about a transfer.
struct BENCHMARK_TRANSFER_PARAM
{
struct BENCHMARK_TEST_PARAM* Test;
HANDLE ThreadHandle;
DWORD ThreadID;
struct usb_endpoint_descriptor Ep;
INT IsoPacketSize;
BOOL IsRunning;
LONGLONG TotalTransferred;
LONG LastTransferred;
LONG Packets;
DWORD StartTick;
DWORD LastTick;
DWORD LastStartTick;
INT TotalTimeoutCount;
INT RunningTimeoutCount;
INT TotalErrorCount;
INT RunningErrorCount;
INT ShortTransferCount;
INT TransferHandleNextIndex;
INT TransferHandleWaitIndex;
INT OutstandingTransferCount;
struct BENCHMARK_TRANSFER_HANDLE TransferHandles[MAX_OUTSTANDING_TRANSFERS];
// Placeholder for end of structure; this is where the raw data for the
// transfer buffer is allocated.
//
BYTE Buffer[0];
};
// Benchmark device api.
struct usb_dev_handle* Bench_Open(WORD vid, WORD pid, INT interfaceNumber, INT altInterfaceNumber, struct usb_device** deviceForHandle);
int Bench_SetTestType(struct usb_dev_handle* dev, enum BENCHMARK_DEVICE_TEST_TYPE testType, int intf);
int Bench_GetTestType(struct usb_dev_handle* dev, enum BENCHMARK_DEVICE_TEST_TYPE* testType, int intf);
// Critical section for running status.
CRITICAL_SECTION DisplayCriticalSection;
// Finds the interface for [interface_number] in a libusb-win32 config descriptor.
// If first_interface is not NULL, it is set to the first interface in the config.
//
struct usb_interface_descriptor* usb_find_interface(struct usb_config_descriptor* config_descriptor,
INT interface_number,
INT alt_interface_number,
struct usb_interface_descriptor** first_interface);
// Internal function used by the benchmark application.
void ShowHelp(void);
void ShowCopyright(void);
void SetTestDefaults(struct BENCHMARK_TEST_PARAM* test);
char* GetParamStrValue(const char* src, const char* paramName);
BOOL GetParamIntValue(const char* src, const char* paramName, INT* returnValue);
int ValidateBenchmarkArgs(struct BENCHMARK_TEST_PARAM* testParam);
int ParseBenchmarkArgs(struct BENCHMARK_TEST_PARAM* testParams, int argc, char **argv);
void FreeTransferParam(struct BENCHMARK_TRANSFER_PARAM** testTransferRef);
struct BENCHMARK_TRANSFER_PARAM* CreateTransferParam(struct BENCHMARK_TEST_PARAM* test, int endpointID);
void GetAverageBytesSec(struct BENCHMARK_TRANSFER_PARAM* transferParam, DOUBLE* bps);
void GetCurrentBytesSec(struct BENCHMARK_TRANSFER_PARAM* transferParam, DOUBLE* bps);
void ShowRunningStatus(struct BENCHMARK_TRANSFER_PARAM* transferParam);
void ShowTestInfo(struct BENCHMARK_TEST_PARAM* testParam);
void ShowTransferInfo(struct BENCHMARK_TRANSFER_PARAM* transferParam);
void WaitForTestTransfer(struct BENCHMARK_TRANSFER_PARAM* transferParam);
void ResetRunningStatus(struct BENCHMARK_TRANSFER_PARAM* transferParam);
// The thread transfer routine.
DWORD TransferThreadProc(struct BENCHMARK_TRANSFER_PARAM* transferParams);
#define TRANSFER_DISPLAY(TransferParam, ReadingString, WritingString) \
((TransferParam->Ep.bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? ReadingString : WritingString)
#define INC_ROLL(IncField, RollOverValue) if ((++IncField) >= RollOverValue) IncField = 0
#define ENDPOINT_TYPE(TransferParam) (TransferParam->Ep.bmAttributes & 3)
const char* TestDisplayString[] = {"None", "Read", "Write", "Loop", NULL};
const char* EndpointTypeDisplayString[] = {"Control", "Isochronous", "Bulk", "Interrupt", NULL};
void SetTestDefaults(struct BENCHMARK_TEST_PARAM* test)
{
memset(test,0,sizeof(struct BENCHMARK_TEST_PARAM));
test->Ep = 0x00;
test->Vid = 0x0666;
test->Pid = 0x0001;
test->Refresh = 1000;
test->Timeout = 5000;
test->TestType = TestTypeLoop;
test->BufferSize = 4096;
test->BufferCount = 1;
test->Priority = THREAD_PRIORITY_NORMAL;
}
struct usb_interface_descriptor* usb_find_interface(struct usb_config_descriptor* config_descriptor,
INT interface_number,
INT alt_interface_number,
struct usb_interface_descriptor** first_interface)
{
struct usb_interface_descriptor* intf;
int intfIndex;
if (first_interface)
*first_interface = NULL;
if (!config_descriptor) return NULL;
for (intfIndex = 0; intfIndex < config_descriptor->bNumInterfaces; intfIndex++)
{
if (config_descriptor->interface[intfIndex].num_altsetting)
{
intf = &config_descriptor->interface[intfIndex].altsetting[0];
if ((first_interface) && *first_interface == NULL)
*first_interface = intf;
if (intf->bInterfaceNumber == interface_number &&
(alt_interface_number==-1 || intf->bAlternateSetting==alt_interface_number))
{
return intf;
}
}
}
return NULL;
}
struct usb_dev_handle* Bench_Open(WORD vid, WORD pid, INT interfaceNumber, INT altInterfaceNumber, struct usb_device** deviceForHandle)
{
struct usb_bus* bus;
struct usb_device* dev;
struct usb_dev_handle* udev;
for (bus = usb_get_busses(); bus; bus = bus->next)
{
for (dev = bus->devices; dev; dev = dev->next)
{
if (dev->descriptor.idVendor == vid && dev->descriptor.idProduct == pid)
{
if ((udev = usb_open(dev)))
{
if (dev->descriptor.bNumConfigurations)
{
if (usb_find_interface(&dev->config[0], interfaceNumber, altInterfaceNumber, NULL) != NULL)
{
if (deviceForHandle) *deviceForHandle = dev;
return udev;
}
}
usb_close(udev);
}
}
}
}
return NULL;
}
int Bench_SetTestType(struct usb_dev_handle* dev, enum BENCHMARK_DEVICE_TEST_TYPE testType, int intf)
{
char buffer[1];
int ret = 0;
ret = usb_control_msg(dev,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN,
SET_TEST, testType, intf,
buffer, 1,
1000);
return ret;
}
int Bench_GetTestType(struct usb_dev_handle* dev, enum BENCHMARK_DEVICE_TEST_TYPE* testType, int intf)
{
char buffer[1];
int ret = 0;
ret = usb_control_msg(dev,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN,
GET_TEST, 0, intf,
buffer, 1,
1000);
if (ret == 1)
*testType = buffer[0];
return ret;
}
enum TRANSFER_VERIFY_STATE
{
TVS_START,
TVS_KEY,
TVS_DATA,
TVS_FIND_START,
};
INT VerifyData(struct BENCHMARK_TRANSFER_PARAM* transferParam, BYTE* data, INT dataLength)
{
WORD verifyDataSize = transferParam->Test->VerifyBufferSize;
BYTE* verifyData = transferParam->Test->VerifyBuffer;
BYTE keyC = 0;
BOOL seedKey = TRUE;
INT dataLeft = dataLength;
INT dataIndex = 0;
INT packetIndex = 0;
INT verifyIndex = 0;
while(dataLeft > 1)
{
verifyDataSize = dataLeft > transferParam->Test->VerifyBufferSize ? transferParam->Test->VerifyBufferSize : dataLeft;
if (seedKey)
keyC = data[dataIndex+1];
else
{
if (data[dataIndex+1]==0)
{
keyC=0;
}
else
{
keyC++;
}
}
seedKey = FALSE;
// Index 0 is always 0.
// The key is always at index 1
verifyData[1] = keyC;
if (memcmp(&data[dataIndex],verifyData,verifyDataSize) != 0)
{
// Packet verification failed.
// Reset the key byte on the next packet.
seedKey = TRUE;
CONVDAT("data mismatch packet-index=%d data-index=%d\n", packetIndex, dataIndex);
if (transferParam->Test->VerifyDetails)
{
for (verifyIndex=0; verifyIndex<verifyDataSize; verifyIndex++)
{
if (verifyData[verifyIndex] == data[dataIndex + verifyIndex])
continue;
CONVDAT("packet-offset=%d expected %02Xh got %02Xh\n",
verifyIndex,
verifyData[verifyIndex],
data[dataIndex+verifyIndex]);
}
}
}
// Move to the next packet.
packetIndex++;
dataLeft -= verifyDataSize;
dataIndex+= verifyDataSize;
}
return 0;
}
int TransferSync(struct BENCHMARK_TRANSFER_PARAM* transferParam)
{
int ret;
if (transferParam->Ep.bEndpointAddress & USB_ENDPOINT_DIR_MASK)
{
ret = usb_bulk_read(
transferParam->Test->DeviceHandle, transferParam->Ep.bEndpointAddress,
transferParam->Buffer, transferParam->Test->BufferSize,
transferParam->Test->Timeout);
}
else
{
ret = usb_bulk_write(
transferParam->Test->DeviceHandle, transferParam->Ep.bEndpointAddress,
transferParam->Buffer, transferParam->Test->BufferSize,
transferParam->Test->Timeout);
}
return ret;
}
int TransferAsync(struct BENCHMARK_TRANSFER_PARAM* transferParam, struct BENCHMARK_TRANSFER_HANDLE** handleRef)
{
int ret;
struct BENCHMARK_TRANSFER_HANDLE* handle;
*handleRef = NULL;
// Submit transfers until the maximum number of outstanding transfer(s) is reached.
while (transferParam->OutstandingTransferCount < transferParam->Test->BufferCount)
{
// Get the next available benchmark transfer handle.
*handleRef = handle = &transferParam->TransferHandles[transferParam->TransferHandleNextIndex];
// If a libusb-win32 transfer context hasn't been setup for this benchmark transfer
// handle, do it now.
//
if (!handle->Context)
{
// Data buffer(s) are located at the end of the transfer param.
handle->Data = transferParam->Buffer + (transferParam->TransferHandleNextIndex * transferParam->Test->BufferSize);
handle->DataMaxLength = transferParam->Test->BufferSize;
switch (ENDPOINT_TYPE(transferParam))
{
case USB_ENDPOINT_TYPE_ISOCHRONOUS:
ret = usb_isochronous_setup_async(transferParam->Test->DeviceHandle,
&handle->Context,
transferParam->Ep.bEndpointAddress,
transferParam->IsoPacketSize ? transferParam->IsoPacketSize : transferParam->Ep.wMaxPacketSize);
break;
case USB_ENDPOINT_TYPE_BULK:
ret = usb_bulk_setup_async(transferParam->Test->DeviceHandle,
&handle->Context,
transferParam->Ep.bEndpointAddress);
break;
case USB_ENDPOINT_TYPE_INTERRUPT:
ret = usb_interrupt_setup_async(transferParam->Test->DeviceHandle,
&handle->Context,
transferParam->Ep.bEndpointAddress);
break;
default:
ret = -1;
break;
}
if (ret < 0)
{
CONERR("failed creating transfer context ret=%d\n",ret);
goto Done;
}
}
// Submit this transfer now.
handle->ReturnCode = ret = usb_submit_async(handle->Context, handle->Data, handle->DataMaxLength);
if (ret < 0) goto Done;
// Mark this handle has InUse.
handle->InUse = TRUE;
// When transfers ir successfully submitted, OutstandingTransferCount goes up; when
// they are completed it goes down.
//
transferParam->OutstandingTransferCount++;
// Move TransferHandleNextIndex to the next available transfer.
INC_ROLL(transferParam->TransferHandleNextIndex, transferParam->Test->BufferCount);
}
// If the number of outstanding transfers has reached the limit, wait for the
// oldest outstanding transfer to complete.
//
if (transferParam->OutstandingTransferCount == transferParam->Test->BufferCount)
{
// TransferHandleWaitIndex is the index of the oldest outstanding transfer.
*handleRef = handle = &transferParam->TransferHandles[transferParam->TransferHandleWaitIndex];
// Only wait, cancelling & freeing is handled by the caller.
handle->ReturnCode = ret = usb_reap_async_nocancel(handle->Context, transferParam->Test->Timeout);
if (ret < 0) goto Done;
// Mark this handle has no longer InUse.
handle->InUse = FALSE;
// When transfers ir successfully submitted, OutstandingTransferCount goes up; when
// they are completed it goes down.
//
transferParam->OutstandingTransferCount--;
// Move TransferHandleWaitIndex to the oldest outstanding transfer.
INC_ROLL(transferParam->TransferHandleWaitIndex, transferParam->Test->BufferCount);
}
Done:
return ret;
}
DWORD TransferThreadProc(struct BENCHMARK_TRANSFER_PARAM* transferParam)
{
int ret, i;
struct BENCHMARK_TRANSFER_HANDLE* handle;
char* data;
transferParam->IsRunning = TRUE;
while (!transferParam->Test->IsCancelled)
{
data = NULL;
handle = NULL;
if (transferParam->Test->TransferMode == TRANSFER_MODE_SYNC)
{
ret = TransferSync(transferParam);
if (ret >= 0) data = transferParam->Buffer;
}
else if (transferParam->Test->TransferMode == TRANSFER_MODE_ASYNC)
{
ret = TransferAsync(transferParam, &handle);
if ((handle) && ret >= 0) data = handle->Data;
}
else
{
CONERR("invalid transfer mode %d\n",transferParam->Test->TransferMode);
goto Done;
}
if (ret < 0)
{
// The user pressed 'Q'.
if (transferParam->Test->IsUserAborted) break;
// Transfer timed out
if (ret == TRANSFER_TIMEDOUT)
{
transferParam->TotalTimeoutCount++;
transferParam->RunningTimeoutCount++;
CONWRN("Timeout #%d %s on Ep%02Xh..\n",
transferParam->RunningTimeoutCount,
TRANSFER_DISPLAY(transferParam,"reading","writing"),
transferParam->Ep.bEndpointAddress);
if (transferParam->RunningTimeoutCount > transferParam->Test->Retry)
break;
}
else
{
// An error (other than a timeout) occured.
// usb_strerror()is not thread safe and should not be used
// in a multi-threaded app. It's used here because
// this is a test program.
//
transferParam->TotalErrorCount++;
transferParam->RunningErrorCount++;
CONERR("failed %s! %d of %d ret=%d: %s\n",
TRANSFER_DISPLAY(transferParam,"reading","writing"),
transferParam->RunningErrorCount,
transferParam->Test->Retry+1,
ret,
usb_strerror());
usb_resetep(transferParam->Test->DeviceHandle, transferParam->Ep.bEndpointAddress);
if (transferParam->RunningErrorCount > transferParam->Test->Retry)
break;
}
ret = 0;
}
else
{
if (ret < transferParam->Test->BufferSize && !transferParam->Test->IsCancelled)
{
if (ret > 0)
{
transferParam->ShortTransferCount++;
CONWRN("Short transfer on Ep%02Xh expected %d got %d.\n",
transferParam->Ep.bEndpointAddress,
transferParam->Test->BufferSize,
ret);
}
else
{
CONWRN("Zero-length transfer on Ep%02Xh expected %d.\n",
transferParam->Ep.bEndpointAddress,
transferParam->Test->BufferSize);
transferParam->TotalErrorCount++;
transferParam->RunningErrorCount++;
if (transferParam->RunningErrorCount > transferParam->Test->Retry)
break;
usb_resetep(transferParam->Test->DeviceHandle, transferParam->Ep.bEndpointAddress);
}
}
else
{
transferParam->RunningErrorCount = 0;
transferParam->RunningTimeoutCount = 0;
}
if ((transferParam->Test->Verify) &&
(transferParam->Ep.bEndpointAddress & USB_ENDPOINT_DIR_MASK))
{
VerifyData(transferParam, data, ret);
}
}
EnterCriticalSection(&DisplayCriticalSection);
if (!transferParam->StartTick && transferParam->Packets >= 0)
{
transferParam->StartTick = GetTickCount();
transferParam->LastStartTick = transferParam->StartTick;
transferParam->LastTick = transferParam->StartTick;
transferParam->LastTransferred = 0;
transferParam->TotalTransferred = 0;
transferParam->Packets = 0;
}
else
{
if (!transferParam->LastStartTick)
{
transferParam->LastStartTick = transferParam->LastTick;
transferParam->LastTransferred = 0;
}
transferParam->LastTick = GetTickCount();
transferParam->LastTransferred += ret;
transferParam->TotalTransferred += ret;
transferParam->Packets++;
}
LeaveCriticalSection(&DisplayCriticalSection);
}
Done:
for (i=0; i < transferParam->Test->BufferCount; i++)
{
if (transferParam->TransferHandles[i].Context)
{
if (transferParam->TransferHandles[i].InUse)
{
if ((ret = usb_cancel_async(transferParam->TransferHandles[i].Context)) < 0)
if (!transferParam->Test->IsUserAborted)
CONERR("failed cancelling transfer! ret=%d\n",ret);
transferParam->TransferHandles[i].InUse=FALSE;
}
usb_free_async(&transferParam->TransferHandles[i].Context);
}
}
transferParam->IsRunning = FALSE;
return 0;
}
char* GetParamStrValue(const char* src, const char* paramName)
{
return (strstr(src,paramName)==src) ? (char*)(src+strlen(paramName)) : NULL;
}
BOOL GetParamIntValue(const char* src, const char* paramName, INT* returnValue)
{
char* value = GetParamStrValue(src, paramName);
if (value)
{
*returnValue = strtol(value, NULL, 0);
return TRUE;
}
return FALSE;
}
int ValidateBenchmarkArgs(struct BENCHMARK_TEST_PARAM* testParam)
{
if (testParam->BufferCount < 1 || testParam->BufferCount > MAX_OUTSTANDING_TRANSFERS)
{
CONERR("Invalid BufferCount argument %d. BufferCount must be greater than 0 and less than or equal to %d.\n",
testParam->BufferCount, MAX_OUTSTANDING_TRANSFERS);
return -1;
}
return 0;
}
int ParseBenchmarkArgs(struct BENCHMARK_TEST_PARAM* testParams, int argc, char **argv)
{
#define GET_INT_VAL
char arg[128];
char* value;
int iarg;
for (iarg=1; iarg < argc; iarg++)
{
if (strcpy_s(arg, _countof(arg), argv[iarg])!=ERROR_SUCCESS)
return -1;
strlwr(arg);
if (GetParamIntValue(arg, "vid=", &testParams->Vid)) {}
else if (GetParamIntValue(arg, "pid=", &testParams->Pid)) {}
else if (GetParamIntValue(arg, "retry=", &testParams->Retry)) {}
else if (GetParamIntValue(arg, "buffercount=", &testParams->BufferCount))
{
if (testParams->BufferCount > 1)
testParams->TransferMode = TRANSFER_MODE_ASYNC;
}
else if (GetParamIntValue(arg, "buffersize=", &testParams->BufferSize)) {}
else if (GetParamIntValue(arg, "size=", &testParams->BufferSize)) {}
else if (GetParamIntValue(arg, "timeout=", &testParams->Timeout)) {}
else if (GetParamIntValue(arg, "intf=", &testParams->Intf)) {}
else if (GetParamIntValue(arg, "altf=", &testParams->Altf)) {}
else if (GetParamIntValue(arg, "ep=", &testParams->Ep))
{
testParams->Ep &= 0xf;
}
else if (GetParamIntValue(arg, "refresh=", &testParams->Refresh)) {}
else if (GetParamIntValue(arg, "isopacketsize=", &testParams->IsoPacketSize)) {}
else if ((value=GetParamStrValue(arg,"mode=")))
{
if (GetParamStrValue(value,"sync"))
{
testParams->TransferMode = TRANSFER_MODE_SYNC;
}
else if (GetParamStrValue(value,"async"))
{
testParams->TransferMode = TRANSFER_MODE_ASYNC;
}
else
{
// Invalid EndpointType argument.
CONERR("invalid transfer mode argument! %s\n",argv[iarg]);
return -1;
}
}
else if ((value=GetParamStrValue(arg,"priority=")))
{
if (GetParamStrValue(value,"lowest"))
{
testParams->Priority=THREAD_PRIORITY_LOWEST;
}
else if (GetParamStrValue(value,"belownormal"))
{
testParams->Priority=THREAD_PRIORITY_BELOW_NORMAL;
}
else if (GetParamStrValue(value,"normal"))
{
testParams->Priority=THREAD_PRIORITY_NORMAL;
}
else if (GetParamStrValue(value,"abovenormal"))
{
testParams->Priority=THREAD_PRIORITY_ABOVE_NORMAL;
}
else if (GetParamStrValue(value,"highest"))
{
testParams->Priority=THREAD_PRIORITY_HIGHEST;
}
else
{
CONERR("invalid priority argument! %s\n",argv[iarg]);
return -1;
}
}
else if (!stricmp(arg,"notestselect"))
{
testParams->NoTestSelect = TRUE;
}
else if (!stricmp(arg,"read"))
{
testParams->TestType = TestTypeRead;
}
else if (!stricmp(arg,"write"))
{
testParams->TestType = TestTypeWrite;
}
else if (!stricmp(arg,"loop"))
{
testParams->TestType = TestTypeLoop;
}
else if (!stricmp(arg,"list"))
{
testParams->UseList = TRUE;
}
else if (!stricmp(arg,"verifydetails"))
{
testParams->VerifyDetails = TRUE;
testParams->Verify = TRUE;
}
else if (!stricmp(arg,"verify"))
{
testParams->Verify = TRUE;
}
else
{
CONERR("invalid argument! %s\n",argv[iarg]);
return -1;
}
}
return ValidateBenchmarkArgs(testParams);
}
INT CreateVerifyBuffer(struct BENCHMARK_TEST_PARAM* testParam, WORD endpointMaxPacketSize)
{
int i;
BYTE indexC = 0;
testParam->VerifyBuffer = malloc(endpointMaxPacketSize);
if (!testParam->VerifyBuffer)
{
CONERR("memory allocation failure at line %d!\n",__LINE__);
return -1;
}
testParam->VerifyBufferSize = endpointMaxPacketSize;
for(i=0; i < endpointMaxPacketSize; i++)
{
testParam->VerifyBuffer[i] = indexC++;
if (indexC == 0) indexC = 1;
}
return 0;
}
void FreeTransferParam(struct BENCHMARK_TRANSFER_PARAM** testTransferRef)
{
struct BENCHMARK_TRANSFER_PARAM* pTransferParam;
if ((!testTransferRef) || !*testTransferRef) return;
pTransferParam = *testTransferRef;
if (pTransferParam->ThreadHandle)
{
CloseHandle(pTransferParam->ThreadHandle);
pTransferParam->ThreadHandle = NULL;
}
free(pTransferParam);
*testTransferRef = NULL;
}
struct BENCHMARK_TRANSFER_PARAM* CreateTransferParam(struct BENCHMARK_TEST_PARAM* test, int endpointID)
{
struct BENCHMARK_TRANSFER_PARAM* transferParam;
struct usb_interface_descriptor* testInterface;
int i;
int allocSize = sizeof(struct BENCHMARK_TRANSFER_PARAM)+(test->BufferSize * test->BufferCount);
transferParam = (struct BENCHMARK_TRANSFER_PARAM*) malloc(allocSize);
if (transferParam)
{
memset(transferParam, 0, allocSize);
transferParam->Test = test;
if (!(testInterface = usb_find_interface(&test->Device->config[0], test->Intf, test->Altf, NULL)))
{
CONERR("failed locating interface %02Xh!\n", test->Intf);
FreeTransferParam(&transferParam);
goto Done;
}
for(i=0; i < testInterface->bNumEndpoints; i++)
{
if (!(endpointID & USB_ENDPOINT_ADDRESS_MASK))
{
// Use first endpoint that matches the direction
if ((testInterface->endpoint[i].bEndpointAddress & USB_ENDPOINT_DIR_MASK) == endpointID)
{
memcpy(&transferParam->Ep, &testInterface->endpoint[i],sizeof(struct usb_endpoint_descriptor));
break;
}
}
else
{
if ((int)testInterface->endpoint[i].bEndpointAddress == endpointID)
{
memcpy(&transferParam->Ep, &testInterface->endpoint[i],sizeof(struct usb_endpoint_descriptor));
break;
}
}
}
if (!transferParam->Ep.bEndpointAddress)
{
CONERR("failed locating EP%02Xh!\n", endpointID);
FreeTransferParam(&transferParam);
goto Done;
}
if (transferParam->Test->BufferSize % transferParam->Ep.wMaxPacketSize)
{
CONERR("buffer size %d is not an interval of EP%02Xh maximum packet size of %d!\n",
transferParam->Test->BufferSize,
transferParam->Ep.bEndpointAddress,
transferParam->Ep.wMaxPacketSize);
FreeTransferParam(&transferParam);
goto Done;
}
if (test->IsoPacketSize)
transferParam->IsoPacketSize = test->IsoPacketSize;
else
transferParam->IsoPacketSize = transferParam->Ep.wMaxPacketSize;
if (ENDPOINT_TYPE(transferParam) == USB_ENDPOINT_TYPE_ISOCHRONOUS)
transferParam->Test->TransferMode = TRANSFER_MODE_ASYNC;
ResetRunningStatus(transferParam);
transferParam->ThreadHandle = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)TransferThreadProc,
transferParam,
CREATE_SUSPENDED,
&transferParam->ThreadID);
if (!transferParam->ThreadHandle)
{
CONERR0("failed creating thread!\n");
FreeTransferParam(&transferParam);
goto Done;
}
// If verify mode is on, this is a loop test, and this is a write endpoint, fill
// the buffers with the same test data sent by a benchmark device when running
// a read only test.
if (transferParam->Test->Verify &&
transferParam->Test->TestType == TestTypeLoop &&
!(transferParam->Ep.bEndpointAddress & USB_ENDPOINT_DIR_MASK))
{
// Data Format:
// [0][KeyByte] 2 3 4 5 ..to.. wMaxPacketSize (if data byte rolls it is incremented to 1)
// Increment KeyByte and repeat
//
BYTE indexC=0;
INT bufferIndex = 0;
WORD dataIndex;
INT packetIndex;
INT packetCount = ((transferParam->Test->BufferCount*transferParam->Test->BufferSize) / transferParam->Ep.wMaxPacketSize);
for(packetIndex = 0; packetIndex < packetCount; packetIndex++)
{
indexC = 2;
for (dataIndex=0; dataIndex < transferParam->Ep.wMaxPacketSize; dataIndex++)
{
if (dataIndex == 0) // Start
transferParam->Buffer[bufferIndex] = 0;
else if (dataIndex == 1) // Key
transferParam->Buffer[bufferIndex] = packetIndex & 0xFF;
else // Data
transferParam->Buffer[bufferIndex] = indexC++;
// if wMaxPacketSize is > 255, indexC resets to 1.
if (indexC == 0) indexC = 1;
bufferIndex++;
}
}
}
}
Done:
if (!transferParam)
CONERR0("failed creating transfer param!\n");
return transferParam;
}
void GetAverageBytesSec(struct BENCHMARK_TRANSFER_PARAM* transferParam, DOUBLE* bps)
{
DOUBLE ticksSec;
if ((!transferParam->StartTick) ||
(transferParam->StartTick >= transferParam->LastTick) ||
transferParam->TotalTransferred==0)
{
*bps=0;
}
else
{
ticksSec = (transferParam->LastTick - transferParam->StartTick) / 1000.0;
*bps = (transferParam->TotalTransferred / ticksSec);
}
}
void GetCurrentBytesSec(struct BENCHMARK_TRANSFER_PARAM* transferParam, DOUBLE* bps)
{
DOUBLE ticksSec;
if ((!transferParam->StartTick) ||
(!transferParam->LastStartTick) ||
(transferParam->LastTick <= transferParam->LastStartTick) ||
transferParam->LastTransferred==0)
{
*bps=0;
}
else
{
ticksSec = (transferParam->LastTick - transferParam->LastStartTick) / 1000.0;
*bps = transferParam->LastTransferred / ticksSec;
}
}
void ShowRunningStatus(struct BENCHMARK_TRANSFER_PARAM* transferParam)
{
struct BENCHMARK_TRANSFER_PARAM temp;
DOUBLE bpsOverall;
DOUBLE bpsLastTransfer;
// LOCK the display critical section
EnterCriticalSection(&DisplayCriticalSection);
memcpy(&temp, transferParam, sizeof(struct BENCHMARK_TRANSFER_PARAM));
// UNLOCK the display critical section
LeaveCriticalSection(&DisplayCriticalSection);
if ((!temp.StartTick) || (temp.StartTick >= temp.LastTick))
{
CONMSG("Synchronizing %d..\n", abs(transferParam->Packets));
}
else
{
GetAverageBytesSec(&temp,&bpsOverall);
GetCurrentBytesSec(&temp,&bpsLastTransfer);
transferParam->LastStartTick = 0;
CONMSG("Avg. Bytes/s: %.2f Transfers: %d Bytes/s: %.2f\n",
bpsOverall, temp.Packets, bpsLastTransfer);
}
}
void ShowTransferInfo(struct BENCHMARK_TRANSFER_PARAM* transferParam)
{
DOUBLE bpsAverage;
DOUBLE bpsCurrent;
DOUBLE elapsedSeconds;
if (!transferParam) return;
CONMSG("%s %s (Ep%02Xh) max packet size: %d\n",
EndpointTypeDisplayString[ENDPOINT_TYPE(transferParam)],
TRANSFER_DISPLAY(transferParam,"Read","Write"),
transferParam->Ep.bEndpointAddress,
transferParam->Ep.wMaxPacketSize);
if (transferParam->StartTick)
{
GetAverageBytesSec(transferParam,&bpsAverage);
GetCurrentBytesSec(transferParam,&bpsCurrent);
CONMSG("\tTotal Bytes : %I64d\n", transferParam->TotalTransferred);
CONMSG("\tTotal Transfers : %d\n", transferParam->Packets);
if (transferParam->ShortTransferCount)
{
CONMSG("\tShort Transfers : %d\n", transferParam->ShortTransferCount);
}
if (transferParam->TotalTimeoutCount)
{
CONMSG("\tTimeout Errors : %d\n", transferParam->TotalTimeoutCount);
}
if (transferParam->TotalErrorCount)
{
CONMSG("\tOther Errors : %d\n", transferParam->TotalErrorCount);
}
CONMSG("\tAvg. Bytes/sec : %.2f\n", bpsAverage);
if (transferParam->StartTick && transferParam->StartTick < transferParam->LastTick)
{
elapsedSeconds = (transferParam->LastTick - transferParam->StartTick) / 1000.0;
CONMSG("\tElapsed Time : %.2f seconds\n", elapsedSeconds);
}
CONMSG0("\n");
}
}
void ShowTestInfo(struct BENCHMARK_TEST_PARAM* testParam)
{
if (!testParam) return;
CONMSG("%s Test Information\n",TestDisplayString[testParam->TestType & 3]);
CONMSG("\tVid / Pid : %04Xh / %04Xh\n", testParam->Vid, testParam->Pid);
CONMSG("\tInterface # : %02Xh\n", testParam->Intf);
CONMSG("\tPriority : %d\n", testParam->Priority);
CONMSG("\tBuffer Size : %d\n", testParam->BufferSize);
CONMSG("\tBuffer Count : %d\n", testParam->BufferCount);
CONMSG("\tDisplay Refresh : %d (ms)\n", testParam->Refresh);
CONMSG("\tTransfer Timeout: %d (ms)\n", testParam->Timeout);
CONMSG("\tRetry Count : %d\n", testParam->Retry);
CONMSG("\tVerify Data : %s%s\n",
testParam->Verify ? "On" : "Off",
(testParam->Verify && testParam->VerifyDetails) ? " (Detailed)" : "");
CONMSG0("\n");
}
void WaitForTestTransfer(struct BENCHMARK_TRANSFER_PARAM* transferParam)
{
DWORD exitCode;
while (transferParam)
{
if (!transferParam->IsRunning)
{
if (GetExitCodeThread(transferParam->ThreadHandle, &exitCode))
{
if (exitCode == 0)
{
CONMSG("stopped Ep%02Xh thread.\tExitCode=%d\n",
transferParam->Ep.bEndpointAddress, exitCode);
break;
}
}
else
{
CONERR("failed getting Ep%02Xh thread exit code!\n",transferParam->Ep.bEndpointAddress);
break;
}
}
Sleep(100);
CONMSG("waiting for Ep%02Xh thread..\n", transferParam->Ep.bEndpointAddress);
}
}
void ResetRunningStatus(struct BENCHMARK_TRANSFER_PARAM* transferParam)
{
if (!transferParam) return;
transferParam->StartTick=0;
transferParam->TotalTransferred=0;
transferParam->Packets=-2;
transferParam->LastTick=0;
transferParam->RunningTimeoutCount=0;
}
int GetTestDeviceFromList(struct BENCHMARK_TEST_PARAM* testParam)
{
const int LINE_MAX_SIZE = 1024;
const int STRING_MAX_SIZE = 256;
const int NUM_STRINGS = 3;
const int ALLOC_SIZE = LINE_MAX_SIZE + (STRING_MAX_SIZE * NUM_STRINGS);
int userInput;
char* buffer;
char* line;
char* product;
char* manufacturer;
char* serial;
struct usb_bus* bus;
struct usb_device* dev;
usb_dev_handle* udev;
struct usb_device* validDevices[256];
int deviceIndex=0;
struct usb_interface_descriptor* firstInterface;
int ret = -1;
buffer = malloc(ALLOC_SIZE);
if (!buffer)
{
CONERR0("failed allocating memory!\n");
return ret;
}
line = buffer;
product = buffer + LINE_MAX_SIZE;
manufacturer = product + STRING_MAX_SIZE;
serial = manufacturer + STRING_MAX_SIZE;
for (bus = usb_get_busses(); bus; bus = bus->next)
{
for (dev = bus->devices; dev; dev = dev->next)
{
udev = usb_open(dev);
if (udev)
{
memset(buffer, 0, ALLOC_SIZE);
line = buffer;
if (dev->descriptor.iManufacturer)
{
if (usb_get_string_simple(udev, dev->descriptor.iManufacturer, manufacturer, STRING_MAX_SIZE - 1) > 0)
{
strcat(line,"(");
strcat(line,manufacturer);
strcat(line,") ");
}
}
if (dev->descriptor.iProduct)
{
if (usb_get_string_simple(udev, dev->descriptor.iProduct, product, STRING_MAX_SIZE - 1) > 0)
{
strcat(line,product);
strcat(line," ");
}
}
if (dev->descriptor.iSerialNumber)
{
if (usb_get_string_simple(udev, dev->descriptor.iSerialNumber, serial, STRING_MAX_SIZE - 1) > 0)
{
strcat(line,"[");
strcat(line,serial);
strcat(line,"] ");
}
}
if (!deviceIndex)
CONMSG0("\n");
validDevices[deviceIndex++] = dev;
CONMSG("%d. %04X:%04X %s\n",
deviceIndex, dev->descriptor.idVendor, dev->descriptor.idProduct, line);
usb_close(udev);
}
}
}
if (!deviceIndex)
{
CONERR0("No devices where found!\n");
ret = -1;
goto Done;
}
CONMSG("\nSelect device (1-%d) :",deviceIndex);
ret = _cscanf("%i",&userInput);
if (ret != 1 || userInput < 1)
{
CONMSG0("\n");
CONMSG0("Aborting..\n");
ret = -1;
goto Done;
}
CONMSG0("\n");
userInput--;
if (userInput >= 0 && userInput < deviceIndex)
{
testParam->DeviceHandle = usb_open(validDevices[userInput]);
if (testParam->DeviceHandle)
{
testParam->Device = validDevices[userInput];
testParam->Vid = testParam->Device->descriptor.idVendor;
testParam->Pid = testParam->Device->descriptor.idProduct;
if (usb_find_interface(&validDevices[userInput]->config[0],testParam->Intf, testParam->Altf, &firstInterface) == NULL)
{
// the specified (or default) interface didn't exist, use the first one.
if (firstInterface != NULL)
{
testParam->Intf = firstInterface->bInterfaceNumber;
}
else
{
CONERR("device %04X:%04X does not have any interfaces!\n",
testParam->Vid, testParam->Pid);
ret = -1;
goto Done;
}
}
ret = 0;
}
}
Done:
if (buffer)
free(buffer);
return ret;
}
int main(int argc, char** argv)
{
struct BENCHMARK_TEST_PARAM Test;
struct BENCHMARK_TRANSFER_PARAM* ReadTest = NULL;
struct BENCHMARK_TRANSFER_PARAM* WriteTest = NULL;
int key;
if (argc == 1)
{
ShowHelp();
return -1;
}
ShowCopyright();
// NOTE: This is the log level for the benchmark application.
//
#if defined __ERROR_H__
usb_log_set_level(255);
#endif
SetTestDefaults(&Test);
// Load the command line arguments.
if (ParseBenchmarkArgs(&Test, argc, argv) < 0)
return -1;
// Initialize the critical section used for locking
// the volatile members of the transfer params in order
// to update/modify the running statistics.
//
InitializeCriticalSection(&DisplayCriticalSection);
// Initialize the library.
usb_init();
// Find all busses.
usb_find_busses();
// Find all connected devices.
usb_find_devices();
if (Test.UseList)
{
if (GetTestDeviceFromList(&Test) < 0)
goto Done;
}
else
{
// Open a benchmark device. see Bench_Open().
Test.DeviceHandle = Bench_Open(Test.Vid, Test.Pid, Test.Intf, Test.Altf, &Test.Device);
}
if (!Test.DeviceHandle || !Test.Device)
{
CONERR("device %04X:%04X not found!\n",Test.Vid, Test.Pid);
goto Done;
}
// If "NoTestSelect" appears in the command line then don't send the control
// messages for selecting the test type.
//
if (!Test.NoTestSelect)
{
if (Bench_SetTestType(Test.DeviceHandle, Test.TestType, Test.Intf) != 1)
{
CONERR("setting bechmark test type #%d!\n%s\n", Test.TestType, usb_strerror());
goto Done;
}
}
CONMSG("Benchmark device %04X:%04X opened..\n",Test.Vid, Test.Pid);
// If reading from the device create the read transfer param. This will also create
// a thread in a suspended state.
//
if (Test.TestType & TestTypeRead)
{
ReadTest = CreateTransferParam(&Test, Test.Ep | USB_ENDPOINT_DIR_MASK);
if (!ReadTest) goto Done;
}
// If writing to the device create the write transfer param. This will also create
// a thread in a suspended state.
//
if (Test.TestType & TestTypeWrite)
{
WriteTest = CreateTransferParam(&Test, Test.Ep);
if (!WriteTest) goto Done;
}
// Set configuration #1.
if (usb_set_configuration(Test.DeviceHandle, 1) < 0)
{
CONERR("setting configuration #%d!\n%s\n",1,usb_strerror());
goto Done;
}
// Claim_interface Test.Intf (Default is #0)
if (usb_claim_interface(Test.DeviceHandle, Test.Intf) < 0)
{
CONERR("claiming interface #%d!\n%s\n", Test.Intf, usb_strerror());
goto Done;
}
// Set the alternate setting (Default is #0)
if (usb_set_altinterface(Test.DeviceHandle, Test.Altf) < 0)
{
CONERR("selecting alternate setting #%d on interface #%d!\n%s\n", Test.Altf, Test.Intf, usb_strerror());
goto Done;
}
else
{
if (Test.Altf > 0)
{
CONDBG("selected alternate setting #%d on interface #%d\n",Test.Altf, Test.Intf);
}
}
if (Test.Verify)
{
if (ReadTest && WriteTest)
{
if (CreateVerifyBuffer(&Test, WriteTest->Ep.wMaxPacketSize) < 0)
goto Done;
}
else if (ReadTest)
{
if (CreateVerifyBuffer(&Test, ReadTest->Ep.wMaxPacketSize) < 0)
goto Done;
}
}
ShowTestInfo(&Test);
ShowTransferInfo(ReadTest);
ShowTransferInfo(WriteTest);
CONMSG0("\nWhile the test is running:\n");
CONMSG0("Press 'Q' to quit\n");
CONMSG0("Press 'T' for test details\n");
CONMSG0("Press 'I' for status information\n");
CONMSG0("Press 'R' to reset averages\n");
CONMSG0("\nPress 'Q' to exit, any other key to begin..");
key = _getch();
CONMSG0("\n");
if (key=='Q' || key=='q') goto Done;
// Set the thread priority and start it.
if (ReadTest)
{
SetThreadPriority(ReadTest->ThreadHandle, Test.Priority);
ResumeThread(ReadTest->ThreadHandle);
}
// Set the thread priority and start it.
if (WriteTest)
{
SetThreadPriority(WriteTest->ThreadHandle, Test.Priority);
ResumeThread(WriteTest->ThreadHandle);
}
while (!Test.IsCancelled)
{
Sleep(Test.Refresh);
if (_kbhit())
{
// A key was pressed.
key = _getch();
switch (key)
{
case 'Q':
case 'q':
Test.IsUserAborted = TRUE;
Test.IsCancelled = TRUE;
break;
case 'T':
case 't':
ShowTestInfo(&Test);
break;
case 'I':
case 'i':
// LOCK the display critical section
EnterCriticalSection(&DisplayCriticalSection);
// Print benchmark test details.
ShowTransferInfo(ReadTest);
ShowTransferInfo(WriteTest);
// UNLOCK the display critical section
LeaveCriticalSection(&DisplayCriticalSection);
break;
case 'R':
case 'r':
// LOCK the display critical section
EnterCriticalSection(&DisplayCriticalSection);
// Reset the running status.
ResetRunningStatus(ReadTest);
ResetRunningStatus(WriteTest);
// UNLOCK the display critical section
LeaveCriticalSection(&DisplayCriticalSection);
break;
}
// Only one key at a time.
while (_kbhit()) _getch();
}
// If the read test should be running and it isn't, cancel the test.
if ((ReadTest) && !ReadTest->IsRunning)
{
Test.IsCancelled = TRUE;
break;
}
// If the write test should be running and it isn't, cancel the test.
if ((WriteTest) && !WriteTest->IsRunning)
{
Test.IsCancelled = TRUE;
break;
}
// Print benchmark stats
if (ReadTest)
ShowRunningStatus(ReadTest);
else
ShowRunningStatus(WriteTest);
}
// Wait for the transfer threads to complete gracefully if it
// can be done in 10ms. All of the code from this point to
// WaitForTestTransfer() is not required. It is here only to
// improve response time when the test is cancelled.
//
Sleep(10);
// If the thread is still running, abort and reset the endpoint.
if ((ReadTest) && ReadTest->IsRunning)
usb_resetep(Test.DeviceHandle, ReadTest->Ep.bEndpointAddress);
// If the thread is still running, abort and reset the endpoint.
if ((WriteTest) && WriteTest->IsRunning)
usb_resetep(Test.DeviceHandle, WriteTest->Ep.bEndpointAddress);
// Small delay incase usb_resetep() was called.
Sleep(10);
// WaitForTestTransfer will not return until the thread
// has exited.
WaitForTestTransfer(ReadTest);
WaitForTestTransfer(WriteTest);
// Print benchmark detailed stats
ShowTestInfo(&Test);
if (ReadTest) ShowTransferInfo(ReadTest);
if (WriteTest) ShowTransferInfo(WriteTest);
Done:
if (Test.DeviceHandle)
{
usb_close(Test.DeviceHandle);
Test.DeviceHandle = NULL;
}
if (Test.VerifyBuffer)
{
free(Test.VerifyBuffer);
Test.VerifyBuffer = NULL;
}
FreeTransferParam(&ReadTest);
FreeTransferParam(&WriteTest);
DeleteCriticalSection(&DisplayCriticalSection);
CONMSG0("Press any key to exit..");
_getch();
CONMSG0("\n");
return 0;
}
//////////////////////////////////////////////////////////////////////////////
/* END OF PROGRAM */
//////////////////////////////////////////////////////////////////////////////
void ShowHelp(void)
{
#define ID_HELP_TEXT 10020
#define ID_DOS_TEXT 300
CONST CHAR* src;
DWORD src_count, charsWritten;
HGLOBAL res_data;
HANDLE handle;
HRSRC hSrc;
ShowCopyright();
hSrc = FindResourceA(NULL, MAKEINTRESOURCEA(ID_HELP_TEXT),MAKEINTRESOURCEA(ID_DOS_TEXT));
if (!hSrc) return;
src_count = SizeofResource(NULL, hSrc);
res_data = LoadResource(NULL, hSrc);
if (!res_data) return;
src = (char*) LockResource(res_data);
if (!src) return;
if ((handle=GetStdHandle(STD_ERROR_HANDLE)) != INVALID_HANDLE_VALUE)
WriteConsoleA(handle, src, src_count, &charsWritten, NULL);
}
void ShowCopyright(void)
{
CONMSG0("libusb-win32 USB Benchmark v" RC_VERSION_STR "\n");
CONMSG0("Copyright (c) 2010 Travis Robinson. <libusbdotnet@gmail.com>\n");
CONMSG0("website: http://sourceforge.net/projects/libusbdotnet\n");
}