blob: 018d447d051ab33c8248810875b570809976553f [file] [log] [blame]
Nico Huber48d4a042026-03-08 16:07:28 +01001/*
2 * This file is part of the flashprog project.
3 *
4 * Copyright (C) 2026 Nico Huber <nico.h@gmx.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include <stdlib.h>
18
19#define flashprog_chip flashchip /* For now, we use direct pointers */
20#include "libflashprog.h" /* to the internal struct flashchip. */
21
22#include "flash.h"
23#include "flashchips.h"
Nico Huberd9301142026-03-08 21:19:27 +010024#include "programmer.h"
Nico Huber48d4a042026-03-08 16:07:28 +010025
26/**
27 * @defgroup flashprog-chips Chip Enumeration
28 * @{
29 */
30
31/* Magic pointer that represents our built in database. */
32#define FLASHCHIPS_DB (struct flashprog_chips *)(uintptr_t)-1
33
Nico Huberd9301142026-03-08 21:19:27 +010034/** @private */
35struct flashprog_chips {
36 /** @private */
37 struct chip_entry {
38 struct flashprog_chip chip;
39 const struct master_common *bus;
40 struct chip_entry *next;
41 } *entries;
42};
43
Nico Huber48d4a042026-03-08 16:07:28 +010044static bool chip_from_db(const struct flashprog_chip *chip) {
45 return flashchips <= chip && chip < flashchips + flashchips_size;
46}
47
48/**
49 * @brief Enumerate the internal chips database.
50 *
51 * @param[out] chips Points to a struct flashprog_chips pointer that gets
52 * set if the enumeration is successful. *chips has to be
53 * freed by the caller with @ref flashprog_chips_release.
54 * @return 0 on success
55 */
56int flashprog_chips_all(struct flashprog_chips **chips) {
57 *chips = FLASHCHIPS_DB;
58 return 0;
59}
60
Nico Huber3311f1c2026-03-14 12:22:18 +010061static struct found_id *flashprog_bus_probe(struct registered_master *bus, const struct flashprog_chip *chip)
Nico Huber50bf19f2026-03-14 12:14:18 +010062{
Nico Huber3311f1c2026-03-14 12:22:18 +010063 struct found_id *found_ids = NULL, **next_ptr = &found_ids;
64 unsigned int least_priority, priority, i;
Nico Huber50bf19f2026-03-14 12:14:18 +010065
Nico Huber3311f1c2026-03-14 12:22:18 +010066 for (i = 0, least_priority = 0; i < bus->probing.probe_count; ++i) {
67 if (least_priority < bus->probing.probes[i].priority)
68 least_priority = bus->probing.probes[i].priority;
69 }
70
71 for (priority = 0; priority <= least_priority; ++priority) {
72 for (i = 0; i < bus->probing.probe_count; ++i) {
73 if (bus->probing.probes[i].priority != priority)
74 continue;
75
76 if (chip && chip->id.type != bus->probing.probes[i].type)
77 continue;
78
79 *next_ptr = bus->probing.probes[i].run(&bus->probing.probes[i], &bus->common, chip);
80
81 /* walk to end in case multiple IDs were found in a single call */
82 while (*next_ptr)
83 next_ptr = &(*next_ptr)->next;
84 }
85
86 /* Skip lower-priority probing if any chip replied. */
87 if (found_ids)
Nico Huber50bf19f2026-03-14 12:14:18 +010088 break;
89 }
Nico Huber3311f1c2026-03-14 12:22:18 +010090
91 return found_ids;
92}
93
94static void flashprog_bus_probe_cleanup(struct found_id *found_ids)
95{
96 struct found_id *next;
97 for (; found_ids; found_ids = next) {
98 next = found_ids->next;
99 free(found_ids);
100 }
101}
102
103static bool flashprog_bus_match_chip(struct registered_master *bus,
104 const struct found_id *found_ids,
105 const struct flashprog_chip *chip)
106{
107 for (; found_ids; found_ids = found_ids->next) {
108 if (found_ids->info.id.type != chip->id.type)
109 continue;
110
111 if (bus->probing.match(chip, &found_ids->info))
112 break;
113 }
114 return !!found_ids;
Nico Huber50bf19f2026-03-14 12:14:18 +0100115}
116
Nico Huberd9301142026-03-08 21:19:27 +0100117static int flashprog_chips_probe_bus(struct flashprog_chips *chips,
118 struct registered_master *bus)
119{
Nico Huber3311f1c2026-03-14 12:22:18 +0100120 struct found_id *const found_ids = flashprog_bus_probe(bus, NULL);
121 int chip, ret = 0;
Nico Huberd9301142026-03-08 21:19:27 +0100122
Nico Huberd9301142026-03-08 21:19:27 +0100123 for (chip = 0; flashchips[chip].name; ++chip) {
124 /* Ignore generic entries if we already have a match. */
125 if (chips->entries &&
126 ((flashchips[chip].id.model == SFDP_DEVICE_ID) ||
127 (flashchips[chip].id.model == GENERIC_DEVICE_ID)))
128 continue;
129
Nico Huber3311f1c2026-03-14 12:22:18 +0100130 if (!flashprog_bus_match_chip(bus, found_ids, &flashchips[chip]))
Nico Huberd9301142026-03-08 21:19:27 +0100131 continue;
132
133 struct chip_entry *const entry = malloc(sizeof(*entry));
134 if (!entry) {
135 msg_cerr("Out of memory!\n");
Nico Huber3311f1c2026-03-14 12:22:18 +0100136 ret = 1;
137 break;
Nico Huberd9301142026-03-08 21:19:27 +0100138 }
139
140 entry->chip = flashchips[chip];
141 entry->bus = &bus->common;
142 entry->next = chips->entries;
143
144 chips->entries = entry;
145 }
146
Nico Huber3311f1c2026-03-14 12:22:18 +0100147 flashprog_bus_probe_cleanup(found_ids);
148 return ret;
Nico Huberd9301142026-03-08 21:19:27 +0100149}
150
Nico Huberd4926fe2026-03-08 21:37:52 +0100151/** @private */
152const struct master_common *flashprog_chip_probe(
153 const struct flashprog_programmer *flashprog,
154 const struct flashchip *chip)
155{
156 if (!chip_from_db(chip))
157 /* If not in the DB, it must be a probed `chip_entry`. */
158 return ((const struct chip_entry *)chip)->bus;
159
160 int i;
161 for (i = 0; i < registered_master_count; ++i) {
162 struct registered_master *const bus = &registered_masters[i];
163
164 if (!(bus->buses_supported & chip->bustype))
165 continue;
166
167 /* If it can't be probed, assume it's there. */
168 if (chip->id.type == ID_NONE)
169 return &bus->common;
170
171 /* We probe for a specific chip, so we can adapt the voltage early. */
172 if (bus->common.adapt_voltage &&
173 bus->common.adapt_voltage(&bus->common, chip->voltage.min, chip->voltage.max))
174 return NULL;
175
Nico Huber3311f1c2026-03-14 12:22:18 +0100176 struct found_id *const found_ids = flashprog_bus_probe(bus, chip);
177 const bool match = flashprog_bus_match_chip(bus, found_ids, chip);
178 flashprog_bus_probe_cleanup(found_ids);
179
180 if (match)
Nico Huberd4926fe2026-03-08 21:37:52 +0100181 return &bus->common;
182 }
183
184 return NULL;
185}
186
Nico Huberd9301142026-03-08 21:19:27 +0100187/**
188 * @brief Probe for flash chips.
189 *
190 * Probes for flash chips on a given programmer. Can return multiple
191 * matches in case of ambiguous IDs or when the programmer features
192 * multiple buses.
193 *
194 * @param[out] chips Points to a struct flashprog_chips pointer that gets
195 * set if probing is successful. *chips has to be freed
196 * by the caller with @ref flashprog_chips_release after
197 * successful calls.
198 * @param[in] flashprog The flash programmer used to access the chip.
199 * @return 0 on success
200 */
201int flashprog_chips_probe(struct flashprog_chips **chips, const struct flashprog_programmer *flashprog)
202{
203 struct flashprog_chips *const matched_chips = calloc(1, sizeof(*matched_chips));
204 if (!matched_chips) {
205 msg_gerr("Out of memory!\n");
206 return 1;
207 }
208
209 int bus_index;
210 for (bus_index = 0; bus_index < registered_master_count; bus_index++) {
211 if (flashprog_chips_probe_bus(matched_chips, &registered_masters[bus_index])) {
212 flashprog_chips_release(matched_chips);
213 return 1;
214 }
215 }
216
217 *chips = matched_chips;
218 return 0;
219}
220
Nico Huber48d4a042026-03-08 16:07:28 +0100221/**
222 * @brief Count the chips in an enumeration.
223 *
224 * @return The number of chips.
225 */
226unsigned int flashprog_chips_count(const struct flashprog_chips *chips)
227{
228 unsigned int count = 0;
229 const struct flashprog_chip *chip;
230 for (chip = flashprog_chip_first(chips); chip; chip = flashprog_chip_next(chip))
231 ++count;
232 return count;
233}
234
235/**
236 * @brief Free a set of enumerated chips.
237 *
238 * This also invalidates all references that were acquired via
239 * @ref flashprog_chip_first or @ref flashprog_chip_next from
240 * the given set.
241 *
242 * @param chips Chip enumeration to free.
243 */
244void flashprog_chips_release(struct flashprog_chips *chips)
245{
Nico Huberd9301142026-03-08 21:19:27 +0100246 if (!chips || chips == FLASHCHIPS_DB)
Nico Huber48d4a042026-03-08 16:07:28 +0100247 return;
248
Nico Huberd9301142026-03-08 21:19:27 +0100249 struct chip_entry *next;
250 for (; chips->entries; chips->entries = next) {
251 next = chips->entries->next;
252 free(chips->entries);
253 }
Nico Huber48d4a042026-03-08 16:07:28 +0100254 free(chips);
255}
256
257/**
258 * @brief Starts an iteration over a given set of enumerated chips.
259 *
260 * The referenced chip structure will stay valid until either the iteration
261 * is advanced (@ref flashprog_chip_next) or the provided chips enumeration
262 * is released. Note, each call may allocated additional resources that are
263 * only freed by walking the iteration to its end, or releasing the entire
264 * chips set.
265 *
266 * @param chips A set of enumerated chips.
267 * @return A pointer to the structure of the first chip or NULL if the set is empty.
268 */
269const struct flashprog_chip *flashprog_chip_first(const struct flashprog_chips *chips)
270{
271 if (chips == FLASHCHIPS_DB)
272 return flashchips;
273
Nico Huberd9301142026-03-08 21:19:27 +0100274 return &chips->entries->chip;
Nico Huber48d4a042026-03-08 16:07:28 +0100275}
276
277/**
278 * @brief Iterates to the next chip structure in a set of enumerated chips.
279 *
280 * The referenced chip structure will stay valid until either the iteration
281 * is advanced further or the original chips enumeration is released (cf.
282 * @ref flashprog_chip_first).
283 *
284 * @param chip The previous chip in the enumeration. The referenced
285 * structure will be invalidated by the call.
286 * @return A pointer to the structure of the next chip or NULL if there is none.
287 */
288const struct flashprog_chip *flashprog_chip_next(const struct flashprog_chip *chip)
289{
290 if (chip_from_db(chip)) {
291 for (++chip; chip->name; ++chip) {
292 if (chip->id.manufacture == PROGMANUF_ID)
293 continue;
294 if (chip->id.manufacture == GENERIC_MANUF_ID)
295 continue;
296 if (chip->id.model == GENERIC_DEVICE_ID)
297 continue;
298 return chip;
299 }
300 return NULL;
301 }
302
Nico Huberd9301142026-03-08 21:19:27 +0100303 /* If not in the DB, it must be a probed `chip_entry`. */
304 return &((const struct chip_entry *)chip)->next->chip;
Nico Huber48d4a042026-03-08 16:07:28 +0100305}
306
307/** @} */ /* end flashprog-chips */
308
309
310/**
311 * @defgroup flashprog-chip Chip Information
312 * @{
313 */
314
315/**
316 * @brief Get the vendor string of a given chip structure.
317 *
318 * @param chip reference to query.
319 * @return Vendor string.
320 */
321const char *flashprog_chip_vendor(const struct flashprog_chip *chip) {
322 return chip->vendor;
323}
324
325/**
326 * @brief Get the name string of a given chip structure.
327 *
328 * @param chip reference to query.
329 * @return Name string.
330 */
331const char *flashprog_chip_name(const struct flashprog_chip *chip) {
332 return chip->name;
333}
334
335/**
336 * @brief Get the size of a given chip in bytes.
337 *
338 * @param chip reference to query.
339 * @return Size in bytes.
340 */
341size_t flashprog_chip_size(const struct flashprog_chip *chip) {
342 return chip->total_size * KiB;
343}
344
Nico Huber0d2b45e2026-03-23 22:42:46 +0100345/**
346 * @brief Get a bit mask of the supported bus types.
347 *
348 * @param chip reference to query.
349 * @return bit mask of supported bus types.
350 */
351enum flashprog_bus_type flashprog_chip_buses(const struct flashprog_chip *chip) {
352 return chip->bustype;
353}
354
355/**
356 * @brief Get a string that lists the supported bus types.
357 *
358 * The resulting string needs to be freed with free().
359 *
360 * @param chip reference to query.
361 * @return comma-separated string of supported bus types.
362 */
363char *flashprog_chip_bus_names(const struct flashprog_chip *chip) {
364 return flashbuses_to_text(chip->bustype);
365}
366
367/**
368 * @brief Get the supported operating voltage range.
369 *
370 * @param chip reference to query.
371 * @return supported operating voltage range.
372 */
373struct flashprog_voltage_range flashprog_chip_voltage_range(const struct flashprog_chip *chip)
374{
375 return (struct flashprog_voltage_range) {
376 .min = chip->voltage.min / 1000.f,
377 .max = chip->voltage.max / 1000.f,
378 };
379}
380
381/**
382 * @brief Get the test status of a chip's standard features.
383 *
384 * @param chip reference to query.
385 * @return struct with the test status of the chip's standard features.
386 */
387struct flashprog_test_status flashprog_chip_test_status(const struct flashprog_chip *chip) {
388 return chip->tested;
389}
390
Nico Huber48d4a042026-03-08 16:07:28 +0100391/** @} */ /* end flashprog-chip */