diff options
author | Stefan Agner <stefan.agner@toradex.com> | 2017-01-25 22:59:21 -0800 |
---|---|---|
committer | Krzysztof Opasiak <k.opasiak@samsung.com> | 2017-12-12 14:06:36 +0100 |
commit | 952f19c7ce13a454f699830c8138d1ad82805a0b (patch) | |
tree | 85d7bf259fa0a72104501ee4028eed10a4f63636 | |
parent | db490f1ca55b003afd46ad80464ca7f0d7a5bdcc (diff) | |
download | libusbg-952f19c7ce13a454f699830c8138d1ad82805a0b.tar.gz libusbg-952f19c7ce13a454f699830c8138d1ad82805a0b.tar.bz2 libusbg-952f19c7ce13a454f699830c8138d1ad82805a0b.zip |
libusbgx: Add support for OS Descriptor configuration bindings
OS Descriptors require an association to a single configuration. This
allows to use a specific configuration for hosts supporting OS
Descriptors.
Signed-off-by: Stefan Agner <stefan.agner@toradex.com>
[Allow to also remove os desc binding]
Signed-off-by: Krzysztof Opasiak <k.opasiak@samsung.com>
-rw-r--r-- | include/usbg/usbg.h | 15 | ||||
-rw-r--r-- | include/usbg/usbg_internal.h | 1 | ||||
-rw-r--r-- | src/usbg.c | 176 | ||||
-rw-r--r-- | src/usbg_schemes_libconfig.c | 36 |
4 files changed, 228 insertions, 0 deletions
diff --git a/include/usbg/usbg.h b/include/usbg/usbg.h index 4b67f4e..2dc06ad 100644 --- a/include/usbg/usbg.h +++ b/include/usbg/usbg.h @@ -1004,6 +1004,21 @@ extern const char *usbg_get_binding_name(usbg_binding *b); */ extern int usbg_get_binding_name_s(usbg_binding *b, char *buf, int len); +/** + * @brief Get configuration selected for OS Descriptors + * @param g Pointer to gadget + * @return usbg_config or NULL + */ +usbg_config *usbg_get_os_desc_binding(usbg_gadget *g); + +/** + * @brief Set configuration for OS Descriptors + * @param g Pointer to gadget + * @param c Pointer to config + * @return 0 on success, usbg_error on failure. + */ +extern int usbg_set_os_desc_config(usbg_gadget *g, usbg_config *c); + /* USB gadget setup and teardown */ /** diff --git a/include/usbg/usbg_internal.h b/include/usbg/usbg_internal.h index cde3ad3..441df63 100644 --- a/include/usbg/usbg_internal.h +++ b/include/usbg/usbg_internal.h @@ -110,6 +110,7 @@ struct usbg_gadget usbg_state *parent; config_t *last_failed_import; usbg_udc *udc; + usbg_config *os_desc_binding; }; struct usbg_config @@ -361,6 +361,7 @@ static usbg_gadget *usbg_allocate_gadget(const char *path, const char *name, g->path = strdup(path); g->parent = parent; g->udc = NULL; + g->os_desc_binding = NULL; if (!(g->name) || !(g->path)) goto cleanup; @@ -645,6 +646,83 @@ out: return ret; } +static int usbg_parse_gadget_os_desc_binding(usbg_gadget *g) +{ + int i, n, nmb, id; + int ret = USBG_SUCCESS; + struct dirent **dent; + char bpath[USBG_MAX_PATH_LENGTH]; + char target[USBG_MAX_PATH_LENGTH]; + char *target_name; + usbg_config *c; + char *label = NULL; + int end; + + end = snprintf(bpath, sizeof(bpath), "%s/%s/%s", g->path, g->name, + OS_DESC_DIR); + if (end >= sizeof(bpath)) { + ret = USBG_ERROR_PATH_TOO_LONG; + goto out; + } + + n = scandir(bpath, &dent, bindings_select, alphasort); + if (n < 0) { + ret = usbg_translate_error(errno); + goto out; + } + + /* Not having any binding is ok */ + if (n < 1) { + ret = USBG_SUCCESS; + free(dent); + goto out; + } + + /* + * Only one configuration can be bound to os_descx, n should + * equal 1. + */ + nmb = snprintf(&(bpath[end]), sizeof(bpath) - end, + "/%s", dent[0]->d_name); + + for (i = 0; i < n; i++) + free(dent[i]); + free(dent); + + if (nmb >= sizeof(bpath) - end) { + ret = USBG_ERROR_PATH_TOO_LONG; + goto out; + } + + nmb = readlink(bpath, target, sizeof(target) - 1 ); + if (nmb < 0) { + ret = usbg_translate_error(errno); + goto out; + } + + /* readlink() don't add this, so we have to do it manually */ + target[nmb] = '\0'; + /* Target contains a full path but we only need function dir name */ + target_name = strrchr(target, '/') + 1; + id = usbg_split_config_label_id(target_name, &label); + if (id <= 0) { + ret = id; + goto out; + } + + c = usbg_get_config(g, id, label); + if (!c) { + ret = USBG_ERROR_NO_MEM; + goto out; + } + + g->os_desc_binding = c; + +out: + return ret; +} + + static int usbg_parse_config(const char *path, const char *name, usbg_gadget *g) { @@ -871,6 +949,10 @@ static inline int usbg_parse_gadget(usbg_gadget *g) goto out; ret = usbg_parse_configs(g->path, g); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_parse_gadget_os_desc_binding(g); out: return ret; } @@ -1150,6 +1232,11 @@ out: return ret; } +usbg_config *usbg_get_os_desc_binding(usbg_gadget *g) +{ + return g->os_desc_binding; +} + int usbg_rm_config(usbg_config *c, int opts) { int ret = USBG_ERROR_INVALID_PARAM; @@ -2353,6 +2440,95 @@ int usbg_get_binding_name_s(usbg_binding *b, char *buf, int len) return snprintf(buf, len, "%s", b->name); } +static int usbg_create_os_desc_link(usbg_gadget *g, usbg_config *c) +{ + char bpath[USBG_MAX_PATH_LENGTH]; + char cpath[USBG_MAX_PATH_LENGTH]; + int nmb; + int ret; + + nmb = snprintf(cpath, sizeof(cpath), "%s/%s", c->path, c->name); + if (nmb >= sizeof(cpath)) { + ret = USBG_ERROR_PATH_TOO_LONG; + goto out; + } + + nmb = snprintf(bpath, sizeof(bpath), "%s/%s/%s/%s", g->path, g->name, + OS_DESC_DIR, c->name); + if (nmb >= sizeof(bpath)) { + ret = USBG_ERROR_PATH_TOO_LONG; + goto out; + } + + ret = symlink(cpath, bpath); + if (ret != 0) { + ret = usbg_translate_error(errno); + goto out; + } + + g->os_desc_binding = c; + + return USBG_SUCCESS; +out: + return ret; +} + +static int usbg_rm_os_desc_link(usbg_gadget *g) +{ + char bpath[USBG_MAX_PATH_LENGTH]; + struct dirent **dent; + int end, n, i; + int ret; + + end = snprintf(bpath, sizeof(bpath), "%s/%s/%s", g->path, g->name, + OS_DESC_DIR); + if (end >= sizeof(bpath)) { + ret = USBG_ERROR_PATH_TOO_LONG; + goto out; + } + + n = scandir(bpath, &dent, bindings_select, alphasort); + if (n < 0) { + ret = usbg_translate_error(errno); + goto out; + } + + if (n < 1) { + ret = USBG_ERROR_NOT_FOUND; + goto free_dent; + } + + ret = ubsg_rm_file(bpath, dent[0]->d_name); + + for (i = 0; i < n; i++) + free(dent[i]); +free_dent: + free(dent); +out: + return ret; +} + +int usbg_set_os_desc_config(usbg_gadget *g, usbg_config *c) +{ + int ret; + + if (!g) + return USBG_ERROR_INVALID_PARAM; + + if (c) { + if (g->os_desc_binding) { + ERROR("os desc binding exist\n"); + return USBG_ERROR_EXIST; + } + + ret = usbg_create_os_desc_link(g, c); + } else { + ret = usbg_rm_os_desc_link(g); + } + + return ret; +} + int usbg_enable_gadget(usbg_gadget *g, usbg_udc *udc) { int ret = USBG_ERROR_INVALID_PARAM; diff --git a/src/usbg_schemes_libconfig.c b/src/usbg_schemes_libconfig.c index c24b449..22e405f 100644 --- a/src/usbg_schemes_libconfig.c +++ b/src/usbg_schemes_libconfig.c @@ -29,6 +29,7 @@ #define USBG_ID_TAG "id" #define USBG_FUNCTION_TAG "function" #define USBG_INTERFACE_TAG "interface" +#define USBG_CONFIG_ID_TAG "config_id" #define USBG_TAB_WIDTH 4 static inline int generate_function_label(usbg_function *f, char *buf, int size) @@ -593,6 +594,19 @@ static int usbg_export_gadget_os_descs(usbg_gadget *g, config_setting_t *root) int usbg_ret, cfg_ret; int ret = USBG_ERROR_NO_MEM; + if (g->os_desc_binding) { + node = config_setting_add(root, USBG_CONFIG_ID_TAG, + CONFIG_TYPE_INT); + if (!node) + goto out; + + cfg_ret = config_setting_set_int(node, g->os_desc_binding->id); + if (cfg_ret != CONFIG_TRUE) { + ret = USBG_ERROR_OTHER_ERROR; + goto out; + } + } + usbg_ret = usbg_get_gadget_os_descs(g, &g_os_descs); if (usbg_ret) { ret = usbg_ret; @@ -1578,6 +1592,28 @@ static int usbg_import_gadget_os_descs(config_setting_t *root, usbg_gadget *g) ret = usbg_set_gadget_os_descs(g, &g_os_descs); + /* + * Configs are optional, because some config may not be + * fully configured and not contain any config yet + */ + node = config_setting_get_member(root, USBG_CONFIG_ID_TAG); + if (node) { + usbg_config *target; + if (!usbg_config_is_int(node)) + goto out; + + val = config_setting_get_int(node); + target = usbg_get_config(g, val, NULL); + if (!target) { + ret = USBG_ERROR_NOT_FOUND; + goto out; + } + + usbg_ret = usbg_set_os_desc_config(g, target); + if (usbg_ret != USBG_SUCCESS) + goto out; + } + out: return ret; } |