gbmc-release-25.18.x: meta-security: libhoth: add patch that lets htool retry

Tested:
Manully checked: htool includes the new chagne from the patch.
```
root@wkc27-nfd01:~# htool 2>&1 | grep "usb_"
  --usb_loc (default: "")
  --usb_product (default: "")
  --usb_retry_duration (default: "1000ms")
  --usb_retry_delay (default: "50ms")
root@wkc27-nfd01:~# htool --version
e4827163741e0804f12ac96c81b8e97649be6795-dirty
```

Fusion-Link: fusion2/51a51fa9-2b47-3ba8-856f-d849962b1c92 (platform11)
Fusion-Link: fusion2/64b92888-41c3-39d6-b133-c23425b850bb (platform5)
Fusion-Link: fusion2/ea3d191d-cc33-3a16-9814-03371d53a042 (platform15)
Fusion-Link: fusion2/43899abd-8425-3c62-b092-533da44e8154 (platform17)
Platforms-Affected: All TTF platforms
Google-Bug-Id: 415725820
Change-Id: Icfef421276471a6a97cc53b055233a4aaf5e990e
Signed-off-by: Leo Tu <leotu@google.com>
(cherry picked from commit 6375cfc064461661601d90fedcc3e2cc41e1395e)
Signed-off-by: Leo Tu <leotu@google.com>
diff --git a/recipes-tpm1/hoth/libhoth/0002-Add-retry-loop-to-htool_libhoth_usb_device-for-LIBUSB.patch b/recipes-tpm1/hoth/libhoth/0002-Add-retry-loop-to-htool_libhoth_usb_device-for-LIBUSB.patch
new file mode 100644
index 0000000..1cecf59
--- /dev/null
+++ b/recipes-tpm1/hoth/libhoth/0002-Add-retry-loop-to-htool_libhoth_usb_device-for-LIBUSB.patch
@@ -0,0 +1,214 @@
+From ff3816a67857ab025ad7aa6d6bd4fd121c4fd657 Mon Sep 17 00:00:00 2001
+From: Leo Tu <leotu@google.com>
+Date: Wed, 11 Jun 2025 22:11:37 +0800
+Subject: [PATCH] Add retry loop to htool_libhoth_usb_device, for
+ LIBUSB_ERROR_BUSY.
+
+In some cases the USB device may be temporarily busy (ie, held by
+another process), and htool will sometimes return -6 (ie,
+LIBUSB_ERROR_BUSY).  Retrying in this case (for some limited amount of
+time) may be effective.
+
+Cherry-pick of 9095f644b7577659af083a5703be46acdda8cfde
+---
+ examples/htool.c     |   7 ++-
+ examples/htool_usb.c | 139 ++++++++++++++++++++++++++++++++++++++++---
+ 2 files changed, 138 insertions(+), 8 deletions(-)
+
+diff --git a/examples/htool.c b/examples/htool.c
+index 0956d91..032d896 100644
+--- a/examples/htool.c
++++ b/examples/htool.c
+@@ -1149,10 +1149,15 @@ static const struct htool_param GLOBAL_FLAGS[] = {
+              "or 'mtd' transports; for example '0x900000'."},
+     {HTOOL_FLAG_VALUE, .name = "dbus_hoth_id", .default_value = "",
+      .desc = "The hoth ID associated with the RoT's hothd service."},
++    {HTOOL_FLAG_VALUE, .name = "usb_retry_duration", .default_value = "1000ms",
++     .desc = "Maximum duration to retry opening a busy USB device (e.g., "
++             "'1s', '1500ms')."},
++    {HTOOL_FLAG_VALUE, .name = "usb_retry_delay", .default_value = "50ms",
++     .desc = "Delay between USB open retries (e.g., '50ms', '10000us')."},
+     {HTOOL_FLAG_BOOL, .name = "version", .default_value = "false",
+      .desc = "Print htool version."},
+     {}};
+ 
+ int main(int argc, const char* const* argv) {
+   return htool_main(GLOBAL_FLAGS, CMDS, argc - 1, &argv[1]);
+-}
++}
+\ No newline at end of file
+diff --git a/examples/htool_usb.c b/examples/htool_usb.c
+index cedc249..8801471 100644
+--- a/examples/htool_usb.c
++++ b/examples/htool_usb.c
+@@ -14,6 +14,8 @@
+ 
+ #include "htool_usb.h"
+ 
++#include <ctype.h>
++#include <errno.h>
+ #include <libusb.h>
+ #include <stdbool.h>
+ #include <stddef.h>
+@@ -21,6 +23,8 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#include <time.h>
++#include <unistd.h>
+ 
+ #include "../libhoth_usb.h"
+ #include "ec_util.h"
+@@ -314,6 +318,62 @@ libusb_device* htool_libusb_device(void) {
+   return select_device(ctx, filter_allow_all, NULL);
+ }
+ 
++// Helper function to get current monotonic time in milliseconds
++static uint64_t get_monotonic_ms() {
++    struct timespec ts;
++    if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
++        perror("clock_gettime failed");
++        // Return 0 or a value indicating error, relying on caller checks
++        return 0;
++    }
++    return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
++}
++
++// Helper function to parse time string with units (s, ms, us) into microseconds
++// Returns -1 on error.
++static int64_t parse_time_string_us(const char* time_str) {
++    if (!time_str || *time_str == '\0') {
++        return -1; // Invalid input
++    }
++
++    char* endptr;
++    long long val = strtoll(time_str, &endptr, 10);
++
++    if (endptr == time_str || val < 0) {
++        return -1; // No digits found or negative value
++    }
++
++    // Skip whitespace
++    while (*endptr != '\0' && isspace((unsigned char)*endptr)) {
++        endptr++;
++    }
++
++    uint64_t multiplier = 1000000; // Default to seconds if no unit
++
++    if (*endptr != '\0') {
++        // Check for units (case-insensitive)
++        if (tolower((unsigned char)endptr[0]) == 's' && endptr[1] == '\0') {
++            multiplier = 1000000; // seconds
++        } else if (tolower((unsigned char)endptr[0]) == 'm' &&
++                   tolower((unsigned char)endptr[1]) == 's' && endptr[2] == '\0') {
++            multiplier = 1000; // milliseconds
++        } else if (tolower((unsigned char)endptr[0]) == 'u' &&
++                   tolower((unsigned char)endptr[1]) == 's' && endptr[2] == '\0') {
++            multiplier = 1; // microseconds
++        } else {
++            return -1; // Invalid unit or extra characters
++        }
++    }
++
++    // Check for potential overflow before multiplying
++    if (val > INT64_MAX / multiplier) {
++         return -1; // Overflow
++    }
++
++    return (int64_t)val * multiplier;
++}
++
++
+ struct libhoth_device* htool_libhoth_usb_device(void) {
+   static struct libhoth_device* result;
+   if (result) {
+@@ -324,14 +384,79 @@ struct libhoth_device* htool_libhoth_usb_device(void) {
+   if (!ctx || !usb_dev) {
+     return NULL;
+   }
+-  struct libhoth_usb_device_init_options opts = {.usb_device = usb_dev,
+-                                                 .usb_ctx = ctx};
+-  int rv = libhoth_usb_open(&opts, &result);
+-  if (rv) {
+-    // TODO: Convert error-code to a string
+-    fprintf(stderr, "libhoth_usb_open error: %d\n", rv);
+-    return NULL;
++
++  // Get retry parameters from global flags
++  const char* duration_str;
++  const char* delay_str;
++  if (htool_get_param_string(htool_global_flags(), "usb_retry_duration", &duration_str) ||
++      htool_get_param_string(htool_global_flags(), "usb_retry_delay", &delay_str)) {
++      return NULL;
++  }
++
++  int64_t retry_duration_us = parse_time_string_us(duration_str);
++  int64_t retry_delay_us = parse_time_string_us(delay_str);
++
++  if (retry_duration_us < 0) {
++      fprintf(stderr, "Invalid format for --usb_retry_duration: %s\n", duration_str);
++      return NULL;
++  }
++  if (retry_delay_us < 0) {
++      fprintf(stderr, "Invalid format for --usb_retry_delay: %s\n", delay_str);
++      return NULL;
++  }
++  // Convert duration to milliseconds for comparison with monotonic time helper
++  uint64_t retry_duration_ms = (uint64_t)retry_duration_us / 1000;
++
++  struct libhoth_usb_device_init_options opts = {
++      .usb_device = usb_dev, .usb_ctx = ctx};
++
++  int rv = LIBUSB_ERROR_BUSY; // Initialize rv to trigger the loop
++  uint64_t start_time_ms = get_monotonic_ms();
++  if (start_time_ms == 0 && errno != 0) { // Check if get_monotonic_ms failed
++      return NULL;
+   }
++  uint64_t current_time_ms;
++
++  while (rv == LIBUSB_ERROR_BUSY) {
++      rv = libhoth_usb_open(&opts, &result);
++      if (rv == LIBUSB_SUCCESS) {
++          break; // Successfully opened
++      }
++      if (rv != LIBUSB_ERROR_BUSY) {
++          // A different error occurred, report it and exit
++          fprintf(stderr, "libhoth_usb_open error: %d (%s)\n", rv, libusb_strerror(rv));
++          return NULL;
++      }
++
++      // Check elapsed time
++      current_time_ms = get_monotonic_ms();
++       if (current_time_ms == 0 && errno != 0) {
++           return NULL;
++       }
++      // Handle potential timer wrap-around or error from get_monotonic_ms
++      if (current_time_ms < start_time_ms) {
++          fprintf(stderr, "Monotonic clock error detected during retry loop.\n");
++          return NULL;
++      }
++
++      if (current_time_ms - start_time_ms >= retry_duration_ms) {
++          fprintf(stderr, "libhoth_usb_open timed out after %s (error: %d (%s))\n",
++                  duration_str, rv, libusb_strerror(rv));
++          return NULL; // Timeout
++      }
++
++      // Wait before retrying
++      // Ensure delay doesn't exceed reasonable limits for usleep (~10s)
++      useconds_t sleep_us = (retry_delay_us > 10000000) ? 10000000 : (useconds_t)retry_delay_us;
++      usleep(sleep_us);
++  }
++
++  if (rv != LIBUSB_SUCCESS) {
++      fprintf(stderr, "libhoth_usb_open error: %d (%s)\n", rv, libusb_strerror(rv));
++      result = NULL;
++      return NULL;
++  }
++
+   return result;
+ }
+ 
+-- 
+2.50.0.rc0.642.g800a2b2222-goog
+
diff --git a/recipes-tpm1/hoth/libhoth_%.bbappend b/recipes-tpm1/hoth/libhoth_%.bbappend
index e8771c5..0835d64 100644
--- a/recipes-tpm1/hoth/libhoth_%.bbappend
+++ b/recipes-tpm1/hoth/libhoth_%.bbappend
@@ -5,4 +5,5 @@
 #TODO(b/411502579): remove this patch once libhoth pin is updated to newer upstream version.
 SRC_URI:append:gbmc = " \
     file://0001-Expose-spi-headers-in-libhoth.patch \
+    file://0002-Add-retry-loop-to-htool_libhoth_usb_device-for-LIBUSB.patch \
 "