samples: improve gpio-pci-idio-16 emulation (#821)

Newer kernels implement read register caching for the gpio-pci-idio-16 which
means that the pin counter is never incremented, making it impossible to change
the input register for testing.

Improve the gpio-pci-idio-16 emulation so that the input and output registers
match those of the real hardware, and update the logic so that toggling an
output line 3 times will toggle the corresponding input line to allow testing
individual inputs and outputs.

Signed-off-by: Mark Cave-Ayland <mark.caveayland@nutanix.com>
diff --git a/samples/gpio-pci-idio-16.c b/samples/gpio-pci-idio-16.c
index 6c4e99b..739e242 100644
--- a/samples/gpio-pci-idio-16.c
+++ b/samples/gpio-pci-idio-16.c
@@ -51,17 +51,62 @@
     fprintf(stderr, "gpio: %s\n", msg);
 }
 
-static int pin;
+static int pin[16];
 bool dirty = true;
 
 static ssize_t
 bar2_access(vfu_ctx_t *vfu_ctx UNUSED, char * const buf,
             size_t count, loff_t offset, const bool is_write)
 {
-    if (offset == 0 && !is_write)
-        buf[0] = pin++ / 3;
+    int i;
 
-    dirty = true;
+    if (is_write) {
+        /* Output registers are 0x0 and 0x4 */
+        switch (offset) {
+        case 0x0:
+            /* Output pins 0-7 */
+            for (i = 0; i < 8; i++) {
+                if (buf[0] & (1 << i)) {
+                    pin[i]++;
+                }
+            }
+            break;
+
+        case 0x4:
+            /* Output pins 8-15 */
+            for (i = 0; i < 8; i++) {
+                if (buf[0] & (1 << i)) {
+                    pin[i + 8]++;
+                }
+            }
+            break;
+        }
+
+        dirty = true;
+    } else {
+        /* Input registers are 0x1 and 0x5 */
+        switch (offset) {
+        case 0x1:
+            /* Input pins 0-7 */
+            buf[0] = 0;
+            for (i = 0; i < 8; i++) {
+                if ((pin[i] % 3) == 0) {
+                    buf[0] |= (1 << i);
+                }
+            }
+            break;
+
+        case 0x5:
+            /* Input pins 8-15 */
+            buf[0] = 0;
+            for (i = 0; i < 8; i++) {
+                if ((pin[i + 8] % 3) == 0) {
+                    buf[0] |= (1 << i);
+                }
+            }
+            break;
+        }
+    }
 
     return count;
 }