[PATCH v2 08/12] sysinfo: Add support for iterating string list

Marek Behún kabel at kernel.org
Thu Nov 4 00:23:28 CET 2021


From: Marek Behún <marek.behun at nic.cz>

Add function
  sysinfo_get_str_list_max_len()
to determine length of the longest string in a string list, functions
  sysinfo_str_list_first() and
  sysinfo_str_list_next()
to support iteration over string list elements and macro
  for_each_sysinfo_str_list()
to make the iteration simple to use.

Signed-off-by: Marek Behún <marek.behun at nic.cz>
---
 drivers/sysinfo/sysinfo-uclass.c | 79 +++++++++++++++++++++++++
 include/sysinfo.h                | 99 ++++++++++++++++++++++++++++++++
 test/dm/sysinfo.c                | 35 +++++++++++
 3 files changed, 213 insertions(+)

diff --git a/drivers/sysinfo/sysinfo-uclass.c b/drivers/sysinfo/sysinfo-uclass.c
index d945f073c5..78035a95aa 100644
--- a/drivers/sysinfo/sysinfo-uclass.c
+++ b/drivers/sysinfo/sysinfo-uclass.c
@@ -8,6 +8,7 @@
 
 #include <common.h>
 #include <dm.h>
+#include <malloc.h>
 #include <sysinfo.h>
 
 struct sysinfo_priv {
@@ -104,6 +105,84 @@ int sysinfo_get_str_list(struct udevice *dev, int id, unsigned idx, size_t size,
 	return ops->get_str_list(dev, id, idx, size, val);
 }
 
+int sysinfo_get_str_list_max_len(struct udevice *dev, int id)
+{
+	int maxlen = 0;
+	unsigned i;
+
+	/* first find out length of the longest string in the list */
+	for (i = 0; ; ++i) {
+		char dummy[1];
+		int len;
+
+		len = sysinfo_get_str_list(dev, id, i, 0, dummy);
+		if (len == -ERANGE)
+			break;
+		else if (len < 0)
+			return len;
+		else if (len > maxlen)
+			maxlen = len;
+	}
+
+	return maxlen;
+}
+
+struct sysinfo_str_list_iter {
+	struct udevice *dev;
+	int id;
+	size_t size;
+	unsigned idx;
+	char value[];
+};
+
+char *sysinfo_str_list_first(struct udevice *dev, int id, void *_iter)
+{
+	struct sysinfo_str_list_iter *iter, **iterp = _iter;
+	int maxlen, res;
+
+	maxlen = sysinfo_get_str_list_max_len(dev, id);
+	if (maxlen < 0)
+		return NULL;
+
+	iter = malloc(sizeof(struct sysinfo_str_list_iter) + maxlen + 1);
+	if (!iter) {
+		printf("No memory for sysinfo string list iterator\n");
+		return NULL;
+	}
+
+	iter->dev = dev;
+	iter->id = id;
+	iter->size = maxlen + 1;
+	iter->idx = 0;
+
+	res = sysinfo_get_str_list(dev, id, 0, iter->size, iter->value);
+	if (res < 0) {
+		*iterp = NULL;
+		free(iter);
+		return NULL;
+	}
+
+	*iterp = iter;
+
+	return iter->value;
+}
+
+char *sysinfo_str_list_next(void *_iter)
+{
+	struct sysinfo_str_list_iter **iterp = _iter, *iter = *iterp;
+	int res;
+
+	res = sysinfo_get_str_list(iter->dev, iter->id, ++iter->idx, iter->size,
+				   iter->value);
+	if (res < 0) {
+		*iterp = NULL;
+		free(iter);
+		return NULL;
+	}
+
+	return iter->value;
+}
+
 UCLASS_DRIVER(sysinfo) = {
 	.id		= UCLASS_SYSINFO,
 	.name		= "sysinfo",
diff --git a/include/sysinfo.h b/include/sysinfo.h
index 0d8a2d1676..d32bf3e808 100644
--- a/include/sysinfo.h
+++ b/include/sysinfo.h
@@ -218,6 +218,61 @@ int sysinfo_get_str(struct udevice *dev, int id, size_t size, char *val);
 int sysinfo_get_str_list(struct udevice *dev, int id, unsigned idx, size_t size,
 			 char *val);
 
+/**
+ * sysinfo_get_str_list_max_len() - Get length of longest string in a string
+ *				    list that describes hardware setup.
+ * @dev:	The sysinfo instance to gather the data.
+ * @id:		A unique identifier for the string list to read from.
+ *
+ * Return: Length (excluding the terminating NULL-byte) of the longest string in
+ *	   the string list, or -ve on error.
+ */
+int sysinfo_get_str_list_max_len(struct udevice *dev, int id);
+
+/**
+ * sysinfo_str_list_first() - Start iterating a string list.
+ * @dev:	The sysinfo instance to gather the data.
+ * @id:		A unique identifier for the string list to read from.
+ * @_iter:	Pointer where iterator data will be stored.
+ *
+ * Pass a reference to a void * pointer as @_iter, i.e.
+ *	void *iter;
+ *	first = sysinfo_str_list_first(dev, id, &iter);
+ *
+ * The function will allocate space for the value. You need to iterate all
+ * elements with sysinfo_str_list_next() for the space to be freed, or free
+ * the pointer stored in @_iter, i.e.
+ *	void *iter;
+ *	first = sysinfo_str_list_first(dev, id, &iter);
+ *	if (first)
+ *		free(iter);
+ *
+ * Return: First string in the string list, or NULL on error.
+ */
+char *sysinfo_str_list_first(struct udevice *dev, int id, void *_iter);
+
+/**
+ * sysinfo_str_list_next() - Get next string in the string string list.
+ * @_iter:	Pointer to iterator, filled in by sysinfo_str_list_first().
+ *
+ * Pass a reference to a void * pointer as @_iter, i.e.
+ *	void *iter;
+ *	first = sysinfo_str_list_first(dev, id, &iter);
+ *	next = sysinfo_str_list_next(&iter);
+ *
+ * All elements must be iterated until the function returns NULL for the
+ * resources allocated for the iteration to be freed, or pointer stored in
+ * @_iter must be freed, i.e.:
+ *	void *iter;
+ *	first = sysinfo_str_list_first(dev, id, &iter);
+ *	next = sysinfo_str_list_next(&iter);
+ *	if (next)
+ *		free(iter);
+ *
+ * Return: Next string in the string list, NULL on end of list or NULL on error.
+ */
+char *sysinfo_str_list_next(void *_iter);
+
 /**
  * sysinfo_get() - Return the sysinfo device for the sysinfo in question.
  * @devp: Pointer to structure to receive the sysinfo device.
@@ -279,6 +334,22 @@ static inline int sysinfo_get_str_list(struct udevice *dev, int id,
 	return -ENOSYS;
 }
 
+static inline int sysinfo_get_str_list_max_len(struct udevice *dev, int id)
+{
+	return -ENOSYS;
+}
+
+static inline char *sysinfo_str_list_first(struct udevice *dev, int id,
+					   void *_iter)
+{
+	return NULL;
+}
+
+static inline char *sysinfo_str_list_next(void *_iter)
+{
+	return NULL;
+}
+
 static inline int sysinfo_get(struct udevice **devp)
 {
 	return -ENOSYS;
@@ -291,4 +362,32 @@ static inline int sysinfo_get_fit_loadable(struct udevice *dev, int index,
 }
 
 #endif
+
+/**
+ * for_each_sysinfo_str_list - Iterate a sysinfo string list
+ * @dev:	The sysinfo instance to gather the data.
+ * @id:		A unique identifier for the string list to read from.
+ * @val:	String pointer for the value.
+ * @iter:	Pointer where iteration data are stored.
+ *
+ * Important: all elements of the list must be iterated for the iterator
+ * resources to be freed automatically. If you need to break from the for cycle,
+ * you need to free the iterator.
+ *
+ * Example:
+ *	char *value;
+ *	void *iter;
+ *	for_each_sysinfo_str_list(dev, MY_STR_LIST, value, iter) {
+ *		printf("Value: %s\n", value);
+ *		if (!strcmp(value, "needle")) {
+ *			free(iter);
+ *			break;
+ *		}
+ *	}
+ */
+#define for_each_sysinfo_str_list(dev, id, val, iter)			\
+	for ((val) = sysinfo_str_list_first((dev), (id), &(iter));	\
+	     (val);							\
+	     (val) = sysinfo_str_list_next(&(iter)))
+
 #endif
diff --git a/test/dm/sysinfo.c b/test/dm/sysinfo.c
index a6b246f2df..e9b70d8e1a 100644
--- a/test/dm/sysinfo.c
+++ b/test/dm/sysinfo.c
@@ -75,3 +75,38 @@ static int dm_test_sysinfo(struct unit_test_state *uts)
 }
 
 DM_TEST(dm_test_sysinfo, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_sysinfo_str_list_iter(struct unit_test_state *uts)
+{
+	struct udevice *sysinfo;
+	char *value;
+	void *iter;
+	int idx;
+
+	ut_assertok(sysinfo_get(&sysinfo));
+	ut_assert(sysinfo);
+
+	sysinfo_detect(sysinfo);
+
+	idx = 0;
+	for_each_sysinfo_str_list(sysinfo, STR_VACATIONSPOT, value, iter) {
+		switch (idx) {
+		case 0:
+			ut_asserteq_str(value, "R'lyeh");
+			break;
+		case 2:
+			ut_asserteq_str(value, "Plateau of Leng");
+			break;
+		case 3:
+			ut_asserteq_str(value, "Carcosa");
+			break;
+		}
+		++idx;
+	}
+
+	ut_assert(NULL == iter);
+
+	return 0;
+}
+
+DM_TEST(dm_test_sysinfo_str_list_iter, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
-- 
2.32.0



More information about the U-Boot mailing list