*/
#include <hw/usb_client.h>
-#include <hw/systemd.h>
#include <hw/shared.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <usbg/usbg.h>
+#include <usbg/function/net.h>
#include <unistd.h>
-#include <unistd.h>
+#include <libsyscommon/dbus-systemd.h>
+
#define zalloc(amount) calloc(1, amount)
free(gadget->funcs);
}
+
+ free(gadget);
}
static int cfs_read_gadget_attrs_strs(usbg_gadget *gadget,
usb_gadget->attrs.idProduct = attrs.idProduct;
usb_gadget->attrs.bcdDevice = attrs.bcdDevice;
-
ret = usbg_get_gadget_strs(gadget, LANG_US_ENG, &strs);
if (ret)
goto out;
usb_gadget->strs[0].product = strdup(strs.product);
usb_gadget->strs[0].serial = strdup(strs.serial);
+ usbg_free_gadget_strs(&strs);
+
if (!usb_gadget->strs[0].manufacturer ||
!usb_gadget->strs[0].product ||
!usb_gadget->strs[0].serial) {
{
int i;
- for (i = 0; i < ARRAY_SIZE(_available_funcs); ++i)
+ for (i = 0; _available_funcs[i]; ++i)
if (cfs_match_func(_available_funcs[i], name, instance))
return i;
if (!usb)
return -EINVAL;
- cfs_client = container_of(usb, struct cfs_client,
- client);
+ cfs_client = container_of(usb, struct cfs_client, client);
usb_gadget = zalloc(sizeof(*usb_gadget));
if (!usb_gadget)
if (ret)
goto free_strs;
-
n_funcs = cfs_count_functions(cfs_client->gadget);
usb_funcs = calloc(n_funcs + 1, sizeof(*usb_funcs));
if (!usb_funcs)
bool res;
int ret;
- switch (func->function_group) {
- case USB_FUNCTION_GROUP_SIMPLE:
+ if (!func->is_functionfs) {
ret = usbg_lookup_function_type(func->name);
res = ret >= 0;
- break;
- case USB_FUNCTION_GROUP_WITH_SERVICE:
+ } else {
/* TODO: Check if socket is available */
res = true;
- break;
- default:
- res = false;
}
return res;
int ret;
/* TODO: Add some good error handling */
+ if (!socket_name)
+ return -EINVAL;
left = sizeof(buf);
pos = buf;
if (ret < 0)
return ret;
- ret = systemd_start_socket(socket_name);
+ ret = systemd_start_unit_wait_started(socket_name, ".socket", -1);
if (ret < 0)
goto umount_ffs;
return ret;
}
+static int cfs_set_rndis_mac_addr(usbg_gadget *gadget, usbg_function *func)
+{
+ int i, ret;
+ struct ether_addr ethaddr;
+ struct usbg_gadget_strs strs;
+ struct usbg_f_net *nf = usbg_to_net_function(func);
+
+ if (!nf)
+ return -EINVAL;
+
+ ret = usbg_get_gadget_strs(gadget, LANG_US_ENG, &strs);
+ if (ret != USBG_SUCCESS)
+ return ret;
+
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ ethaddr.ether_addr_octet[i] = 0;
+
+ for (i = 0; (i < 256) && strs.serial[i]; i++) {
+ ethaddr.ether_addr_octet[i % (ETHER_ADDR_LEN - 1) + 1] ^= strs.serial[i];
+ }
+ ethaddr.ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
+ ethaddr.ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
+
+ usbg_free_gadget_strs(&strs);
+
+ /* host_addr changes mac address */
+ ret = usbg_f_net_set_host_addr(nf, ðaddr);
+
+ return ret;
+}
+
static int cfs_set_gadget_config(struct cfs_client *cfs_client,
int config_id,
struct usb_configuration *usb_config)
int type;
usbg_function *func;
- switch (usb_func->function_group) {
- case USB_FUNCTION_GROUP_SIMPLE:
+ 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';
- break;
- case USB_FUNCTION_GROUP_WITH_SERVICE:
+ } 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;
- break;
- default:
- return -EINVAL;
}
-
func = usbg_get_function(cfs_client->gadget, type, instance);
if (!func) {
ret = usbg_create_function(cfs_client->gadget,
if (ret)
return ret;
- if (usb_func->function_group ==
- USB_FUNCTION_GROUP_WITH_SERVICE) {
- struct usb_function_with_service *fws;
+ /* 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 */
- fws = container_of(usb_func,
- struct usb_function_with_service,
- func);
+ if (usb_func->is_functionfs) {
ret = cfs_prep_ffs_service(usb_func->name,
- usb_func->instance,
- instance,
- fws->service);
+ usb_func->instance,
+ instance,
+ usb_func->service);
if (ret)
return ret;
}
-
}
ret = usbg_add_config_function(config, NULL, func);
int i;
int ret;
- if (!usb)
+ if (!usb || !gadget || !cfs_is_gadget_supported(usb, gadget))
return -EINVAL;
cfs_client = container_of(usb, struct cfs_client,
client);
- if (!usb || !gadget || !cfs_is_gadget_supported(usb, gadget))
- return -EINVAL;
-
ret = cfs_set_gadget_attrs(cfs_client, &gadget->attrs);
if (ret)
goto out;
goto out;
}
- /* Workaround for enabling extcon notification */
- ret = usbg_enable_gadget(cfs_client->gadget, cfs_client->udc);
- if (ret)
- goto out;
-
ret = cfs_cleanup_left_configs(cfs_client, i);
/* TODO
static int cfs_enable(struct usb_client *usb)
{
+ int i;
+ int ret;
+ struct usb_gadget *gadget;
+ struct usb_function *func;
struct cfs_client *cfs_client;
if (!usb)
return -EINVAL;
- cfs_client = container_of(usb, struct cfs_client,
- client);
+ cfs_client = container_of(usb, struct cfs_client, client);
+ ret = usbg_enable_gadget(cfs_client->gadget, cfs_client->udc);
+ if (ret)
+ return ret;
+
+ ret = cfs_get_current_gadget(usb, &gadget);
+ if (ret) {
+ usbg_disable_gadget(cfs_client->gadget);
+ return ret;
+ }
+
+ for (i = 0; gadget->funcs[i]; ++i) {
+ func = gadget->funcs[i];
+
+ if (func->handler)
+ func->handler(1);
+
+ /* functionfs service is automatically started by socket activation */
+ if (!func->is_functionfs && func->service)
+ (void)systemd_start_unit_wait_started(func->service, ".service", -1);
+ }
+
+ cfs_free_gadget(gadget);
- return usbg_enable_gadget(cfs_client->gadget, cfs_client->udc);
+ return 0;
}
static int cfs_disable(struct usb_client *usb)
{
+ int i;
+ int ret;
+ struct usb_gadget *gadget;
+ struct usb_function *func;
struct cfs_client *cfs_client;
if (!usb)
return -EINVAL;
- cfs_client = container_of(usb, struct cfs_client,
- client);
+ ret = cfs_get_current_gadget(usb, &gadget);
+ if (ret)
+ return ret;
+
+ for (i = 0; gadget->funcs[i]; ++i) {
+ func = gadget->funcs[i];
+
+ if (!func->is_functionfs && func->service)
+ (void)systemd_stop_unit_wait_stopped(func->service, ".service", -1);
- return usbg_disable_gadget(cfs_client->gadget);
+ if (func->handler)
+ func->handler(0);
+ }
+
+ cfs_client = container_of(usb, struct cfs_client, client);
+ ret = usbg_disable_gadget(cfs_client->gadget); /* ignore error checking */
+
+ /*
+ * Since functionfs service works with socket activation, you must stop it after disabling gadget.
+ * If usb data may come in after stopping functionfs service and before disabling gadget,
+ * functionfs service wakes up again by socket activation.
+ */
+ for (i = 0; gadget->funcs[i]; ++i) {
+ func = gadget->funcs[i];
+
+ if (func->is_functionfs && func->service)
+ (void)systemd_stop_unit_wait_stopped(func->service, ".service", -1);
+ }
+
+ cfs_free_gadget(gadget);
+
+ return ret;
}
EXPORT
int hw_cfs_gadget_close(struct hw_common *common)
{
struct cfs_client *cfs_client;
+ usbg_function *func;
+ int ret;
if (!common)
return -EINVAL;
cfs_client = container_of(common, struct cfs_client,
client.common);
+ usbg_for_each_function(func, cfs_client->gadget) {
+ char *name = (char *)usbg_get_function_type_str(
+ usbg_get_function_type(func));
+ char *instance = (char *)usbg_get_function_instance(func);
+ struct usb_function *usb_func;
+
+ ret = cfs_find_func(name, instance);
+ if (ret < 0)
+ continue;
+
+ usb_func = _available_funcs[ret];
+
+ if (usb_func->is_functionfs && usb_func->service) {
+ (void)systemd_stop_unit_wait_stopped(usb_func->service, ".socket", -1);
+ (void)systemd_stop_unit_wait_stopped(usb_func->service, ".service", -1);
+ }
+ }
+
/*
* For now we don't check for errors
* but we should somehow handle them