'What is passed as the user_data in libusb_fill_bulk_transfer?

I have a working code which seems to be correctly reading the data through the usb using libusb_fill_bulk_transfer(libusb_transfer* transfer, libusb_device_handle * dev_handle, unsigned char endpoint, unsigned char* buffer, int length, libusb_transfer_cb_fn callback, void* user_data, unsigned int timeout ), however i didn't quiet understand what value should be used for user_data and how the transfer call back mechanism works for the libusb.

Prologue, i have a class UsbCommunicator which manages the Usb context and the device handle(s) . It has function UsbCommunicator::Activate which populates the transfer structure and then submits it.In the code below, note the statement **STATEMENT 1 **, where for the user_data of type void*, this pointer is passed. I agree it is allowed, but i am not really sure how the callback i.e. libusb_transfer_cb_fn which is typedef void( * libusb_transfer_cb_fn) (struct libusb_transfer *transfer) casts this to libusb_transfer* transfer My guess (from cppreference.com) is it is possible because Two objects a and b are pointer-interconvertible if: one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no non-static data members, any base class sub object of that object

QUESTION 1: Is it possible to cast this to libusb_transfer* p_transfer because of the reason mentioned above?

QUESTION 2: If the above is false, how is the callback called ?

Here is the function UsbCommunicator::Activate

void UsbCommunicator::Activate()
{
    // Start the timer for the first set of transfers.
    start_timestamp_ = common::CurrentTimeStampInMilliseconds();

    // Launch all transfers.
    INFO(p_logger_, StartUsbService, id_ + ": Starting USB transfers with size " + std::to_string(bytes_per_transfer_));
    for (unsigned int i = 0; i < num_transfers_; i++)
    {
        // Populate the transfer structure.
                    auto data_buffer = reinterpret_cast<unsigned char*>(data_buffers_[i].get());

       // Pass "this" as user data, so the callback function can pass on the received data. **STATEMENT 1 **
        libusb_fill_bulk_transfer(transfers_[i], p_device_handle, endpoint_address_, data_buffer,
                                  bytes_per_transfer_, LibusbTransferCallback, this, transfer_timeout_);

        // Submit the transfer.
        int ret = libusb_submit_transfer(transfers_[i]);
        if (ret == 0)
        {
            rqts_in_flight_++;
        }
        else
        {
            WARNING(p_logger_, StartUsbService,
                    id_ + ": Failed to submit libusb transfer request number: " + std::to_string(i) + ". Ret: " + std::to_string(ret));
            continue;
        }
    }

    initialised_ = true;
}

I have further has LibusbTransferCallback, which has the type typedef void( * libusb_transfer_cb_fn) (struct libusb_transfer *transfer) like so:. I think it works based on the assumption i made in Question 2 that this call is possible after some internal casting of void* data to libusb_transfer* p_transfer, is that right again ?

//! @brief Call-back function called by libusb upon completion of a queued data transfer.
//! @param[in] p_transfer  Libusb transfer structure.
static void LibusbTransferCallback(struct libusb_transfer* p_transfer)
{
    // Handle the transfer within the USB Communicator.
    UsbCommunicator* p_usb_comm = static_cast<UsbCommunicator*>(p_transfer->user_data);
    p_usb_comm->HandleTransferCallback(p_transfer);
}

And finally UsbCommunicator::HandleTransferCallback like so:

void UsbCommunicator::HandleTransferCallback(libusb_transfer* p_transfer)
{
    std::unique_ptr<common::DataPacket> x_data_packet = std::make_unique<common::DataPacket>();
    x_data_packet->status = common::DataPacketStatus::Success;

    int bytes_received = p_transfer->actual_length;

    rqts_in_flight_--;

    if (p_transfer->status == LIBUSB_TRANSFER_COMPLETED)
    {
        x_data_packet->size = bytes_received;
        x_data_packet->x_data = std::make_unique<uint8_t[]>(bytes_received);
        std::memcpy(x_data_packet->x_data.get(), p_transfer->buffer, bytes_received);
        x_data_packet->timestamp_ms = common::CurrentTimeStampInMilliseconds();

        success_count_++;
    }
    // Some more lines
    //..
}


Solution 1:[1]

You can pass in anything and access it within the callback via transfer->user_data.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1