I “created” a very basic USB device of communication class using an STM32 (f446re) that simply sends a sentence every second. Now I am writing a program to read the said sentence on the computer the device is connected to. I decided to use libusb library as it is apparently cross-platform and it seemed fairly easy to use.
I wrote my little program on Linux as it is the OS I use to code, I can provide details on the code if you want me to but basically I:
- list all devices
- select mine using PID and VID
- open it
- detach kernel driver
- claim interface
- and then submit bulk transfer to receive the data and print it to the terminal
After setting some custom udev rule with my VID and PID it worked perfectly fine.
So I decided to try it on Windows and the first issue I encountered was a LIBUSB_ERROR_NOT_SUPPORTED when trying to open the device. After some googling I found out that USB CDC has no driver by default on Windows or whatever (unlike Mass Storage devices and Human Machine Interface devices) so I had to install some generic USB driver like WinUSB. So I did, using a software called Zadig, I also removed the code to detach kernel driver because I had to apparently and now I can open the device but the transfer fails with LIBUSB_ERROR_NOT_FOUND.
I have tried many things and none worked, here is the full log with debug level set to 4 starting from the opening of the device:
[ 0.077542] [00005830] libusb: debug [libusb_open] open 3.2
[ 0.078566] [000022f0] libusb: debug [libusb_claim_interface] interface 0
[ 0.078694] [000022f0] libusb: debug [winusbx_claim_interface] claimed interface 0
[ 0.079206] [000022f0] libusb: debug [windows_assign_endpoints] (re)assigned endpoint 82 to interface 0
[ 0.079853] [00000280] libusb: debug [windows_iocp_thread] ignoring overlapped 000000FB54DFF8D0 for handle 000002181F1A3310
[ 0.080480] [00000280] libusb: debug [windows_iocp_thread] ignoring overlapped 000000FB54DFF8D0 for handle 000002181F1A3310
[ 0.081122] [000022f0] libusb: debug [libusb_submit_transfer] transfer 000002181F1930E0
[ 0.081842] [00000280] libusb: debug [windows_iocp_thread] ignoring overlapped 000000FB54DFF8D0 for handle 000002181F1A3310
[ 0.085782] [000022f0] libusb: debug [add_to_flying_list] arm timer for timeout in 2000ms (first in line)
[ 0.086302] [00000280] libusb: debug [windows_iocp_thread] ignoring overlapped 000000FB54DFF8D0 for handle 000002181F1A3310
[ 0.086975] [000022f0] libusb: error [winusbx_submit_bulk_transfer] unable to match endpoint to an open interface - cancelling transfer
[ 0.087657] [00000280] libusb: debug [windows_iocp_thread] ignoring overlapped 000000FB54DFF9B0 for handle 000002181F1A3310
[ 0.088588] [000022f0] libusb: debug [arm_timer_for_next_timeout] no timeouts, disarming timer
Failed to submit bulk transfer: LIBUSB_ERROR_NOT_FOUND
[ 0.091200] [000022f0] libusb: debug [libusb_free_transfer] transfer 000002181F1930E0
[ 0.091677] [000022f0] libusb: debug [libusb_release_interface] interface 0
I don’t understand shit, including the fact that it says “(re)assigned endpoint 82” even though in my code the endpoint is set to 81 because this is the endpoint my USB device is sending data through.
I can post details on the USB device using USBView or send parts of the code if needed.
EDIT (add some code):
void LIBUSB_CALL bulk_callback(struct libusb_transfer* transfer) {
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
std::cout << "Bulk transfer completed successfully. Received " << transfer->actual_length << " bytes" << std::endl;
// Re-submit the transfer for continuous reception
int error = libusb_submit_transfer(transfer);
if (error < 0) {
std::cerr << "Error re-submitting transfer: " << libusb_error_name(error) << std::endl;
}
} else {
std::cerr << "Transfer failed with status: " << libusb_error_name(transfer->status) << std::endl;
libusb_free_transfer(transfer);
}
}
int main() {
int error = libusb_open(device, &m_selected_device);
if (error < 0) {
m_working = false;
std::cerr << "Error: failed to open device: " << libusb_error_name(error) << std::endl;
return;
}
error = libusb_claim_interface(m_selected_device, INTERFACE_NUMBER); // 0
if (error < 0) {
std::cerr << "Error claiming interface: " << libusb_error_name(error) << std::endl;
return;
}
// Communication here
struct libusb_transfer* transfer = libusb_alloc_transfer(0);
if (!transfer) {
m_error = true;
std::cerr << "Failed to allocate transfer" << std::endl;
libusb_release_interface(m_selected_device, INTERFACE_NUMBER);
clean_up();
return;
}
// Allocate a buffer for receiving data
unsigned char* buffer = (unsigned char*) malloc(TRANSFER_SIZE);
if (!buffer) {
std::cerr << "Failed to allocate buffer" << std::endl;
libusb_free_transfer(transfer);
libusb_release_interface(m_selected_device, INTERFACE_NUMBER);
clean_up();
return;
}
// Fill the bulk transfer with the endpoint, buffer, size, and callback
libusb_fill_bulk_transfer(
transfer,
m_selected_device,
ENDPOINT_IN, // 0x81
buffer,
TRANSFER_SIZE, // 64
bulk_callback,
NULL,
TIMEOUT
);
// Submit the transfer to the USB stack
error = libusb_submit_transfer(transfer);
if (error < 0) {
std::cerr << "Failed to submit bulk transfer: " << libusb_error_name(error) << std::endl;
free(buffer);
libusb_free_transfer(transfer);
libusb_release_interface(m_selected_device, INTERFACE_NUMBER);
clean_up();
return;
}
// Main loop
while (m_working) {
error = libusb_handle_events(m_context);
if (error < 0) {
std::cerr << "Error handling events: " << libusb_error_name(error) << std::endl;
break;
}
}
error = libusb_release_interface(m_selected_device, INTERFACE_NUMBER);
if (error < 0) {
std::cerr << "Error releasing interface: " << libusb_error_name(error) << std::endl;
}
free(buffer);
if (m_selected_device) {
libusb_close(m_selected_device);
m_selected_device = NULL;
}
return 0;
}
Coffee Spoon is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
1
If your USB device implements the USB CDC protocol, it will appear as a serial port. All recent operating systems have the required driver installed by default (even Windows has had it for about 10 years now).
You have three options to work with it:
- Use a cross-platform library for serial communication. Use Google to find one.
- Use the native APIs for serial communication. They are almost the same on Linux and macOS, but different on Windows.
- Use libusb for communication. In order to do so, you have unload the USB CDC driver. On Linux, that’s easily possible. On macOS, it requires root privileges. On Windows, you have to do it manually with Zadig. In addition, you have to install the WinUSB driver instead.
It’s difficult to say what your specific problems are:
- Unless your Windows version is more than 10 years old, it will have the USB CDC driver. But it is in the way of libusb.
- You might have uninstalled the USB CDC driver, but not installed the WinUSB driver instead. It is required for libusb.
- If your board is an STM development board with an integrated ST-Link, you might have uninstalled or replaced the drivers for the wrong device. ST-Link is a composite device with serial port, debug port and possibly mass storage.
- Support for composite devices on Windows was only recently added to libusb. Your version of libusb might not support it yet. (It’s not an issue on Linux or macOS, and only relevant if it is an ST-Link.)
Anyhow, use a library or API for serial ports so you don’t have to mess with drivers.
1