Hi,
I use LPC4370 in one of my projects and started experiencing issues with USB bulk data transfer when connecting the device to a host computer running in a virtual environment.
The issue can also be reproduced using LPC-Link-2 dev board in the following scenario:
MCUXpresso IDE v11.1.0 with LPC-Link-2 board that runs usbd_rom_libusb example from lpcopen_3_02_lpcxpresso_link2_4370
The host computer is running a sample C program using 'libusb' that successfully communicates with the LPC-Link-2 board in Linux, macOS and Windows.
However, when using in a virtual environment like VBox, VmWare and other VMs, very often the sample C programs gets timeout when sending data to LPC-Link-2 board. The only remedy for that error condition is to physically unplug LPC-Link-2 and plug it back into the USB port, or by switching ownership of the USB device between guest OS and host OS. It seems like there is some sort of USB re-initialization problem with LPC-Link-2 board or with the usbd_rom_libusb sample code project.
I tried different ways to reset the device using libusb library with no luck.
Has anyone experienced similar issues with LPC-Link-2 (usbd_rom_libusb) in virtual environment?
Thanks for your comments in advance.
Hi,
Here is one of the possible ways to reproduce the issue. I am using the following environment:
Product: MCUXpresso IDE
Version: MCUXpresso IDE v11.1.0 [Build 3209] [2019-12-12]
LPCOpen:
lpcopen_3_02_lpcxpresso_link2_4370.zip
You will need to build and deploy 'usbd_rom_libusb' sample project to 'LPC-Link 2' board with the following modifications of the main():
int main(void)
{
/* Initialize board and chip */
SystemCoreClockUpdate();
Board_Init();
/* Init USB subsystem and LibUSBDevice */
libusbdev_init(USB_STACK_MEM_BASE, USB_STACK_MEM_SIZE);
while (1) {
/* wait until host is connected */
while (libusbdev_Connected() == 0) {
/* Sleep until next IRQ happens */
__WFI();
}
while (libusbdev_Connected()) {
if (libusbdev_QueueReadDone() != -1 && libusbdev_QueueSendDone() == 0) {
/* read data */
libusbdev_QueueReadReq(g_rxBuff, 1);
/* send data */
libusbdev_QueueSendReq(g_rxBuff, PACKET_BUFFER_SIZE);
}
}
}
}
You will need Ubuntu environment to build and run the following 'test.cpp' sample code for testing USB bulk data transmission:
#include <iostream>
#include <libusb-1.0/libusb.h>
#define VENDOR_ID (0x1fc9)
#define PRODUCT_ID (0x8A)
#define BULK_EP_OUT 0x01
#define BULK_EP_IN 0x81
#define BUFF_SIZE 4096
using namespace std;
int main() {
libusb_device_handle *dev_handle;
libusb_context *ctx = NULL;
int r;
int actual;
r = libusb_init(&ctx);
if(r < 0) {
cout << "Could not initialize USB library, error code: " << r << endl;
return 1;
}
dev_handle = libusb_open_device_with_vid_pid(ctx, VENDOR_ID, PRODUCT_ID);
if(dev_handle == NULL) {
cout << "Could not open usb device" << endl;
return 1;
}
cout << "Device was open successfully" << endl;
unsigned char *data = new unsigned char[BUFF_SIZE];
if(libusb_kernel_driver_active(dev_handle, 0) == 1) {
cout << "There is a kernel driver attached to the device" << endl;
if(libusb_detach_kernel_driver(dev_handle, 0) == 0)
cout << "Kernel driver detached successfully" << endl;
}
r = libusb_claim_interface(dev_handle, 0);
if(r < 0) {
cout << "Could not claim the interface" << endl;
return 1;
}
cout << "Interface claimed successfully" << endl;
cout << "Clearing device sending buffer ..." << endl;
r = libusb_bulk_transfer(dev_handle, BULK_EP_IN, data, BUFF_SIZE, &actual, 200);
cout << "Sending data to usb device ..." << endl;
r = libusb_bulk_transfer(dev_handle, BULK_EP_OUT, data, 1, &actual, 200);
if(r == 0 && actual == 1) {
cout << "Data sent successfully to usb device" << endl;
} else {
cout << "Could not send data to usb device, error: " << r << " actual bytes sent: " << actual << endl;
}
cout << "Receiving data from usb device ..." << endl;
r = libusb_bulk_transfer(dev_handle, BULK_EP_IN, data, BUFF_SIZE, &actual, 200);
if(r == 0 && actual == BUFF_SIZE) {
cout << "Received successfully " << actual << " bytes" << endl;
} else {
cout << "Could not receive data from usb device, error: " << r << " actual bytes received: " << actual << endl;
}
r = libusb_release_interface(dev_handle, 0);
if( r != 0) {
cout << "Could not release interface, error: " << r << endl;
return 1;
}
cout << "Interface released successfully" << endl;
libusb_close(dev_handle);
cout << "device closed successfully" << endl;
libusb_exit(ctx);
return 0;
}
To build the 'test.cpp' you will need libusb library that on Ubuntu can be installed with the following command:
sudo apt install libusb-1.0
To build 'test.cpp' sample program:
cc test.cpp -o test -lusb-1.0 -O2 -lstdc++
Plug the LPC-Link 2 board into USB port and run the 'test' program on the host:
sudo ./test
When successfully executed, 'test' program should log the following:
Device was open successfully
Interface claimed successfully
Clearing device sending buffer ...
Sending data to usb device ...
Data sent successfully to usb device
Receiving data from usb device ...
Received successfully 4096 bytes
Interface released successfully
device closed successfully
On a single host machine, with no VMs running, the 'test' program will run successfully every time you kick it off.
To reproduce the issue, install a VM software like VBox on Ubuntu host machine and spin up a guest Ubuntu OS.
When running the guest OS, repeatedly assign the USB device 'LPC18xx GENERIC USB' between Host Ubuntu machine and the guest Ubuntu instance and run 'test' program on the machine that the device is assigned to.
Eventually the 'test' program will throw an error -7 when sending data which indicates a bulk transfer time out, like in the output shown below:
Device was open successfully
Interface claimed successfully
Clearing device sending buffer ...
Sending data to usb device ...
Could not send data to usb device, error: -7 actual bytes sent: 0
Receiving data from usb device ...
Could not receive data from usb device, error: -7 actual bytes received: 0
Interface released successfully
device closed successfully
I could not find a software solution for the timeout error (-7) shown. The only way to get rid of the timeout is to unplug the device from the USB port and plug it back in.
It seems like there is a race condition with the libusbdev.c example or with the USB ROM (LPC4370) when switching device ownership between host and guest machines (I suspect it is related to USB connection re-initialization). I can also reproduce the same issue on CentOS or when running guest machines using VMWare.
Any comments appreciated.
thanks,
A.B.
Hello Adrian,
I have tried this example with the script and all works fine but after a reassign from the virtual machine the device isn't enumerated anymore, this using a host Windows machine and an Ubuntu VM. This is strange since using other examples like CDC or HID this doesn't happen. I think this is related to the class used in this example WCID.
I obtain a better performance with the CDC example using your script only I modified the bulk transfer since the maximum bulk size is 512.
Let me know if this helps you.
Best Regards,
Alexis Andalon
Hello Alexis,
The CDC example works better indeed on Ubuntu VM and it handles well when switching device between VMs. One problem is that it will require a different approach in handling communication with the device on multiple platforms: on Linux using libusb, on Windows and macOS using VCOM.
I modified the usbd_rom_cdc_vcom for using the following main loop for my testing:
while (1) {
if (vcom_connected() && vcom_tx_busy() == 0) {
rdCnt = vcom_bread(&g_rxBuff[0], 1);
if (rdCnt) {
vcom_write(&g_rxBuff[0], 4096);
}
}
//__WFI(); commenting out this line will stop loosing data transfers
}
BTW, the CDC usbd_rom_cdc_vcom example provided with LPCOpen, when running, will result in 10% of data loss (test.cpp will occasionally report a read timeout). I was able to reduce the data loss to 0% after I commented out '__WFI()' statement in the while loop.
thanks,
A.B.
Hello,
Would be possible to share a script where I could test this? I tried using the VCOM examples and the HID and after some hours there wasn't any disconnection.
Best Regards,
Alexis Andalon