* limitations under the License.
*/
-#include <hw/usb_client.h>
-#include <hw/systemd.h>
-#include <hw/shared.h>
-#include <limits.h>
-#include <stdio.h>
+#include <errno.h>
#include <string.h>
-#include <unistd.h>
+#include <stdbool.h>
-#define zalloc(amount) calloc(1, amount)
+#include <libsyscommon/dbus-systemd.h>
+
+#include <hw/shared.h>
+#include <hw/usb_gadget.h>
+#include <hw/usb_client.h>
#define MAX_GADGET_STR_LEN 256
-#define MAX_FUNCS 32
#define LEGACY_ROOTPATH "/sys/class/usb_mode/usb0"
/* Strings */
#define LEGACY_IMANUFACTURER_PATH LEGACY_ROOTPATH"/iManufacturer"
#define LEGACY_IPRODUCT_PATH LEGACY_ROOTPATH"/iProduct"
-#define LEGACY_ISERIAL_PATH LEGACY_ROOTPATH"/iSerial"
/* Functions in each config */
#define LEGACY_CONFIG_1_PATH LEGACY_ROOTPATH"/funcs_fconf"
#define LEGACY_CONFIG_2_PATH LEGACY_ROOTPATH"/funcs_sconf"
+
/* should be single char */
#define LEGACY_FUNC_SEP ","
#define LEGACY_ENABLE "1"
#define LEGACY_DISABLE "0"
-#define LEGACY_BMATTRIBUTES ((1 << 7) | (1 << 6))
-#define LEGACY_MAX_POWER 500
-
-/* +5 to be always big enough */
-#define INT_BUF_SIZE (sizeof(int)*8 + 5)
-
#ifndef EXPORT
#define EXPORT __attribute__ ((visibility("default")))
#endif
-static void legacy_free_gadget(struct usb_gadget *gadget);
-
-static int get_int_from_file(char *path, int *_val, int base)
-{
- char buf[INT_BUF_SIZE];
- char *endptr;
- long int val;
- int ret;
-
- ret = sys_get_str(path, buf, sizeof(buf));
- if (ret)
- return ret;
-
- val = strtol(buf, &endptr, base);
- if (val == LONG_MIN || val == LONG_MAX ||
- buf[0] == '\0' || (*endptr != '\0' && *endptr != '\n')
- || val >= INT_MAX)
- return -EINVAL;
-
- *_val = (int)val;
- return 0;
-}
-
-static int legacy_read_gadget_attrs_strs(struct usb_gadget *gadget)
+static bool legacy_is_function_supported(struct usb_function *func)
{
- int val;
- int ret;
- /* We assume that values received from kernel will be valid */
-#define GET_VALUE_FROM_SYSFS(path, field, type, base) \
- do { \
- ret = get_int_from_file(path, &val, base); \
- if (ret) \
- return ret; \
- \
- gadget->attrs.field = (type)val; \
- } while (0)
-
- GET_VALUE_FROM_SYSFS(LEGACY_CLASS_PATH, bDeviceClass, uint8_t, 0);
- GET_VALUE_FROM_SYSFS(LEGACY_SUBCLASS_PATH, bDeviceSubClass, uint8_t, 0);
- GET_VALUE_FROM_SYSFS(LEGACY_PROTOCOL_PATH, bDeviceProtocol, uint8_t, 0);
- GET_VALUE_FROM_SYSFS(LEGACY_ID_VENDOR_PATH, idVendor, uint16_t, 16);
- GET_VALUE_FROM_SYSFS(LEGACY_ID_PRODUCT_PATH, idProduct, uint16_t, 16);
- GET_VALUE_FROM_SYSFS(LEGACY_BCD_DEVICE_PATH, bcdDevice, uint16_t, 0);
-#undef GET_VALUE_FROM_SYSFS
-
-#define GET_STRING_FROM_SYSFS(path, field) \
- do { \
- char buf[MAX_GADGET_STR_LEN]; \
- \
- ret = sys_get_str(path, buf, sizeof(buf)); \
- if (ret) \
- goto err_##field; \
- \
- gadget->strs[0].field = strdup(buf); \
- if (!gadget->strs[0].field) { \
- ret = -ENOMEM; \
- goto err_##field; \
- } \
- } while (0)
-
- GET_STRING_FROM_SYSFS(LEGACY_IMANUFACTURER_PATH, manufacturer);
- GET_STRING_FROM_SYSFS(LEGACY_IPRODUCT_PATH, product);
- GET_STRING_FROM_SYSFS(LEGACY_ISERIAL_PATH, serial);
-#undef GET_STRING_FROM_SYSFS
-
- return 0;
+ char buf[PATH_MAX];
-err_serial:
- free(gadget->strs[0].product);
-err_product:
- free(gadget->strs[0].manufacturer);
-err_manufacturer:
- return ret;
-}
-
-static int legacy_find_func(const char *name)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(_available_funcs); ++i)
- if (!strcmp(name, _available_funcs[i]->name))
- return i;
-
- return -1;
-}
-
-static struct usb_function *legacy_find_func_in_gadget(
- struct usb_gadget *gadget, const char *name)
-{
- int i;
-
- for (i = 0; gadget->funcs[i]; ++i)
- if (!strcmp(name, gadget->funcs[i]->name))
- return gadget->funcs[i];
- return NULL;
-}
-
-static int legacy_alloc_config(int n_funcs, struct usb_configuration **_config)
-{
- struct usb_configuration *config;
-
- config = zalloc(sizeof(*config));
- if (!config)
- goto out;
-
- config->strs = calloc(1, sizeof(*config->strs));
- if (!config->strs)
- goto free_config;
-
- config->funcs = calloc(n_funcs + 1, sizeof(*config->funcs));
- if (!config->funcs)
- goto free_strs;
-
- /*
- * We cannot read correct values
- * so assume that they are always default
- */
- config->attrs.bmAttributs = LEGACY_BMATTRIBUTES;
- config->attrs.MaxPower = LEGACY_MAX_POWER;
-
- *_config = config;
-
- return 0;
-free_strs:
- free(config->strs);
-free_config:
- free(config);
-out:
- return -ENOMEM;
-}
-
-static int legacy_alloc_new_func(struct usb_gadget *gadget, const char *fname,
- struct usb_function **_func)
-{
- struct usb_function *func;
- int ret;
-
- ret = legacy_find_func(fname);
- if (ret < 0)
- return -ENOTSUP;
-
- ret = _available_funcs[ret]->clone(_available_funcs[ret], &func);
- if (ret)
- return ret;
+ snprintf (buf, sizeof(buf), "%s/f_%s", LEGACY_ROOTPATH, func->name);
+ if (access(buf, F_OK))
+ return false;
- *_func = func;
- return 0;
+ return true;
}
-static int legacy_read_config(struct usb_gadget *gadget,
- char *cpath,
- struct usb_configuration **_config)
+static bool legacy_is_gadget_supported(struct usb_gadget *gadget)
{
+ int i, j;
struct usb_configuration *config;
- char buf[MAX_GADGET_STR_LEN];
- char *begin = buf;
- char *fname;
- char *sep = LEGACY_FUNC_SEP;
- int i, f_cnt;
- int f_idx;
- int g_f_idx;
- int ret;
-
- ret = sys_get_str(cpath, buf, sizeof(buf));
- if (ret)
- return ret;
-
- /* Empty */
- if (buf[0] == '\0' || buf[0] == '\n')
- return 0;
-
- /* count number of functions in this config */
- f_cnt = 1;
- for (i = 0; buf[i] != '\0'; ++i) {
- if (buf[i] == sep[0])
- ++f_cnt;
- if (buf[i] == '\n') /* buf ends with it */
- buf[i] = 0;
- }
-
- ret = legacy_alloc_config(f_cnt, &config);
- if (ret)
- return ret;
-
- for (g_f_idx = 0; gadget->funcs[g_f_idx]; ++g_f_idx);
-
- f_idx = 0;
- for (fname = strsep(&begin, sep); fname; fname = strsep(&begin, sep)) {
- struct usb_function *func;
-
- func = legacy_find_func_in_gadget(gadget, fname);
- if (!func) {
- /* new function not added yet to gadget */
- ret = legacy_alloc_new_func(gadget, fname, &func);
- if (ret)
- goto free_config;
-
- gadget->funcs[g_f_idx++] = func;
- }
-
- config->funcs[f_idx++] = func;
- }
-
- *_config = config;
- return 0;
-free_config:
- free(config->strs);
- free(config->funcs);
- free(config);
- return ret;
-}
-
-static int legacy_get_current_gadget(struct usb_client *usb,
- struct usb_gadget **_gadget)
-{
- struct usb_gadget *gadget;
- struct usb_gadget_strings *strs;
- struct usb_configuration **configs;
- struct usb_function **funcs;
- int i;
- int ret = -ENOMEM;
-
- gadget = zalloc(sizeof(*gadget));
- if (!gadget)
- goto out;
-
- strs = calloc(2, sizeof(*strs));
- if (!strs)
- goto free_gadget;
-
- strs[0].lang_code = 0x409;
-
- gadget->strs = strs;
-
- ret = legacy_read_gadget_attrs_strs(gadget);
- if (ret)
- goto free_strs;
-
- /* There will be no more functions than bits in int */
- funcs = calloc(MAX_FUNCS, sizeof(*funcs));
- if (!funcs)
- goto free_strs_with_content;
-
- gadget->funcs = funcs;
-
- /* slp-gadget use max 2 confiuration and NULL termination */
- configs = calloc(3, sizeof(*configs));
- if (!configs)
- goto free_funcs;
-
- gadget->configs = configs;
-
- ret = legacy_read_config(gadget, LEGACY_CONFIG_1_PATH, configs + 0);
- if (ret)
- goto free_configs;
-
- ret = legacy_read_config(gadget, LEGACY_CONFIG_2_PATH, configs + 1);
- if (ret)
- goto free_config_1;
- *_gadget = gadget;
- return 0;
-
-free_config_1:
- free(configs[0]->funcs);
- free(configs[0]->strs);
- free(configs[0]);
-free_configs:
- free(configs);
- for (i = 0; gadget->funcs[i]; ++i)
- gadget->funcs[i]->free_func(gadget->funcs[i]);
-free_funcs:
- free(funcs);
-free_strs_with_content:
- free(gadget->strs[0].manufacturer);
- free(gadget->strs[0].product);
- free(gadget->strs[0].serial);
-free_strs:
- free(gadget->strs);
-free_gadget:
- free(gadget);
-out:
- return ret;
-}
-
-static bool legacy_is_function_supported(struct usb_client *usb,
- struct usb_function *func)
-{
- int ret;
-
- /*
- * TODO
- * Instead of only checking whether we know this function
- * we should also parse sysfs to check if it is build into
- * slp-gadget.
- */
- ret = legacy_find_func(func->name);
+ if (!gadget || !gadget->configs || !gadget->configs[0] || !gadget->configs[0]->funcs[0])
+ return false;
- return ret >= 0;
-}
+ if (!gadget->attrs.idVendor || !gadget->attrs.idProduct || !gadget->attrs.bcdDevice)
+ return false;
-static bool legacy_is_gadget_supported(struct usb_client *usb,
- struct usb_gadget *gadget)
-{
- int i, j;
+ /* only strings in US_en are allowed */
+ if (gadget->strs.lang_code != DEFAULT_LANG)
+ return false;
- if (!gadget || !gadget->configs || !gadget->funcs)
+ if (!gadget->strs.manufacturer || !gadget->strs.product)
return false;
- /*
- * TODO
- * Here is a good place to ensure that serial is immutable
- */
- if (gadget->strs) {
- /* only strings in US_en are allowed */
- if (gadget->strs[0].lang_code != 0x409 ||
- gadget->strs[1].lang_code)
- return false;
- }
-
for (j = 0; gadget->configs[j]; ++j) {
- struct usb_configuration *config = gadget->configs[j];
-
- if (config->strs && config->strs[0].lang_code)
- return false;
+ config = gadget->configs[j];
if (!config->funcs)
return false;
for (i = 0; config->funcs[i]; ++i)
- if (!legacy_is_function_supported(usb, config->funcs[i]))
+ if (!legacy_is_function_supported(config->funcs[i]))
return false;
}
- if (j == 0 || j > 2)
- return false;
-
return true;
}
/* TODO. Maybe move this to sys ? */
static int legacy_set_int_hex(char *path, int val)
{
- char buf[MAX_GADGET_STR_LEN];
int r;
+ char buf[MAX_GADGET_STR_LEN];
if (!path)
return -EINVAL;
return ret;
ret = legacy_set_int_hex(LEGACY_BCD_DEVICE_PATH, attrs->bcdDevice);
+ if (ret)
+ return ret;
- return ret;
+ return 0;
}
static int legacy_set_gadget_strs(struct usb_gadget_strings *strs)
{
- int ret = 0;
+ int ret;
- /*
- * TODO
- * Here is a good place to ensure that serial is immutable
- */
+ if (!strs->manufacturer || !strs->product)
+ return -EINVAL;
- if (strs->manufacturer) {
- ret = sys_set_str(LEGACY_IMANUFACTURER_PATH,
- strs->manufacturer);
- if (ret)
- return ret;
- }
+ ret = sys_set_str(LEGACY_IMANUFACTURER_PATH, strs->manufacturer);
+ if (ret)
+ return ret;
- if (strs->product) {
- ret = sys_set_str(LEGACY_IPRODUCT_PATH,
- strs->product);
- if (ret)
- return ret;
- }
+ ret = sys_set_str(LEGACY_IPRODUCT_PATH, strs->product);
+ if (ret)
+ return ret;
+
+ /* The serial is written by the slp gadget kernel driver */
- return ret;
+ return 0;
}
static int legacy_set_gadget_config(char *cpath,
return sys_set_str(cpath, buf);
}
-static int legacy_reconfigure_gadget(struct usb_client *usb,
- struct usb_gadget *gadget)
+static int legacy_reconfigure_gadget(struct usb_client *usb, struct usb_gadget *gadget)
{
int ret;
- if (!usb || !gadget || !legacy_is_gadget_supported(usb, gadget))
+ if (!usb || !gadget)
return -EINVAL;
+ /* Verify the gadget and check if function is supported */
+ if (!legacy_is_gadget_supported(gadget))
+ return -ENOTSUP;
+
ret = legacy_set_gadget_attrs(&gadget->attrs);
if (ret)
return ret;
- if (gadget->strs) {
- ret = legacy_set_gadget_strs(gadget->strs + 0);
- if (ret)
- return ret;
- }
+ ret = legacy_set_gadget_strs(&gadget->strs);
+ if (ret)
+ return ret;
ret = legacy_set_gadget_config(LEGACY_CONFIG_1_PATH, gadget->configs[0]);
if (ret)
return ret;
ret = legacy_set_gadget_config(LEGACY_CONFIG_2_PATH, gadget->configs[1]);
+ if (ret)
+ return ret;
- return ret;
+ return 0;
}
-static int legacy_enable(struct usb_client *usb)
+static void legacy_start_stop_service_and_handler(bool start)
{
- int ret;
int i;
- struct usb_gadget *gadget;
- struct usb_function_with_service *fws;
+ int ret;
+ int n_func = 0;
+ char *ptr;
+ char *begin;
+ char *fname;
+ char *sep = LEGACY_FUNC_SEP;
+ char buf[MAX_GADGET_STR_LEN];
+ struct usb_function *func;
+ struct usb_function *funcs[USB_FUNCTION_IDX_MAX];
+
+ /* SLP gadget uses two USB configuration.
+ * (/sys/class/usb_mode/usb0/funcs_fconf and /sys/class/usb_mode/usb0/funcs_sconf)
+ *
+ * One usb function can be included in two configurations simultaneously.
+ * In this situation, a handler associated with function can be called twice for a usb function.
+ * To prevent duplicate calls,
+ * Collect all functions and remove duplicates and process them.
+ */
- ret = sys_set_str(LEGACY_ENABLE_PATH,
- LEGACY_ENABLE);
- if (ret < 0)
- return ret;
+ /* First configuration */
+ ret = sys_get_str(LEGACY_CONFIG_1_PATH, buf, sizeof(buf));
+ if (ret)
+ goto second_configuration;
- ret = legacy_get_current_gadget(usb, &gadget);
- if (ret < 0)
- goto disable_gadget;
+ /* Caution: buf ends with '\n' */
+ ptr = strchr(buf, '\n');
+ if (ptr)
+ *ptr = 0;
- for (i = 0; gadget->funcs[i]; ++i) {
- if (gadget->funcs[i]->function_group !=
- USB_FUNCTION_GROUP_WITH_SERVICE)
+ begin = buf;
+ for (fname = strsep(&begin, sep); fname; fname = strsep(&begin, sep)) {
+ func = find_usb_function_by_name(fname);
+ if (!func)
continue;
- fws = container_of(gadget->funcs[i],
- struct usb_function_with_service, func);
- ret = systemd_start_service(fws->service);
- if (ret < 0)
- goto stop_services;
- }
+ for (i = 0; i < n_func; i++)
+ if (funcs[i] == func)
+ continue;
- legacy_free_gadget(gadget);
- return 0;
-stop_services:
- while (--i >= 0) {
- if (gadget->funcs[i]->function_group !=
- USB_FUNCTION_GROUP_WITH_SERVICE)
- continue;
+ if(n_func >= USB_FUNCTION_IDX_MAX) /* What happen */
+ break;
- fws = container_of(gadget->funcs[i],
- struct usb_function_with_service, func);
- systemd_stop_service(fws->service);
+ funcs[n_func] = func;
+ n_func++;
}
- legacy_free_gadget(gadget);
-disable_gadget:
- sys_set_str(LEGACY_ENABLE_PATH, LEGACY_DISABLE);
- return ret;
-}
-
-static int legacy_disable(struct usb_client *usb)
-{
- int ret;
- int i;
- struct usb_gadget *gadget;
- struct usb_function_with_service *fws;
+ /* Second configuration */
+second_configuration:
+ ret = sys_get_str(LEGACY_CONFIG_2_PATH, buf, sizeof(buf));
+ if (ret)
+ return;
- ret = legacy_get_current_gadget(usb, &gadget);
- if (ret < 0)
- return ret;
+ /* Caution: buf ends with '\n' */
+ ptr = strchr(buf, '\n');
+ if (ptr)
+ *ptr = 0;
- for (i = 0; gadget->funcs[i]; ++i) {
- if (gadget->funcs[i]->function_group != USB_FUNCTION_GROUP_WITH_SERVICE)
+ begin = buf;
+ for (fname = strsep(&begin, sep); fname; fname = strsep(&begin, sep)) {
+ func = find_usb_function_by_name(fname);
+ if (!func)
continue;
- fws = container_of(gadget->funcs[i], struct usb_function_with_service, func);
- ret = systemd_stop_service(fws->service);
- if (ret < 0)
- goto free_gadget;
- }
-
- ret = sys_set_str(LEGACY_ENABLE_PATH, LEGACY_DISABLE);
+ for (i = 0; i < n_func; i++)
+ if (funcs[i] == func)
+ continue;
-free_gadget:
- legacy_free_gadget(gadget);
- return ret;
-}
+ if(n_func >= USB_FUNCTION_IDX_MAX) /* What happen */
+ break;
-static void legacy_free_config(struct usb_configuration *config)
-{
- int i;
+ funcs[n_func] = func;
+ n_func++;
+ }
- if (!config)
- return;
+ for (i = 0; i < n_func; i++) {
+ if (start) {
+ if (funcs[i]->handler)
+ funcs[i]->handler(1);
- if (config->strs) {
- for (i = 0; config->strs[i].lang_code; ++i)
- free(config->strs[i].config_str);
+ if (funcs[i]->service)
+ (void)systemd_start_unit_wait_started(funcs[i]->service, ".service", -1);
+ } else {
+ if (funcs[i]->service)
+ (void)systemd_stop_unit_wait_stopped(funcs[i]->service, ".service", -1);
- free(config->strs);
+ if (funcs[i]->handler)
+ funcs[i]->handler(0);
+ }
}
-
- /*
- * Each function will be free later,
- * for now we cleanup only pointers.
- */
- if (config->funcs)
- free(config->funcs);
-
- free(config);
}
-static void legacy_free_gadget(struct usb_gadget *gadget)
+static int legacy_enable(struct usb_client *usb)
{
- int i;
-
- if (!gadget)
- return;
+ int ret;
- if (gadget->strs) {
- for (i = 0; gadget->strs[i].lang_code; ++i) {
- free(gadget->strs[i].manufacturer);
- free(gadget->strs[i].product);
- free(gadget->strs[i].serial);
- }
- free(gadget->strs);
- }
+ ret = sys_set_str(LEGACY_ENABLE_PATH, LEGACY_ENABLE);
+ if (ret < 0)
+ return ret;
- if (gadget->configs) {
- for (i = 0; gadget->configs[i]; ++i)
- legacy_free_config(gadget->configs[i]);
+ legacy_start_stop_service_and_handler(true);
- free(gadget->configs);
- }
+ return 0;
+}
- if (gadget->funcs) {
- for (i = 0; gadget->funcs[i]; ++i)
- gadget->funcs[i]->free_func(gadget->funcs[i]);
+static int legacy_disable(struct usb_client *usb)
+{
- free(gadget->funcs);
- }
+ legacy_start_stop_service_and_handler(false);
- free(gadget);
+ return sys_set_str(LEGACY_ENABLE_PATH, LEGACY_DISABLE);
}
EXPORT
if (access("/sys/class/usb_mode/usb0/enable", F_OK))
return -ENOENT;
- legacy = zalloc(sizeof(*legacy));
+ legacy = calloc(1, sizeof(*legacy));
if (!legacy)
return -ENOMEM;
legacy->common.info = info;
- legacy->get_current_gadget = legacy_get_current_gadget;
+
legacy->reconfigure_gadget = legacy_reconfigure_gadget;
- legacy->is_gadget_supported = legacy_is_gadget_supported;
- legacy->is_function_supported = legacy_is_function_supported;
legacy->enable = legacy_enable;
legacy->disable = legacy_disable;
- legacy->free_gadget = legacy_free_gadget;
*common = &legacy->common;
return 0;