summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Agner <stefan.agner@toradex.com>2017-01-25 22:59:21 -0800
committerKrzysztof Opasiak <k.opasiak@samsung.com>2017-12-12 14:06:36 +0100
commit952f19c7ce13a454f699830c8138d1ad82805a0b (patch)
tree85d7bf259fa0a72104501ee4028eed10a4f63636
parentdb490f1ca55b003afd46ad80464ca7f0d7a5bdcc (diff)
downloadlibusbg-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.h15
-rw-r--r--include/usbg/usbg_internal.h1
-rw-r--r--src/usbg.c176
-rw-r--r--src/usbg_schemes_libconfig.c36
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
diff --git a/src/usbg.c b/src/usbg.c
index 090aea2..00fc55e 100644
--- a/src/usbg.c
+++ b/src/usbg.c
@@ -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;
}