[prev in list] [next in list] [prev in thread] [next in thread] 

List:       rockbox-cvs
Subject:    hwstub lib: Introduce framework for device filtering
From:       rockbox-gerrit-noreply--- via rockbox-cvs <rockbox-cvs () lists ! haxx ! se>
Date:       2023-11-09 7:06:18
Message-ID: 202311090706.3A976IYd1019196 () archos ! rockbox ! org
[Download RAW message or body]

commit 7f455af9053894241291f094865aee8808a1d3df
Author: Marcin Bukat <marcin.bukat@gmail.com>
Date:   Wed Oct 4 10:51:54 2023 +0200

    hwstub lib: Introduce framework for device filtering
    
    The rationale behind this was ability to filter usb device by
    bus numer and device address. This allows to connect with selected
    device in case there is more then one  connected device implementing
    hwstub interface. For now only USB backend makes use of this
    but the foundation is generic and can be easily extended to other
    backends.
    
    Change-Id: I618cfdeeb09162d5fa1002db00e40ea17c43e727

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<std::shared_ptr<device>>& list);
+
+    /** Opaque device type */
+    typedef void* ctx_dev_t;
+    typedef std::function<bool(ctx_dev_t)> 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<device> dev);
     /** Do device notification */
     void notify_device(bool arrived, std::shared_ptr<device> 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<context> 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<context> 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<bool(void *)> 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<context>();
+            }
+        }
+        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<context>();
         }
-        /* 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> context::create(libusb_context *ctx, bool cleanup_ctx,
-    std::string *error)
+std::shared_ptr<context> 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<context>(new context(ctx, cleanup_ctx));
+    return std::shared_ptr<context>(new context(ctx, cleanup_ctx, nullptr, f));
 }
 
 libusb_context *context::native_context()
@@ -81,7 +89,11 @@ error context::fetch_device_list(std::vector<ctx_dev_t>& 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
  */
-- 
rockbox-cvs mailing list
rockbox-cvs@lists.haxx.se
https://lists.haxx.se/mailman/listinfo/rockbox-cvs


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic