mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git
synced 2026-04-18 03:23:53 -04:00
usb: gadget: u_ether: add gether_opts for config caching
Currently, the net_device is allocated when the function instance is created (e.g., in ncm_alloc_inst()). While this allows userspace to configure the device early, it decouples the net_device lifecycle from the actual USB connection state (bind/unbind). The goal is to defer net_device creation to the bind callback to properly align the lifecycle with its parent gadget device. However, deferring net_device allocation would prevent userspace from configuring parameters (like interface name or MAC address) before the net_device exists. Introduce a new structure, struct gether_opts, associated with the usb_function_instance, to cache settings independently of the net_device. These settings include the interface name pattern, MAC addresses (device and host), queue multiplier, and address assignment type. New helper functions are added: - gether_setup_opts_default(): Initializes struct gether_opts with defaults, including random MAC addresses. - gether_apply_opts(): Applies the cached options from a struct gether_opts to a valid net_device. To expose these options to userspace, new configfs macros (USB_ETHER_OPTS_ITEM and USB_ETHER_OPTS_ATTR_*) are defined in u_ether_configfs.h. These attributes are part of the function instance's configfs group. This refactoring is a preparatory step. It allows the subsequent patch to safely move the net_device allocation from the instance creation phase to the bind phase without losing the ability to pre-configure the interface via configfs. Signed-off-by: Kuen-Han Tsai <khtsai@google.com> Link: https://patch.msgid.link/20251230-ncm-refactor-v1-1-793e347bc7a7@google.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
c5177144b5
commit
e065c6a7e4
@@ -13,6 +13,12 @@
|
||||
#ifndef __U_ETHER_CONFIGFS_H
|
||||
#define __U_ETHER_CONFIGFS_H
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#define USB_ETHERNET_CONFIGFS_ITEM(_f_) \
|
||||
static void _f_##_attr_release(struct config_item *item) \
|
||||
{ \
|
||||
@@ -197,4 +203,174 @@ out: \
|
||||
\
|
||||
CONFIGFS_ATTR(_f_##_opts_, _n_)
|
||||
|
||||
#define USB_ETHER_OPTS_ITEM(_f_) \
|
||||
static void _f_##_attr_release(struct config_item *item) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
\
|
||||
usb_put_function_instance(&opts->func_inst); \
|
||||
} \
|
||||
\
|
||||
static struct configfs_item_operations _f_##_item_ops = { \
|
||||
.release = _f_##_attr_release, \
|
||||
}
|
||||
|
||||
#define USB_ETHER_OPTS_ATTR_DEV_ADDR(_f_) \
|
||||
static ssize_t _f_##_opts_dev_addr_show(struct config_item *item, \
|
||||
char *page) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
\
|
||||
guard(mutex)(&opts->lock); \
|
||||
return sysfs_emit(page, "%pM\n", opts->net_opts.dev_mac); \
|
||||
} \
|
||||
\
|
||||
static ssize_t _f_##_opts_dev_addr_store(struct config_item *item, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
u8 new_addr[ETH_ALEN]; \
|
||||
const char *p = page; \
|
||||
\
|
||||
guard(mutex)(&opts->lock); \
|
||||
if (opts->refcnt) \
|
||||
return -EBUSY; \
|
||||
\
|
||||
for (int i = 0; i < ETH_ALEN; i++) { \
|
||||
unsigned char num; \
|
||||
if ((*p == '.') || (*p == ':')) \
|
||||
p++; \
|
||||
num = hex_to_bin(*p++) << 4; \
|
||||
num |= hex_to_bin(*p++); \
|
||||
new_addr[i] = num; \
|
||||
} \
|
||||
if (!is_valid_ether_addr(new_addr)) \
|
||||
return -EINVAL; \
|
||||
memcpy(opts->net_opts.dev_mac, new_addr, ETH_ALEN); \
|
||||
opts->net_opts.addr_assign_type = NET_ADDR_SET; \
|
||||
return len; \
|
||||
} \
|
||||
\
|
||||
CONFIGFS_ATTR(_f_##_opts_, dev_addr)
|
||||
|
||||
#define USB_ETHER_OPTS_ATTR_HOST_ADDR(_f_) \
|
||||
static ssize_t _f_##_opts_host_addr_show(struct config_item *item, \
|
||||
char *page) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
\
|
||||
guard(mutex)(&opts->lock); \
|
||||
return sysfs_emit(page, "%pM\n", opts->net_opts.host_mac); \
|
||||
} \
|
||||
\
|
||||
static ssize_t _f_##_opts_host_addr_store(struct config_item *item, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
u8 new_addr[ETH_ALEN]; \
|
||||
const char *p = page; \
|
||||
\
|
||||
guard(mutex)(&opts->lock); \
|
||||
if (opts->refcnt) \
|
||||
return -EBUSY; \
|
||||
\
|
||||
for (int i = 0; i < ETH_ALEN; i++) { \
|
||||
unsigned char num; \
|
||||
if ((*p == '.') || (*p == ':')) \
|
||||
p++; \
|
||||
num = hex_to_bin(*p++) << 4; \
|
||||
num |= hex_to_bin(*p++); \
|
||||
new_addr[i] = num; \
|
||||
} \
|
||||
if (!is_valid_ether_addr(new_addr)) \
|
||||
return -EINVAL; \
|
||||
memcpy(opts->net_opts.host_mac, new_addr, ETH_ALEN); \
|
||||
return len; \
|
||||
} \
|
||||
\
|
||||
CONFIGFS_ATTR(_f_##_opts_, host_addr)
|
||||
|
||||
#define USB_ETHER_OPTS_ATTR_QMULT(_f_) \
|
||||
static ssize_t _f_##_opts_qmult_show(struct config_item *item, \
|
||||
char *page) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
\
|
||||
guard(mutex)(&opts->lock); \
|
||||
return sysfs_emit(page, "%u\n", opts->net_opts.qmult); \
|
||||
} \
|
||||
\
|
||||
static ssize_t _f_##_opts_qmult_store(struct config_item *item, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
u32 val; \
|
||||
int ret; \
|
||||
\
|
||||
guard(mutex)(&opts->lock); \
|
||||
if (opts->refcnt) \
|
||||
return -EBUSY; \
|
||||
\
|
||||
ret = kstrtou32(page, 0, &val); \
|
||||
if (ret) \
|
||||
return ret; \
|
||||
\
|
||||
opts->net_opts.qmult = val; \
|
||||
return len; \
|
||||
} \
|
||||
\
|
||||
CONFIGFS_ATTR(_f_##_opts_, qmult)
|
||||
|
||||
#define USB_ETHER_OPTS_ATTR_IFNAME(_f_) \
|
||||
static ssize_t _f_##_opts_ifname_show(struct config_item *item, \
|
||||
char *page) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
const char *name; \
|
||||
\
|
||||
guard(mutex)(&opts->lock); \
|
||||
rtnl_lock(); \
|
||||
if (opts->net_opts.ifname_set) \
|
||||
name = opts->net_opts.name; \
|
||||
else if (opts->net) \
|
||||
name = netdev_name(opts->net); \
|
||||
else \
|
||||
name = "(inactive net_device)"; \
|
||||
rtnl_unlock(); \
|
||||
return sysfs_emit(page, "%s\n", name); \
|
||||
} \
|
||||
\
|
||||
static ssize_t _f_##_opts_ifname_store(struct config_item *item, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
char tmp[IFNAMSIZ]; \
|
||||
const char *p; \
|
||||
size_t c_len = len; \
|
||||
\
|
||||
if (c_len > 0 && page[c_len - 1] == '\n') \
|
||||
c_len--; \
|
||||
\
|
||||
if (c_len >= sizeof(tmp)) \
|
||||
return -E2BIG; \
|
||||
\
|
||||
strscpy(tmp, page, c_len + 1); \
|
||||
if (!dev_valid_name(tmp)) \
|
||||
return -EINVAL; \
|
||||
\
|
||||
/* Require exactly one %d */ \
|
||||
p = strchr(tmp, '%'); \
|
||||
if (!p || p[1] != 'd' || strchr(p + 2, '%')) \
|
||||
return -EINVAL; \
|
||||
\
|
||||
guard(mutex)(&opts->lock); \
|
||||
if (opts->refcnt) \
|
||||
return -EBUSY; \
|
||||
strscpy(opts->net_opts.name, tmp, sizeof(opts->net_opts.name)); \
|
||||
opts->net_opts.ifname_set = true; \
|
||||
return len; \
|
||||
} \
|
||||
\
|
||||
CONFIGFS_ATTR(_f_##_opts_, ifname)
|
||||
|
||||
#endif /* __U_ETHER_CONFIGFS_H */
|
||||
|
||||
Reference in New Issue
Block a user