AravisProject / aravis

A vision library for genicam based cameras
GNU Lesser General Public License v2.1
840 stars 316 forks source link

Option to force device connection #885

Open patrickwasp opened 3 months ago

patrickwasp commented 3 months ago

When starting an aravis program I sometimes get: Failed to bootstrap USB device '(null)-(null)-(null)-2978000AF0CC'. I'm not sure exactly why this happens but unplugging the device and plugging it back it in fixes the issue, although it has to go through a (CID: DD4CCACF1905) device communication failure error the first time it retries after being re-plugged in.

If possible a force option when calling arv_camera_new() to claim the device regardless of whatever else is using it would be helpful. If this is something deeper that cannot be done at the aravis level, are there any suggestions on a fix that doesn't involve physically unplugging?

lsusb -t has the following output for the camera, without a Driver, when it doesn't work

    |__ Port 1: Dev 2, If 0, Class=, Driver=, 5000M
    |__ Port 1: Dev 2, If 1, Class=, Driver=, 5000M

When it works lsusb -t shows

    |__ Port 1: Dev 8, If 0, Class=, Driver=usbfs, 5000M
    |__ Port 1: Dev 8, If 1, Class=, Driver=usbfs, 5000M
horchler commented 1 month ago

@patrickwasp have you tried arv_gv_device_take_control (ARV_GV_DEVICE (arv_camera_get_device (camera)), &error) and arv_gv_device_leave_control (ARV_GV_DEVICE (arv_camera_get_device (camera)), &error) for this?

charliebudd commented 1 month ago

It seems the suggested functions are for the GigEVision cameras denoted by gv. An equivalent implementation for USB cameras could be provided, and then both implementations could be offered under a generic interface through the ArvDevice or even the ArvCamera class. The USB implementation could simply toggle the power of the USB device on/off, or use libusb to claim the interface, as in the snippet provided in #500.

charliebudd commented 1 month ago

I've been playing around with this a little using libusb. The code below finds no kernel driver active on my camera's interface and is able to successfully claim said interface. I then call libusb_reset_device on the camera and close it. However, I still get the same Failed to bootstrap USB device error.

static libusb_device* getDevice(libusb_context* ctx, int guid, libusb_device_descriptor* desc) {
    libusb_device** device_list;
    libusb_device* device;

    ssize_t device_count = libusb_get_device_list(ctx, &device_list);
    if (device_count < 0) {
        std::cerr << "Failed to get device list" << std::endl;
        return nullptr;
    }

    for (ssize_t i = 0; i < device_count; ++i) {
        device = device_list[i];
        if (libusb_get_device_descriptor(device, desc) != 0) {
            std::cerr << "Failed to get device descriptor" << std::endl;
            continue;
        }

        if (desc->idVendor == guid) {
            libusb_free_device_list(device_list, 1);
            return device;
        }
    }

    libusb_free_device_list(device_list, 1);
    return nullptr;
}

static void resetDevice(int guid) {
    // Initialize libusb
    libusb_context* ctx = nullptr;
    if (libusb_init(&ctx) != 0) {
        std::cerr << "Failed to initialize libusb" << std::endl;
        return;
    }

    // Get matching device
    libusb_device_descriptor desc;
    libusb_device* dev = getDevice(ctx, guid, &desc);
    if (dev == nullptr) {
        std::cerr << "Failed to find device" << std::endl;
        libusb_exit(ctx);
        return;
    }

    libusb_device_handle* dev_handle = nullptr;
    if (libusb_open(dev, &dev_handle) != 0) {
        std::cerr << "Failed to open USB device" << std::endl;
        libusb_exit(ctx);
        return;
    }

    for (int i = 0; i < desc.bNumConfigurations; ++i) {
        if(libusb_kernel_driver_active(dev_handle, i) == 1) {
            std::cout << "Kernel drive active" << std::endl;
            if(libusb_detach_kernel_driver(dev_handle, i) == 0) {
                std::cout << "Kernel drive Detached" << std::endl;
            }
            else {
                std::cout << "Couldn't detach kernel driver" << std::endl;
            }
        }

        if (libusb_claim_interface(dev_handle, i) != 0) {
            std::cerr << "Failed to claim interface " << i << std::endl;
            continue;
        }

        libusb_release_interface(dev_handle, i);
        std::cout << "Interface " << i << " claimed successfully" << std::endl;
    }

    libusb_reset_device(dev_handle);

    libusb_close(dev_handle);
    libusb_exit(ctx);
    return;
}

std::string deviceGuidString = std::string(arv_get_device_physical_id(0)).substr(0, 4);
int deviceGuid = std::stoi(deviceGuidString, 0, 16);
resetDevice(deviceGuid);
charliebudd commented 1 month ago

After enabling debug...

[15:58:29.932] 🅸 device> SIRM_MAX_LEADER_SIZE = 0x00000040 [15:58:29.932] 🅸 device> SIRM_PAYLOAD_SIZE = 0x00100000 [15:58:29.932] 🅸 device> SIRM_PAYLOAD_COUNT = 0x00000005 [15:58:29.932] 🅸 device> SIRM_TRANSFER1_SIZE = 0x00000000 [15:58:29.932] 🅸 device> SIRM_TRANSFER2_SIZE = 0x00000000 [15:58:29.932] 🅸 device> SIRM_MAX_TRAILER_SIZE = 0x00000040 [15:58:29.932] 🆆 device> [UvDevice::_bootstrap] Error during memory read

I have narrowed down the problem to when _bootstrap tries to read from manifest_table_address... https://github.com/AravisProject/aravis/blob/81ee947eba9d3805b11e12bad14558d9240378ed/src/arvuvdevice.c#L603C1-L610C3