diff --git a/utils/hwstub/include/hwstub.hpp b/utils/hwstub/include/hwstub.hpp index 90b29ebf9d..96072dde4f 100644 --- a/utils/hwstub/include/hwstub.hpp +++ b/utils/hwstub/include/hwstub.hpp @@ -96,6 +96,12 @@ public: * are still connected (or believe to be). This function will update the device * list. */ error get_device_list(std::vector>& list); + + /** Opaque device type */ + typedef void* ctx_dev_t; + typedef std::function device_filter_t; + /** Default device filter function accept any device */ + device_filter_t device_filter = [](ctx_dev_t d){(void)d; return true;}; /** Force the context to update its internal list of devices. */ error update_list(); /** Ask the context to automatically poll for device changes. @@ -130,8 +136,6 @@ protected: void change_device(bool arrived, std::shared_ptr dev); /** Do device notification */ void notify_device(bool arrived, std::shared_ptr dev); - /** Opaque device type */ - typedef void* ctx_dev_t; /** Fetch the device list. Each item in the list is an opaque pointer. The function * can also provide a pointer that will be used to free the list resources * if necessary. Return <0 on error. */ diff --git a/utils/hwstub/include/hwstub_usb.hpp b/utils/hwstub/include/hwstub_usb.hpp index 579594067c..0fa64a4f63 100644 --- a/utils/hwstub/include/hwstub_usb.hpp +++ b/utils/hwstub/include/hwstub_usb.hpp @@ -34,6 +34,7 @@ class context : public hwstub::context { protected: context(libusb_context *ctx, bool cleanup_ctx); + context(libusb_context *ctx, bool cleanup_ctx, std::string *error, device_filter_t f); public: virtual ~context(); /** Return native libusb context */ @@ -42,7 +43,7 @@ public: * called on the context on deletion of this class. If ctx is NULL, libusb_init() * will be called with NULL so there is no need to init the default context. */ static std::shared_ptr create(libusb_context *ctx, bool cleanup_ctx = false, - std::string *error = nullptr); + std::string *error = nullptr, device_filter_t f = [](ctx_dev_t d){(void)d; return true;}); protected: /* NOTE ctx_dev_t = libusb_device* */ @@ -77,6 +78,8 @@ public: uint16_t get_vid(); /** Get device PID */ uint16_t get_pid(); + /** Return true if device matches provided bus and address */ + static bool is_bus_addr_device(libusb_device *dev, uint8_t bus, uint8_t addr); protected: /** Return true if this might be a hwstub device and should appear in the list */ diff --git a/utils/hwstub/lib/hwstub_uri.cpp b/utils/hwstub/lib/hwstub_uri.cpp index e2f252f3dc..e940fe69e2 100644 --- a/utils/hwstub/lib/hwstub_uri.cpp +++ b/utils/hwstub/lib/hwstub_uri.cpp @@ -48,8 +48,12 @@ void print_usage(FILE *f, bool client, bool server) fprintf(f, " default Default choice made by the library\n"); if(client) { - fprintf(f, "When creating a USB context, the domain and port must be empty:\n"); - fprintf(f, " usb:\n"); + fprintf(f, "When creating a USB context, the domain and port represent usb_port and device_address\n"); + fprintf(f, "as reported by lsusb. You can leave empty usb_port:device_address to match any usb device\n"); + fprintf(f, "which provide hwstub interface\n"); + fprintf(f, " usb://\n"); + fprintf(f, "or\n"); + fprintf(f, " usb://usb_port:device_address\n"); } fprintf(f, "When creating a TCP context, the domain and port are the usual TCP parameters:\n"); fprintf(f, " tcp://localhost\n"); @@ -220,17 +224,55 @@ std::shared_ptr create_context(const uri& uri, std::string *error) /* handle different types of contexts */ if(uri.scheme() == "usb") { - /* domain and port must be empty */ - if(!uri.domain().empty() || !uri.port().empty()) + if(!uri.domain().empty() && !uri.port().empty()) + { + /** URI usb://usb_port:device_address + * for example usb://6:31 + * usb_port and device_address match numbers + * reported by lsusb + * + * This filtering is additional to filtering + * by hwstub interface so final outcome will be device + * with usb_port:device_address which IS hwstub device + */ + try + { + int bus = std::stoi(uri.domain()); + int addr = std::stoi(uri.port()); + libusb_context *ctx; + libusb_init(&ctx); + + /** Build closure used to filter valid devices */ + std::function f = [bus, addr](void *d){ + libusb_device *dev = (libusb_device *)d; + return hwstub::usb::device::is_bus_addr_device(dev, bus, addr); + }; + + return hwstub::usb::context::create(ctx, true, nullptr, f); + } + catch(...) + { + if(error) + *error = "USB URI bus_port:device_address format error"; + return std::shared_ptr(); + } + } + else if(uri.domain().empty() && uri.port().empty()) + { + /** URI usb:// + * No filtering, any usb device with hwstub + * interface will match + */ + libusb_context *ctx; + libusb_init(&ctx); + return hwstub::usb::context::create(ctx, true); + } + else { if(error) - *error = "USB URI cannot contain a domain or a port"; + *error = "USB URI format error"; return std::shared_ptr(); } - /* in doubt, create a new libusb context and let the context destroy it */ - libusb_context *ctx; - libusb_init(&ctx); - return hwstub::usb::context::create(ctx, true); } else if(uri.scheme() == "virt") { diff --git a/utils/hwstub/lib/hwstub_usb.cpp b/utils/hwstub/lib/hwstub_usb.cpp index e8b8e7bc3d..e6679ff780 100644 --- a/utils/hwstub/lib/hwstub_usb.cpp +++ b/utils/hwstub/lib/hwstub_usb.cpp @@ -41,20 +41,28 @@ context::context(libusb_context *ctx, bool cleanup_ctx) { } +context::context(libusb_context *ctx, bool cleanup_ctx, std::string *error, device_filter_t f) + :m_usb_ctx(ctx), m_cleanup_ctx(cleanup_ctx) +{ + (void)error; + // NOTE: can't use initializer list since this member is from parent class + // and parent's class constructor is NOT called when initializer list is built + device_filter = f; +} + context::~context() { if(m_cleanup_ctx) libusb_exit(m_usb_ctx); } -std::shared_ptr context::create(libusb_context *ctx, bool cleanup_ctx, - std::string *error) +std::shared_ptr context::create(libusb_context *ctx, bool cleanup_ctx, std::string *error, device_filter_t f) { (void) error; if(ctx == nullptr) libusb_init(nullptr); // NOTE: can't use make_shared() because of the protected ctor */ - return std::shared_ptr(new context(ctx, cleanup_ctx)); + return std::shared_ptr(new context(ctx, cleanup_ctx, nullptr, f)); } libusb_context *context::native_context() @@ -81,7 +89,11 @@ error context::fetch_device_list(std::vector& list, void*& ptr) ptr = (void *)usb_list; list.clear(); for(int i = 0; i < ret; i++) - if(device::is_hwstub_dev(usb_list[i])) + /* filter devices by hwstub interface and by other filtering criteria + * if provided + */ + if(device::is_hwstub_dev(usb_list[i]) && + context::device_filter(usb_list[i])) list.push_back(to_ctx_dev(usb_list[i])); return error::SUCCESS; } @@ -231,6 +243,12 @@ uint16_t device::get_pid() return dev_desc.idProduct; } +bool device::is_bus_addr_device(libusb_device *dev, uint8_t bus, uint8_t addr) +{ + return ((libusb_get_bus_number(dev) == bus) && + (libusb_get_device_address(dev) == addr)); +} + /** * USB handle */