Whole gadget can be exported to file using usbg_export_gadget(). This commit adds complementary API function usbg_import_gadget() which allows to import whole gadget (including attrs, strings, configs, functions and bindings) from file to configfs. Signed-off-by: Krzysztof Opasiak <k.opasiak@xxxxxxxxxxx> --- include/usbg/usbg.h | 12 ++ src/usbg.c | 348 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 360 insertions(+) diff --git a/include/usbg/usbg.h b/include/usbg/usbg.h index 70b9910..23783eb 100644 --- a/include/usbg/usbg.h +++ b/include/usbg/usbg.h @@ -951,6 +951,18 @@ extern int usbg_import_function(usbg_gadget *g, FILE *stream, */ extern int usbg_import_config(usbg_gadget *g, FILE *stream, int id, usbg_config **c); +/** + * @brief Imports usb gadget from file + * @param s current state of library + * @param stream from which gadget should be imported + * @param name which shuld be used for new gadget + * @param g place for pointer to imported gadget + * if NULL this param will be ignored. + * @return 0 on success, usbg_error otherwise + */ +extern int usbg_import_gadget(usbg_state *s, FILE *stream, + const char *name, usbg_gadget **g); +/** * @} */ #endif /* __USBG_H__ */ diff --git a/src/usbg.c b/src/usbg.c index 3527c1b..32cefe4 100644 --- a/src/usbg.c +++ b/src/usbg.c @@ -3777,6 +3777,311 @@ error2: return ret; } +static int usbg_import_gadget_configs(config_setting_t *root, usbg_gadget *g) +{ + config_setting_t *node, *id_node; + int usbg_ret, cfg_ret; + int id; + usbg_config *c; + 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 (!node) { + ret = USBG_ERROR_OTHER_ERROR; + break; + } + + if (!config_setting_is_group(node)) { + ret = USBG_ERROR_INVALID_TYPE; + break; + } + + /* Look for id */ + id_node = config_setting_get_member(node, USBG_ID_TAG); + if (!id_node) { + ret = USBG_ERROR_MISSING_TAG; + break; + } + + if (!usbg_config_is_int(id_node)) { + ret = USBG_ERROR_INVALID_TYPE; + break; + } + + id = config_setting_get_int(id_node); + + ret = usbg_import_config_run(g, node, id, &c); + if (ret != USBG_SUCCESS) + break; + } + + return ret; +} + +static int usbg_import_gadget_functions(config_setting_t *root, usbg_gadget *g) +{ + config_setting_t *node, *inst_node; + int usbg_ret, cfg_ret; + const char *instance; + const char *label; + usbg_function *f; + 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 (!node) { + ret = USBG_ERROR_OTHER_ERROR; + break; + } + + if (!config_setting_is_group(node)) { + ret = USBG_ERROR_INVALID_TYPE; + break; + } + + /* Look for instance name */ + inst_node = config_setting_get_member(node, USBG_INSTANCE_TAG); + if (!inst_node) { + ret = USBG_ERROR_MISSING_TAG; + break; + } + + if (!usbg_config_is_string(inst_node)) { + ret = USBG_ERROR_INVALID_TYPE; + break; + } + + instance = config_setting_get_string(inst_node); + if (!instance) { + ret = USBG_ERROR_OTHER_ERROR; + break; + } + + ret = usbg_import_function_run(g, node, instance, &f); + if (ret != USBG_SUCCESS) + break; + + /* Set the label given by user */ + label = config_setting_name(node); + if (!label) { + ret = USBG_ERROR_OTHER_ERROR; + break; + } + + f->label = strdup(label); + if (!f->label) { + ret = USBG_ERROR_NO_MEM; + break; + } + } + + return ret; +} + +static int usbg_import_gadget_strs_lang(config_setting_t *root, usbg_gadget *g) +{ + config_setting_t *node; + int lang; + const char *str; + usbg_gadget_strs g_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); + + /* Auto truncate the string to max length */ +#define GET_OPTIONAL_GADGET_STR(NAME, FIELD) \ + do { \ + node = config_setting_get_member(root, #NAME); \ + if (node) { \ + if (!usbg_config_is_string(node)) \ + goto out; \ + str = config_setting_get_string(node); \ + strncpy(g_strs.FIELD, str, USBG_MAX_STR_LENGTH); \ + g_strs.FIELD[USBG_MAX_STR_LENGTH - 1] = '\0'; \ + } \ + } while (0) + + GET_OPTIONAL_GADGET_STR(manufacturer, str_mnf); + GET_OPTIONAL_GADGET_STR(product, str_prd); + GET_OPTIONAL_GADGET_STR(serialnumber, str_ser); + +#undef GET_OPTIONAL_GADGET_STR + + ret = usbg_set_gadget_strs(g, lang, &g_strs); + +out: + return ret; +} + +static int usbg_import_gadget_strings(config_setting_t *root, usbg_gadget *g) +{ + 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_gadget_strs_lang(node, g); + if (ret != USBG_SUCCESS) + break; + } + + return ret; +} + + +static int usbg_import_gadget_attrs(config_setting_t *root, usbg_gadget *g) +{ + config_setting_t *node; + int usbg_ret, cfg_ret; + int val; + int ret = USBG_ERROR_INVALID_TYPE; + +#define GET_OPTIONAL_GADGET_ATTR(NAME, FUNC_END, TYPE) \ + do { \ + node = config_setting_get_member(root, #NAME); \ + if (node) { \ + if (!usbg_config_is_int(node)) \ + goto out; \ + val = config_setting_get_int(node); \ + if (val < 0 || val > (1L << (sizeof(TYPE)*8) - 1)) { \ + ret = USBG_ERROR_INVALID_VALUE; \ + goto out; \ + } \ + usbg_ret = usbg_set_gadget_##FUNC_END(g, (TYPE)val); \ + if (usbg_ret != USBG_SUCCESS) { \ + ret = usbg_ret; \ + goto out; \ + } \ + } \ + } while (0) + + GET_OPTIONAL_GADGET_ATTR(bcdUSB, device_bcd_usb, uint16_t); + GET_OPTIONAL_GADGET_ATTR(bDeviceClass, device_class, uint8_t); + GET_OPTIONAL_GADGET_ATTR(bDeviceSubClass, device_subclass, uint8_t); + GET_OPTIONAL_GADGET_ATTR(bDeviceProtocol, device_protocol, uint8_t); + GET_OPTIONAL_GADGET_ATTR(bMaxPacketSize0, device_max_packet, uint8_t); + GET_OPTIONAL_GADGET_ATTR(idVendor, vendor_id, uint16_t); + GET_OPTIONAL_GADGET_ATTR(idProduct, product_id, uint16_t); + GET_OPTIONAL_GADGET_ATTR(bcdDevice, device_bcd_device, uint16_t); + +#undef GET_OPTIONAL_GADGET_ATTR + + /* Empty attrs section is also considered to be valid */ + ret = USBG_SUCCESS; +out: + return ret; + +} + +static int usbg_import_gadget_run(usbg_state *s, config_setting_t *root, + const char *name, usbg_gadget **g) +{ + config_setting_t *node; + usbg_gadget *newg; + int usbg_ret; + int ret = USBG_ERROR_MISSING_TAG; + + /* There is no mandatory data in gadget so let's start with + * creating a new gadget */ + usbg_ret = usbg_create_gadget(s, name, NULL, NULL, &newg); + 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_gadget_attrs(node, newg); + 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_gadget_strings(node, newg); + if (usbg_ret != USBG_SUCCESS) + goto error; + } + + /* Functions too, because some gadgets may not be fully + * configured and don't have any funciton or have all functions + * defined inline in configurations */ + node = config_setting_get_member(root, USBG_FUNCTIONS_TAG); + if (node) { + if (!config_setting_is_group(node)) { + ret = USBG_ERROR_INVALID_TYPE; + goto error2; + } + usbg_ret = usbg_import_gadget_functions(node, newg); + if (usbg_ret != USBG_SUCCESS) + goto error; + } + + /* Some gadget may not be fully configured + * so configs are also optional */ + node = config_setting_get_member(root, USBG_CONFIGS_TAG); + if (node) { + if (!config_setting_is_list(node)) { + ret = USBG_ERROR_INVALID_TYPE; + goto error2; + } + usbg_ret = usbg_import_gadget_configs(node, newg); + if (usbg_ret != USBG_SUCCESS) + goto error; + } + + *g = newg; + 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_gadget(newg, USBG_RM_RECURSE); + return ret; +} + int usbg_import_function(usbg_gadget *g, FILE *stream, const char *instance, usbg_function **f) { @@ -3863,6 +4168,49 @@ int usbg_import_config(usbg_gadget *g, FILE *stream, int id, usbg_config **c) usbg_set_failed_import(&g->last_failed_import, NULL); out: return ret; +} +int usbg_import_gadget(usbg_state *s, FILE *stream, const char *name, + usbg_gadget **g) +{ + config_t *cfg; + config_setting_t *root; + usbg_gadget *newg; + int ret, cfg_ret; + + if (!s || !stream || !name) + 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(&s->last_failed_import, cfg); + ret = USBG_ERROR_INVALID_FORMAT; + goto out; + } + + /* Allways successful */ + root = config_root_setting(cfg); + + ret = usbg_import_gadget_run(s, root, name, &newg); + if (ret != USBG_SUCCESS) { + usbg_set_failed_import(&s->last_failed_import, cfg); + goto out; + } + + if (g) + *g = newg; + + config_destroy(cfg); + free(cfg); + /* Clean last error */ + usbg_set_failed_import(&s->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