libflashprog: Start a new API for chip enumeration

For now, allow to query all chips, their vendor, name and size.

There's a tricky optimization coming up: We don't want to allocate
additional memory when enumerating the chip database. But, we also
want to use the same API interface to access information about de-
tected chips, which requires dynamic memory.  So we have to handle
these distinct cases explicitly.  Where `struct flashprog_chips *`
is used, we set it to the special value `FLASHCHIPS_DB`  to denote
the flash database. For `struct flashprog_chip *` pointers, we add
chip_from_db(), which tells us whether the given pointer is within
the database range.

Change-Id: I7ecc12e1b74ca11f2be1b3472d6e470da44bfef1
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.sourcearcade.org/c/flashprog/+/481
diff --git a/Doxyfile b/Doxyfile
index 5fdf5e8..c0c3528 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -780,8 +780,9 @@
 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = libflashprog.c \
-                         libflashprog.h \
+INPUT                  = include/libflashprog.h \
+                         libflashprog.c \
+                         libflashprog/ \
                          flashprog.c \
                          layout.c \
 
@@ -808,7 +809,7 @@
 # *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
 # *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
 
-FILE_PATTERNS          =
+FILE_PATTERNS          = *.c
 
 # The RECURSIVE tag can be used to specify whether or not subdirectories should
 # be searched for input files as well.
diff --git a/Makefile b/Makefile
index 11eb07b..7d6e668 100644
--- a/Makefile
+++ b/Makefile
@@ -400,8 +400,10 @@
 ###############################################################################
 # Library code.
 
-LIB_OBJS = libflashprog.o layout.o flashprog.o udelay.o parallel.o programmer.o programmer_table.o \
-	helpers.o helpers_fileio.o ich_descriptors.o fmap.o platform/endian_$(ENDIAN).o platform/memaccess.o
+LIB_OBJS = libflashprog.o libflashprog/chips.o layout.o flashprog.o udelay.o \
+	parallel.o programmer.o programmer_table.o \
+	helpers.o helpers_fileio.o ich_descriptors.o fmap.o \
+	platform/endian_$(ENDIAN).o platform/memaccess.o
 
 
 ###############################################################################
diff --git a/include/libflashprog.h b/include/libflashprog.h
index e1d4d80..1f46be1 100644
--- a/include/libflashprog.h
+++ b/include/libflashprog.h
@@ -45,6 +45,25 @@
 int flashprog_programmer_init(struct flashprog_programmer **, const char *prog_name, const char *prog_params);
 int flashprog_programmer_shutdown(struct flashprog_programmer *);
 
+struct flashprog_chips;
+__attribute__((nonnull))
+int flashprog_chips_all(struct flashprog_chips **);
+__attribute__((nonnull))
+unsigned int flashprog_chips_count(const struct flashprog_chips *);
+void flashprog_chips_release(struct flashprog_chips *);
+
+struct flashprog_chip;
+__attribute__((nonnull))
+const struct flashprog_chip *flashprog_chip_first(const struct flashprog_chips *);
+__attribute__((nonnull))
+const struct flashprog_chip *flashprog_chip_next(const struct flashprog_chip *);
+__attribute__((nonnull))
+const char *flashprog_chip_vendor(const struct flashprog_chip *);
+__attribute__((nonnull))
+const char *flashprog_chip_name(const struct flashprog_chip *);
+__attribute__((nonnull))
+size_t flashprog_chip_size(const struct flashprog_chip *);
+
 struct flashprog_flashctx;
 int flashprog_flash_probe(struct flashprog_flashctx **, const struct flashprog_programmer *, const char *chip_name);
 size_t flashprog_flash_getsize(const struct flashprog_flashctx *);
diff --git a/libflashprog.c b/libflashprog.c
index 9c28eff..7e2cea1 100644
--- a/libflashprog.c
+++ b/libflashprog.c
@@ -100,17 +100,6 @@
 
 
 /**
- * @defgroup flashprog-query Querying
- * @{
- */
-
-/* TBD */
-
-/** @} */ /* end flashprog-query */
-
-
-
-/**
  * @defgroup flashprog-prog Programmers
  * @{
  */
diff --git a/libflashprog.map b/libflashprog.map
index a0dd125..c13f7e4 100644
--- a/libflashprog.map
+++ b/libflashprog.map
@@ -1,5 +1,13 @@
 LIBFLASHPROG_1.0 {
   global:
+    flashprog_chip_first;
+    flashprog_chip_name;
+    flashprog_chip_next;
+    flashprog_chip_size;
+    flashprog_chip_vendor;
+    flashprog_chips_all;
+    flashprog_chips_count;
+    flashprog_chips_release;
     flashprog_flag_get;
     flashprog_flag_set;
     flashprog_flash_erase;
diff --git a/libflashprog/chips.c b/libflashprog/chips.c
new file mode 100644
index 0000000..ec4adbc
--- /dev/null
+++ b/libflashprog/chips.c
@@ -0,0 +1,168 @@
+/*
+ * This file is part of the flashprog project.
+ *
+ * Copyright (C) 2026 Nico Huber <nico.h@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdlib.h>
+
+#define flashprog_chip flashchip	/* For now, we use direct pointers   */
+#include "libflashprog.h"		/* to the internal struct flashchip. */
+
+#include "flash.h"
+#include "flashchips.h"
+
+/**
+ * @defgroup flashprog-chips Chip Enumeration
+ * @{
+ */
+
+/* Magic pointer that represents our built in database. */
+#define FLASHCHIPS_DB (struct flashprog_chips *)(uintptr_t)-1
+
+static bool chip_from_db(const struct flashprog_chip *chip) {
+	return flashchips <= chip && chip < flashchips + flashchips_size;
+}
+
+/**
+ * @brief Enumerate the internal chips database.
+ *
+ * @param[out] chips Points to a struct flashprog_chips pointer that gets
+ *                   set if the enumeration is successful. *chips has to be
+ *                   freed by the caller with @ref flashprog_chips_release.
+ * @return 0 on success
+ */
+int flashprog_chips_all(struct flashprog_chips **chips) {
+	*chips = FLASHCHIPS_DB;
+	return 0;
+}
+
+/**
+ * @brief Count the chips in an enumeration.
+ *
+ * @return The number of chips.
+ */
+unsigned int flashprog_chips_count(const struct flashprog_chips *chips)
+{
+	unsigned int count = 0;
+	const struct flashprog_chip *chip;
+	for (chip = flashprog_chip_first(chips); chip; chip = flashprog_chip_next(chip))
+		++count;
+	return count;
+}
+
+/**
+ * @brief Free a set of enumerated chips.
+ *
+ * This also invalidates all references that were acquired via
+ * @ref flashprog_chip_first or @ref flashprog_chip_next from
+ * the given set.
+ *
+ * @param chips Chip enumeration to free.
+ */
+void flashprog_chips_release(struct flashprog_chips *chips)
+{
+	if (chips == FLASHCHIPS_DB)
+		return;
+
+	free(chips);
+}
+
+/**
+ * @brief Starts an iteration over a given set of enumerated chips.
+ *
+ * The referenced chip structure will stay valid until either the iteration
+ * is advanced (@ref flashprog_chip_next) or the provided chips enumeration
+ * is released. Note, each call may allocated additional resources that are
+ * only freed by walking the iteration to its end,  or releasing the entire
+ * chips set.
+ *
+ * @param chips A set of enumerated chips.
+ * @return A pointer to the structure of the first chip or NULL if the set is empty.
+ */
+const struct flashprog_chip *flashprog_chip_first(const struct flashprog_chips *chips)
+{
+	if (chips == FLASHCHIPS_DB)
+		return flashchips;
+
+	return NULL;
+}
+
+/**
+ * @brief Iterates to the next chip structure in a set of enumerated chips.
+ *
+ * The referenced chip structure will stay valid until either the iteration
+ * is advanced further or the original chips enumeration is released (cf.
+ * @ref flashprog_chip_first).
+ *
+ * @param chip The previous chip in the enumeration. The referenced
+ *             structure will be invalidated by the call.
+ * @return A pointer to the structure of the next chip or NULL if there is none.
+ */
+const struct flashprog_chip *flashprog_chip_next(const struct flashprog_chip *chip)
+{
+	if (chip_from_db(chip)) {
+		for (++chip; chip->name; ++chip) {
+			if (chip->id.manufacture == PROGMANUF_ID)
+				continue;
+			if (chip->id.manufacture == GENERIC_MANUF_ID)
+				continue;
+			if (chip->id.model == GENERIC_DEVICE_ID)
+				continue;
+			return chip;
+		}
+		return NULL;
+	}
+
+	return NULL;
+}
+
+/** @} */ /* end flashprog-chips */
+
+
+/**
+ * @defgroup flashprog-chip Chip Information
+ * @{
+ */
+
+/**
+ * @brief Get the vendor string of a given chip structure.
+ *
+ * @param chip reference to query.
+ * @return Vendor string.
+ */
+const char *flashprog_chip_vendor(const struct flashprog_chip *chip) {
+	return chip->vendor;
+}
+
+/**
+ * @brief Get the name string of a given chip structure.
+ *
+ * @param chip reference to query.
+ * @return Name string.
+ */
+const char *flashprog_chip_name(const struct flashprog_chip *chip) {
+	return chip->name;
+}
+
+/**
+ * @brief Get the size of a given chip in bytes.
+ *
+ * @param chip reference to query.
+ * @return Size in bytes.
+ */
+size_t flashprog_chip_size(const struct flashprog_chip *chip) {
+	return chip->total_size * KiB;
+}
+
+/** @} */ /* end flashprog-chip */
diff --git a/meson.build b/meson.build
index 9e996e9..2b40d52 100644
--- a/meson.build
+++ b/meson.build
@@ -60,6 +60,7 @@
   'jedec.c',
   'layout.c',
   'libflashprog.c',
+  'libflashprog/chips.c',
   'm28f.c',
   'memory_bus.c',
   'opaque.c',