mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:04:41 +01: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:
parent
c5177144b5
commit
e065c6a7e4
3 changed files with 234 additions and 0 deletions
|
|
@ -1039,6 +1039,36 @@ int gether_set_ifname(struct net_device *net, const char *name, int len)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(gether_set_ifname);
|
||||
|
||||
void gether_setup_opts_default(struct gether_opts *opts, const char *name)
|
||||
{
|
||||
opts->qmult = QMULT_DEFAULT;
|
||||
snprintf(opts->name, sizeof(opts->name), "%s%%d", name);
|
||||
eth_random_addr(opts->dev_mac);
|
||||
opts->addr_assign_type = NET_ADDR_RANDOM;
|
||||
eth_random_addr(opts->host_mac);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gether_setup_opts_default);
|
||||
|
||||
void gether_apply_opts(struct net_device *net, struct gether_opts *opts)
|
||||
{
|
||||
struct eth_dev *dev = netdev_priv(net);
|
||||
|
||||
dev->qmult = opts->qmult;
|
||||
|
||||
if (opts->ifname_set) {
|
||||
strscpy(net->name, opts->name, sizeof(net->name));
|
||||
dev->ifname_set = true;
|
||||
}
|
||||
|
||||
memcpy(dev->host_mac, opts->host_mac, sizeof(dev->host_mac));
|
||||
|
||||
if (opts->addr_assign_type == NET_ADDR_SET) {
|
||||
memcpy(dev->dev_mac, opts->dev_mac, sizeof(dev->dev_mac));
|
||||
net->addr_assign_type = opts->addr_assign_type;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gether_apply_opts);
|
||||
|
||||
void gether_suspend(struct gether *link)
|
||||
{
|
||||
struct eth_dev *dev = link->ioport;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,31 @@
|
|||
|
||||
struct eth_dev;
|
||||
|
||||
/**
|
||||
* struct gether_opts - Options for Ethernet gadget function instances
|
||||
* @name: Pattern for the network interface name (e.g., "usb%d").
|
||||
* Used to generate the net device name.
|
||||
* @qmult: Queue length multiplier for high/super speed.
|
||||
* @host_mac: The MAC address to be used by the host side.
|
||||
* @dev_mac: The MAC address to be used by the device side.
|
||||
* @ifname_set: True if the interface name pattern has been set by userspace.
|
||||
* @addr_assign_type: The method used for assigning the device MAC address
|
||||
* (e.g., NET_ADDR_RANDOM, NET_ADDR_SET).
|
||||
*
|
||||
* This structure caches network-related settings provided through configfs
|
||||
* before the net_device is fully instantiated. This allows for early
|
||||
* configuration while deferring net_device allocation until the function
|
||||
* is bound.
|
||||
*/
|
||||
struct gether_opts {
|
||||
char name[IFNAMSIZ];
|
||||
unsigned int qmult;
|
||||
u8 host_mac[ETH_ALEN];
|
||||
u8 dev_mac[ETH_ALEN];
|
||||
bool ifname_set;
|
||||
unsigned char addr_assign_type;
|
||||
};
|
||||
|
||||
/*
|
||||
* This represents the USB side of an "ethernet" link, managed by a USB
|
||||
* function which provides control and (maybe) framing. Two functions
|
||||
|
|
@ -259,6 +284,9 @@ int gether_set_ifname(struct net_device *net, const char *name, int len);
|
|||
|
||||
void gether_cleanup(struct eth_dev *dev);
|
||||
|
||||
void gether_setup_opts_default(struct gether_opts *opts, const char *name);
|
||||
void gether_apply_opts(struct net_device *net, struct gether_opts *opts);
|
||||
|
||||
void gether_suspend(struct gether *link);
|
||||
void gether_resume(struct gether *link);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue