mirror of
https://github.com/linux-usb-gadgets/libusbgx.git
synced 2025-07-13 14:49:43 +03:00
In commit: abf422bffc
[
Author: Stefan Agner <stefan.agner@toradex.com>
Date: Tue Jan 24 14:22:25 2017 -0800
libusbgx: Add interface name for Feature Descriptors
This adds interface name required for "Feature Descriptors". If
specified, we can assume that a Feature Descriptor with the
interface name of the specified string is understood by the
kernel (e.g. interface.rndis).
]
it only added Feature Descriptors for RNDIS, NCM also needs that, or
else it could not be recognized by Windows systems.
Add Feature Descriptors interface name for NCM.
Signed-off-by: Ming Liu <liu.ming50@gmail.com>
309 lines
7 KiB
C
309 lines
7 KiB
C
/*
|
|
* 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 "usbg/function/net.h"
|
|
|
|
#include <malloc.h>
|
|
#ifdef HAS_GADGET_SCHEMES
|
|
#include <libconfig.h>
|
|
#endif
|
|
|
|
struct usbg_f_net {
|
|
struct usbg_function func;
|
|
};
|
|
|
|
#define NET_DEC_ATTR(_name) \
|
|
{ \
|
|
.name = #_name, \
|
|
.ro = false, \
|
|
.offset = offsetof(struct usbg_f_net_attrs, _name), \
|
|
.get = usbg_get_dec, \
|
|
.set = usbg_set_dec, \
|
|
.import = usbg_get_config_node_int, \
|
|
.export = usbg_set_config_node_int, \
|
|
}
|
|
|
|
#define NET_RO_STRING_ATTR(_name) \
|
|
{ \
|
|
.name = #_name, \
|
|
.ro = true, \
|
|
.offset = offsetof(struct usbg_f_net_attrs, _name), \
|
|
.get = usbg_get_string, \
|
|
.export = usbg_set_config_node_string, \
|
|
}
|
|
|
|
#define NET_ETHER_ADDR_ATTR(_name) \
|
|
{ \
|
|
.name = #_name, \
|
|
.ro = false, \
|
|
.offset = offsetof(struct usbg_f_net_attrs, _name), \
|
|
.get = usbg_get_ether_addr, \
|
|
.set = usbg_set_ether_addr, \
|
|
.import = usbg_get_config_node_ether_addr, \
|
|
.export = usbg_set_config_node_ether_addr, \
|
|
}
|
|
|
|
static struct {
|
|
const char *name;
|
|
bool ro;
|
|
size_t offset;
|
|
usbg_attr_get_func get;
|
|
usbg_attr_set_func set;
|
|
usbg_import_node_func import;
|
|
usbg_export_node_func export;
|
|
} net_attr[USBG_F_NET_ATTR_MAX] = {
|
|
[USBG_F_NET_DEV_ADDR] = NET_ETHER_ADDR_ATTR(dev_addr),
|
|
[USBG_F_NET_HOST_ADDR] = NET_ETHER_ADDR_ATTR(host_addr),
|
|
[USBG_F_NET_IFNAME] = NET_RO_STRING_ATTR(ifname),
|
|
[USBG_F_NET_QMULT] = NET_DEC_ATTR(qmult),
|
|
[USBG_F_NET_CLASS] = NET_DEC_ATTR(class_),
|
|
[USBG_F_NET_SUBCLASS] = NET_DEC_ATTR(subclass),
|
|
[USBG_F_NET_PROTOCOL] = NET_DEC_ATTR(protocol)
|
|
};
|
|
|
|
#undef NET_DEC_ATTR
|
|
#undef NET_STRING_ATTR
|
|
|
|
GENERIC_ALLOC_INST(ether, struct usbg_f_net, func);
|
|
|
|
GENERIC_FREE_INST(ether, struct usbg_f_net, func);
|
|
|
|
static int ether_set_attrs(struct usbg_function *f, void *f_attrs)
|
|
{
|
|
const struct usbg_f_net_attrs *attrs = f_attrs;
|
|
|
|
/* ifname is read only so we accept only empty string for this param */
|
|
if (attrs->ifname && attrs->ifname[0])
|
|
return USBG_ERROR_INVALID_PARAM;
|
|
|
|
return usbg_f_net_set_attrs(usbg_to_net_function(f), attrs);
|
|
}
|
|
|
|
static int ether_get_attrs(struct usbg_function *f, void *f_attrs)
|
|
{
|
|
struct usbg_f_net_attrs *attrs = f_attrs;
|
|
|
|
return usbg_f_net_get_attrs(usbg_to_net_function(f), attrs);
|
|
}
|
|
|
|
static void ether_cleanup_attrs(struct usbg_function *f, void *f_attrs)
|
|
{
|
|
usbg_f_net_cleanup_attrs(f_attrs);
|
|
}
|
|
|
|
#ifdef HAS_GADGET_SCHEMES
|
|
|
|
static int ether_libconfig_import(struct usbg_function *f,
|
|
config_setting_t *root)
|
|
{
|
|
struct usbg_f_net *nf = usbg_to_net_function(f);
|
|
union usbg_f_net_attr_val val;
|
|
int i;
|
|
int ret = 0;
|
|
|
|
for (i = USBG_F_NET_ATTR_MIN; i < USBG_F_NET_ATTR_MAX; ++i) {
|
|
if (net_attr[i].ro)
|
|
continue;
|
|
|
|
ret = net_attr[i].import(root, net_attr[i].name, &val);
|
|
/* node not found */
|
|
if (ret == 0)
|
|
continue;
|
|
/* error */
|
|
if (ret < 0)
|
|
break;
|
|
|
|
ret = usbg_f_net_set_attr_val(nf, i, val);
|
|
if (ret)
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ether_libconfig_export(struct usbg_function *f,
|
|
config_setting_t *root)
|
|
{
|
|
struct usbg_f_net *nf = usbg_to_net_function(f);
|
|
union usbg_f_net_attr_val val;
|
|
int i;
|
|
int ret = 0;
|
|
|
|
for (i = USBG_F_NET_ATTR_MIN; i < USBG_F_NET_ATTR_MAX; ++i) {
|
|
if (net_attr[i].ro)
|
|
continue;
|
|
|
|
ret = usbg_f_net_get_attr_val(nf, i, &val);
|
|
if (ret)
|
|
break;
|
|
|
|
ret = net_attr[i].export(root, net_attr[i].name, &val);
|
|
if (ret)
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define ETHER_LIBCONFIG_DEP_OPS \
|
|
.import = ether_libconfig_import, \
|
|
.export = ether_libconfig_export
|
|
|
|
#else
|
|
|
|
#define ETHER_LIBCONFIG_DEP_OPS
|
|
|
|
#endif /* HAS_GADGET_SCHEMES */
|
|
|
|
#define ETHER_FUNCTION_OPTS \
|
|
.alloc_inst = ether_alloc_inst, \
|
|
.free_inst = ether_free_inst, \
|
|
.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
|
|
};
|
|
|
|
static char *ncm_os_desc_ifnames[] = {
|
|
"ncm",
|
|
NULL
|
|
};
|
|
|
|
struct usbg_function_type usbg_f_type_ncm = {
|
|
.name = "ncm",
|
|
.os_desc_iname = ncm_os_desc_ifnames,
|
|
ETHER_FUNCTION_OPTS
|
|
};
|
|
|
|
struct usbg_function_type usbg_f_type_eem = {
|
|
.name = "eem",
|
|
ETHER_FUNCTION_OPTS
|
|
};
|
|
|
|
static char *rndis_os_desc_ifnames[] = {
|
|
"rndis",
|
|
NULL
|
|
};
|
|
|
|
struct usbg_function_type usbg_f_type_rndis = {
|
|
.name = "rndis",
|
|
.os_desc_iname = rndis_os_desc_ifnames,
|
|
ETHER_FUNCTION_OPTS
|
|
};
|
|
|
|
/* API implementation */
|
|
|
|
usbg_f_net *usbg_to_net_function(usbg_function *f)
|
|
{
|
|
return f->ops == &usbg_f_type_ecm
|
|
|| f->ops == &usbg_f_type_subset
|
|
|| f->ops == &usbg_f_type_ncm
|
|
|| f->ops == &usbg_f_type_eem
|
|
|| f->ops == &usbg_f_type_rndis ?
|
|
container_of(f, struct usbg_f_net, func) : NULL;
|
|
}
|
|
|
|
usbg_function *usbg_from_net_function(usbg_f_net *nf)
|
|
{
|
|
return &nf->func;
|
|
}
|
|
|
|
int usbg_f_net_get_attrs(usbg_f_net *nf,
|
|
struct usbg_f_net_attrs *attrs)
|
|
{
|
|
int i;
|
|
int ret = 0;
|
|
|
|
for (i = USBG_F_NET_ATTR_MIN; i < USBG_F_NET_ATTR_MAX; ++i) {
|
|
ret = usbg_f_net_get_attr_val(nf, i,
|
|
(union usbg_f_net_attr_val *)
|
|
((char *)attrs
|
|
+ net_attr[i].offset));
|
|
if (ret)
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
int usbg_f_net_set_attrs(usbg_f_net *nf,
|
|
const struct usbg_f_net_attrs *attrs)
|
|
{
|
|
int i;
|
|
int ret = 0;
|
|
|
|
for (i = USBG_F_NET_ATTR_MIN; i < USBG_F_NET_ATTR_MAX; ++i) {
|
|
if (net_attr[i].ro)
|
|
continue;
|
|
|
|
ret = usbg_f_net_set_attr_val(nf, i,
|
|
*(union usbg_f_net_attr_val *)
|
|
((char *)attrs
|
|
+ net_attr[i].offset));
|
|
if (ret)
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
int usbg_f_net_get_attr_val(usbg_f_net *nf, enum usbg_f_net_attr attr,
|
|
union usbg_f_net_attr_val *val)
|
|
{
|
|
return net_attr[attr].get(nf->func.path, nf->func.name,
|
|
net_attr[attr].name, val);
|
|
}
|
|
|
|
int usbg_f_net_set_attr_val(usbg_f_net *nf, enum usbg_f_net_attr attr,
|
|
union usbg_f_net_attr_val val)
|
|
{
|
|
return net_attr[attr].ro ?
|
|
USBG_ERROR_INVALID_PARAM :
|
|
net_attr[attr].set(nf->func.path, nf->func.name,
|
|
net_attr[attr].name, &val);
|
|
}
|
|
|
|
int usbg_f_net_get_ifname_s(usbg_f_net *nf, char *buf, int len)
|
|
{
|
|
struct usbg_function *f;
|
|
int ret;
|
|
|
|
if (!nf || !buf)
|
|
return USBG_ERROR_INVALID_PARAM;
|
|
|
|
f = &nf->func;
|
|
/*
|
|
* TODO:
|
|
* Rework usbg_common to make this function consistent with doc.
|
|
* This below is only an ugly hack
|
|
*/
|
|
ret = usbg_read_string_limited(f->path, f->name, "ifname", buf, len);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = strlen(buf);
|
|
out:
|
|
return ret;
|
|
}
|