libflashprog: Introduce new probing API flashprog_flash_probe_chip()
This new probing API acts about the same as the old one, except that
it accepts a `struct flashprog_chip *` handle instead of a chip name.
Under the hood, however, it uses our new bus probing directly.
We introduce a new function, flashprog_flash_prepare_context(), that
creates a flash context from a probed chip, and the bus it was found
on. This will be the single place that creates flash contexts in the
future.
Change-Id: I1a344aedf573d92f8f297e6eed8daadb511ebc10
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.sourcearcade.org/c/flashprog/+/484
diff --git a/include/chipdrivers/probing.h b/include/chipdrivers/probing.h
index 090f2ab..2e081d1 100644
--- a/include/chipdrivers/probing.h
+++ b/include/chipdrivers/probing.h
@@ -88,4 +88,10 @@
void flashprog_bus_probe(struct registered_master *, const struct flashchip *);
bool flashprog_chip_match(struct registered_master *, const struct flashchip *);
+struct flashprog_chips;
+struct flashprog_flashctx;
+struct flashprog_programmer;
+const struct master_common *flashprog_chip_probe(const struct flashprog_programmer *, const struct flashchip *);
+int flashprog_flash_prepare_context(struct flashprog_flashctx **, const struct flashprog_programmer *, const struct master_common *, const struct flashchip *);
+
#endif /* !__PROBING_H__ */
diff --git a/include/flash.h b/include/flash.h
index 3ad9d1f..31b4f0c 100644
--- a/include/flash.h
+++ b/include/flash.h
@@ -440,10 +440,10 @@
uintptr_t physical_registers;
chipaddr virtual_registers;
union {
- struct master_common *common;
- struct par_master *par;
- struct spi_master *spi;
- struct opaque_master *opaque;
+ const struct master_common *common;
+ const struct par_master *par;
+ const struct spi_master *spi;
+ const struct opaque_master *opaque;
} mst;
const struct flashprog_layout *layout;
struct flashprog_layout *default_layout;
diff --git a/include/libflashprog.h b/include/libflashprog.h
index 8afd8d5..6f4193b 100644
--- a/include/libflashprog.h
+++ b/include/libflashprog.h
@@ -122,6 +122,8 @@
struct flashprog_flashctx;
int flashprog_flash_probe(struct flashprog_flashctx **, const struct flashprog_programmer *, const char *chip_name);
+__attribute__((nonnull))
+int flashprog_flash_probe_chip(struct flashprog_flashctx **, const struct flashprog_programmer *, const struct flashprog_chip *);
size_t flashprog_flash_getsize(const struct flashprog_flashctx *);
int flashprog_flash_erase(struct flashprog_flashctx *);
void flashprog_flash_release(struct flashprog_flashctx *);
diff --git a/libflashprog.c b/libflashprog.c
index 6cdb135..d32f9bd 100644
--- a/libflashprog.c
+++ b/libflashprog.c
@@ -4,6 +4,8 @@
* Copyright (C) 2012, 2016 secunet Security Networks AG
* (Written by Nico Huber <nico.huber@secunet.com> for secunet)
*
+ * 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
@@ -25,12 +27,15 @@
#include <string.h>
#include <stdarg.h>
+#define flashprog_chip flashchip /* For now, we use direct pointers */
+#include "libflashprog.h" /* to the internal struct flashchip. */
+
#include "flash.h"
#include "fmap.h"
#include "programmer.h"
#include "layout.h"
+#include "hwaccess_physmap.h"
#include "ich_descriptors.h"
-#include "libflashprog.h"
#include "writeprotect.h"
/**
@@ -239,6 +244,101 @@
return ret;
}
+/** @private */
+int flashprog_flash_prepare_context(struct flashprog_flashctx **flashctx,
+ const struct flashprog_programmer *flashprog,
+ const struct master_common *bus,
+ const struct flashchip *chip)
+{
+ if (bus->adapt_voltage) {
+ if (bus->adapt_voltage(bus, chip->voltage.min, chip->voltage.max))
+ return 4;
+ }
+
+ struct flashprog_flashctx *const flash = calloc(1, sizeof(*flash));
+ if (!flash) {
+ msg_gerr("Out of memory!\n");
+ return 1;
+ }
+
+ flash->chip = calloc(1, sizeof(*flash->chip));
+ if (!flash->chip) {
+ msg_gerr("Out of memory!\n");
+ free(flash);
+ return 1;
+ }
+
+ *flash->chip = *chip;
+ flash->mst.common = bus; /* `mst` is a union, so we need only one pointer */
+
+ if (chip->prepare_access && chip->prepare_access(flash, PREPARE_POST_PROBE))
+ goto free_flash;
+
+ /* Fill default layout covering the whole chip. */
+ if (flashprog_layout_new(&flash->default_layout) ||
+ flashprog_layout_add_region(flash->default_layout,
+ 0, flash->chip->total_size * 1024 - 1, "complete flash") ||
+ flashprog_layout_include_region(flash->default_layout, "complete flash"))
+ goto free_flash;
+
+ char *const tmp = flashbuses_to_text(flash->chip->bustype);
+ msg_cinfo("Using %s flash chip \"%s\" (%d kB, %s) ",
+ flash->chip->vendor, flash->chip->name, flash->chip->total_size, tmp);
+ free(tmp);
+ if (strcmp(flashprog->driver->name, "internal") == 0 && flash->physical_memory != 0)
+ msg_cinfo("mapped at physical address 0x%0*" PRIxPTR ".\n",
+ PRIxPTR_WIDTH, flash->physical_memory);
+ else
+ msg_cinfo("on %s.\n", flashprog->driver->name);
+
+ if (flash->chip->printlock)
+ flash->chip->printlock(flash);
+
+ if (flash->chip->finish_access)
+ flash->chip->finish_access(flash);
+
+ *flashctx = flash;
+ return 0;
+
+free_flash:
+ if (flash->chip && flash->chip->finish_access)
+ flash->chip->finish_access(flash);
+ free(flash->chip);
+ free(flash);
+ return 4;
+}
+
+/**
+ * @brief Probe for a specific flash chip.
+ *
+ * Probes for a flash chip and returns a flash context, that can be used
+ * later with flash chip and @ref flashprog-ops "image operations".
+ *
+ * @param[out] flashctx Points to a pointer of type struct flashprog_flashctx
+ * that will be set. *flashctx has to be freed by the
+ * caller with @ref flashprog_flash_release.
+ * @param[in] flashprog The flash programmer used to access the chip.
+ * @param[in] chip A reference to a chip structure that was previously
+ * fetched by @ref flashprog-chips "enumeration". Its
+ * contents will be copied, so the underlying chip
+ * enumeration can be released directly after the call.
+ * @return 0 on success,
+ * 4 if the chip was detected, but preparation failed,
+ * 3 if multiple chips were found,
+ * 2 if no chip was detected,
+ * or 1 on any other error.
+ */
+int flashprog_flash_probe_chip(struct flashprog_flashctx **flashctx,
+ const struct flashprog_programmer *flashprog,
+ const struct flashprog_chip *chip)
+{
+ const struct master_common *const bus = flashprog_chip_probe(flashprog, chip);
+ if (!bus)
+ return 2;
+
+ return flashprog_flash_prepare_context(flashctx, flashprog, bus, chip);
+}
+
/**
* @brief Returns the size of the specified flash chip in bytes.
*
diff --git a/libflashprog.map b/libflashprog.map
index 033d5f8..42ebc86 100644
--- a/libflashprog.map
+++ b/libflashprog.map
@@ -18,6 +18,7 @@
flashprog_flash_erase;
flashprog_flash_getsize;
flashprog_flash_probe;
+ flashprog_flash_probe_chip;
flashprog_flash_release;
flashprog_image_read;
flashprog_image_verify;
diff --git a/libflashprog/chips.c b/libflashprog/chips.c
index 919d0eb..ac5cdc2 100644
--- a/libflashprog/chips.c
+++ b/libflashprog/chips.c
@@ -90,6 +90,39 @@
return 0;
}
+/** @private */
+const struct master_common *flashprog_chip_probe(
+ const struct flashprog_programmer *flashprog,
+ const struct flashchip *chip)
+{
+ if (!chip_from_db(chip))
+ /* If not in the DB, it must be a probed `chip_entry`. */
+ return ((const struct chip_entry *)chip)->bus;
+
+ int i;
+ for (i = 0; i < registered_master_count; ++i) {
+ struct registered_master *const bus = ®istered_masters[i];
+
+ if (!(bus->buses_supported & chip->bustype))
+ continue;
+
+ /* If it can't be probed, assume it's there. */
+ if (chip->id.type == ID_NONE)
+ return &bus->common;
+
+ /* We probe for a specific chip, so we can adapt the voltage early. */
+ if (bus->common.adapt_voltage &&
+ bus->common.adapt_voltage(&bus->common, chip->voltage.min, chip->voltage.max))
+ return NULL;
+
+ flashprog_bus_probe(bus, chip);
+ if (flashprog_chip_match(bus, chip))
+ return &bus->common;
+ }
+
+ return NULL;
+}
+
/**
* @brief Probe for flash chips.
*