Skip to content

Commit

Permalink
Add simple loopback data integrity test program, for demonstrating US…
Browse files Browse the repository at this point in the history
…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
sonatique committed Dec 7, 2023
1 parent 43c6fe0 commit 383ff6c
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 0 deletions.
Binary file added examples/USBBulkLoopAuto.img
Binary file not shown.
186 changes: 186 additions & 0 deletions examples/loopbacktest.c
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;
}
34 changes: 34 additions & 0 deletions msvc/libusb.sln
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xusb", "xusb.vcxproj", "{3F
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "set_option", "set_option.vcxproj", "{35BD5D4B-5102-4A08-81C0-AAF3285FCB01}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "loopbacktest", "loopbacktest.vcxproj", "{E1A74588-56DF-4BE2-8210-31774E6DBF3A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
Expand Down Expand Up @@ -498,6 +500,38 @@ Global
{35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-MT|Win32.Build.0 = Release|Win32
{35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-MT|x64.ActiveCfg = Release|x64
{35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-MT|x64.Build.0 = Release|x64
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Debug|ARM.ActiveCfg = Debug|ARM
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Debug|ARM.Build.0 = Debug|ARM
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Debug|ARM64.ActiveCfg = Debug|ARM64
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Debug|ARM64.Build.0 = Debug|ARM64
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Debug|Win32.ActiveCfg = Debug|Win32
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Debug|Win32.Build.0 = Debug|Win32
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Debug|x64.ActiveCfg = Debug|x64
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Debug|x64.Build.0 = Debug|x64
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Debug-MT|ARM.ActiveCfg = Debug-MT|ARM
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Debug-MT|ARM.Build.0 = Debug-MT|ARM
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Debug-MT|ARM64.ActiveCfg = Debug-MT|ARM64
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Debug-MT|ARM64.Build.0 = Debug-MT|ARM64
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Debug-MT|Win32.ActiveCfg = Debug-MT|Win32
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Debug-MT|Win32.Build.0 = Debug-MT|Win32
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Debug-MT|x64.ActiveCfg = Debug-MT|x64
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Debug-MT|x64.Build.0 = Debug-MT|x64
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Release|ARM.ActiveCfg = Release|ARM
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Release|ARM.Build.0 = Release|ARM
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Release|ARM64.ActiveCfg = Release|ARM64
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Release|ARM64.Build.0 = Release|ARM64
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Release|Win32.ActiveCfg = Release|Win32
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Release|Win32.Build.0 = Release|Win32
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Release|x64.ActiveCfg = Release|x64
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Release|x64.Build.0 = Release|x64
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Release-MT|ARM.ActiveCfg = Release-MT|ARM
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Release-MT|ARM.Build.0 = Release-MT|ARM
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Release-MT|ARM64.ActiveCfg = Release-MT|ARM64
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Release-MT|ARM64.Build.0 = Release-MT|ARM64
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Release-MT|Win32.ActiveCfg = Release-MT|Win32
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Release-MT|Win32.Build.0 = Release-MT|Win32
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Release-MT|x64.ActiveCfg = Release-MT|x64
{E1A74588-56DF-4BE2-8210-31774E6DBF3A}.Release-MT|x64.Build.0 = Release-MT|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
33 changes: 33 additions & 0 deletions msvc/loopbacktest.vcxproj
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>

0 comments on commit 383ff6c

Please sign in to comment.