LPC-Link-2 usbd_rom_libusb example - BULK transfer timeout on VM

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LPC-Link-2 usbd_rom_libusb example - BULK transfer timeout on VM

1,960 Views
abe1
Contributor I

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.

Labels (2)
0 Kudos
4 Replies

1,694 Views
abe1
Contributor I

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.

0 Kudos

1,694 Views
Alexis_A
NXP TechSupport
NXP TechSupport

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 

0 Kudos

1,694 Views
abe1
Contributor I

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.  

0 Kudos

1,694 Views
Alexis_A
NXP TechSupport
NXP TechSupport

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

0 Kudos