From 016db0c77249547cb9711419feb692d57a0c9030 Mon Sep 17 00:00:00 2001 From: Munawar Hussain Date: Mon, 15 Dec 2025 12:05:07 +0000 Subject: [PATCH] Add --usb_path option to htool It is possible that the usb_loc is dynamic and changes each time the machine boot. As a result, there is no way to understand which physical usb device is related to which usb_loc. Hence introducing usb_path glob pattern such that user can always query the correct device and run necessary sub command despite the dynamic behavior. This change introduces a new command-line option `--usb_path` to htool, allowing users to select a specific USB device by providing a glob pattern that matches its real sysfs path. A new function, libhoth_get_usb_sys_path, is added to libhoth_usb to retrieve the canonical sysfs path for a given libusb_device. The device selection logic in htool_usb.c is updated to use this new path-based filtering. Also the change introduces new column in `htool usb list` showing the real path. --- examples/htool.c | 2 ++ examples/htool_usb.c | 31 ++++++++++++++++++++++++++++++- transports/libhoth_usb.c | 38 ++++++++++++++++++++++++++++++++++++++ transports/libhoth_usb.h | 4 ++++ 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/examples/htool.c b/examples/htool.c index 9d0839e..e75c38b 100644 --- a/examples/htool.c +++ b/examples/htool.c @@ -1677,6 +1677,8 @@ static const struct htool_param GLOBAL_FLAGS[] = { "'1s', '1500ms')."}, {HTOOL_FLAG_VALUE, .name = "usb_retry_delay", .default_value = "50ms", .desc = "Delay between USB open retries (e.g., '50ms', '10000us')."}, + {HTOOL_FLAG_VALUE, .name = "usb_path", .default_value = "", + .desc = "Glob Pattern for matching the USB device path."}, {HTOOL_FLAG_BOOL, .name = "version", .default_value = "false", .desc = "Print htool version."}, {}}; diff --git a/examples/htool_usb.c b/examples/htool_usb.c index 446efa3..21ad29a 100644 --- a/examples/htool_usb.c +++ b/examples/htool_usb.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -75,6 +76,7 @@ static void print_device(void* cb_param, libusb_device* dev, } libusb_device_handle* dev_handle; + char sys_path[256]; rv = libusb_open(dev, &dev_handle); if (rv != LIBUSB_SUCCESS) { fprintf(stderr, " (unable to open device: %s)", libusb_strerror(rv)); @@ -95,6 +97,12 @@ static void print_device(void* cb_param, libusb_device* dev, cleanup2: libusb_close(dev_handle); cleanup: + rv = libhoth_get_usb_sys_path(dev, sys_path, sizeof(sys_path)); + if (rv != LIBUSB_SUCCESS) { + fprintf(stderr, " (unable to get sys path: %s)", libusb_strerror(rv)); + } else { + fprintf(stderr, " (%s)", sys_path); + } fprintf(stderr, "\n"); } @@ -256,6 +264,20 @@ bool filter_by_usb_product_substr( return strstr(product_name, usb_product_substr) != NULL; } +static bool filter_by_usb_path_glob( + void* cb_param, libusb_device* dev, + const struct libusb_device_descriptor* descriptor) { + const char* glob_pattern = (const char*)cb_param; + char sys_path[256]; + int rv = libhoth_get_usb_sys_path(dev, sys_path, sizeof(sys_path)); + if (rv != LIBUSB_SUCCESS) { + fprintf(stderr, "libhoth_get_usb_sys_path failed: %s\n", + libusb_strerror(rv)); + return false; + } + return fnmatch(glob_pattern, sys_path, 0) == 0; +} + bool filter_allow_all(void* cb_param, libusb_device* dev, const struct libusb_device_descriptor* descriptor) { return true; @@ -272,10 +294,13 @@ libusb_device* htool_libusb_device(void) { } const char* usb_loc_str; const char* usb_product_substr; + const char* usb_path_glob_str; int rv = htool_get_param_string(htool_global_flags(), "usb_loc", &usb_loc_str) || htool_get_param_string(htool_global_flags(), "usb_product", - &usb_product_substr); + &usb_product_substr) || + htool_get_param_string(htool_global_flags(), "usb_path", + &usb_path_glob_str); if (rv) { return NULL; } @@ -292,6 +317,10 @@ libusb_device* htool_libusb_device(void) { return select_device(ctx, filter_by_usb_product_substr, (void*)usb_product_substr); } + if (strlen(usb_path_glob_str) > 0) { + return select_device(ctx, filter_by_usb_path_glob, + (void*)usb_path_glob_str); + } return select_device(ctx, filter_allow_all, NULL); } diff --git a/transports/libhoth_usb.c b/transports/libhoth_usb.c index 16d936a..4e09f5d 100644 --- a/transports/libhoth_usb.c +++ b/transports/libhoth_usb.c @@ -15,6 +15,8 @@ #include "transports/libhoth_usb.h" #include +#include +#include #include #include "transports/libhoth_device.h" @@ -348,3 +350,39 @@ int libhoth_usb_get_device(libusb_context* ctx, libusb_free_device_list(device, /*unref_devices=*/1); return found_device ? LIBHOTH_OK : LIBHOTH_ERR_INTERFACE_NOT_FOUND; } + +int libhoth_get_usb_sys_path(libusb_device* dev, char* path, size_t path_len) { + struct libhoth_usb_loc loc; + char bus_port_path[PATH_MAX]; + int ret; + + if (dev == NULL || path == NULL || path_len == 0) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + ret = libhoth_get_usb_loc(dev, &loc); + if (ret) { + return ret; + } + + ret = snprintf(bus_port_path, sizeof(bus_port_path), + "/sys/bus/usb/devices/%d-%d", loc.bus, loc.ports[0]); + if (ret < 0 || (size_t)ret >= sizeof(bus_port_path)) { + return LIBUSB_ERROR_OVERFLOW; + } + + for (int i = 1; i < loc.num_ports; i++) { + size_t current_len = strlen(bus_port_path); + ret = snprintf(bus_port_path + current_len, + sizeof(bus_port_path) - current_len, ".%d", loc.ports[i]); + if (ret < 0 || current_len + (size_t)ret >= sizeof(bus_port_path)) { + return LIBUSB_ERROR_OVERFLOW; + } + } + + if (realpath(bus_port_path, path) == NULL) { + return LIBUSB_ERROR_OTHER; + } + + return LIBUSB_SUCCESS; +} diff --git a/transports/libhoth_usb.h b/transports/libhoth_usb.h index 0625399..150c514 100644 --- a/transports/libhoth_usb.h +++ b/transports/libhoth_usb.h @@ -63,6 +63,10 @@ int libhoth_usb_get_device(libusb_context* ctx, libusb_device** out); int libhoth_get_usb_loc(libusb_device* dev, struct libhoth_usb_loc* result); +// Returns the sysfs path for the given libusb_device. +// Returns LIBUSB_SUCCESS on success, or a libusb error code on failure. +int libhoth_get_usb_sys_path(libusb_device* dev, char* path, size_t path_len); + #ifdef __cplusplus } #endif