[PATCH v2 11/16] libusbg: Add import config functionality

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Configurations can be exported to file using usbg_export_config().
This commit adds complementary API function usbg_import_config()
which allows to import configuration (with attrs, strings and
bindings) from file to chosen gadget.

Signed-off-by: Krzysztof Opasiak <k.opasiak@xxxxxxxxxxx>
---
 include/usbg/usbg.h |   11 ++
 src/usbg.c          |  416 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 427 insertions(+)

diff --git a/include/usbg/usbg.h b/include/usbg/usbg.h
index 5e75208..70b9910 100644
--- a/include/usbg/usbg.h
+++ b/include/usbg/usbg.h
@@ -940,6 +940,17 @@ extern int usbg_export_gadget(usbg_gadget *g, FILE *stream);
 extern int usbg_import_function(usbg_gadget *g, FILE *stream,
 				const char *instance, usbg_function **f);
 
+/**
+ * @brief Imports usb configuration from file and adds it to given gadget
+ * @param g Gadget where configuration should be placed
+ * @param stream from which configuration should be imported
+ * @param id which shuld be used for new configuration
+ * @param c place for pointer to imported configuration
+ * if NULL this param will be ignored.
+ * @return 0 on success, usbg_error otherwise
+ */
+extern int usbg_import_config(usbg_gadget *g, FILE *stream, int id,
+				usbg_config **c);
  * @}
  */
 #endif /* __USBG_H__ */
diff --git a/src/usbg.c b/src/usbg.c
index a6c9fdf..3e4725d 100644
--- a/src/usbg.c
+++ b/src/usbg.c
@@ -3260,6 +3260,38 @@ out:
 #define usbg_config_is_string(node) \
 	(config_setting_type(node) == CONFIG_TYPE_STRING)
 
+static int split_function_label(const char *label, usbg_function_type *type,
+				const char **instance)
+{
+	const char *floor;
+	char buf[USBG_MAX_NAME_LENGTH];
+	int len;
+	int function_type;
+	int ret = USBG_ERROR_NOT_FOUND;
+
+	/* We assume that function type string doesn't contain '_' */
+	floor = strchr(label, '_');
+	/* if phrase before _ is longer than max name length we may
+	 * stop looking */
+	len = floor - label;
+	if (len >= USBG_MAX_NAME_LENGTH || floor == label)
+		goto out;
+
+	strncpy(buf, label, len);
+	buf[len] = '\0';
+
+	function_type = usbg_lookup_function_type(buf);
+	if (function_type < 0)
+		goto out;
+
+	*type = (usbg_function_type)function_type;
+	*instance = floor + 1;
+
+	ret = USBG_SUCCESS;
+out:
+	return ret;
+}
+
 static void usbg_set_failed_import(config_t **to_set, config_t *failed)
 {
 	if (*to_set != NULL) {
@@ -3398,6 +3430,346 @@ out:
 	return ret;
 }
 
+static usbg_function *usbg_lookup_function(usbg_gadget *g, const char *label)
+{
+	usbg_function *f;
+	int usbg_ret;
+
+	/* check if such function has also been imported */
+	TAILQ_FOREACH(f, &g->functions, fnode) {
+		if (f->label && !strcmp(f->label, label))
+			break;
+	}
+
+	/* if not let's check if label follows the naming convention */
+	if (!f) {
+		usbg_function_type type;
+		const char *instance;
+
+		usbg_ret = split_function_label(label, &type, &instance);
+		if (usbg_ret != USBG_SUCCESS)
+			goto out;
+
+		/* check if such function exist */
+		f = usbg_get_function(g, type, instance);
+	}
+
+out:
+	return f;
+}
+
+/* We have a string which should match with one of function names */
+static int usbg_import_binding_string(config_setting_t *root, usbg_config *c)
+{
+	const char *func_label;
+	usbg_function *target;
+	int ret;
+
+	func_label = config_setting_get_string(root);
+	if (!func_label) {
+		ret = USBG_ERROR_OTHER_ERROR;
+		goto out;
+	}
+
+	target = usbg_lookup_function(c->parent, func_label);
+	if (!target) {
+		ret = USBG_ERROR_NOT_FOUND;
+		goto out;
+	}
+
+	ret = usbg_add_config_function(c, target->name, target);
+out:
+	return ret;
+}
+
+static int usbg_import_binding_group(config_setting_t *root, usbg_config *c)
+{
+	config_setting_t *node;
+	const char *func_label, *name;
+	usbg_function *target;
+	int ret;
+
+	node = config_setting_get_member(root, USBG_FUNCTION_TAG);
+	if (!node) {
+		ret = USBG_ERROR_MISSING_TAG;
+		goto out;
+	}
+
+	/* It is allowed to provide link to existing function
+	 * or define unlabeled instance of function in this place */
+	if (usbg_config_is_string(node)) {
+		func_label = config_setting_get_string(node);
+		if (!func_label) {
+			ret = USBG_ERROR_OTHER_ERROR;
+			goto out;
+		}
+
+		target = usbg_lookup_function(c->parent, func_label);
+		if (!target) {
+			ret = USBG_ERROR_NOT_FOUND;
+			goto out;
+		}
+	} else if (config_setting_is_group(node)) {
+		config_setting_t *inst_node;
+		const char *instance;
+
+		inst_node = config_setting_get_member(node, USBG_INSTANCE_TAG);
+		if (!inst_node) {
+			ret = USBG_ERROR_MISSING_TAG;
+			goto out;
+		}
+
+		instance = config_setting_get_string(inst_node);
+		if (!instance) {
+			ret = USBG_ERROR_OTHER_ERROR;
+			goto out;
+		}
+
+		ret = usbg_import_function_run(c->parent, node,
+					       instance, &target);
+		if (ret != USBG_SUCCESS)
+			goto out;
+	} else {
+		ret = USBG_ERROR_INVALID_TYPE;
+		goto out;
+	}
+
+	/* Name tag is optional. When no such tag, default one will be used */
+	node = config_setting_get_member(root, USBG_NAME_TAG);
+	if (node) {
+		if (!usbg_config_is_string(node)) {
+			ret = USBG_ERROR_INVALID_TYPE;
+			goto out;
+		}
+
+		name = config_setting_get_string(node);
+		if (!name) {
+			ret = USBG_ERROR_OTHER_ERROR;
+			goto out;
+		}
+	} else {
+		name = target->name;
+	}
+
+	ret = usbg_add_config_function(c, name, target);
+out:
+	return ret;
+}
+
+static int usbg_import_config_bindings(config_setting_t *root, usbg_config *c)
+{
+	config_setting_t *node;
+	int usbg_ret, cfg_ret;
+	int ret = USBG_SUCCESS;
+	int count, i;
+
+	count = config_setting_length(root);
+
+	for (i = 0; i < count; ++i) {
+		node = config_setting_get_elem(root, i);
+
+		if (usbg_config_is_string(node))
+			ret = usbg_import_binding_string(node, c);
+		else if (config_setting_is_group(node))
+			ret = usbg_import_binding_group(node, c);
+		else
+			ret = USBG_ERROR_INVALID_TYPE;
+
+		if (ret != USBG_SUCCESS)
+			break;
+	}
+
+	return ret;
+}
+
+static int usbg_import_config_strs_lang(config_setting_t *root, usbg_config *c)
+{
+	config_setting_t *node;
+	int lang;
+	const char *str;
+	usbg_config_strs c_strs = {0};
+	int usbg_ret, cfg_ret;
+	int ret = USBG_ERROR_INVALID_TYPE;
+
+	node = config_setting_get_member(root, USBG_LANG_TAG);
+	if (!node) {
+		ret = USBG_ERROR_MISSING_TAG;
+		goto out;
+	}
+
+	if (!usbg_config_is_int(node))
+		goto out;
+
+	lang = config_setting_get_int(node);
+
+	/* Configuratin string is optional */
+	node = config_setting_get_member(root, "configuration");
+	if (node) {
+		if (!usbg_config_is_string(node))
+			goto out;
+
+		str = config_setting_get_string(node);
+
+		/* Auto truncate the string to max length */
+		strncpy(c_strs.configuration, str, USBG_MAX_STR_LENGTH);
+		c_strs.configuration[USBG_MAX_STR_LENGTH - 1] = 0;
+	}
+
+	ret = usbg_set_config_strs(c, lang, &c_strs);
+
+out:
+	return ret;
+}
+
+static int usbg_import_config_strings(config_setting_t *root, usbg_config *c)
+{
+	config_setting_t *node;
+	int usbg_ret, cfg_ret;
+	int ret = USBG_SUCCESS;
+	int count, i;
+
+	count = config_setting_length(root);
+
+	for (i = 0; i < count; ++i) {
+		node = config_setting_get_elem(root, i);
+		if (!config_setting_is_group(node)) {
+			ret = USBG_ERROR_INVALID_TYPE;
+			break;
+		}
+
+		ret = usbg_import_config_strs_lang(node, c);
+		if (ret != USBG_SUCCESS)
+			break;
+	}
+
+	return ret;
+}
+
+static int usbg_import_config_attrs(config_setting_t *root, usbg_config *c)
+{
+	config_setting_t *node;
+	int usbg_ret, cfg_ret;
+	int bmAttributes, bMaxPower;
+	short format;
+	int ret = USBG_ERROR_INVALID_TYPE;
+
+	node = config_setting_get_member(root, "bmAttributes");
+	if (node) {
+		if (!usbg_config_is_int(node))
+			goto out;
+
+		bmAttributes = config_setting_get_int(node);
+		usbg_ret = usbg_set_config_bm_attrs(c, bmAttributes);
+		if (usbg_ret != USBG_SUCCESS) {
+			ret = usbg_ret;
+			goto out;
+		}
+	}
+
+	node = config_setting_get_member(root, "bMaxPower");
+	if (node) {
+		if (!usbg_config_is_int(node))
+			goto out;
+
+		bMaxPower = config_setting_get_int(node);
+		usbg_ret = usbg_set_config_max_power(c, bMaxPower);
+		if (usbg_ret != USBG_SUCCESS) {
+			ret = usbg_ret;
+			goto out;
+		}
+	}
+
+	/* Empty attrs section is also considered to be valid */
+	ret = USBG_SUCCESS;
+out:
+	return ret;
+
+}
+
+static int usbg_import_config_run(usbg_gadget *g, config_setting_t *root,
+				  int id, usbg_config **c)
+{
+	config_setting_t *node;
+	const char *name;
+	usbg_config *newc;
+	int usbg_ret;
+	int ret = USBG_ERROR_MISSING_TAG;
+
+	/*
+	 * Label is mandatory,
+	 * if attrs aren't present defaults are used
+	 */
+	node = config_setting_get_member(root, USBG_NAME_TAG);
+	if (!node)
+		goto out;
+
+	name = config_setting_get_string(node);
+	if (!name) {
+		ret = USBG_ERROR_INVALID_TYPE;
+		goto out;
+	}
+
+	/* Required data collected, let's create our config */
+	usbg_ret = usbg_create_config(g, id, name, NULL, NULL, &newc);
+	if (usbg_ret != USBG_SUCCESS) {
+		ret = usbg_ret;
+		goto out;
+	}
+
+	/* Attrs are optional */
+	node = config_setting_get_member(root, USBG_ATTRS_TAG);
+	if (node) {
+		if (!config_setting_is_group(node)) {
+			ret = USBG_ERROR_INVALID_TYPE;
+			goto error2;
+		}
+
+		usbg_ret = usbg_import_config_attrs(node, newc);
+		if (usbg_ret != USBG_SUCCESS)
+			goto error;
+	}
+
+	/* Strings are also optional */
+	node = config_setting_get_member(root, USBG_STRINGS_TAG);
+	if (node) {
+		if (!config_setting_is_list(node)) {
+			ret = USBG_ERROR_INVALID_TYPE;
+			goto error2;
+		}
+
+		usbg_ret = usbg_import_config_strings(node, newc);
+		if (usbg_ret != USBG_SUCCESS)
+			goto error;
+	}
+
+	/* Functions too, because some config may not be
+	 * fully configured and not contain any function */
+	node = config_setting_get_member(root, USBG_FUNCTIONS_TAG);
+	if (node) {
+		if (!config_setting_is_list(node)) {
+			ret = USBG_ERROR_INVALID_TYPE;
+			goto error2;
+		}
+
+		usbg_ret = usbg_import_config_bindings(node, newc);
+		if (usbg_ret != USBG_SUCCESS)
+			goto error;
+	}
+
+	*c = newc;
+	ret = USBG_SUCCESS;
+out:
+	return ret;
+
+error:
+	ret = usbg_ret;
+error2:
+	/* We ignore returned value, if function fails
+	 * there is no way to handle it */
+	usbg_rm_config(newc, USBG_RM_RECURSE);
+	return ret;
+}
+
 int usbg_import_function(usbg_gadget *g, FILE *stream, const char *instance,
 			 usbg_function **f)
 {
@@ -3443,3 +3815,47 @@ out:
 
 }
 
+int usbg_import_config(usbg_gadget *g, FILE *stream, int id,  usbg_config **c)
+{
+	config_t *cfg;
+	config_setting_t *root;
+	usbg_config *newc;
+	int ret, cfg_ret;
+
+	if (!g || !stream || id < 0)
+		return USBG_ERROR_INVALID_PARAM;
+
+	cfg = malloc(sizeof(*cfg));
+	if (!cfg)
+		return USBG_ERROR_NO_MEM;
+
+	config_init(cfg);
+
+	cfg_ret = config_read(cfg, stream);
+	if (cfg_ret != CONFIG_TRUE) {
+		usbg_set_failed_import(&g->last_failed_import, cfg);
+		ret = USBG_ERROR_INVALID_FORMAT;
+		goto out;
+	}
+
+	/* Allways successful */
+	root = config_root_setting(cfg);
+
+	ret = usbg_import_config_run(g, root, id, &newc);
+	if (ret != USBG_SUCCESS) {
+		usbg_set_failed_import(&g->last_failed_import, cfg);
+		goto out;
+	}
+
+	if (c)
+		*c = newc;
+
+	config_destroy(cfg);
+	free(cfg);
+	/* Clean last error */
+	usbg_set_failed_import(&g->last_failed_import, NULL);
+out:
+	return ret;
+
+}
+
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux