#define NAME_INSTANCE_SEP '.'
#define MAX_INSTANCE_LEN 512
-#define USB_FUNCS_PATH "/dev/usb-funcs/"
+#define USB_FUNCS_PATH "/dev/usb-funcs"
#ifndef EXPORT
#define EXPORT __attribute__ ((visibility("default")))
return ret;
}
-static int cfs_prep_ffs_service(const char *name, const char *instance,
- const char *dev_name, const char *socket_name)
+
+static int cfs_prep_ffs_service(struct usb_function *usb_func, usbg_function *function)
{
- char buf[PATH_MAX];
- size_t left;
- char *pos;
int ret;
+ const char *name;
+ const char *service;
+ const char *instance;
+ const char *dev_name;
+ char buf[MAX_INSTANCE_LEN];
- /* TODO: Add some good error handling */
- if (!socket_name)
+ if (!usb_func || !function)
return -EINVAL;
- left = sizeof(buf);
- pos = buf;
- ret = snprintf(pos, left, "%s", USB_FUNCS_PATH);
- if (ret < 0 || ret >= left) {
+ if (usbg_get_function_type(function) != USBG_F_FFS)
+ return -EINVAL;
+
+ name = usb_func->name;
+ service = usb_func->service;
+ instance = usb_func->instance;
+ dev_name = usbg_get_function_instance(function);
+
+ /* "/dev/usb-funcs" + "/" + "sdb" + "/" + "default" + '0' */
+ if (strlen(USB_FUNCS_PATH) + strlen(name) + strlen(instance) + 3 > sizeof(buf))
return -ENAMETOOLONG;
- } else {
- left -= ret;
- pos += ret;
- }
- ret = cfs_ensure_dir(buf);
+
+ /* mkdir /dev/usb-funcs */
+ ret = cfs_ensure_dir(USB_FUNCS_PATH);
if (ret < 0)
return ret;
- ret = snprintf(pos, left, "/%s", name);
- if (ret < 0 || ret >= left) {
- return -ENAMETOOLONG;
- } else {
- left -= ret;
- pos += ret;
- }
+ /* mkdir /dev/usb-funcs/sdb */
+ snprintf(buf, sizeof(buf), "%s/%s", USB_FUNCS_PATH, name);
ret = cfs_ensure_dir(buf);
if (ret < 0)
- return ret;
+ goto out_rmdir;
- ret = snprintf(pos, left, "/%s", instance);
- if (ret < 0 || ret >= left) {
- return -ENAMETOOLONG;
- } else {
- left -= ret;
- pos += ret;
- }
+ /* mkdir /dev/usb-funcs/sdb/default */
+ snprintf(buf, sizeof(buf), "%s/%s/%s", USB_FUNCS_PATH, name, instance);
ret = cfs_ensure_dir(buf);
if (ret < 0)
- return ret;
+ goto out_rmdir;
+ /* mount -t functionfs sdb.default /dev/usb-funcs/sdb/default */
ret = mount(dev_name, buf, "functionfs", 0, NULL);
if (ret < 0)
- return ret;
+ goto out_rmdir;
- ret = systemd_start_unit_wait_started(socket_name, ".socket", -1);
+ /* start sdbd.socket */
+ ret = systemd_start_unit_wait_started(service, ".socket", -1);
if (ret < 0)
- goto umount_ffs;
+ goto out_unmount;
return 0;
-umount_ffs:
+
+out_unmount:
umount(buf);
+
+out_rmdir:
+ snprintf(buf, sizeof(buf), "%s/%s/%s", USB_FUNCS_PATH, name, instance);
+ rmdir(buf);
+
+ snprintf(buf, sizeof(buf), "%s/%s", USB_FUNCS_PATH, name);
+ rmdir(buf);
+
+ rmdir(USB_FUNCS_PATH);
+
return ret;
}
}
/* umount /dev/usb-funcs/[sdb|mtp]/default and remove it's directory */
- ret = snprintf(buf, sizeof(buf), "%s%s/%s", USB_FUNCS_PATH, usb_function->name, usb_function->instance);
+ ret = snprintf(buf, sizeof(buf), "%s/%s/%s", USB_FUNCS_PATH, usb_function->name, usb_function->instance);
if (ret < 0)
return ret;
return ret;
/* remove /dev/usb-funcs/[sdb|mtp] directory */
- ret = snprintf(buf, sizeof(buf), "%s%s", USB_FUNCS_PATH, usb_function->name);
+ ret = snprintf(buf, sizeof(buf), "%s/%s", USB_FUNCS_PATH, usb_function->name);
if (ret < 0)
return ret;
return ret;
}
-static int cfs_set_gadget_config(struct cfs_client *cfs_client,
- int config_id,
- struct usb_configuration *usb_config)
+static int cfs_set_gadget_config(struct cfs_client *cfs_client, int config_id, struct usb_configuration *usb_config)
{
+ int i;
+ int ret;
+ int function_type;
+ usbg_config *config;
+ usbg_function *function;
+ struct usb_function *usb_func;
+ char instance[MAX_INSTANCE_LEN];
struct usbg_config_attrs cattrs = {
.bmAttributes = usb_config->attrs.bmAttributs,
.bMaxPower = usb_config->attrs.MaxPower/2,
};
- usbg_config *config;
- int i;
- int ret;
if (!usb_config->funcs || !usb_config->funcs[0])
return -EINVAL;
- config = usbg_get_config(cfs_client->gadget, config_id, NULL);
- if (config) {
- ret = usbg_rm_config(config, USBG_RM_RECURSE);
- if (ret)
- return ret;
- }
-
- ret = usbg_create_config(cfs_client->gadget, config_id,
- CONFIGFS_CONFIG_LABEL, &cattrs, NULL, &config);
+ ret = usbg_create_config(cfs_client->gadget, config_id, CONFIGFS_CONFIG_LABEL, &cattrs, NULL, &config);
if (ret)
return ret;
for (i = 0; usb_config->strs && usb_config->strs[i].lang_code; ++i) {
- ret = usbg_set_config_string(config, usb_config->strs[i].lang_code,
- usb_config->strs[i].config_str);
+ ret = usbg_set_config_string(config, usb_config->strs[i].lang_code, usb_config->strs[i].config_str);
if (ret)
return ret;
}
- for (i = 0; usb_config->funcs && usb_config->funcs[i]; ++i) {
- struct usb_function *usb_func = usb_config->funcs[i];
- char instance[MAX_INSTANCE_LEN];
- int type;
- usbg_function *func;
-
- if (!usb_func->is_functionfs) {
- type = usbg_lookup_function_type(usb_func->name);
- if (strlen(usb_func->instance) >= MAX_INSTANCE_LEN)
- return -ENAMETOOLONG;
- strncpy(instance, usb_func->instance, MAX_INSTANCE_LEN);
- instance[MAX_INSTANCE_LEN - 1] = '\0';
- } else {
- type = USBG_F_FFS;
- ret = snprintf(instance, sizeof(instance), "%s%c%s",
- usb_func->name, NAME_INSTANCE_SEP,
- usb_func->instance);
- if (ret < 0 || ret >= sizeof(instance))
- return -ENAMETOOLONG;
- }
+ for (i = 0; usb_config->funcs[i]; ++i) {
+ usb_func = usb_config->funcs[i];
- func = usbg_get_function(cfs_client->gadget, type, instance);
- if (!func) {
- ret = usbg_create_function(cfs_client->gadget,
- type,
- instance,
- NULL, &func);
- if (ret)
- return ret;
+ /* name("sdb") + NAME_INSTANCE_SEP(".") + instance("default") + '\0' */
+ if (strlen(usb_func->name) + strlen(usb_func->instance) + 2 > sizeof(instance))
+ return -ENAMETOOLONG;
- /* Setting rndis mac address. This should be done at this point,
- * since the node host_addr changes to read only after the function
- * is added to config. */
- if (usbg_get_function_type(func) == USBG_F_RNDIS)
- (void)cfs_set_rndis_mac_addr(cfs_client->gadget, func); /* A random value is used if fails */
-
- if (usb_func->is_functionfs) {
- ret = cfs_prep_ffs_service(usb_func->name,
- usb_func->instance,
- instance,
- usb_func->service);
- if (ret)
- return ret;
- }
+ /* In functionfs, the instance is used in the format "[sdb|mtp].default" instead of "default" */
+ if (usb_func->is_functionfs) {
+ function_type = USBG_F_FFS;
+ snprintf(instance, sizeof(instance) - 1, "%s%c%s", usb_func->name, NAME_INSTANCE_SEP, usb_func->instance);
+ } else {
+ function_type = usbg_lookup_function_type(usb_func->name);
+ strncpy(instance, usb_func->instance, MAX_INSTANCE_LEN - 1);
}
- ret = usbg_add_config_function(config, NULL, func);
- if (ret)
- return ret;
- }
-
- return ret;
-}
-
-static int cfs_cleanup_left_configs(struct cfs_client *cfs_client,
- int last_config)
-{
- usbg_config *lconfig, *config;
- int ret;
-
- lconfig = usbg_get_config(cfs_client->gadget, last_config, NULL);
- for (config = usbg_get_next_config(lconfig);
- config;
- config = usbg_get_next_config(lconfig)) {
- ret = usbg_rm_config(config, USBG_RM_RECURSE);
+ ret = usbg_create_function(cfs_client->gadget, function_type, instance, NULL, &function);
if (ret)
return ret;
- }
-
- return 0;
-}
-
-static int cfs_function_is_binded(struct usbg_gadget *gadget, struct usbg_function *func)
-{
- usbg_binding *b;
- usbg_config *config;
-
- usbg_for_each_config(config, gadget)
- usbg_for_each_binding(b, config)
- if (usbg_get_binding_target(b) == func)
- return 1;
- return 0;
-}
-
-static int cfs_cleanup_unused_function(usbg_gadget *gadget)
-{
- int ret;
- usbg_function *function;
-
- if (!gadget)
- return -EINVAL;
-
-restart:
- usbg_for_each_function(function, gadget) {
- if(cfs_function_is_binded(gadget, function))
- continue;
+ /* Setting rndis mac address. This should be done at this point,
+ * since the node host_addr changes to read only after the function
+ * is added to config. */
+ if (usbg_get_function_type(function) == USBG_F_RNDIS)
+ (void)cfs_set_rndis_mac_addr(cfs_client->gadget, function); /* A random value is used if fails */
if (usbg_get_function_type(function) == USBG_F_FFS) {
- ret = cfs_cleanup_ffs_service(function);
+ ret = cfs_prep_ffs_service(usb_func, function);
if (ret)
return ret;
}
- ret = usbg_rm_function(function, USBG_RM_RECURSE);
+ ret = usbg_add_config_function(config, NULL, function);
if (ret)
return ret;
-
- /* You cannot delete a usbg_function directly in an iterator. */
- goto restart;
}
return 0;
}
-
static int cfs_reconfigure_gadget(struct usb_client *usb,
struct usb_gadget *gadget)
{
- struct cfs_client *cfs_client;
int i;
int ret;
+ usbg_config *config;
+ usbg_function *function;
+ struct cfs_client *cfs_client;
if (!usb || !gadget || !cfs_is_gadget_supported(usb, gadget))
return -EINVAL;
- cfs_client = container_of(usb, struct cfs_client,
- client);
+ cfs_client = container_of(usb, struct cfs_client, client);
ret = cfs_set_gadget_attrs(cfs_client, &gadget->attrs);
if (ret)
- goto out;
+ return ret;
for (i = 0; gadget->strs && gadget->strs[i].lang_code > 0; ++i) {
ret = cfs_set_gadget_strs(cfs_client, gadget->strs + i);
if (ret)
- goto out;
+ return ret;
}
- for (i = 0; gadget->configs && gadget->configs[i]; ++i) {
- ret = cfs_set_gadget_config(cfs_client, i + 1,
- gadget->configs[i]);
+ /* delete all configs */
+restart_rm_config:
+ usbg_for_each_config(config, cfs_client->gadget) {
+ ret = usbg_rm_config(config, USBG_RM_RECURSE);
if (ret)
- goto out;
+ return ret;
+
+ goto restart_rm_config; /* You cannot delete a config directly in an iterator. */
}
- ret = cfs_cleanup_left_configs(cfs_client, i);
- if(ret)
- goto out;
+ /* delete all functions */
+restart_rm_function:
+ usbg_for_each_function(function, cfs_client->gadget) {
+ if (usbg_get_function_type(function) == USBG_F_FFS) {
+ ret = cfs_cleanup_ffs_service(function);
+ if (ret)
+ return ret;
+ }
+
+ ret = usbg_rm_function(function, USBG_RM_RECURSE);
+ if (ret)
+ return ret;
- /* Cleanup things which are left after previous gadget */
- ret = cfs_cleanup_unused_function(cfs_client->gadget);
+ goto restart_rm_function; /* You cannot delete a function directly in an iterator. */
+ }
-out:
- return ret;
+ for (i = 0; gadget->configs && gadget->configs[i]; ++i) {
+ ret = cfs_set_gadget_config(cfs_client, i + 1, gadget->configs[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
static void cfs_start_stop_service_and_handler(usbg_gadget *gadget, enum cfs_function_service_operation operation)