diff --git a/include/usbg/usbg_internal.h b/include/usbg/usbg_internal.h index 0e9b2df..cc9f734 100644 --- a/include/usbg/usbg_internal.h +++ b/include/usbg/usbg_internal.h @@ -21,6 +21,7 @@ #include #else typedef struct _should_not_be_used config_t; + typedef struct _should_not_be_used config_setting_t; void config_destroy(config_t *config); #endif @@ -39,6 +40,37 @@ }) #endif /* container_of */ +struct usbg_function_type +{ + /* Name of this function type */ + char *name; + + /* + * Called when user would like to remove this function. + * If this callback is provided it will be always called + * before rmdir on function directory. This function + * should check received flags and remove composed function + * attributes (directories) only if USBG_RM_RECURSE flag + * has been passed. + */ + int (*remove)(struct usbg_function *, int); + + /* Set the value of all given attributes */ + int (*set_attrs)(struct usbg_function *, const usbg_function_attrs *); + + /* Get the value of all function attributes */ + int (*get_attrs)(struct usbg_function *, usbg_function_attrs *); + + /* Free the additional memory allocated for function attributes */ + void (*cleanup_attrs)(struct usbg_function *, usbg_function_attrs *); + + /* Should import all function attributes from libconfig format */ + int (*import)(struct usbg_function *, config_setting_t *); + + /* Should export all functions attributes to libconfig format */ + int (*export)(struct usbg_function *, config_setting_t *); +}; + struct usbg_state { char *path; @@ -74,8 +106,6 @@ struct usbg_config int id; }; -typedef int (*usbg_rm_function_callback)(usbg_function *, int); - struct usbg_function { TAILQ_ENTRY(usbg_function) fnode; @@ -87,7 +117,7 @@ struct usbg_function /* Only for internal library usage */ char *label; usbg_function_type type; - usbg_rm_function_callback rm_callback; + struct usbg_function_type *ops; }; struct usbg_binding @@ -170,6 +200,8 @@ int usbg_translate_error(int error); char *usbg_ether_ntoa_r(const struct ether_addr *addr, char *buf); + + int usbg_read_buf(const char *path, const char *name, const char *file, char *buf); @@ -209,5 +241,9 @@ int usbg_rm_dir(const char *path, const char *name); int usbg_rm_all_dirs(const char *path); int usbg_check_dir(const char *path); +#define usbg_config_is_int(node) (config_setting_type(node) == CONFIG_TYPE_INT) +#define usbg_config_is_string(node) \ + (config_setting_type(node) == CONFIG_TYPE_STRING) + #endif /* USBG_INTERNAL_H */ diff --git a/src/Makefile.am b/src/Makefile.am index 18e95ad..3ebcbb6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,6 @@ +AUTOMAKE_OPTIONS = std-options subdir-objects lib_LTLIBRARIES = libusbgx.la -libusbgx_la_SOURCES = usbg.c usbg_error.c usbg_common.c +libusbgx_la_SOURCES = usbg.c usbg_error.c usbg_common.c function/ether.c function/ffs.c function/midi.c function/ms.c function/phonet.c function/serial.c function/loopback.c if TEST_GADGET_SCHEMES libusbgx_la_SOURCES += usbg_schemes_libconfig.c else diff --git a/src/function/ether.c b/src/function/ether.c new file mode 100644 index 0000000..ca9dee7 --- /dev/null +++ b/src/function/ether.c @@ -0,0 +1,248 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "usbg/usbg.h" +#include "usbg/usbg_internal.h" + +#include +#ifdef HAS_LIBCONFIG +#include +#endif + +static int ether_set_attrs(struct usbg_function *f, + const usbg_function_attrs *f_attrs) +{ + int ret = USBG_SUCCESS; + char addr_buf[USBG_MAX_STR_LENGTH]; + const usbg_f_net_attrs *attrs = &f_attrs->attrs.net; + char *addr; + + /* ifname is read only so we accept only empty string for this param */ + if (attrs->ifname && attrs->ifname[0]) { + ret = USBG_ERROR_INVALID_PARAM; + goto out; + } + + addr = usbg_ether_ntoa_r(&attrs->dev_addr, addr_buf); + ret = usbg_write_string(f->path, f->name, "dev_addr", addr); + if (ret != USBG_SUCCESS) + goto out; + + addr = usbg_ether_ntoa_r(&attrs->host_addr, addr_buf); + ret = usbg_write_string(f->path, f->name, "host_addr", addr); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_write_dec(f->path, f->name, "qmult", attrs->qmult); + +out: + return ret; +} + +static int ether_get_attrs(struct usbg_function *f, + usbg_function_attrs *f_attrs) +{ + struct ether_addr *addr; + struct ether_addr addr_buf; + char str_addr[USBG_MAX_STR_LENGTH]; + usbg_f_net_attrs *attrs = &f_attrs->attrs.net; + int ret = USBG_ERROR_INVALID_PARAM; + + ret = usbg_read_string(f->path, f->name, "dev_addr", str_addr); + if (ret != USBG_SUCCESS) + goto out; + + addr = ether_aton_r(str_addr, &addr_buf); + if (addr) { + attrs->dev_addr = *addr; + } else { + ret = USBG_ERROR_IO; + goto out; + } + + ret = usbg_read_string(f->path, f->name, "host_addr", str_addr); + if (ret != USBG_SUCCESS) + goto out; + + addr = ether_aton_r(str_addr, &addr_buf); + if (addr) { + attrs->host_addr = *addr; + } else { + ret = USBG_ERROR_IO; + goto out; + } + + ret = usbg_read_dec(f->path, f->name, "qmult", &(attrs->qmult)); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_read_string_alloc(f->path, f->name, "ifname", + &(attrs->ifname)); + + f_attrs->header.attrs_type = USBG_F_ATTRS_NET; +out: + return ret; +} + +static void ether_cleanup_attrs(struct usbg_function *f, + usbg_function_attrs *f_attrs) +{ + free((char*)f_attrs->attrs.net.ifname); + f_attrs->attrs.net.ifname = NULL; +} + +#ifdef HAS_LIBCONFIG + +static int ether_libconfig_import(struct usbg_function *f, + config_setting_t *root) +{ + config_setting_t *node; + int ret = USBG_SUCCESS; + int qmult; + struct ether_addr *addr; + struct ether_addr addr_buf; + const char *str; + +#define GET_OPTIONAL_ADDR(NAME) \ + do { \ + node = config_setting_get_member(root, #NAME); \ + if (node) { \ + str = config_setting_get_string(node); \ + if (!str) { \ + ret = USBG_ERROR_INVALID_TYPE; \ + goto out; \ + } \ + \ + addr = ether_aton_r(str, &addr_buf); \ + if (!addr) { \ + ret = USBG_ERROR_INVALID_VALUE; \ + goto out; \ + } \ + ret = usbg_set_net_##NAME(f, addr); \ + if (ret != USBG_SUCCESS) \ + goto out; \ + } \ + } while (0) + + GET_OPTIONAL_ADDR(host_addr); + GET_OPTIONAL_ADDR(dev_addr); + +#undef GET_OPTIONAL_ADDR + + node = config_setting_get_member(root, "qmult"); + if (node) { + if (!usbg_config_is_int(node)) { + ret = USBG_ERROR_INVALID_TYPE; + goto out; + } + qmult = config_setting_get_int(node); + ret = usbg_set_net_qmult(f, qmult); + } + +out: + return ret; +} + +static int ether_libconfig_export(struct usbg_function *f, + config_setting_t *root) +{ + config_setting_t *node; + char *addr; + char addr_buf[USBG_MAX_STR_LENGTH]; + int cfg_ret, usbg_ret; + usbg_function_attrs f_attrs; + usbg_f_net_attrs *attrs = &f_attrs.attrs.net; + int ret = USBG_ERROR_NO_MEM; + + usbg_ret = ether_get_attrs(f, &f_attrs); + if (usbg_ret != USBG_SUCCESS) { + ret = usbg_ret; + goto out; + } + + node = config_setting_add(root, "dev_addr", CONFIG_TYPE_STRING); + if (!node) + goto cleanup; + + addr = usbg_ether_ntoa_r(&attrs->dev_addr, addr_buf); + cfg_ret = config_setting_set_string(node, addr); + if (cfg_ret != CONFIG_TRUE) { + ret = USBG_ERROR_OTHER_ERROR; + goto cleanup; + } + + node = config_setting_add(root, "host_addr", CONFIG_TYPE_STRING); + if (!node) + goto cleanup; + + addr = usbg_ether_ntoa_r(&attrs->host_addr, addr_buf); + cfg_ret = config_setting_set_string(node, addr); + if (cfg_ret != CONFIG_TRUE) { + ret = USBG_ERROR_OTHER_ERROR; + goto cleanup; + } + + node = config_setting_add(root, "qmult", CONFIG_TYPE_INT); + if (!node) + goto cleanup; + + cfg_ret = config_setting_set_int(node, attrs->qmult); + ret = cfg_ret == CONFIG_TRUE ? 0 : USBG_ERROR_OTHER_ERROR; + + /* ifname is read only so we don't export it */ +cleanup: + ether_cleanup_attrs(f, &f_attrs); +out: + return ret; +} + +#define ETHER_LIBCONFIG_DEP_OPS \ + .import = ether_libconfig_import, \ + .export = ether_libconfig_export + +#else + +#define ETHER_LIBCONFIG_DEP_OPS + +#endif /* HAS_LIBCONFIG */ + +#define ETHER_FUNCTION_OPTS \ + .set_attrs = ether_set_attrs, \ + .get_attrs = ether_get_attrs, \ + .cleanup_attrs = ether_cleanup_attrs, \ + ETHER_LIBCONFIG_DEP_OPS + +struct usbg_function_type usbg_f_type_ecm = { + .name = "ecm", + ETHER_FUNCTION_OPTS +}; + +struct usbg_function_type usbg_f_type_subset = { + .name = "geth", + ETHER_FUNCTION_OPTS +}; + +struct usbg_function_type usbg_f_type_ncm = { + .name = "ncm", + ETHER_FUNCTION_OPTS +}; + +struct usbg_function_type usbg_f_type_eem = { + .name = "eem", + ETHER_FUNCTION_OPTS +}; + +struct usbg_function_type usbg_f_type_rndis = { + .name = "rndis", + ETHER_FUNCTION_OPTS +}; + diff --git a/src/function/ffs.c b/src/function/ffs.c new file mode 100644 index 0000000..05f750d --- /dev/null +++ b/src/function/ffs.c @@ -0,0 +1,80 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "usbg/usbg.h" +#include "usbg/usbg_internal.h" + +#ifdef HAS_LIBCONFIG +#include +#endif + +static int ffs_set_attrs(struct usbg_function *f, + const usbg_function_attrs *f_attrs) +{ + const usbg_f_ffs_attrs *ffs_attrs = &(f_attrs->attrs.ffs); + int ret = USBG_ERROR_INVALID_PARAM; + + if (f_attrs->header.attrs_type && + f_attrs->header.attrs_type != USBG_F_ATTRS_FFS) + goto out; + + ret = ffs_attrs->dev_name && ffs_attrs->dev_name[0] ? + USBG_ERROR_INVALID_PARAM : USBG_SUCCESS; + +out: + return ret; +} + +static int ffs_get_attrs(struct usbg_function *f, + usbg_function_attrs *f_attrs) +{ + usbg_f_ffs_attrs *ffs_attrs = &(f_attrs->attrs.ffs); + int ret = USBG_SUCCESS; + + ffs_attrs->dev_name = strdup(f->instance); + if (!ffs_attrs->dev_name) { + ret = USBG_ERROR_NO_MEM; + goto out; + } + + f_attrs->header.attrs_type = USBG_F_ATTRS_FFS; +out: + return ret; +} + +static void ffs_cleanup_attrs(struct usbg_function *f, + usbg_function_attrs *f_attrs) +{ + free((char*)f_attrs->attrs.ffs.dev_name); + f_attrs->attrs.ffs.dev_name = NULL; +} + +static int ffs_libconfig_import(struct usbg_function *f, + config_setting_t *root) +{ + return USBG_SUCCESS; +} + +static int ffs_libconfig_export(struct usbg_function *f, + config_setting_t *root) +{ + return USBG_SUCCESS; +} + +struct usbg_function_type usbg_f_type_ffs = { + .name = "ffs", + .set_attrs = ffs_set_attrs, + .get_attrs = ffs_get_attrs, + .cleanup_attrs = ffs_cleanup_attrs, + .import = ffs_libconfig_import, + .export = ffs_libconfig_export, +}; diff --git a/src/function/loopback.c b/src/function/loopback.c new file mode 100644 index 0000000..178e5e8 --- /dev/null +++ b/src/function/loopback.c @@ -0,0 +1,151 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "usbg/usbg.h" +#include "usbg/usbg_internal.h" + +#ifdef HAS_LIBCONFIG +#include +#endif + +static int loopback_set_attrs(struct usbg_function *f, + const usbg_function_attrs *f_attrs) +{ + int ret = USBG_ERROR_INVALID_PARAM; + const usbg_f_loopback_attrs *attrs = &f_attrs->attrs.loopback; + + if (f_attrs->header.attrs_type && + f_attrs->header.attrs_type != USBG_F_ATTRS_LOOPBACK) + goto out; + + ret = usbg_write_dec(f->path, f->name, "buflen", attrs->buflen); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_write_dec(f->path, f->name, "qlen", attrs->qlen); + + out: + return ret; +} + +static int loopback_get_attrs(struct usbg_function *f, + usbg_function_attrs *f_attrs) +{ + int ret; + const usbg_f_loopback_attrs *attrs = &f_attrs->attrs.loopback; + + ret = usbg_read_dec(f->path, f->name, "buflen", (int *)&(attrs->buflen)); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_read_dec(f->path, f->name, "qlen", (int *)&(attrs->qlen)); + if (ret != USBG_SUCCESS) + goto out; + + f_attrs->header.attrs_type = USBG_F_ATTRS_LOOPBACK; +out: + return ret; +} + +#ifdef HAS_LIBCONFIG + +static int loopback_libconfig_export(struct usbg_function *f, + config_setting_t *root) +{ + config_setting_t *node; + usbg_function_attrs f_attrs; + usbg_f_loopback_attrs *attrs = &f_attrs.attrs.loopback; + int cfg_ret; + int ret = USBG_ERROR_NO_MEM; + + ret = loopback_get_attrs(f, &f_attrs); + if (ret) + goto out; + +#define ADD_F_LOOPBACK_INT_ATTR(attr, minval) \ + do { \ + if ((int)attrs->attr < minval) { \ + ret = USBG_ERROR_INVALID_VALUE; \ + goto out; \ + } \ + node = config_setting_add(root, #attr, CONFIG_TYPE_INT);\ + if (!node) \ + goto out; \ + cfg_ret = config_setting_set_int(node, attrs->attr); \ + if (cfg_ret != CONFIG_TRUE) { \ + ret = USBG_ERROR_OTHER_ERROR; \ + goto out; \ + } \ + } while (0) + + ADD_F_LOOPBACK_INT_ATTR(buflen, 0); + ADD_F_LOOPBACK_INT_ATTR(qlen, 0); + +#undef ADD_F_LOOPBACK_INT_ATTR + + ret = USBG_SUCCESS; +out: + return ret; +} + +static int loopback_libconfig_import(struct usbg_function *f, + config_setting_t *root) +{ + config_setting_t *node; + int ret = USBG_ERROR_NO_MEM; + int tmp; + usbg_function_attrs attrs; + usbg_f_loopback_attrs *loopback_attrs = &attrs.attrs.loopback; + + attrs.header.attrs_type = USBG_F_ATTRS_LOOPBACK; + +#define ADD_F_LOOPBACK_INT_ATTR(attr, defval, minval) \ + do { \ + node = config_setting_get_member(root, #attr); \ + if (node) { \ + if (!usbg_config_is_int(node)) { \ + ret = USBG_ERROR_INVALID_TYPE; \ + goto out; \ + } \ + tmp = config_setting_get_int(node); \ + if (tmp < minval) { \ + ret = USBG_ERROR_INVALID_VALUE; \ + goto out; \ + } \ + loopback_attrs->attr = tmp; \ + } else { \ + loopback_attrs->attr = defval; \ + } \ + } while (0) + + ADD_F_LOOPBACK_INT_ATTR(buflen, 4096, 0); + ADD_F_LOOPBACK_INT_ATTR(qlen, 32, 0); + +#undef ADD_F_LOOPBACK_INT_ATTR + + ret = usbg_set_function_attrs(f, &attrs); +out: + return ret; +} + +#endif /* HAS_LIBCONFIG */ + +struct usbg_function_type usbg_f_type_loopback = { + .name = "loopback", + .set_attrs = loopback_set_attrs, + .get_attrs = loopback_get_attrs, +#ifdef HAS_LIBCONFIG + .import = loopback_libconfig_import, + .export = loopback_libconfig_export, +#endif +}; + diff --git a/src/function/midi.c b/src/function/midi.c new file mode 100644 index 0000000..a377c73 --- /dev/null +++ b/src/function/midi.c @@ -0,0 +1,228 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "usbg/usbg.h" +#include "usbg/usbg_internal.h" + +#include +#ifdef HAS_LIBCONFIG +#include +#endif + +static int midi_set_attrs(struct usbg_function *f, + const usbg_function_attrs *f_attrs) +{ + const usbg_f_midi_attrs *attrs = &f_attrs->attrs.midi; + int ret = USBG_ERROR_INVALID_PARAM; + + if (f_attrs->header.attrs_type && + f_attrs->header.attrs_type != USBG_F_ATTRS_MIDI) + goto out; + + ret = usbg_write_dec(f->path, f->name, "index", attrs->index); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_write_string(f->path, f->name, "id", attrs->id); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_write_dec(f->path, f->name, "in_ports", attrs->in_ports); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_write_dec(f->path, f->name, "out_ports", attrs->out_ports); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_write_dec(f->path, f->name, "buflen", attrs->buflen); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_write_dec(f->path, f->name, "qlen", attrs->qlen); + +out: + return ret; + +} + +static int midi_get_attrs(struct usbg_function *f, + usbg_function_attrs *f_attrs) +{ + int ret; + usbg_f_midi_attrs *attrs = &f_attrs->attrs.midi; + + ret = usbg_read_dec(f->path, f->name, "index", &(attrs->index)); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_read_string_alloc(f->path, f->name, "id", &(attrs->id)); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_read_dec(f->path, f->name, "in_ports", (int*)&(attrs->in_ports)); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_read_dec(f->path, f->name, "out_ports", (int*)&(attrs->out_ports)); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_read_dec(f->path, f->name, "buflen", (int*)&(attrs->buflen)); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_read_dec(f->path, f->name, "qlen", (int*)&(attrs->qlen)); + if (ret != USBG_SUCCESS) + goto out; + + f_attrs->header.attrs_type = USBG_F_ATTRS_MIDI; +out: + return ret; +} + +static void midi_cleanup_attrs(struct usbg_function *f, + usbg_function_attrs *f_attrs) +{ + free((char*)f_attrs->attrs.midi.id); + f_attrs->attrs.midi.id = NULL; +} + +#ifdef HAS_LIBCONFIG + +static int midi_libconfig_import(struct usbg_function *f, + config_setting_t *root) +{ + config_setting_t *node; + int ret = USBG_ERROR_NO_MEM; + int tmp; + usbg_function_attrs attrs; + usbg_f_midi_attrs *midi_attrs = &attrs.attrs.midi; + + attrs.header.attrs_type = USBG_F_ATTRS_MIDI; + +#define ADD_F_MIDI_INT_ATTR(attr, defval, minval) \ + do { \ + node = config_setting_get_member(root, #attr); \ + if (node) { \ + if (!usbg_config_is_int(node)) { \ + ret = USBG_ERROR_INVALID_TYPE; \ + goto out; \ + } \ + tmp = config_setting_get_int(node); \ + if (tmp < minval) { \ + ret = USBG_ERROR_INVALID_VALUE; \ + goto out; \ + } \ + midi_attrs->attr = tmp; \ + } else { \ + midi_attrs->attr = defval; \ + } \ + } while (0) + + ADD_F_MIDI_INT_ATTR(index, -1, INT_MIN); + ADD_F_MIDI_INT_ATTR(in_ports, 1, 0); + ADD_F_MIDI_INT_ATTR(out_ports, 1, 0); + ADD_F_MIDI_INT_ATTR(buflen, 256, 0); + ADD_F_MIDI_INT_ATTR(qlen, 32, 0); + +#undef ADD_F_MIDI_INT_ATTR + + node = config_setting_get_member(root, "id"); + if (node) { + if (!usbg_config_is_string(node)) { + ret = USBG_ERROR_INVALID_TYPE; + goto out; + } + + midi_attrs->id = config_setting_get_string(node); + } else { + midi_attrs->id = ""; + } + + + ret = usbg_set_function_attrs(f, &attrs); +out: + return ret; +} + +static int midi_libconfig_export(struct usbg_function *f, + config_setting_t *root) +{ + config_setting_t *node; + int cfg_ret, usbg_ret; + usbg_function_attrs f_attrs; + usbg_f_midi_attrs *attrs = &f_attrs.attrs.midi; + int ret = USBG_ERROR_NO_MEM; + + usbg_ret = midi_get_attrs(f, &f_attrs); + if (usbg_ret != USBG_SUCCESS) { + ret = usbg_ret; + goto out; + } + +#define ADD_F_MIDI_INT_ATTR(attr, minval) \ + do { \ + if ((int)attrs->attr < minval) { \ + ret = USBG_ERROR_INVALID_VALUE; \ + goto cleanup; \ + } \ + node = config_setting_add(root, #attr, CONFIG_TYPE_INT);\ + if (!node) \ + goto cleanup; \ + cfg_ret = config_setting_set_int(node, attrs->attr); \ + if (cfg_ret != CONFIG_TRUE) { \ + ret = USBG_ERROR_OTHER_ERROR; \ + goto cleanup; \ + } \ + } while (0) + + ADD_F_MIDI_INT_ATTR(index, INT_MIN); + + node = config_setting_add(root, "id", CONFIG_TYPE_STRING); + if (!node) + goto cleanup; + + cfg_ret = config_setting_set_string(node, attrs->id); + if (cfg_ret != CONFIG_TRUE) { + ret = USBG_ERROR_OTHER_ERROR; + goto cleanup; + } + + ADD_F_MIDI_INT_ATTR(in_ports, 0); + ADD_F_MIDI_INT_ATTR(out_ports, 0); + ADD_F_MIDI_INT_ATTR(buflen, 0); + ADD_F_MIDI_INT_ATTR(qlen, 0); + +#undef ADD_F_MIDI_INT_ATTR + + ret = USBG_SUCCESS; +cleanup: + midi_cleanup_attrs(f, &f_attrs); +out: + return ret; +} + +#endif /* HAS_LIBCONFIG */ + +struct usbg_function_type usbg_f_type_midi = { + .name = "midi", + .set_attrs = midi_set_attrs, + .get_attrs = midi_get_attrs, + .cleanup_attrs = midi_cleanup_attrs, + +#ifdef HAS_LIBCONFIG + .import = midi_libconfig_import, + .export = midi_libconfig_export, +#endif +}; + diff --git a/src/function/ms.c b/src/function/ms.c new file mode 100644 index 0000000..40797f1 --- /dev/null +++ b/src/function/ms.c @@ -0,0 +1,666 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "usbg/usbg.h" +#include "usbg/usbg_internal.h" + +#include +#include +#include +#include +#ifdef HAS_LIBCONFIG +#include +#endif + +static inline int lun_select(const struct dirent *dent) +{ + int ret; + int id; + + ret = file_select(dent); + if (!ret) + goto out; + + ret = sscanf(dent->d_name, "lun.%d", &id); +out: + return ret; +} + +static inline int lun_sort(const struct dirent **d1, const struct dirent **d2) +{ + int ret; + int id1, id2; + + ret = sscanf((*d1)->d_name, "lun.%d", &id1); + if (ret != 1) + goto err; + + ret = sscanf((*d2)->d_name, "lun.%d", &id2); + if (ret != 1) + goto err; + + if (id1 < id2) + ret = 1; + + return id1 < id2 ? -1 : id1 > id2; +err: + /* + * This should not happened because dentries has been + * already checked by lun_select function. This + * error procedure is just in case. + */ + return -1; +} + +static int ms_set_lun_attrs(const char *path, const char *lun, + usbg_f_ms_lun_attrs *lun_attrs) +{ + int ret; + + ret = usbg_write_bool(path, lun, "cdrom", lun_attrs->cdrom); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_write_bool(path, lun, "ro", lun_attrs->ro); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_write_bool(path, lun, "nofua", lun_attrs->nofua); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_write_bool(path, lun, "removable", lun_attrs->removable); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_write_string(path, lun, "file", + lun_attrs->filename); + +out: + return ret; +} + +static int ms_set_attrs(struct usbg_function *f, + const usbg_function_attrs *f_attrs) +{ + int i, nmb; + int space_left; + char *new_lun_mask; + char lpath[USBG_MAX_PATH_LENGTH]; + char *lpath_end; + DIR *dir; + struct dirent **dent; + const usbg_f_ms_attrs *attrs = &f_attrs->attrs.ms; + int ret = USBG_ERROR_INVALID_PARAM; + + if (f_attrs->header.attrs_type && + f_attrs->header.attrs_type != USBG_F_ATTRS_MS) + goto out; + + ret = usbg_write_bool(f->path, f->name, "stall", attrs->stall); + if (ret != USBG_SUCCESS) + goto out; + + /* lun0 cannot be removed */ + if (!attrs->luns || attrs->nluns <= 0) + goto out; + + ret = snprintf(lpath, sizeof(lpath), "%s/%s/", f->path, f->name); + if (ret >= sizeof(lpath)) { + ret = USBG_ERROR_PATH_TOO_LONG; + goto out; + } + + lpath_end = lpath + strlen(lpath); + space_left = sizeof(lpath) - (lpath_end - lpath); + + new_lun_mask = calloc(attrs->nluns, sizeof (char)); + if (!new_lun_mask) { + ret = USBG_ERROR_NO_MEM; + goto out; + } + + for (i = 0; i < attrs->nluns; ++i) { + usbg_f_ms_lun_attrs *lun = attrs->luns[i]; + + /* + * id may be left unset in lun attrs but + * if it is set it has to be equal to position + * in lun array + */ + if (lun && lun->id >= 0 && lun->id != i) { + ret = USBG_ERROR_INVALID_PARAM; + goto err_lun_loop; + } + + ret = snprintf(lpath_end, space_left, "/lun.%d/", i); + if (ret >= space_left) { + ret = USBG_ERROR_PATH_TOO_LONG; + goto err_lun_loop; + } + + /* + * Check if dir exist and create it if needed + */ + dir = opendir(lpath); + if (dir) { + closedir(dir); + } else if (errno != ENOENT) { + ret = usbg_translate_error(errno); + goto err_lun_loop; + } else { + ret = mkdir(lpath, S_IRWXU|S_IRWXG|S_IRWXO); + if (!ret) { + /* + * If we have created a new directory in + * this function let's mark it so we can + * cleanup in case of error + */ + new_lun_mask[i] = 1; + } else { + ret = usbg_translate_error(errno); + goto err_lun_loop; + } + } + + /* if attributes has not been provided just go to next one */ + if (!lun) + continue; + + ret = ms_set_lun_attrs(lpath, "", lun); + if (ret != USBG_SUCCESS) + goto err_lun_loop; + } + + /* Check if function has more luns and remove them */ + *lpath_end = '\0'; + i = 0; + nmb = scandir(lpath, &dent, lun_select, lun_sort); + if (nmb < 0) { + ret = usbg_translate_error(errno); + goto err_lun_loop; + } + + for (i = 0; i < attrs->nluns; ++i) + free(dent[i]); + + for (; i < nmb; ++i) { + ret = usbg_rm_dir(lpath, dent[i]->d_name); + free(dent[i]); + /* There is no good way to recover form this */ + if (ret != USBG_SUCCESS) + goto err_rm_loop; + } + free(dent); + + return USBG_SUCCESS; + +err_rm_loop: + while (++i < nmb) + free(dent[i]); + free(dent); + + i = attrs->nluns; +err_lun_loop: + /* array is null terminated so we may access lun[nluns] */ + for (; i >= 0; --i) { + if (!new_lun_mask[i]) + continue; + + ret = snprintf(lpath_end, space_left, "/lun.%d/", i); + if (ret >= space_left) { + /* + * This should not happen because if we were + * able to create this directory we should be + * also able to remove it. + */ + continue; + } + rmdir(lpath); + } + free(new_lun_mask); + +out: + return ret; +} + +static void ms_cleanup_lun_attrs(usbg_f_ms_lun_attrs *lun_attrs) +{ + if (!lun_attrs) + return; + + free((char*)lun_attrs->filename); + lun_attrs->id = -1; +} + + +static void ms_cleanup_attrs(struct usbg_function *f, + usbg_function_attrs *f_attrs) +{ + int i; + usbg_f_ms_attrs *attrs = &f_attrs->attrs.ms; + + if (!attrs->luns) + return; + + for (i = 0; i < attrs->nluns; ++i) { + if (!attrs->luns[i]) + continue; + + ms_cleanup_lun_attrs(attrs->luns[i]); + free(attrs->luns[i]); + } + free(attrs->luns); + attrs->luns = NULL; + attrs->nluns = -1; +} + +static int ms_get_lun_attrs(const char *path, const char *lun, + usbg_f_ms_lun_attrs *lun_attrs) +{ + int ret; + + memset(lun_attrs, 0, sizeof(*lun_attrs)); + + ret = sscanf(lun, "lun.%d", &lun_attrs->id); + if (ret != 1) + goto out; + + ret = usbg_read_bool(path, lun, "cdrom", &(lun_attrs->cdrom)); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_read_bool(path, lun, "ro", &(lun_attrs->ro)); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_read_bool(path, lun, "nofua", &(lun_attrs->nofua)); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_read_bool(path, lun, "removable", &(lun_attrs->removable)); + if (ret != USBG_SUCCESS) + goto out; + + ret = usbg_read_string_alloc(path, lun, "file", + &(lun_attrs->filename)); + +out: + return ret; +} + +static int ms_get_attrs(struct usbg_function *f, + usbg_function_attrs *f_attrs) +{ + int nmb; + int i = 0; + char fpath[USBG_MAX_PATH_LENGTH]; + usbg_f_ms_lun_attrs *lun_attrs; + usbg_f_ms_lun_attrs **luns; + struct dirent **dent; + usbg_f_ms_attrs *attrs = &f_attrs->attrs.ms; + int ret; + + ret = usbg_read_bool(f->path, f->name, "stall", + &(attrs->stall)); + if (ret != USBG_SUCCESS) + goto out; + + + nmb = snprintf(fpath, sizeof(fpath), "%s/%s/", + f->path, f->name); + if (nmb >= sizeof(fpath)) { + ret = USBG_ERROR_PATH_TOO_LONG; + goto out; + } + + nmb = scandir(fpath, &dent, lun_select, lun_sort); + if (nmb < 0) { + ret = usbg_translate_error(errno); + goto out; + } + + luns = calloc(nmb + 1, sizeof(*luns)); + if (!luns) { + ret = USBG_ERROR_NO_MEM; + goto err; + } + + attrs->luns = luns; + attrs->nluns = nmb; + + for (i = 0; i < nmb; i++) { + lun_attrs = malloc(sizeof(*lun_attrs)); + if (!lun_attrs) { + ret = USBG_ERROR_NO_MEM; + goto err; + } + + ret = ms_get_lun_attrs(fpath, dent[i]->d_name, + lun_attrs); + if (ret != USBG_SUCCESS) { + free(lun_attrs); + goto err; + } + + luns[i] = lun_attrs; + free(dent[i]); + } + free(dent); + + f_attrs->header.attrs_type = USBG_F_ATTRS_MS; + + return USBG_SUCCESS; + +err: + while (i < nmb) { + free(dent[i]); + ++i; + } + free(dent); + + ms_cleanup_attrs(f, f_attrs); +out: + return ret; +} + +#ifdef HAS_LIBCONFIG + +static int ms_import_lun_attrs(usbg_f_ms_lun_attrs *lattrs, + config_setting_t *root) +{ + config_setting_t *node; + int i; + int ret = USBG_ERROR_NO_MEM; + +#define BOOL_ATTR(_name, _default_val) \ + { .name = #_name, .value = &lattrs->_name, } + struct { + char *name; + bool *value; + bool default_val; + } bool_attrs[] = { + BOOL_ATTR(cdrom, false), + BOOL_ATTR(ro, false), + BOOL_ATTR(nofua, false), + BOOL_ATTR(removable, true), + }; +#undef BOOL_ATTR + + memset(lattrs, 0, sizeof(*lattrs)); + lattrs->id = -1; + + for (i = 0; i < ARRAY_SIZE(bool_attrs); ++i) { + *(bool_attrs[i].value) = bool_attrs[i].default_val; + + node = config_setting_get_member(root, bool_attrs[i].name); + if (!node) + continue; + + ret = config_setting_type(node); + switch (ret) { + case CONFIG_TYPE_INT: + *(bool_attrs[i].value) = !!config_setting_get_int(node); + break; + case CONFIG_TYPE_BOOL: + *(bool_attrs[i].value) = config_setting_get_bool(node); + break; + default: + ret = USBG_ERROR_INVALID_TYPE; + goto out; + } + } + + node = config_setting_get_member(root, "filename"); + if (node) { + if (!usbg_config_is_string(node)) { + ret = USBG_ERROR_INVALID_PARAM; + goto out; + } + lattrs->filename = (char *)config_setting_get_string(node); + } else { + lattrs->filename = ""; + } + + ret = USBG_SUCCESS; +out: + return ret; +} + +static int ms_libconfig_import(struct usbg_function *f, + config_setting_t *root) +{ + config_setting_t *luns_node, *node; + int i; + int ret = USBG_ERROR_NO_MEM; + usbg_function_attrs f_attrs; + usbg_f_ms_attrs *attrs = &f_attrs.attrs.ms; + + memset(&attrs, 0, sizeof(attrs)); + + node = config_setting_get_member(root, "stall"); + if (node) { + ret = config_setting_type(node); + switch (ret) { + case CONFIG_TYPE_INT: + attrs->stall = !!config_setting_get_int(node); + break; + case CONFIG_TYPE_BOOL: + attrs->stall = config_setting_get_bool(node); + break; + default: + ret = USBG_ERROR_INVALID_TYPE; + goto out; + } + } + + luns_node = config_setting_get_member(root, "luns"); + if (!node) { + ret = USBG_ERROR_INVALID_PARAM; + goto out; + } + + if (!config_setting_is_list(luns_node)) { + ret = USBG_ERROR_INVALID_TYPE; + goto out; + } + + attrs->nluns = config_setting_length(luns_node); + + attrs->luns = calloc(attrs->nluns + 1, sizeof(*(attrs->luns))); + if (!attrs->luns) { + ret = USBG_ERROR_NO_MEM; + goto out; + } + + for (i = 0; i < attrs->nluns; ++i) { + node = config_setting_get_elem(luns_node, i); + if (!node) { + ret = USBG_ERROR_INVALID_FORMAT; + goto free_luns; + } + + if (!config_setting_is_group(node)) { + ret = USBG_ERROR_INVALID_TYPE; + goto free_luns; + } + + attrs->luns[i] = malloc(sizeof(*(attrs->luns[i]))); + if (!attrs->luns[i]) { + ret = USBG_ERROR_NO_MEM; + goto free_luns; + } + + ret = ms_import_lun_attrs(attrs->luns[i], node); + if (ret != USBG_SUCCESS) + goto free_luns; + } + + ret = usbg_set_function_attrs(f, &f_attrs); + +free_luns: + while (--i >= 0) + if (attrs->luns[i]) + free(attrs->luns[i]); + free(attrs->luns); +out: + return ret; +} + +static int ms_export_lun_attrs(usbg_f_ms_lun_attrs *lattrs, + config_setting_t *root) +{ + config_setting_t *node; + int cfg_ret; + int i; + int ret = USBG_ERROR_NO_MEM; + +#define BOOL_ATTR(_name) { .name = #_name, .value = &lattrs->_name, } + struct { + char *name; + bool *value; + } bool_attrs[] = { + BOOL_ATTR(cdrom), + BOOL_ATTR(ro), + BOOL_ATTR(nofua), + BOOL_ATTR(removable), + }; +#undef BOOL_ATTR + + for (i = 0; i < ARRAY_SIZE(bool_attrs); ++i) { + node = config_setting_add(root, bool_attrs[i].name, CONFIG_TYPE_BOOL); + if (!node) + goto out; + + cfg_ret = config_setting_set_bool(node, *(bool_attrs[i].value)); + if (cfg_ret != CONFIG_TRUE) { + ret = USBG_ERROR_OTHER_ERROR; + goto out; + } + } + + node = config_setting_add(root, "filename", CONFIG_TYPE_STRING); + if (!node) + goto out; + + cfg_ret = config_setting_set_string(node, lattrs->filename); + ret = cfg_ret == CONFIG_TRUE ? USBG_SUCCESS : USBG_ERROR_OTHER_ERROR; + +out: + return ret; +} + +static int ms_libconfig_export(struct usbg_function *f, + config_setting_t *root) +{ + config_setting_t *luns_node, *node; + int i; + int cfg_ret, usbg_ret; + usbg_function_attrs f_attrs; + usbg_f_ms_attrs *attrs = &f_attrs.attrs.ms; + int ret = USBG_ERROR_NO_MEM; + + usbg_ret = ms_get_attrs(f, &f_attrs); + if (usbg_ret != USBG_SUCCESS) { + ret = usbg_ret; + goto out; + } + + node = config_setting_add(root, "stall", CONFIG_TYPE_BOOL); + if (!node) + goto cleanup; + + cfg_ret = config_setting_set_bool(node, attrs->stall); + if (cfg_ret != CONFIG_TRUE) { + ret = USBG_ERROR_OTHER_ERROR; + goto cleanup; + } + + luns_node = config_setting_add(root, "luns", CONFIG_TYPE_LIST); + if (!luns_node) + goto cleanup; + + for (i = 0; i < attrs->nluns; ++i) { + node = config_setting_add(luns_node, "", CONFIG_TYPE_GROUP); + if (!node) + goto cleanup; + + ret = ms_export_lun_attrs(attrs->luns[i], node); + if (ret != USBG_SUCCESS) + goto cleanup; + } + + ret = USBG_SUCCESS; +cleanup: + ms_cleanup_attrs(f, &f_attrs); +out: + return ret; +} + +#endif /* HAS_LIBCONFIG */ + +static int ms_remove(struct usbg_function *f, int opts) +{ + int ret; + int nmb; + int i; + char lpath[USBG_MAX_PATH_LENGTH]; + struct dirent **dent; + + if (!(opts & USBG_RM_RECURSE)) + return 0; + + ret = snprintf(lpath, sizeof(lpath), "%s/%s/", f->path, f->name); + if (ret >= sizeof(lpath)) { + ret = USBG_ERROR_PATH_TOO_LONG; + goto out; + } + + nmb = scandir(lpath, &dent, lun_select, lun_sort); + if (nmb < 0) { + ret = usbg_translate_error(errno); + goto out; + } + + for (i = nmb - 1; i > 0; --i) { + ret = usbg_rm_dir(lpath, dent[i]->d_name); + free(dent[i]); + if (ret) + goto err_free_dent_loop; + } + free(dent[0]); + free(dent); + + return USBG_SUCCESS; + +err_free_dent_loop: + while (--i >= 0) + free(dent[i]); + free(dent[i]); +out: + return ret; +} + +struct usbg_function_type usbg_f_type_ms = { + .name = "mass_storage", + .set_attrs = ms_set_attrs, + .get_attrs = ms_get_attrs, + .cleanup_attrs = ms_cleanup_attrs, + +#ifdef HAS_LIBCONFIG + .import = ms_libconfig_import, + .export = ms_libconfig_export, +#endif + + .remove = ms_remove, +}; + diff --git a/src/function/phonet.c b/src/function/phonet.c new file mode 100644 index 0000000..57484f9 --- /dev/null +++ b/src/function/phonet.c @@ -0,0 +1,79 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "usbg/usbg.h" +#include "usbg/usbg_internal.h" + +#include +#ifdef HAS_LIBCONFIG +#include +#endif + +static int phonet_set_attrs(struct usbg_function *f, + const usbg_function_attrs *f_attrs) +{ + int ret = USBG_ERROR_INVALID_PARAM; + const usbg_f_phonet_attrs *attrs = &f_attrs->attrs.phonet; + + if (f_attrs->header.attrs_type && + f_attrs->header.attrs_type != USBG_F_ATTRS_PHONET) + goto out; + + ret = attrs->ifname && attrs->ifname[0] ? + USBG_ERROR_INVALID_PARAM : USBG_SUCCESS; + +out: + return ret; +} + +static int phonet_get_attrs(struct usbg_function *f, + usbg_function_attrs *f_attrs) +{ + int ret; + + ret = usbg_read_string_alloc(f->path, f->name, "ifname", + &(f_attrs->attrs.phonet.ifname)); + if (ret != USBG_SUCCESS) + goto out; + + f_attrs->header.attrs_type = USBG_F_ATTRS_PHONET; +out: + return ret; +} + +static void phonet_cleanup_attrs(struct usbg_function *f, + usbg_function_attrs *f_attrs) +{ + free((char*)f_attrs->attrs.phonet.ifname); + f_attrs->attrs.phonet.ifname = NULL; +} + +static int phonet_libconfig_import(struct usbg_function *f, + config_setting_t *root) +{ + return USBG_SUCCESS; +} + +static int phonet_libconfig_export(struct usbg_function *f, + config_setting_t *root) +{ + return USBG_SUCCESS; +} + +struct usbg_function_type usbg_f_type_phonet = { + .name = "phonet", + .set_attrs = phonet_set_attrs, + .get_attrs = phonet_get_attrs, + .cleanup_attrs = phonet_cleanup_attrs, + .import = phonet_libconfig_import, + .export = phonet_libconfig_export, +}; diff --git a/src/function/serial.c b/src/function/serial.c new file mode 100644 index 0000000..e4f1130 --- /dev/null +++ b/src/function/serial.c @@ -0,0 +1,113 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "usbg/usbg.h" +#include "usbg/usbg_internal.h" + +#ifdef HAS_LIBCONFIG +#include +#endif + +static int serial_set_attrs(struct usbg_function *f, + const usbg_function_attrs *f_attrs) +{ + int ret = USBG_ERROR_INVALID_PARAM; + + if (f_attrs->header.attrs_type && + f_attrs->header.attrs_type != USBG_F_ATTRS_SERIAL) + goto out; + + ret = f_attrs->attrs.serial.port_num ? + USBG_ERROR_INVALID_PARAM : USBG_SUCCESS; + out: + return ret; +} + +static int serial_get_attrs(struct usbg_function *f, + usbg_function_attrs *f_attrs) +{ + int ret; + + ret = usbg_read_dec(f->path, f->name, "port_num", + &(f_attrs->attrs.serial.port_num)); + if (ret != USBG_SUCCESS) + goto out; + + f_attrs->header.attrs_type = USBG_F_ATTRS_SERIAL; +out: + return ret; +} + +#ifdef HAS_LIBCONFIG + +static int serial_libconfig_export(struct usbg_function *f, + config_setting_t *root) +{ + config_setting_t *node; + usbg_function_attrs f_attrs; + usbg_f_serial_attrs *attrs = &f_attrs.attrs.serial; + int cfg_ret; + int ret = USBG_ERROR_NO_MEM; + + ret = serial_get_attrs(f, &f_attrs); + if (ret) + goto out; + + node = config_setting_add(root, "port_num", CONFIG_TYPE_INT); + if (!node) { + ret = USBG_ERROR_NO_MEM; + goto out; + } + + cfg_ret = config_setting_set_int(node, attrs->port_num); + ret = cfg_ret == CONFIG_TRUE ? 0 : USBG_ERROR_OTHER_ERROR; + +out: + return ret; +} + +#define SERIAL_LIBCONFIG_DEP_OPS \ + .export = serial_libconfig_export + +#else + +#define SERIAL_LIBCONFIG_DEP_OPS + +#endif /* HAS_LIBCONFIG */ + +static int serial_libconfig_import(struct usbg_function *f, + config_setting_t *root) +{ + return USBG_SUCCESS; +} + +/* We don' import port_num as it is read only */ +#define SERIAL_FUNCTION_OPTS \ + .set_attrs = serial_set_attrs, \ + .get_attrs = serial_get_attrs, \ + SERIAL_LIBCONFIG_DEP_OPS, \ + .import = serial_libconfig_import + +struct usbg_function_type usbg_f_type_acm = { + .name = "acm", + SERIAL_FUNCTION_OPTS +}; + +struct usbg_function_type usbg_f_type_serial = { + .name = "gser", + SERIAL_FUNCTION_OPTS +}; + +struct usbg_function_type usbg_f_type_obex = { + .name = "obex", + SERIAL_FUNCTION_OPTS +}; diff --git a/src/usbg.c b/src/usbg.c index fedee6f..8856b4d 100644 --- a/src/usbg.c +++ b/src/usbg.c @@ -33,29 +33,41 @@ * @file usbg.c */ +extern struct usbg_function_type usbg_f_type_acm; +extern struct usbg_function_type usbg_f_type_serial; +extern struct usbg_function_type usbg_f_type_obex; +extern struct usbg_function_type usbg_f_type_ecm; +extern struct usbg_function_type usbg_f_type_subset; +extern struct usbg_function_type usbg_f_type_ncm; +extern struct usbg_function_type usbg_f_type_eem; +extern struct usbg_function_type usbg_f_type_rndis; +extern struct usbg_function_type usbg_f_type_ffs; +extern struct usbg_function_type usbg_f_type_midi; +extern struct usbg_function_type usbg_f_type_ms; +extern struct usbg_function_type usbg_f_type_phonet; +extern struct usbg_function_type usbg_f_type_loopback; /** - * @var function_names - * @brief Name strings for supported USB function types + * @var function_types + * @brief Types of functions supported by library */ -const char *function_names[] = -{ - "gser", - "acm", - "obex", - "ecm", - "geth", - "ncm", - "eem", - "rndis", - "phonet", - "ffs", - "mass_storage", - "midi", - "Loopback", +struct usbg_function_type* function_types[] = { + [F_ACM] = &usbg_f_type_acm, + [F_SERIAL] = &usbg_f_type_serial, + [F_OBEX] = &usbg_f_type_obex, + [F_ECM] = &usbg_f_type_ecm, + [F_SUBSET] = &usbg_f_type_subset, + [F_NCM] = &usbg_f_type_ncm, + [F_EEM] = &usbg_f_type_eem, + [F_RNDIS] = &usbg_f_type_rndis, + [F_FFS] = &usbg_f_type_ffs, + [F_MIDI] = &usbg_f_type_midi, + [F_MASS_STORAGE] = &usbg_f_type_ms, + [F_PHONET] = &usbg_f_type_phonet, + [F_LOOPBACK] = &usbg_f_type_loopback, }; -ARRAY_SIZE_SENTINEL(function_names, USBG_FUNCTION_TYPE_MAX); +ARRAY_SIZE_SENTINEL(function_types, USBG_FUNCTION_TYPE_MAX); const char *gadget_attr_names[] = { @@ -127,7 +139,7 @@ int usbg_lookup_function_type(const char *name) return USBG_ERROR_INVALID_PARAM; do { - if (!strcmp(name, function_names[i])) + if (!strcmp(name, function_types[i]->name)) return i; i++; } while (i != USBG_FUNCTION_TYPE_MAX); @@ -139,7 +151,7 @@ const char *usbg_get_function_type_str(usbg_function_type type) { return type >= USBG_FUNCTION_TYPE_MIN && type < USBG_FUNCTION_TYPE_MAX ? - function_names[type] : NULL; + function_types[type]->name : NULL; } int usbg_lookup_gadget_attr(const char *name) @@ -188,7 +200,7 @@ const char *usbg_get_gadget_str_name(usbg_gadget_str str) gadget_str_names[str] : NULL; } -static usbg_error usbg_split_function_instance_type(const char *full_name, +static int usbg_split_function_instance_type(const char *full_name, usbg_function_type *f_type, const char **instance) { const char *dot; @@ -281,6 +293,7 @@ static inline void usbg_free_function(usbg_function *f) static void usbg_free_config(usbg_config *c) { usbg_binding *b; + while (!TAILQ_EMPTY(&c->bindings)) { b = TAILQ_FIRST(&c->bindings); TAILQ_REMOVE(&c->bindings, b, bnode); @@ -413,8 +426,6 @@ out: return c; } -static int usbg_rm_ms_function(usbg_function *f, int opts); - static usbg_function *usbg_allocate_function(const char *path, usbg_function_type type, const char *instance, usbg_gadget *parent) { @@ -444,16 +455,7 @@ static usbg_function *usbg_allocate_function(const char *path, f->path = strdup(path); f->parent = parent; f->type = type; - - /* only composed functions (with subdirs) require this callback */ - switch (usbg_lookup_function_attrs_type(type)) { - case USBG_F_ATTRS_MS: - f->rm_callback = usbg_rm_ms_function; - break; - default: - f->rm_callback = NULL; - break; - } + f->ops = function_types[type]; if (!(f->path)) { free(f->name); @@ -517,311 +519,6 @@ char *usbg_ether_ntoa_r(const struct ether_addr *addr, char *buf) return buf; } -static int usbg_parse_function_net_attrs(usbg_function *f, - usbg_f_net_attrs *f_net_attrs) -{ - struct ether_addr *addr; - struct ether_addr addr_buf; - char str_addr[USBG_MAX_STR_LENGTH]; - int ret; - - ret = usbg_read_string(f->path, f->name, "dev_addr", str_addr); - if (ret != USBG_SUCCESS) - goto out; - - addr = ether_aton_r(str_addr, &addr_buf); - if (addr) { - f_net_attrs->dev_addr = *addr; - } else { - ret = USBG_ERROR_IO; - goto out; - } - - ret = usbg_read_string(f->path, f->name, "host_addr", str_addr); - if (ret != USBG_SUCCESS) - goto out; - - addr = ether_aton_r(str_addr, &addr_buf); - if (addr) { - f_net_attrs->host_addr = *addr; - } else { - ret = USBG_ERROR_IO; - goto out; - } - - ret = usbg_read_dec(f->path, f->name, "qmult", &(f_net_attrs->qmult)); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_read_string_alloc(f->path, f->name, "ifname", - &(f_net_attrs->ifname)); -out: - return ret; -} - -static int usbg_parse_function_ms_lun_attrs(const char *path, const char *lun, - usbg_f_ms_lun_attrs *lun_attrs) -{ - int ret; - - memset(lun_attrs, 0, sizeof(*lun_attrs)); - - ret = sscanf(lun, "lun.%d", &lun_attrs->id); - if (ret != 1) - goto out; - - ret = usbg_read_bool(path, lun, "cdrom", &(lun_attrs->cdrom)); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_read_bool(path, lun, "ro", &(lun_attrs->ro)); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_read_bool(path, lun, "nofua", &(lun_attrs->nofua)); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_read_bool(path, lun, "removable", &(lun_attrs->removable)); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_read_string_alloc(path, lun, "file", - &(lun_attrs->filename)); - -out: - return ret; -} - -static inline int lun_select(const struct dirent *dent) -{ - int ret; - int id; - - ret = file_select(dent); - if (!ret) - goto out; - - ret = sscanf(dent->d_name, "lun.%d", &id); -out: - return ret; -} - -static inline int lun_sort(const struct dirent **d1, const struct dirent **d2) -{ - int ret; - int id1, id2; - - ret = sscanf((*d1)->d_name, "lun.%d", &id1); - if (ret != 1) - goto err; - - ret = sscanf((*d2)->d_name, "lun.%d", &id2); - if (ret != 1) - goto err; - - if (id1 < id2) - ret = 1; - - return id1 < id2 ? -1 : id1 > id2; -err: - /* - * This should not happened because dentries has been - * already checked by lun_select function. This - * error procedure is just in case. - */ - return -1; -} - -static int usbg_parse_function_ms_attrs(usbg_function *f, - usbg_f_ms_attrs *f_ms_attrs) -{ - int ret; - int nmb; - int i = 0; - char fpath[USBG_MAX_PATH_LENGTH]; - usbg_f_ms_lun_attrs *lun_attrs; - usbg_f_ms_lun_attrs **luns; - struct dirent **dent; - - ret = usbg_read_bool(f->path, f->name, "stall", - &(f_ms_attrs->stall)); - if (ret != USBG_SUCCESS) - goto out; - - - nmb = snprintf(fpath, sizeof(fpath), "%s/%s/", - f->path, f->name); - if (nmb >= sizeof(fpath)) { - ret = USBG_ERROR_PATH_TOO_LONG; - goto out; - } - - nmb = scandir(fpath, &dent, lun_select, lun_sort); - if (nmb < 0) { - ret = usbg_translate_error(errno); - goto out; - } - - luns = calloc(nmb + 1, sizeof(*luns)); - if (!luns) { - ret = USBG_ERROR_NO_MEM; - goto err; - } - - f_ms_attrs->luns = luns; - f_ms_attrs->nluns = nmb; - - for (i = 0; i < nmb; i++) { - lun_attrs = malloc(sizeof(*lun_attrs)); - if (!lun_attrs) { - ret = USBG_ERROR_NO_MEM; - goto err; - } - - ret = usbg_parse_function_ms_lun_attrs(fpath, dent[i]->d_name, - lun_attrs); - if (ret != USBG_SUCCESS) { - free(lun_attrs); - goto err; - } - - luns[i] = lun_attrs; - free(dent[i]); - } - free(dent); - - return USBG_SUCCESS; - -err: - while (i < nmb) { - free(dent[i]); - ++i; - } - free(dent); - - usbg_cleanup_function_attrs( - container_of((usbg_f_attrs *)f_ms_attrs, - usbg_function_attrs, attrs)); -out: - return ret; -} - -static int usbg_parse_function_midi_attrs(usbg_function *f, - usbg_f_midi_attrs *attrs) -{ - int ret; - - ret = usbg_read_dec(f->path, f->name, "index", &(attrs->index)); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_read_string_alloc(f->path, f->name, "id", &(attrs->id)); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_read_dec(f->path, f->name, "in_ports", (int*)&(attrs->in_ports)); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_read_dec(f->path, f->name, "out_ports", (int*)&(attrs->out_ports)); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_read_dec(f->path, f->name, "buflen", (int*)&(attrs->buflen)); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_read_dec(f->path, f->name, "qlen", (int*)&(attrs->qlen)); - if (ret != USBG_SUCCESS) - goto out; - -out: - return ret; -} - -static int usbg_parse_function_loopback_attrs(usbg_function *f, - usbg_f_loopback_attrs *attrs) -{ - int ret; - - ret = usbg_read_dec(f->path, f->name, "buflen", (int *)&(attrs->buflen)); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_read_dec(f->path, f->name, "qlen", (int *)&(attrs->qlen)); - -out: - return ret; -} - -static int usbg_parse_function_attrs(usbg_function *f, - usbg_function_attrs *f_attrs) -{ - int ret; - int attrs_type; - - attrs_type = usbg_lookup_function_attrs_type(f->type); - if (attrs_type < 0) { - ret = attrs_type; - goto out; - } - - switch (attrs_type) { - case USBG_F_ATTRS_SERIAL: - f_attrs->header.attrs_type = USBG_F_ATTRS_SERIAL; - ret = usbg_read_dec(f->path, f->name, "port_num", - &(f_attrs->attrs.serial.port_num)); - break; - - case USBG_F_ATTRS_NET: - f_attrs->header.attrs_type = USBG_F_ATTRS_NET; - ret = usbg_parse_function_net_attrs(f, &(f_attrs->attrs.net)); - break; - - case USBG_F_ATTRS_PHONET: - f_attrs->header.attrs_type = USBG_F_ATTRS_PHONET; - ret = usbg_read_string_alloc(f->path, f->name, "ifname", - &(f_attrs->attrs.phonet.ifname)); - break; - - case USBG_F_ATTRS_FFS: - { - usbg_f_ffs_attrs *ffs_attrs = &(f_attrs->attrs.ffs); - - f_attrs->header.attrs_type = USBG_F_ATTRS_FFS; - ffs_attrs->dev_name = strdup(f->instance); - if (!ffs_attrs->dev_name) - ret = USBG_ERROR_NO_MEM; - else - ret = USBG_SUCCESS; - break; - } - - case USBG_F_ATTRS_MS: - f_attrs->header.attrs_type = USBG_F_ATTRS_MS; - ret = usbg_parse_function_ms_attrs(f, &(f_attrs->attrs.ms)); - break; - - case USBG_F_ATTRS_MIDI: - f_attrs->header.attrs_type = USBG_F_ATTRS_MIDI; - ret = usbg_parse_function_midi_attrs(f, &(f_attrs->attrs.midi)); - break; - - case USBG_F_ATTRS_LOOPBACK: - f_attrs->header.attrs_type = USBG_F_ATTRS_LOOPBACK; - ret = usbg_parse_function_loopback_attrs(f, &(f_attrs->attrs.loopback)); - break; - - default: - ERROR("Unsupported function type\n"); - ret = USBG_ERROR_NOT_SUPPORTED; - break; - } -out: - return ret; -} - static int usbg_parse_functions(const char *path, usbg_gadget *g) { usbg_function *f; @@ -1502,45 +1199,6 @@ out: return ret; } -static int usbg_rm_ms_function(usbg_function *f, int opts) -{ - int ret; - int nmb; - int i; - char lpath[USBG_MAX_PATH_LENGTH]; - struct dirent **dent; - - ret = snprintf(lpath, sizeof(lpath), "%s/%s/", f->path, f->name); - if (ret >= sizeof(lpath)) { - ret = USBG_ERROR_PATH_TOO_LONG; - goto out; - } - - nmb = scandir(lpath, &dent, lun_select, lun_sort); - if (nmb < 0) { - ret = usbg_translate_error(errno); - goto out; - } - - for (i = nmb - 1; i > 0; --i) { - ret = usbg_rm_dir(lpath, dent[i]->d_name); - free(dent[i]); - if (ret) - goto err_free_dent_loop; - } - free(dent[0]); - free(dent); - - return USBG_SUCCESS; - -err_free_dent_loop: - while (--i >= 0) - free(dent[i]); - free(dent[i]); -out: - return ret; -} - int usbg_rm_function(usbg_function *f, int opts) { int ret = USBG_ERROR_INVALID_PARAM; @@ -1574,8 +1232,8 @@ int usbg_rm_function(usbg_function *f, int opts) } /* TAILQ_FOREACH */ } - if (f->rm_callback) { - ret = f->rm_callback(f, opts); + if (f->ops->remove) { + ret = f->ops->remove(f, opts); if (ret != USBG_SUCCESS) goto out; } @@ -2190,14 +1848,6 @@ int usbg_create_function(usbg_gadget *g, usbg_function_type type, if (!g || !f) return ret; - /* if attrs type is set, check if it has correct type */ - if (f_attrs && f_attrs->header.attrs_type) { - int attrs_type; - attrs_type = usbg_lookup_function_attrs_type(type); - if (attrs_type < 0 || attrs_type != f_attrs->header.attrs_type) - return ret; - } - if (!instance) { /* If someone creates ffs function and doesn't pass instance name this means that device name from attrs should be used */ @@ -2233,24 +1883,31 @@ int usbg_create_function(usbg_gadget *g, usbg_function_type type, free_space = sizeof(fpath) - n; n = snprintf(&(fpath[n]), free_space, "/%s", func->name); - if (n < free_space) { - ret = mkdir(fpath, S_IRWXU | S_IRWXG | S_IRWXO); - if (!ret) { - /* Success */ - ret = USBG_SUCCESS; - if (f_attrs) - ret = usbg_set_function_attrs(func, f_attrs); - } else { - ret = usbg_translate_error(errno); - } + if (n >= free_space) { + ret = USBG_ERROR_PATH_TOO_LONG; + goto free_func; } - if (ret == USBG_SUCCESS) - INSERT_TAILQ_STRING_ORDER(&g->functions, fhead, name, - func, fnode); - else - usbg_free_function(func); + ret = mkdir(fpath, S_IRWXU | S_IRWXG | S_IRWXO); + if (ret) { + ret = usbg_translate_error(errno); + goto free_func; + } + if (f_attrs) { + ret = usbg_set_function_attrs(func, f_attrs); + if (ret != USBG_SUCCESS) + goto remove_dir; + } + + INSERT_TAILQ_STRING_ORDER(&g->functions, fhead, name, func, fnode); + + return USBG_SUCCESS; + +remove_dir: + usbg_rm_dir(fpath, ""); +free_func: + usbg_free_function(func); out: return ret; } @@ -2593,7 +2250,7 @@ usbg_function_type usbg_get_function_type(usbg_function *f) int usbg_get_function_attrs(usbg_function *f, usbg_function_attrs *f_attrs) { - return f && f_attrs ? usbg_parse_function_attrs(f, f_attrs) + return f && f_attrs ? f->ops->get_attrs(f, f_attrs) : USBG_ERROR_INVALID_PARAM; } @@ -2670,309 +2327,16 @@ void usbg_cleanup_function_attrs(usbg_function_attrs *f_attrs) } } -int usbg_set_function_net_attrs(usbg_function *f, const usbg_f_net_attrs *attrs) -{ - int ret = USBG_SUCCESS; - char addr_buf[USBG_MAX_STR_LENGTH]; - char *addr; - - /* ifname is read only so we accept only empty string for this param */ - if (attrs->ifname && attrs->ifname[0]) { - ret = USBG_ERROR_INVALID_PARAM; - goto out; - } - - addr = usbg_ether_ntoa_r(&attrs->dev_addr, addr_buf); - ret = usbg_write_string(f->path, f->name, "dev_addr", addr); - if (ret != USBG_SUCCESS) - goto out; - - addr = usbg_ether_ntoa_r(&attrs->host_addr, addr_buf); - ret = usbg_write_string(f->path, f->name, "host_addr", addr); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_write_dec(f->path, f->name, "qmult", attrs->qmult); - -out: - return ret; -} - -static int usbg_set_f_ms_lun_attrs(const char *path, const char *lun, - usbg_f_ms_lun_attrs *lun_attrs) -{ - int ret; - - ret = usbg_write_bool(path, lun, "cdrom", lun_attrs->cdrom); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_write_bool(path, lun, "ro", lun_attrs->ro); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_write_bool(path, lun, "nofua", lun_attrs->nofua); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_write_bool(path, lun, "removable", lun_attrs->removable); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_write_string(path, lun, "file", - lun_attrs->filename); - -out: - return ret; -} - -static int usbg_set_function_ms_attrs(usbg_function *f, - const usbg_f_ms_attrs *f_attrs) -{ - int ret; - int i, nmb; - int space_left; - char *new_lun_mask; - char lpath[USBG_MAX_PATH_LENGTH]; - char *lpath_end; - DIR *dir; - struct dirent **dent; - - ret = usbg_write_bool(f->path, f->name, "stall", f_attrs->stall); - if (ret != USBG_SUCCESS) - goto out; - - /* lun0 cannot be removed */ - if (!f_attrs->luns || f_attrs->nluns <= 0) - goto out; - - ret = snprintf(lpath, sizeof(lpath), "%s/%s/", f->path, f->name); - if (ret >= sizeof(lpath)) { - ret = USBG_ERROR_PATH_TOO_LONG; - goto out; - } - - lpath_end = lpath + strlen(lpath); - space_left = sizeof(lpath) - (lpath_end - lpath); - - new_lun_mask = calloc(f_attrs->nluns, sizeof (char)); - if (!new_lun_mask) { - ret = USBG_ERROR_NO_MEM; - goto out; - } - - for (i = 0; i < f_attrs->nluns; ++i) { - usbg_f_ms_lun_attrs *lun = f_attrs->luns[i]; - - /* - * id may be left unset in lun attrs but - * if it is set it has to be equal to position - * in lun array - */ - if (lun && lun->id >= 0 && lun->id != i) { - ret = USBG_ERROR_INVALID_PARAM; - goto err_lun_loop; - } - - ret = snprintf(lpath_end, space_left, "/lun.%d/", i); - if (ret >= space_left) { - ret = USBG_ERROR_PATH_TOO_LONG; - goto err_lun_loop; - } - - /* - * Check if dir exist and create it if needed - */ - dir = opendir(lpath); - if (dir) { - closedir(dir); - } else if (errno != ENOENT) { - ret = usbg_translate_error(errno); - goto err_lun_loop; - } else { - ret = mkdir(lpath, S_IRWXU|S_IRWXG|S_IRWXO); - if (!ret) { - /* - * If we have created a new directory in - * this function let's mark it so we can - * cleanup in case of error - */ - new_lun_mask[i] = 1; - } else { - ret = usbg_translate_error(errno); - goto err_lun_loop; - } - } - - /* if attributes has not been provided just go to next one */ - if (!lun) - continue; - - ret = usbg_set_f_ms_lun_attrs(lpath, "", lun); - if (ret != USBG_SUCCESS) - goto err_lun_loop; - } - - /* Check if function has more luns and remove them */ - *lpath_end = '\0'; - i = 0; - nmb = scandir(lpath, &dent, lun_select, lun_sort); - if (nmb < 0) { - ret = usbg_translate_error(errno); - goto err_lun_loop; - } - - for (i = 0; i < f_attrs->nluns; ++i) - free(dent[i]); - - for (; i < nmb; ++i) { - ret = usbg_rm_dir(lpath, dent[i]->d_name); - free(dent[i]); - /* There is no good way to recover form this */ - if (ret != USBG_SUCCESS) - goto err_rm_loop; - } - free(dent); - free(new_lun_mask); - - return USBG_SUCCESS; - -err_rm_loop: - while (++i < nmb) - free(dent[i]); - free(dent); - - i = f_attrs->nluns; -err_lun_loop: - /* array is null terminated so we may access lun[nluns] */ - for (; i >= 0; --i) { - if (!new_lun_mask[i]) - continue; - - ret = snprintf(lpath_end, space_left, "/lun.%d/", i); - if (ret >= space_left) { - /* - * This should not happen because if we were - * able to create this directory we should be - * also able to remove it. - */ - continue; - } - rmdir(lpath); - } - free(new_lun_mask); - -out: - return ret; -} - -int usbg_set_function_midi_attrs(usbg_function *f, - const usbg_f_midi_attrs *attrs) -{ - int ret; - - ret = usbg_write_dec(f->path, f->name, "index", attrs->index); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_write_string(f->path, f->name, "id", attrs->id); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_write_dec(f->path, f->name, "in_ports", attrs->in_ports); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_write_dec(f->path, f->name, "out_ports", attrs->out_ports); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_write_dec(f->path, f->name, "buflen", attrs->buflen); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_write_dec(f->path, f->name, "qlen", attrs->qlen); - -out: - return ret; -} - -int usbg_set_function_loopback_attrs(usbg_function *f, - const usbg_f_loopback_attrs *attrs) -{ - int ret; - - ret = usbg_write_dec(f->path, f->name, "buflen", attrs->buflen); - if (ret != USBG_SUCCESS) - goto out; - - ret = usbg_write_dec(f->path, f->name, "qlen", attrs->qlen); - -out: - return ret; -} - int usbg_set_function_attrs(usbg_function *f, const usbg_function_attrs *f_attrs) { int ret = USBG_ERROR_INVALID_PARAM; - int attrs_type; if (!f || !f_attrs) return ret; - attrs_type = usbg_lookup_function_attrs_type(f->type); - if (attrs_type < 0) - return ret; - - /* if attrs type is set, check if it has correct type */ - if (f_attrs->header.attrs_type && attrs_type != f_attrs->header.attrs_type) - return ret; - - switch (attrs_type) { - case USBG_F_ATTRS_SERIAL: - /* port_num attribute is read only so we accept only 0 - * and do nothing with it */ - ret = f_attrs->attrs.serial.port_num ? USBG_ERROR_INVALID_PARAM - : USBG_SUCCESS; - break; - - case USBG_F_ATTRS_NET: - ret = usbg_set_function_net_attrs(f, &f_attrs->attrs.net); - break; - - case USBG_F_ATTRS_PHONET: - /* ifname attribute is read only - * so we accept only empty string */ - ret = f_attrs->attrs.phonet.ifname && f_attrs->attrs.phonet.ifname[0] ? - USBG_ERROR_INVALID_PARAM : USBG_SUCCESS; - break; - - case USBG_F_ATTRS_FFS: - /* dev_name is a virtual attribute so allow only to use empty - * empty string which means nop */ - ret = f_attrs->attrs.ffs.dev_name && f_attrs->attrs.ffs.dev_name[0] ? - USBG_ERROR_INVALID_PARAM : USBG_SUCCESS; - break; - - case USBG_F_ATTRS_MS: - ret = usbg_set_function_ms_attrs(f, &f_attrs->attrs.ms); - break; - - case USBG_F_ATTRS_MIDI: - ret = usbg_set_function_midi_attrs(f, &f_attrs->attrs.midi); - break; - - case USBG_F_ATTRS_LOOPBACK: - ret = usbg_set_function_loopback_attrs(f, &f_attrs->attrs.loopback); - break; - - default: - ERROR("Unsupported function type\n"); - ret = USBG_ERROR_NOT_SUPPORTED; - break; - } - + ret = f->ops->set_attrs(f, f_attrs); +out: return ret; } diff --git a/src/usbg_schemes_libconfig.c b/src/usbg_schemes_libconfig.c index c8944ed..d8b92d2 100644 --- a/src/usbg_schemes_libconfig.c +++ b/src/usbg_schemes_libconfig.c @@ -313,263 +313,14 @@ static int usbg_export_gadget_configs(usbg_gadget *g, config_setting_t *root) return ret; } -static int usbg_export_f_net_attrs(usbg_f_net_attrs *attrs, - config_setting_t *root) -{ - config_setting_t *node; - char *addr; - char addr_buf[USBG_MAX_STR_LENGTH]; - int cfg_ret; - int ret = USBG_ERROR_NO_MEM; - - node = config_setting_add(root, "dev_addr", CONFIG_TYPE_STRING); - if (!node) - goto out; - - addr = usbg_ether_ntoa_r(&attrs->dev_addr, addr_buf); - cfg_ret = config_setting_set_string(node, addr); - if (cfg_ret != CONFIG_TRUE) { - ret = USBG_ERROR_OTHER_ERROR; - goto out; - } - - node = config_setting_add(root, "host_addr", CONFIG_TYPE_STRING); - if (!node) - goto out; - - addr = usbg_ether_ntoa_r(&attrs->host_addr, addr_buf); - cfg_ret = config_setting_set_string(node, addr); - if (cfg_ret != CONFIG_TRUE) { - ret = USBG_ERROR_OTHER_ERROR; - goto out; - } - - node = config_setting_add(root, "qmult", CONFIG_TYPE_INT); - if (!node) - goto out; - - cfg_ret = config_setting_set_int(node, attrs->qmult); - ret = cfg_ret == CONFIG_TRUE ? 0 : USBG_ERROR_OTHER_ERROR; - - /* if name is read only so we don't export it */ -out: - return ret; - -} - -static int usbg_export_f_ms_lun_attrs(usbg_f_ms_lun_attrs *lattrs, - config_setting_t *root) -{ - config_setting_t *node; - int cfg_ret; - int i; - int ret = USBG_ERROR_NO_MEM; - -#define BOOL_ATTR(_name) { .name = #_name, .value = &lattrs->_name, } - struct { - char *name; - bool *value; - } bool_attrs[] = { - BOOL_ATTR(cdrom), - BOOL_ATTR(ro), - BOOL_ATTR(nofua), - BOOL_ATTR(removable), - }; -#undef BOOL_ATTR - - for (i = 0; i < ARRAY_SIZE(bool_attrs); ++i) { - node = config_setting_add(root, bool_attrs[i].name, CONFIG_TYPE_BOOL); - if (!node) - goto out; - - cfg_ret = config_setting_set_bool(node, *(bool_attrs[i].value)); - if (cfg_ret != CONFIG_TRUE) { - ret = USBG_ERROR_OTHER_ERROR; - goto out; - } - } - - node = config_setting_add(root, "filename", CONFIG_TYPE_STRING); - if (!node) - goto out; - - cfg_ret = config_setting_set_string(node, lattrs->filename); - ret = cfg_ret == CONFIG_TRUE ? USBG_SUCCESS : USBG_ERROR_OTHER_ERROR; - -out: - return ret; -} - -static int usbg_export_f_ms_attrs(usbg_f_ms_attrs *attrs, - config_setting_t *root) -{ - config_setting_t *luns_node, *node; - int i; - int cfg_ret; - int ret = USBG_ERROR_NO_MEM; - - node = config_setting_add(root, "stall", CONFIG_TYPE_BOOL); - if (!node) - goto out; - - cfg_ret = config_setting_set_bool(node, attrs->stall); - if (cfg_ret != CONFIG_TRUE) { - ret = USBG_ERROR_OTHER_ERROR; - goto out; - } - - luns_node = config_setting_add(root, "luns", CONFIG_TYPE_LIST); - if (!luns_node) - goto out; - - for (i = 0; i < attrs->nluns; ++i) { - node = config_setting_add(luns_node, "", CONFIG_TYPE_GROUP); - if (!node) - goto out; - - ret = usbg_export_f_ms_lun_attrs(attrs->luns[i], node); - if (ret != USBG_SUCCESS) - goto out; - } - - ret = USBG_SUCCESS; -out: - return ret; - -} - -static int usbg_export_f_midi_attrs(usbg_f_midi_attrs *attrs, - config_setting_t *root) -{ - config_setting_t *node; - int cfg_ret; - int ret = USBG_ERROR_NO_MEM; - -#define ADD_F_MIDI_INT_ATTR(attr, minval) \ - do { \ - if ((int)attrs->attr < minval) { \ - ret = USBG_ERROR_INVALID_VALUE; \ - goto out; \ - } \ - node = config_setting_add(root, #attr, CONFIG_TYPE_INT);\ - if (!node) \ - goto out; \ - cfg_ret = config_setting_set_int(node, attrs->attr); \ - if (cfg_ret != CONFIG_TRUE) { \ - ret = USBG_ERROR_OTHER_ERROR; \ - goto out; \ - } \ - } while (0) - - ADD_F_MIDI_INT_ATTR(index, INT_MIN); - - node = config_setting_add(root, "id", CONFIG_TYPE_STRING); - if (!node) - goto out; - - cfg_ret = config_setting_set_string(node, attrs->id); - if (cfg_ret != CONFIG_TRUE) { - ret = USBG_ERROR_OTHER_ERROR; - goto out; - } - - ADD_F_MIDI_INT_ATTR(in_ports, 0); - ADD_F_MIDI_INT_ATTR(out_ports, 0); - ADD_F_MIDI_INT_ATTR(buflen, 0); - ADD_F_MIDI_INT_ATTR(qlen, 0); - -#undef ADD_F_MIDI_INT_ATTR - - ret = USBG_SUCCESS; -out: - return ret; -} - -static int usbg_export_f_loopback_attrs(usbg_f_loopback_attrs *attrs, - config_setting_t *root) -{ - config_setting_t *node; - int cfg_ret; - int ret = USBG_ERROR_NO_MEM; - -#define ADD_F_LOOPBACK_INT_ATTR(attr, minval) \ - do { \ - if ((int)attrs->attr < minval) { \ - ret = USBG_ERROR_INVALID_VALUE; \ - goto out; \ - } \ - node = config_setting_add(root, #attr, CONFIG_TYPE_INT);\ - if (!node) \ - goto out; \ - cfg_ret = config_setting_set_int(node, attrs->attr); \ - if (cfg_ret != CONFIG_TRUE) { \ - ret = USBG_ERROR_OTHER_ERROR; \ - goto out; \ - } \ - } while (0) - - ADD_F_LOOPBACK_INT_ATTR(buflen, 0); - ADD_F_LOOPBACK_INT_ATTR(qlen, 0); - -#undef ADD_F_LOOPBACK_INT_ATTR - - ret = USBG_SUCCESS; -out: - return ret; -} - static int usbg_export_function_attrs(usbg_function *f, config_setting_t *root) { - config_setting_t *node; - usbg_function_attrs f_attrs; - int usbg_ret, cfg_ret; - int ret = USBG_ERROR_NO_MEM; + int ret = USBG_ERROR_NOT_SUPPORTED; - usbg_ret = usbg_get_function_attrs(f, &f_attrs); - if (usbg_ret) { - ret = usbg_ret; + if (!f->ops->export) goto out; - } - switch (f_attrs.header.attrs_type) { - case USBG_F_ATTRS_SERIAL: - node = config_setting_add(root, "port_num", CONFIG_TYPE_INT); - if (!node) - goto out; - - cfg_ret = config_setting_set_int(node, f_attrs.attrs.serial.port_num); - ret = cfg_ret == CONFIG_TRUE ? 0 : USBG_ERROR_OTHER_ERROR; - break; - - case USBG_F_ATTRS_NET: - ret = usbg_export_f_net_attrs(&f_attrs.attrs.net, root); - break; - - case USBG_F_ATTRS_MS: - ret = usbg_export_f_ms_attrs(&f_attrs.attrs.ms, root); - break; - - case USBG_F_ATTRS_MIDI: - ret = usbg_export_f_midi_attrs(&f_attrs.attrs.midi, root); - break; - - case USBG_F_ATTRS_LOOPBACK: - ret = usbg_export_f_loopback_attrs(&f_attrs.attrs.loopback, root); - break; - - case USBG_F_ATTRS_PHONET: - /* Don't export ifname because it is read only */ - case USBG_F_ATTRS_FFS: - /* We don't need to export ffs attributes - * due to instance name export */ - ret = USBG_SUCCESS; - break; - default: - ERROR("Unsupported function type\n"); - ret = USBG_ERROR_NOT_SUPPORTED; - } - - usbg_cleanup_function_attrs(&f_attrs); + ret = f->ops->export(f, root); out: return ret; } @@ -937,10 +688,6 @@ out: return ret; } -#define usbg_config_is_int(node) (config_setting_type(node) == CONFIG_TYPE_INT) -#define usbg_config_is_string(node) \ - (config_setting_type(node) == CONFIG_TYPE_STRING) - static int split_function_label(const char *label, usbg_function_type *type, const char **instance) { @@ -983,337 +730,14 @@ static void usbg_set_failed_import(config_t **to_set, config_t *failed) *to_set = failed; } -static int usbg_import_f_net_attrs(config_setting_t *root, usbg_function *f) -{ - config_setting_t *node; - int ret = USBG_SUCCESS; - int qmult; - struct ether_addr *addr; - struct ether_addr addr_buf; - const char *str; - -#define GET_OPTIONAL_ADDR(NAME) \ - do { \ - node = config_setting_get_member(root, #NAME); \ - if (node) { \ - str = config_setting_get_string(node); \ - if (!str) { \ - ret = USBG_ERROR_INVALID_TYPE; \ - goto out; \ - } \ - \ - addr = ether_aton_r(str, &addr_buf); \ - if (!addr) { \ - ret = USBG_ERROR_INVALID_VALUE; \ - goto out; \ - } \ - ret = usbg_set_net_##NAME(f, addr); \ - if (ret != USBG_SUCCESS) \ - goto out; \ - } \ - } while (0) - - GET_OPTIONAL_ADDR(host_addr); - GET_OPTIONAL_ADDR(dev_addr); - -#undef GET_OPTIONAL_ADDR - - node = config_setting_get_member(root, "qmult"); - if (node) { - if (!usbg_config_is_int(node)) { - ret = USBG_ERROR_INVALID_TYPE; - goto out; - } - qmult = config_setting_get_int(node); - ret = usbg_set_net_qmult(f, qmult); - } - -out: - return ret; -} - -static int usbg_import_f_ms_lun_attrs(usbg_f_ms_lun_attrs *lattrs, - config_setting_t *root) -{ - config_setting_t *node; - int i; - int ret = USBG_ERROR_NO_MEM; - -#define BOOL_ATTR(_name, _default_val) \ - { .name = #_name, .value = &lattrs->_name, } - struct { - char *name; - bool *value; - bool default_val; - } bool_attrs[] = { - BOOL_ATTR(cdrom, false), - BOOL_ATTR(ro, false), - BOOL_ATTR(nofua, false), - BOOL_ATTR(removable, true), - }; -#undef BOOL_ATTR - - memset(lattrs, 0, sizeof(*lattrs)); - lattrs->id = -1; - - for (i = 0; i < ARRAY_SIZE(bool_attrs); ++i) { - *(bool_attrs[i].value) = bool_attrs[i].default_val; - - node = config_setting_get_member(root, bool_attrs[i].name); - if (!node) - continue; - - ret = config_setting_type(node); - switch (ret) { - case CONFIG_TYPE_INT: - *(bool_attrs[i].value) = !!config_setting_get_int(node); - break; - case CONFIG_TYPE_BOOL: - *(bool_attrs[i].value) = config_setting_get_bool(node); - break; - default: - ret = USBG_ERROR_INVALID_TYPE; - goto out; - } - } - - node = config_setting_get_member(root, "filename"); - if (node) { - if (!usbg_config_is_string(node)) { - ret = USBG_ERROR_INVALID_PARAM; - goto out; - } - lattrs->filename = (char *)config_setting_get_string(node); - } else { - lattrs->filename = ""; - } - - ret = USBG_SUCCESS; -out: - return ret; -} - -static int usbg_import_f_ms_attrs(config_setting_t *root, usbg_function *f) -{ - config_setting_t *luns_node, *node; - int i; - int ret = USBG_ERROR_NO_MEM; - usbg_function_attrs attrs; - usbg_f_ms_attrs *ms_attrs = &attrs.attrs.ms; - - memset(&attrs, 0, sizeof(attrs)); - - node = config_setting_get_member(root, "stall"); - if (node) { - ret = config_setting_type(node); - switch (ret) { - case CONFIG_TYPE_INT: - ms_attrs->stall = !!config_setting_get_int(node); - break; - case CONFIG_TYPE_BOOL: - ms_attrs->stall = config_setting_get_bool(node); - break; - default: - ret = USBG_ERROR_INVALID_TYPE; - goto out; - } - } - - luns_node = config_setting_get_member(root, "luns"); - if (!node) { - ret = USBG_ERROR_INVALID_PARAM; - goto out; - } - - if (!config_setting_is_list(luns_node)) { - ret = USBG_ERROR_INVALID_TYPE; - goto out; - } - - ms_attrs->nluns = config_setting_length(luns_node); - - ms_attrs->luns = calloc(ms_attrs->nluns + 1, sizeof(*(ms_attrs->luns))); - if (!ms_attrs->luns) { - ret = USBG_ERROR_NO_MEM; - goto out; - } - - for (i = 0; i < ms_attrs->nluns; ++i) { - node = config_setting_get_elem(luns_node, i); - if (!node) { - ret = USBG_ERROR_INVALID_FORMAT; - goto free_luns; - } - - if (!config_setting_is_group(node)) { - ret = USBG_ERROR_INVALID_TYPE; - goto free_luns; - } - - ms_attrs->luns[i] = malloc(sizeof(*(ms_attrs->luns[i]))); - if (!ms_attrs->luns[i]) { - ret = USBG_ERROR_NO_MEM; - goto free_luns; - } - - ret = usbg_import_f_ms_lun_attrs(ms_attrs->luns[i], node); - if (ret != USBG_SUCCESS) - goto free_luns; - } - - ret = usbg_set_function_attrs(f, &attrs); - -free_luns: - while (--i >= 0) - if (ms_attrs->luns[i]) - free(ms_attrs->luns[i]); - free(ms_attrs->luns); -out: - return ret; - -} - -static int usbg_import_f_midi_attrs(config_setting_t *root, usbg_function *f) -{ - config_setting_t *node; - int ret = USBG_ERROR_NO_MEM; - int tmp; - usbg_function_attrs attrs; - usbg_f_midi_attrs *midi_attrs = &attrs.attrs.midi; - - attrs.header.attrs_type = USBG_F_ATTRS_MIDI; - -#define ADD_F_MIDI_INT_ATTR(attr, defval, minval) \ - do { \ - node = config_setting_get_member(root, #attr); \ - if (node) { \ - if (!usbg_config_is_int(node)) { \ - ret = USBG_ERROR_INVALID_TYPE; \ - goto out; \ - } \ - tmp = config_setting_get_int(node); \ - if (tmp < minval) { \ - ret = USBG_ERROR_INVALID_VALUE; \ - goto out; \ - } \ - midi_attrs->attr = tmp; \ - } else { \ - midi_attrs->attr = defval; \ - } \ - } while (0) - - ADD_F_MIDI_INT_ATTR(index, -1, INT_MIN); - ADD_F_MIDI_INT_ATTR(in_ports, 1, 0); - ADD_F_MIDI_INT_ATTR(out_ports, 1, 0); - ADD_F_MIDI_INT_ATTR(buflen, 256, 0); - ADD_F_MIDI_INT_ATTR(qlen, 32, 0); - -#undef ADD_F_MIDI_INT_ATTR - - node = config_setting_get_member(root, "id"); - if (node) { - if (!usbg_config_is_string(node)) { - ret = USBG_ERROR_INVALID_TYPE; - goto out; - } - - midi_attrs->id = config_setting_get_string(node); - } else { - midi_attrs->id = ""; - } - - - ret = usbg_set_function_attrs(f, &attrs); -out: - return ret; -} - -static int usbg_import_f_loopback_attrs(config_setting_t *root, usbg_function *f) -{ - config_setting_t *node; - int ret = USBG_ERROR_NO_MEM; - int tmp; - usbg_function_attrs attrs; - usbg_f_loopback_attrs *loopback_attrs = &attrs.attrs.loopback; - - attrs.header.attrs_type = USBG_F_ATTRS_LOOPBACK; - -#define ADD_F_LOOPBACK_INT_ATTR(attr, defval, minval) \ - do { \ - node = config_setting_get_member(root, #attr); \ - if (node) { \ - if (!usbg_config_is_int(node)) { \ - ret = USBG_ERROR_INVALID_TYPE; \ - goto out; \ - } \ - tmp = config_setting_get_int(node); \ - if (tmp < minval) { \ - ret = USBG_ERROR_INVALID_VALUE; \ - goto out; \ - } \ - loopback_attrs->attr = tmp; \ - } else { \ - loopback_attrs->attr = defval; \ - } \ - } while (0) - - ADD_F_LOOPBACK_INT_ATTR(buflen, 4096, 0); - ADD_F_LOOPBACK_INT_ATTR(qlen, 32, 0); - -#undef ADD_F_LOOPBACK_INT_ATTR - - ret = usbg_set_function_attrs(f, &attrs); -out: - return ret; -} - static int usbg_import_function_attrs(config_setting_t *root, usbg_function *f) { - int ret = USBG_SUCCESS; - int attrs_type; + int ret = USBG_ERROR_NOT_SUPPORTED; - attrs_type = usbg_lookup_function_attrs_type(f->type); - if (attrs_type < 0) { - ret = attrs_type; + if (!f->ops->import) goto out; - } - - switch (attrs_type) { - case USBG_F_ATTRS_SERIAL: - /* Don't import port_num because it is read only */ - break; - - case USBG_F_ATTRS_NET: - ret = usbg_import_f_net_attrs(root, f); - break; - - case USBG_F_ATTRS_PHONET: - /* Don't import ifname because it is read only */ - break; - - case USBG_F_ATTRS_FFS: - /* We don't need to import ffs attributes - * due to instance name import */ - break; - - case USBG_F_ATTRS_MS: - ret = usbg_import_f_ms_attrs(root, f); - break; - - case USBG_F_ATTRS_MIDI: - ret = usbg_import_f_midi_attrs(root, f); - break; - - case USBG_F_ATTRS_LOOPBACK: - ret = usbg_import_f_loopback_attrs(root, f); - break; - - default: - ERROR("Unsupported function type\n"); - ret = USBG_ERROR_NOT_SUPPORTED; - break; - } + ret = f->ops->import(f, root); out: return ret; }