libflashprog: Add new API to probe all buses for chips

The new flashprog_chips_probe() will supersede our existing logic to
probe for an unspecified chip. It returns an enumeration of detected
chips. It's up to the caller to handle multiple chip matches.

Change-Id: I21ec201b92289995430130d72823fe4ca8b03bb6
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.sourcearcade.org/c/flashprog/+/483
diff --git a/include/libflashprog.h b/include/libflashprog.h
index eec69ee..8afd8d5 100644
--- a/include/libflashprog.h
+++ b/include/libflashprog.h
@@ -49,6 +49,8 @@
 __attribute__((nonnull))
 int flashprog_chips_all(struct flashprog_chips **);
 __attribute__((nonnull))
+int flashprog_chips_probe(struct flashprog_chips **, const struct flashprog_programmer *);
+__attribute__((nonnull))
 unsigned int flashprog_chips_count(const struct flashprog_chips *);
 void flashprog_chips_release(struct flashprog_chips *);
 
diff --git a/libflashprog.map b/libflashprog.map
index 709db87..033d5f8 100644
--- a/libflashprog.map
+++ b/libflashprog.map
@@ -11,6 +11,7 @@
     flashprog_chip_voltage_range;
     flashprog_chips_all;
     flashprog_chips_count;
+    flashprog_chips_probe;
     flashprog_chips_release;
     flashprog_flag_get;
     flashprog_flag_set;
diff --git a/libflashprog/chips.c b/libflashprog/chips.c
index 0b43e6f..919d0eb 100644
--- a/libflashprog/chips.c
+++ b/libflashprog/chips.c
@@ -21,6 +21,7 @@
 
 #include "flash.h"
 #include "flashchips.h"
+#include "programmer.h"
 
 /**
  * @defgroup flashprog-chips Chip Enumeration
@@ -30,6 +31,16 @@
 /* Magic pointer that represents our built in database. */
 #define FLASHCHIPS_DB (struct flashprog_chips *)(uintptr_t)-1
 
+/** @private */
+struct flashprog_chips {
+	/** @private */
+	struct chip_entry {
+		struct flashprog_chip chip;
+		const struct master_common *bus;
+		struct chip_entry *next;
+	} *entries;
+};
+
 static bool chip_from_db(const struct flashprog_chip *chip) {
 	return flashchips <= chip && chip < flashchips + flashchips_size;
 }
@@ -47,6 +58,72 @@
 	return 0;
 }
 
+static int flashprog_chips_probe_bus(struct flashprog_chips *chips,
+				     struct registered_master *bus)
+{
+	flashprog_bus_probe(bus, NULL);
+
+	int chip;
+	for (chip = 0; flashchips[chip].name; ++chip) {
+		/* Ignore generic entries if we already have a match. */
+		if (chips->entries &&
+		    ((flashchips[chip].id.model == SFDP_DEVICE_ID) ||
+		     (flashchips[chip].id.model == GENERIC_DEVICE_ID)))
+			continue;
+
+		if (!flashprog_chip_match(bus, &flashchips[chip]))
+			continue;
+
+		struct chip_entry *const entry = malloc(sizeof(*entry));
+		if (!entry) {
+			msg_cerr("Out of memory!\n");
+			return 1;
+		}
+
+		entry->chip = flashchips[chip];
+		entry->bus  = &bus->common;
+		entry->next = chips->entries;
+
+		chips->entries = entry;
+	}
+
+	return 0;
+}
+
+/**
+ * @brief Probe for flash chips.
+ *
+ * Probes for flash chips on a given programmer. Can return multiple
+ * matches in case of ambiguous IDs or when the programmer features
+ * multiple buses.
+ *
+ * @param[out] chips Points to a struct flashprog_chips pointer that gets
+ *		     set if probing is successful. *chips has to be freed
+ *		     by the caller with @ref flashprog_chips_release after
+ *		     successful calls.
+ * @param[in] flashprog The flash programmer used to access the chip.
+ * @return 0 on success
+ */
+int flashprog_chips_probe(struct flashprog_chips **chips, const struct flashprog_programmer *flashprog)
+{
+	struct flashprog_chips *const matched_chips = calloc(1, sizeof(*matched_chips));
+	if (!matched_chips) {
+		msg_gerr("Out of memory!\n");
+		return 1;
+	}
+
+	int bus_index;
+	for (bus_index = 0; bus_index < registered_master_count; bus_index++) {
+		if (flashprog_chips_probe_bus(matched_chips, &registered_masters[bus_index])) {
+			flashprog_chips_release(matched_chips);
+			return 1;
+		}
+	}
+
+	*chips = matched_chips;
+	return 0;
+}
+
 /**
  * @brief Count the chips in an enumeration.
  *
@@ -72,9 +149,14 @@
  */
 void flashprog_chips_release(struct flashprog_chips *chips)
 {
-	if (chips == FLASHCHIPS_DB)
+	if (!chips || chips == FLASHCHIPS_DB)
 		return;
 
+	struct chip_entry *next;
+	for (; chips->entries; chips->entries = next) {
+		next = chips->entries->next;
+		free(chips->entries);
+	}
 	free(chips);
 }
 
@@ -95,7 +177,7 @@
 	if (chips == FLASHCHIPS_DB)
 		return flashchips;
 
-	return NULL;
+	return &chips->entries->chip;
 }
 
 /**
@@ -124,7 +206,8 @@
 		return NULL;
 	}
 
-	return NULL;
+	/* If not in the DB, it must be a probed `chip_entry`. */
+	return &((const struct chip_entry *)chip)->next->chip;
 }
 
 /** @} */ /* end flashprog-chips */