From 3dc1e4e72b7da597c98761a7bb7cb5aa5c885584 Mon Sep 17 00:00:00 2001
From: Alexander Smirnov <aliaksandr.smirnou@gmail.com>
Date: Sun, 3 Apr 2022 20:29:56 +0100
Subject: [PATCH 1/2] Wait and retry until deplayed udev permission rule
 applies after exporting gpio pin

---
 src/linuxgpio.c | 54 +++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 48 insertions(+), 6 deletions(-)

diff --git a/src/linuxgpio.c b/src/linuxgpio.c
index 86fefd76..36687612 100644
--- a/src/linuxgpio.c
+++ b/src/linuxgpio.c
@@ -27,6 +27,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
+#include <sys/stat.h>
 
 #include "avrdude.h"
 #include "libavrdude.h"
@@ -108,7 +109,8 @@ static int linuxgpio_dir(unsigned int gpio, unsigned int dir)
 
   fd = open(buf, O_WRONLY);
   if (fd < 0) {
-    perror("Can't open gpioX/direction");
+    snprintf(buf, sizeof(buf), "Can't open gpio%u/direction", gpio);
+    perror(buf);
     return fd;
   }
 
@@ -138,6 +140,11 @@ static int linuxgpio_dir_in(unsigned int gpio)
 
 #define N_GPIO (PIN_MAX + 1)
 
+/* Delay between checks for successful GPIO export (100ms) */
+#define GPIO_SYSFS_OPEN_DELAY      100000
+/* Number of retries to check for successful GPIO exports */
+#define GPIO_SYSFS_OPEN_RETRIES    10
+
 /*
 * an array which holds open FDs to /sys/class/gpio/gpioXX/value for all needed pins
 */
@@ -245,6 +252,8 @@ static void linuxgpio_powerdown(PROGRAMMER *pgm)
 static int linuxgpio_open(PROGRAMMER *pgm, char *port)
 {
   int r, i, pin;
+  char gpio_path[60];
+  struct stat stat_buf;
 
   if (bitbang_check_prerequisites(pgm) < 0)
     return -1;
@@ -272,13 +281,46 @@ static int linuxgpio_open(PROGRAMMER *pgm, char *port)
                     pin, strerror(errno));
             return r;
         }
-        if (i == PIN_AVR_MISO)
-            r=linuxgpio_dir_in(pin);
-        else
-            r=linuxgpio_dir_out(pin);
 
-        if (r < 0)
+        /* Wait until GPIO directory appears */
+        snprintf(gpio_path, sizeof(gpio_path), "/sys/class/gpio/gpio%u", pin);
+        unsigned int retry_count;
+        for (retry_count = 0; retry_count < GPIO_SYSFS_OPEN_RETRIES; retry_count++) {
+            int ret = stat(gpio_path, &stat_buf);
+            if (ret == 0) {
+                break;
+            } else if (ret < 0 && errno != ENOENT) {
+                linuxgpio_unexport(pin);
+                return ret;
+            }
+
+            usleep(GPIO_SYSFS_OPEN_DELAY);
+        }
+
+        /* Write direction, looping in case of EACCES errors due to delayed
+         * udev permission rule application after export */
+        for (retry_count = GPIO_SYSFS_OPEN_RETRIES; retry_count > 0; retry_count--) {
+            usleep(GPIO_SYSFS_OPEN_DELAY);
+            if (i == PIN_AVR_MISO)
+                r=linuxgpio_dir_in(pin);
+            else
+                r=linuxgpio_dir_out(pin);
+
+            if (r >= 0) {
+                break;
+            } else if (errno != EACCES) {
+                linuxgpio_unexport(pin);
+                return r;
+            }
+            if (retry_count > 1) {
+                printf("Retrying...\n");
+            }
+        }
+
+        if (r < 0) {
+            linuxgpio_unexport(pin);
             return r;
+        }
 
         if ((linuxgpio_fds[pin]=linuxgpio_openfd(pin)) < 0)
             return linuxgpio_fds[pin];

From 097c78098d33f19aecdd4ed41d7af7edd5ab9be3 Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Fri, 23 Sep 2022 23:28:47 +0100
Subject: [PATCH 2/2] Change printf() to avrdude_message() in linuxgio.c

---
 src/linuxgpio.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/src/linuxgpio.c b/src/linuxgpio.c
index 36687612..0d4057e7 100644
--- a/src/linuxgpio.c
+++ b/src/linuxgpio.c
@@ -299,24 +299,27 @@ static int linuxgpio_open(PROGRAMMER *pgm, char *port)
 
         /* Write direction, looping in case of EACCES errors due to delayed
          * udev permission rule application after export */
-        for (retry_count = GPIO_SYSFS_OPEN_RETRIES; retry_count > 0; retry_count--) {
+        for (retry_count = 0; retry_count < GPIO_SYSFS_OPEN_RETRIES; retry_count++) {
             usleep(GPIO_SYSFS_OPEN_DELAY);
             if (i == PIN_AVR_MISO)
                 r=linuxgpio_dir_in(pin);
             else
                 r=linuxgpio_dir_out(pin);
 
-            if (r >= 0) {
+            if (r >= 0)
                 break;
-            } else if (errno != EACCES) {
+
+            if (errno != EACCES) {
                 linuxgpio_unexport(pin);
                 return r;
             }
-            if (retry_count > 1) {
-                printf("Retrying...\n");
-            }
         }
 
+        if (retry_count)
+            avrdude_message(MSG_NOTICE2, "%s: needed %d retr%s for linuxgpio_dir_%s(%s)\n",
+                progname, retry_count, retry_count > 1? "ies": "y",
+                i == PIN_AVR_MISO? "in": "out", avr_pin_name(pin));
+
         if (r < 0) {
             linuxgpio_unexport(pin);
             return r;