forked from libusb/libusb
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add simple loopback data integrity test program, for demonstrating US…
…B data loss with libusb0.sys v1.2.7.3 when transfer IN buffer size is larger than 65536. The idea is to write 64bit incrementing numbers, by bunch of 1000 (i.e. 8000 bytes write transfer, value chosen to ensure short packets) to loopback device, and concurrently read from loopback using a buffer of a given size, and compare received value with an incrementing counter. If received value doesn't match expected value, it indicates some data has been lost. I can see no loss data for IN transfer buffer size <= 65536 and lost data with read buffer size > 65536 Steps to reproduce issue "mcuee/libusb-win32#45" with CYUSBKIT-003 EZ-USB FX3 dev board and FX3 SDK demo firmware "dma_examples\cyfxbulklpauto" (binary file "USBBulkLoopAuto.img" attached here for simplification, source code available as part of Cypress/Infineon FX3 SDK) 1) Upload USBBulkLoopAuto.img to EZ-USB FX3 RAM (for instance using Infineon USB Control Center) 2) Use Zadig Version 2.8 to force libusb-win32 (v1.2.7.3) driver for "FX3" device 3) Compile and run loopbacktest program: everything should work (no "Integrity error!" message, only printf of current received data) 4) Stop running program, change source code line 86 from "static uint8_t buf[OK_ReadBufferSize];" to "static uint8_t buf[NotOK_ReadBufferSize];", recompile, run. 5) Program will promptly stop with "Integrity error!" message. From expected and received value we can easily see that some data has been lost. 6) Use Zadig to force libusbK device, run same program again: no issues. Note 1: I verified using a USB physical traffic sniffer that IN and OUT data present on the bus match expectation in any cases (traces available on demand) Note 2: Any loopback capable USB device should work for reproducing the issue
- Loading branch information
Showing
4 changed files
with
253 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
/* | ||
* libusb example program to verify data integrity using loopback firmware | ||
* Copyright (C) 2023 Sylvain Fasel <[email protected]> | ||
* | ||
* This library 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.1 of the License, or (at your option) any later version. | ||
* | ||
* This library 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 library; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
*/ | ||
|
||
#include <errno.h> | ||
#include <signal.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <process.h> | ||
|
||
#include "libusb.h" | ||
#include "libusbi.h" | ||
|
||
#define EP_DATA_IN 0x81 | ||
#define EP_DATA_OUT 0x1 | ||
|
||
static volatile sig_atomic_t do_exit = 0; | ||
static struct libusb_device_handle *devh = NULL; | ||
|
||
static uint64_t expectedValue = 0; | ||
|
||
static void LIBUSB_CALL cb_xfr(struct libusb_transfer *xfr) | ||
{ | ||
if (xfr->status != LIBUSB_TRANSFER_COMPLETED) { | ||
fprintf(stderr, "transfer status %d\n", xfr->status); | ||
libusb_free_transfer(xfr); | ||
exit(3); | ||
} | ||
|
||
if(xfr -> actual_length % 8 != 0) | ||
{ | ||
fprintf(stderr, "transfer actual_length is not a multiple of 8, but is %d\n", xfr->actual_length); | ||
libusb_free_transfer(xfr); | ||
exit(3); | ||
} | ||
|
||
const uint64_t* ulongPtr = (uint64_t*)xfr->buffer; | ||
|
||
fprintf(stdout, "transfer actual_length = %d (firstValue = %llu) %s\n", | ||
xfr->actual_length, | ||
*ulongPtr, | ||
expectedValue == 0 && *ulongPtr != expectedValue ? "[ignored]" : ""); | ||
|
||
for(int i = 0; i < xfr->actual_length / 8; ++i) | ||
{ | ||
if(*(ulongPtr + i) != expectedValue) | ||
{ | ||
if(expectedValue != 0) | ||
{ | ||
fprintf(stderr, "Integrity error! Expected: %llu but got: %llu\n", expectedValue, *(ulongPtr + i)); | ||
exit(3); | ||
} | ||
} | ||
else | ||
{ | ||
++expectedValue; | ||
} | ||
} | ||
|
||
if (libusb_submit_transfer(xfr) < 0) { | ||
fprintf(stderr, "error re-submitting URB\n"); | ||
exit(1); | ||
} | ||
} | ||
|
||
#define OK_ReadBufferSize (65536) | ||
#define NotOK_ReadBufferSize (2*65536) | ||
|
||
static int start_transfer_in() | ||
{ | ||
static uint8_t buf[NotOK_ReadBufferSize]; | ||
static struct libusb_transfer *xfr; | ||
|
||
xfr = libusb_alloc_transfer(0); | ||
if (!xfr) { | ||
errno = ENOMEM; | ||
return -1; | ||
} | ||
|
||
libusb_fill_bulk_transfer(xfr, devh, EP_DATA_IN, buf, sizeof(buf), cb_xfr, NULL, 0); | ||
|
||
return libusb_submit_transfer(xfr); | ||
} | ||
|
||
#define WriteBufferSize (65536) | ||
static uint64_t current_write_value = 0; | ||
|
||
static void write_data(int ulongCount) | ||
{ | ||
if(ulongCount > WriteBufferSize / 8) | ||
{ | ||
fprintf(stderr, "ulongCount value larger than permitted by byte buffer size\n"); | ||
exit(3); | ||
} | ||
|
||
static uint8_t buf[WriteBufferSize]; | ||
static uint64_t* ulongPtr = (uint64_t*)buf; | ||
|
||
for(int i = 0; i < ulongCount; ++i) | ||
{ | ||
*(ulongPtr + i) = current_write_value++; | ||
} | ||
|
||
int actualLength; | ||
int rc = libusb_bulk_transfer(devh, EP_DATA_OUT, buf, ulongCount*8, &actualLength, 2000); | ||
if(rc != LIBUSB_SUCCESS || actualLength != ulongCount*8) | ||
{ | ||
fprintf(stderr, "Writing data failed: %s\n", libusb_error_name(rc)); | ||
exit(3); | ||
} | ||
} | ||
|
||
static unsigned continuous_write(void * arg) | ||
{ | ||
UNUSED(arg); | ||
|
||
while (!do_exit) { | ||
write_data(1000); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static void sig_hdlr(int signum) | ||
{ | ||
(void)signum; | ||
|
||
do_exit = 1; | ||
} | ||
|
||
int main(void) | ||
{ | ||
int rc; | ||
|
||
(void)signal(SIGINT, sig_hdlr); | ||
|
||
rc = libusb_init_context(/*ctx=*/NULL, /*options=*/NULL, /*num_options=*/0); | ||
if (rc < 0) { | ||
fprintf(stderr, "Error initializing libusb: %s\n", libusb_error_name(rc)); | ||
exit(1); | ||
} | ||
|
||
devh = libusb_open_device_with_vid_pid(NULL, 0x04b4, 0x00F0); | ||
if (!devh) { | ||
fprintf(stderr, "Error finding USB device\n"); | ||
goto out; | ||
} | ||
|
||
rc = libusb_claim_interface(devh, 0); | ||
if (rc < 0) { | ||
fprintf(stderr, "Error claiming interface: %s\n", libusb_error_name(rc)); | ||
goto out; | ||
} | ||
|
||
start_transfer_in(); | ||
|
||
_beginthreadex(NULL, 0, continuous_write, NULL, 0, NULL); | ||
|
||
while (!do_exit) { | ||
rc = libusb_handle_events(NULL); | ||
if (rc != LIBUSB_SUCCESS) | ||
break; | ||
} | ||
|
||
libusb_release_interface(devh, 0); | ||
out: | ||
if (devh) | ||
libusb_close(devh); | ||
libusb_exit(NULL); | ||
return rc; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
<Import Project="ProjectConfigurations.Base.props" /> | ||
<PropertyGroup Label="Globals"> | ||
<ProjectGuid>{E1A74588-56DF-4BE2-8210-31774E6DBF3A}</ProjectGuid> | ||
</PropertyGroup> | ||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> | ||
<Import Project="Configuration.Application.props" /> | ||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> | ||
<ImportGroup Label="ExtensionSettings"> | ||
</ImportGroup> | ||
<ImportGroup Label="PropertySheets"> | ||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | ||
<Import Project="Base.props" /> | ||
</ImportGroup> | ||
<PropertyGroup Label="UserMacros" /> | ||
<ItemGroup> | ||
<ClCompile Include="..\examples\loopbacktest.c" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<ClInclude Include=".\config.h" /> | ||
<ClInclude Include="..\libusb\libusb.h" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<ProjectReference Include=".\libusb_static.vcxproj"> | ||
<Project>{349ee8f9-7d25-4909-aaf5-ff3fade72187}</Project> | ||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly> | ||
</ProjectReference> | ||
</ItemGroup> | ||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | ||
<ImportGroup Label="ExtensionTargets"> | ||
</ImportGroup> | ||
</Project> |