1
0
Fork 0
mirror of https://github.com/linux-usb-gadgets/libusbgx.git synced 2025-07-20 15:05:05 +03:00

libusbgx: Add internal API for defining function types

Adding support for a new function type was quite complicated.
Moreover the main library source fail was growing realy fast.

As a solution introduce internal OO API for defining function.
Thanks to this support for each function may be placed in
a separate file.

Signed-off-by: Krzysztof Opasiak <k.opasiak@samsung.com>
This commit is contained in:
Krzysztof Opasiak 2015-12-23 00:17:25 +01:00
parent 639a329af5
commit 013b9cc990
11 changed files with 1674 additions and 1284 deletions

View file

@ -21,6 +21,7 @@
#include <libconfig.h>
#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 */

View file

@ -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

248
src/function/ether.c Normal file
View file

@ -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 <malloc.h>
#ifdef HAS_LIBCONFIG
#include <libconfig.h>
#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
};

80
src/function/ffs.c Normal file
View file

@ -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 <libconfig.h>
#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,
};

151
src/function/loopback.c Normal file
View file

@ -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 <libconfig.h>
#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
};

228
src/function/midi.c Normal file
View file

@ -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 <malloc.h>
#ifdef HAS_LIBCONFIG
#include <libconfig.h>
#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
};

666
src/function/ms.c Normal file
View file

@ -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 <malloc.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef HAS_LIBCONFIG
#include <libconfig.h>
#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,
};

79
src/function/phonet.c Normal file
View file

@ -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 <malloc.h>
#ifdef HAS_LIBCONFIG
#include <libconfig.h>
#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,
};

113
src/function/serial.c Normal file
View file

@ -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 <libconfig.h>
#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
};

View file

@ -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;
}

View file

@ -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;
}