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