Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libusb hotplug support #5492

Open
wants to merge 1 commit into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/libusb/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ target_sources(${LRS_TARGET}
"${CMAKE_CURRENT_LIST_DIR}/request-libusb.cpp"

"${CMAKE_CURRENT_LIST_DIR}/enumerator-libusb.cpp"

"${CMAKE_CURRENT_LIST_DIR}/device-watcher-libusb.cpp"
"${CMAKE_CURRENT_LIST_DIR}/device-watcher-libusb.h"
)
19 changes: 17 additions & 2 deletions src/libusb/context-libusb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ namespace librealsense
{
usb_context::usb_context() : _ctx(NULL), _list(NULL), _count(0)
{
auto sts = libusb_init(NULL);
auto sts = libusb_init(&_ctx);
if(sts != LIBUSB_SUCCESS)
{
LOG_ERROR("libusb_init failed");
}
_count = libusb_get_device_list(_ctx, &_list);
update_device_list();
_event_handler = std::make_shared<active_object<>>([this](dispatcher::cancellable_timer cancellable_timer)
{
if(_kill_handler_thread)
Expand All @@ -24,6 +24,13 @@ namespace librealsense
});
}

void usb_context::update_device_list()
{
if(_list)
libusb_free_device_list(_list, true);
_count = libusb_get_device_list(_ctx, &_list);
}

usb_context::~usb_context()
{
libusb_free_device_list(_list, true);
Expand Down Expand Up @@ -66,7 +73,15 @@ namespace librealsense

size_t usb_context::device_count()
{
update_device_list();
return _count;
}

static std::shared_ptr<usb_context> _context;
const std::shared_ptr<usb_context> get_usb_context()
{
if(!_context) _context = std::make_shared<usb_context>();
return _context;
}
}
}
4 changes: 4 additions & 0 deletions src/libusb/context-libusb.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ namespace librealsense
libusb_device* get_device(uint8_t index);

private:
void update_device_list();

std::mutex _mutex;
libusb_device **_list;
size_t _count;
Expand All @@ -37,5 +39,7 @@ namespace librealsense
bool _handling_events = false;
std::shared_ptr<active_object<>> _event_handler;
};

const std::shared_ptr<usb_context> get_usb_context();
}
}
112 changes: 112 additions & 0 deletions src/libusb/device-watcher-libusb.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2019 Intel Corporation. All Rights Reserved.
#include "device-watcher-libusb.h"

#include "libusb.h"
#include "context-libusb.h"

using namespace librealsense;
using namespace librealsense::platform;

device_watcher_libusb::device_watcher_libusb(const platform::backend* backend_ref) : _backend(backend_ref)
{
LOG_DEBUG("Making a libusb device watcher");
_usb_context = get_usb_context();
_prev_group = {_backend->query_uvc_devices(),
_backend->query_usb_devices(),
_backend->query_hid_devices() };
}

void device_watcher_libusb::update_devices()
{
LOG_DEBUG("Updating devices");
platform::backend_device_group current_group = {_backend->query_uvc_devices(),
_backend->query_usb_devices(),
_backend->query_hid_devices() };
std::lock_guard<std::mutex> lk(_mutex);
_callback(_prev_group, current_group);
_prev_group = current_group;
}

void device_watcher_libusb::watch_thread()
{
int completed = 0;
struct timeval watch_tv;
watch_tv.tv_sec = 0;
watch_tv.tv_usec = 10000;
while (!completed && !_watch_thread_stop) {
libusb_handle_events_timeout_completed(_usb_context->get(), &watch_tv, &completed);
if(update_next) {
update_devices();
update_next = false;
}
}
}

void device_watcher_libusb::start(librealsense::platform::device_changed_callback callback)
{
std::lock_guard<std::mutex> lk(_mutex);
_callback = std::move(callback);

bool hotplug_started = false;
update_next = false;
if(_callback && libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {

int rc = libusb_hotplug_register_callback(_usb_context->get(),
libusb_hotplug_event(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
LIBUSB_HOTPLUG_NO_FLAGS,
LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
[](libusb_context *, libusb_device * d, libusb_hotplug_event event, void * user_data) -> int {
std::string event_str = "libusb device ";
if(event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) event_str += "arrived";
else if(event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) event_str += "left";

int addr = libusb_get_device_address(d);

libusb_device_descriptor desc;
int rc = libusb_get_device_descriptor(d, &desc);
if(rc)
LOG_ERROR("Error getting device descriptor " << libusb_error_name(rc));
else
LOG_INFO(event_str << " address: " << addr << " VID: " << hexify(desc.idVendor) << " PID: " << hexify(desc.idProduct));

// No querying or talking to devices in hotplug callback https://github.com/libusb/libusb/issues/408
((device_watcher_libusb *)user_data)->update_next = true;
return 0; // Continue processing hotplug events
},
this,
&callback_handle);

if(rc != LIBUSB_SUCCESS)
LOG_WARNING("Error registering hotplug " << libusb_error_name(rc));
else
hotplug_started = true;

_watch_thread_stop = false;
_watch_thread = std::thread(&device_watcher_libusb::watch_thread, this);
}

if(!hotplug_started) {
LOG_WARNING("Failed to start USB hotplug, falling back to polling");
_fallback_polling = std::make_shared<polling_device_watcher>(_backend);
_fallback_polling->start(callback);
}
}

void device_watcher_libusb::stop()
{
LOG_DEBUG("Stop device watcher");
std::lock_guard<std::mutex> lk(_mutex);
if(_fallback_polling) {
_fallback_polling->stop();
_fallback_polling.reset();
return;
}

if(_callback) {
libusb_hotplug_deregister_callback(_usb_context->get(), callback_handle);
_watch_thread_stop = true;
_watch_thread.join();
}
_callback = nullptr;
}
44 changes: 44 additions & 0 deletions src/libusb/device-watcher-libusb.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* License: Apache 2.0. See LICENSE file in root directory. */
/* Copyright(c) 2019 Intel Corporation. All Rights Reserved. */
#pragma once

#include "../types.h"
#include "../backend.h"
#include "../usb/usb-device.h"
#include "libusb.h"
#include "context-libusb.h"

#include <memory>

namespace librealsense
{
namespace platform
{
class device_watcher_libusb : public platform::device_watcher
{
public:
device_watcher_libusb(const platform::backend* backend_ref);
virtual void start(device_changed_callback callback) override;
virtual void stop() override;

private:
const platform::backend* _backend;
backend_device_group _prev_group;
libusb_hotplug_callback_handle callback_handle;
std::shared_ptr<platform::usb_context> _usb_context;
libusb_context * _context;

std::mutex _mutex;
device_changed_callback _callback = nullptr;

std::shared_ptr<polling_device_watcher> _fallback_polling;

void update_devices();

void watch_thread();
std::thread _watch_thread;
std::atomic<bool> _watch_thread_stop;
std::atomic<bool> update_next;
};
}
}
5 changes: 3 additions & 2 deletions src/libusb/enumerator-libusb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ namespace librealsense
std::vector<usb_device_info> usb_enumerator::query_devices_info()
{
std::vector<usb_device_info> rv;
auto ctx = std::make_shared<usb_context>();
auto ctx = get_usb_context();

for (uint8_t idx = 0; idx < ctx->device_count(); ++idx)
{
Expand All @@ -93,6 +93,7 @@ namespace librealsense
auto ret = libusb_get_device_descriptor(device, &desc);
if (LIBUSB_SUCCESS == ret)
{
LOG_DEBUG("Found device " << hexify(desc.idVendor) << " " << hexify(desc.idProduct));
auto sd = get_subdevices(device, desc);
rv.insert(rv.end(), sd.begin(), sd.end());
}
Expand All @@ -104,7 +105,7 @@ namespace librealsense

rs_usb_device usb_enumerator::create_usb_device(const usb_device_info& info)
{
auto ctx = std::make_shared<usb_context>();
auto ctx = get_usb_context();

for (uint8_t idx = 0; idx < ctx->device_count(); ++idx)
{
Expand Down
2 changes: 1 addition & 1 deletion src/libusb/messenger-libusb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ namespace librealsense
{
std::string strerr = strerror(errno);
LOG_WARNING("bulk_transfer returned error, endpoint: 0x" << std::hex << int(endpoint->get_address()) << std::dec
<< ", error: " << strerr << ", err. num: " << int(errno));
<< ", error: " << strerr << ", err. num: " << int(errno) << " libusb " << libusb_error_name(sts));
return libusb_status_to_rs(sts);
}
transferred = actual_length;
Expand Down
3 changes: 2 additions & 1 deletion src/libuvc/rsusb-backend-linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "rsusb-backend-linux.h"
#include "types.h"
#include "../uvc/uvc-device.h"
#include "../libusb/device-watcher-libusb.h"

namespace librealsense
{
Expand All @@ -16,7 +17,7 @@ namespace librealsense

std::shared_ptr<device_watcher> rs_backend_linux::create_device_watcher() const
{
return std::make_shared<polling_device_watcher>(this);
return std::make_shared<device_watcher_libusb>(this);
}
}
}
3 changes: 2 additions & 1 deletion wrappers/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ if(UNIX)
../../src/libusb/messenger-libusb.cpp
../../src/libusb/enumerator-libusb.cpp
../../src/libusb/request-libusb.cpp
../../src/libusb/device-watcher-libusb.cpp
)
endif()

Expand Down Expand Up @@ -136,4 +137,4 @@ install(TARGETS pybackend2

if (BUILD_PYTHON_DOCS)
add_subdirectory(docs)
endif()
endif()