summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/function
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-17 09:36:43 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-17 09:36:43 -0800
commit17f8d2009367c3da82882f70ccbdca9f8c7b5f20 (patch)
tree80b8dc44dc22336501072cdc3b3e49d14c3867e1 /drivers/usb/gadget/function
parent3ad7945754000d868ed86315d33085a914c422c1 (diff)
parentda87d45b195148d670ab995367d52aa9e8a9a1fa (diff)
Merge tag 'usb-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB / Thunderbolt updates from Greg KH: "Here is the "big" set of USB and Thunderbolt driver updates for 7.0-rc1. Overall more lines were removed than added, thanks to dropping the obsolete isp1362 USB host controller driver, always a nice change. Other than that, nothing major happening here, highlights are: - lots of dwc3 driver updates and new hardware support added - usb gadget function driver updates - usb phy driver updates - typec driver updates and additions - USB rust binding updates for syntax and formatting changes - more usb serial device ids added - other smaller USB core and driver updates and additions All of these have been in linux-next for a long time, with no reported problems" * tag 'usb-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (77 commits) usb: typec: ucsi: Add Thunderbolt alternate mode support usb: typec: hd3ss3220: Check if regulator needs to be switched usb: phy: tegra: parametrize PORTSC1 register offset usb: phy: tegra: parametrize HSIC PTS value usb: phy: tegra: return error value from utmi_wait_register usb: phy: tegra: cosmetic fixes dt-bindings: usb: renesas,usbhs: Add RZ/G3E SoC support usb: dwc2: fix resume failure if dr_mode is host usb: cdns3: fix role switching during resume usb: dwc3: gadget: Move vbus draw to workqueue context USB: serial: option: add Telit FN920C04 RNDIS compositions usb: dwc3: Log dwc3 address in traces usb: gadget: tegra-xudc: Add handling for BLCG_COREPLL_PWRDN usb: phy: tegra: add HSIC support usb: phy: tegra: use phy type directly usb: typec: ucsi: Enforce mode selection for cros_ec_ucsi usb: typec: ucsi: Support mode selection to activate altmodes usb: typec: Introduce mode_selection bit usb: typec: Implement mode selection usb: typec: Expose alternate mode priority via sysfs ...
Diffstat (limited to 'drivers/usb/gadget/function')
-rw-r--r--drivers/usb/gadget/function/f_acm.c2
-rw-r--r--drivers/usb/gadget/function/f_fs.c26
-rw-r--r--drivers/usb/gadget/function/f_hid.c2
-rw-r--r--drivers/usb/gadget/function/f_loopback.c2
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c6
-rw-r--r--drivers/usb/gadget/function/f_midi.c112
-rw-r--r--drivers/usb/gadget/function/f_midi2.c10
-rw-r--r--drivers/usb/gadget/function/f_ncm.c128
-rw-r--r--drivers/usb/gadget/function/f_obex.c2
-rw-r--r--drivers/usb/gadget/function/f_phonet.c2
-rw-r--r--drivers/usb/gadget/function/f_printer.c2
-rw-r--r--drivers/usb/gadget/function/f_serial.c2
-rw-r--r--drivers/usb/gadget/function/f_sourcesink.c54
-rw-r--r--drivers/usb/gadget/function/f_tcm.c5
-rw-r--r--drivers/usb/gadget/function/f_uac1.c2
-rw-r--r--drivers/usb/gadget/function/f_uac1_legacy.c2
-rw-r--r--drivers/usb/gadget/function/f_uac2.c2
-rw-r--r--drivers/usb/gadget/function/g_zero.h1
-rw-r--r--drivers/usb/gadget/function/u_ether.c45
-rw-r--r--drivers/usb/gadget/function/u_ether.h30
-rw-r--r--drivers/usb/gadget/function/u_ether_configfs.h179
-rw-r--r--drivers/usb/gadget/function/u_midi.h2
-rw-r--r--drivers/usb/gadget/function/u_ncm.h4
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.c36
24 files changed, 483 insertions, 175 deletions
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index 106046e17c4e..0ad857f1f325 100644
--- a/drivers/usb/gadget/function/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -793,7 +793,7 @@ static void acm_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations acm_item_ops = {
+static const struct configfs_item_operations acm_item_ops = {
.release = acm_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index fa467a40949d..2c6986e381ca 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -1509,7 +1509,7 @@ static int ffs_dmabuf_attach(struct file *file, int fd)
goto err_dmabuf_detach;
}
- dir = epfile->in ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ dir = epfile->in ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
err = ffs_dma_resv_lock(dmabuf, nonblock);
if (err)
@@ -1639,7 +1639,7 @@ static int ffs_dmabuf_transfer(struct file *file,
/* Make sure we don't have writers */
timeout = nonblock ? 0 : msecs_to_jiffies(DMABUF_ENQUEUE_TIMEOUT_MS);
retl = dma_resv_wait_timeout(dmabuf->resv,
- dma_resv_usage_rw(epfile->in),
+ dma_resv_usage_rw(!epfile->in),
true, timeout);
if (retl == 0)
retl = -EBUSY;
@@ -1684,7 +1684,7 @@ static int ffs_dmabuf_transfer(struct file *file,
dma_fence_init(&fence->base, &ffs_dmabuf_fence_ops,
&priv->lock, priv->context, seqno);
- resv_dir = epfile->in ? DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ;
+ resv_dir = epfile->in ? DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE;
dma_resv_add_fence(dmabuf->resv, &fence->base, resv_dir);
dma_resv_unlock(dmabuf->resv);
@@ -1744,10 +1744,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
{
int fd;
- if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) {
- ret = -EFAULT;
- break;
- }
+ if (copy_from_user(&fd, (void __user *)value, sizeof(fd)))
+ return -EFAULT;
return ffs_dmabuf_attach(file, fd);
}
@@ -1755,10 +1753,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
{
int fd;
- if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) {
- ret = -EFAULT;
- break;
- }
+ if (copy_from_user(&fd, (void __user *)value, sizeof(fd)))
+ return -EFAULT;
return ffs_dmabuf_detach(file, fd);
}
@@ -1766,10 +1762,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
{
struct usb_ffs_dmabuf_transfer_req req;
- if (copy_from_user(&req, (void __user *)value, sizeof(req))) {
- ret = -EFAULT;
- break;
- }
+ if (copy_from_user(&req, (void __user *)value, sizeof(req)))
+ return -EFAULT;
return ffs_dmabuf_transfer(file, &req);
}
@@ -4000,7 +3994,7 @@ static void ffs_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations ffs_item_ops = {
+static const struct configfs_item_operations ffs_item_ops = {
.release = ffs_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 3ddfd4f66f0b..bee0d0458ff7 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -1328,7 +1328,7 @@ static void hid_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations hidg_item_ops = {
+static const struct configfs_item_operations hidg_item_ops = {
.release = hid_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
index 49b009a7d5d7..39862e236837 100644
--- a/drivers/usb/gadget/function/f_loopback.c
+++ b/drivers/usb/gadget/function/f_loopback.c
@@ -464,7 +464,7 @@ static void lb_attr_release(struct config_item *item)
usb_put_function_instance(&lb_opts->func_inst);
}
-static struct configfs_item_operations lb_item_ops = {
+static const struct configfs_item_operations lb_item_ops = {
.release = lb_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 94d478b6bcd3..5c3f34a2f35c 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -3153,7 +3153,7 @@ static void fsg_lun_attr_release(struct config_item *item)
kfree(lun_opts);
}
-static struct configfs_item_operations fsg_lun_item_ops = {
+static const struct configfs_item_operations fsg_lun_item_ops = {
.release = fsg_lun_attr_release,
};
@@ -3369,7 +3369,7 @@ static void fsg_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations fsg_item_ops = {
+static const struct configfs_item_operations fsg_item_ops = {
.release = fsg_attr_release,
};
@@ -3462,7 +3462,7 @@ static struct configfs_attribute *fsg_attrs[] = {
NULL,
};
-static struct configfs_group_operations fsg_group_ops = {
+static const struct configfs_group_operations fsg_group_ops = {
.make_group = fsg_lun_make,
.drop_item = fsg_lun_drop,
};
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index da82598fcef8..f592f9eb85d4 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -875,6 +875,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_composite_dev *cdev = c->cdev;
struct f_midi *midi = func_to_midi(f);
struct usb_string *us;
+ struct f_midi_opts *opts;
int status, n, jack = 1, i = 0, endpoint_descriptor_index = 0;
midi->gadget = cdev->gadget;
@@ -883,6 +884,10 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
if (status < 0)
goto fail_register;
+ opts = container_of(f->fi, struct f_midi_opts, func_inst);
+ if (opts->interface_string)
+ midi_string_defs[STRING_FUNC_IDX].s = opts->interface_string;
+
/* maybe allocate device-global string ID */
us = usb_gstrings_attach(c->cdev, midi_strings,
ARRAY_SIZE(midi_string_defs));
@@ -1086,7 +1091,7 @@ static void midi_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations midi_item_ops = {
+static const struct configfs_item_operations midi_item_ops = {
.release = midi_attr_release,
};
@@ -1178,59 +1183,60 @@ end: \
\
CONFIGFS_ATTR(f_midi_opts_, name);
+#define F_MIDI_OPT_STRING(name) \
+static ssize_t f_midi_opts_##name##_show(struct config_item *item, char *page) \
+{ \
+ struct f_midi_opts *opts = to_f_midi_opts(item); \
+ ssize_t result; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->name) { \
+ result = strscpy(page, opts->name, PAGE_SIZE); \
+ } else { \
+ page[0] = 0; \
+ result = 0; \
+ } \
+ \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_midi_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_midi_opts *opts = to_f_midi_opts(item); \
+ int ret; \
+ char *c; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt > 1) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ c = kstrndup(page, len, GFP_KERNEL); \
+ if (!c) { \
+ ret = -ENOMEM; \
+ goto end; \
+ } \
+ kfree(opts->name); \
+ opts->name = c; \
+ ret = len; \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_midi_opts_, name)
+
F_MIDI_OPT_SIGNED(index, true, SNDRV_CARDS);
F_MIDI_OPT(buflen, false, 0);
F_MIDI_OPT(qlen, false, 0);
F_MIDI_OPT(in_ports, true, MAX_PORTS);
F_MIDI_OPT(out_ports, true, MAX_PORTS);
-
-static ssize_t f_midi_opts_id_show(struct config_item *item, char *page)
-{
- struct f_midi_opts *opts = to_f_midi_opts(item);
- ssize_t result;
-
- mutex_lock(&opts->lock);
- if (opts->id) {
- result = strscpy(page, opts->id, PAGE_SIZE);
- } else {
- page[0] = 0;
- result = 0;
- }
-
- mutex_unlock(&opts->lock);
-
- return result;
-}
-
-static ssize_t f_midi_opts_id_store(struct config_item *item,
- const char *page, size_t len)
-{
- struct f_midi_opts *opts = to_f_midi_opts(item);
- int ret;
- char *c;
-
- mutex_lock(&opts->lock);
- if (opts->refcnt > 1) {
- ret = -EBUSY;
- goto end;
- }
-
- c = kstrndup(page, len, GFP_KERNEL);
- if (!c) {
- ret = -ENOMEM;
- goto end;
- }
- if (opts->id_allocated)
- kfree(opts->id);
- opts->id = c;
- opts->id_allocated = true;
- ret = len;
-end:
- mutex_unlock(&opts->lock);
- return ret;
-}
-
-CONFIGFS_ATTR(f_midi_opts_, id);
+F_MIDI_OPT_STRING(id);
+F_MIDI_OPT_STRING(interface_string);
static struct configfs_attribute *midi_attrs[] = {
&f_midi_opts_attr_index,
@@ -1239,6 +1245,7 @@ static struct configfs_attribute *midi_attrs[] = {
&f_midi_opts_attr_in_ports,
&f_midi_opts_attr_out_ports,
&f_midi_opts_attr_id,
+ &f_midi_opts_attr_interface_string,
NULL,
};
@@ -1262,8 +1269,8 @@ static void f_midi_free_inst(struct usb_function_instance *f)
mutex_unlock(&opts->lock);
if (free) {
- if (opts->id_allocated)
- kfree(opts->id);
+ kfree(opts->id);
+ kfree(opts->interface_string);
kfree(opts);
}
}
@@ -1279,7 +1286,8 @@ static struct usb_function_instance *f_midi_alloc_inst(void)
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = f_midi_free_inst;
opts->index = SNDRV_DEFAULT_IDX1;
- opts->id = SNDRV_DEFAULT_STR1;
+ opts->id = NULL;
+ opts->interface_string = NULL;
opts->buflen = 512;
opts->qlen = 32;
opts->in_ports = 1;
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index de16b02d857e..95ec87bed3ee 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -2316,7 +2316,7 @@ static void f_midi2_block_opts_release(struct config_item *item)
kfree(opts);
}
-static struct configfs_item_operations f_midi2_block_item_ops = {
+static const struct configfs_item_operations f_midi2_block_item_ops = {
.release = f_midi2_block_opts_release,
};
@@ -2479,11 +2479,11 @@ static void f_midi2_ep_opts_release(struct config_item *item)
kfree(opts);
}
-static struct configfs_item_operations f_midi2_ep_item_ops = {
+static const struct configfs_item_operations f_midi2_ep_item_ops = {
.release = f_midi2_ep_opts_release,
};
-static struct configfs_group_operations f_midi2_ep_group_ops = {
+static const struct configfs_group_operations f_midi2_ep_group_ops = {
.make_group = f_midi2_opts_block_make,
.drop_item = f_midi2_opts_block_drop,
};
@@ -2618,11 +2618,11 @@ static void f_midi2_opts_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations f_midi2_item_ops = {
+static const struct configfs_item_operations f_midi2_item_ops = {
.release = f_midi2_opts_release,
};
-static struct configfs_group_operations f_midi2_group_ops = {
+static const struct configfs_group_operations f_midi2_group_ops = {
.make_group = f_midi2_opts_ep_make,
.drop_item = f_midi2_opts_ep_drop,
};
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 0e38330271d5..e23adc132f88 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -83,6 +83,11 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
return container_of(f, struct f_ncm, port.func);
}
+static inline struct f_ncm_opts *func_to_ncm_opts(struct usb_function *f)
+{
+ return container_of(f->fi, struct f_ncm_opts, func_inst);
+}
+
/*-------------------------------------------------------------------------*/
/*
@@ -859,6 +864,7 @@ invalid:
static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_ncm *ncm = func_to_ncm(f);
+ struct f_ncm_opts *opts = func_to_ncm_opts(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* Control interface has only altsetting 0 */
@@ -881,12 +887,13 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (alt > 1)
goto fail;
- if (ncm->netdev) {
- DBG(cdev, "reset ncm\n");
- ncm->netdev = NULL;
- gether_disconnect(&ncm->port);
- ncm_reset_values(ncm);
- }
+ scoped_guard(mutex, &opts->lock)
+ if (opts->net) {
+ DBG(cdev, "reset ncm\n");
+ opts->net = NULL;
+ gether_disconnect(&ncm->port);
+ ncm_reset_values(ncm);
+ }
/*
* CDC Network only sends data in non-default altsettings.
@@ -919,7 +926,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
net = gether_connect(&ncm->port);
if (IS_ERR(net))
return PTR_ERR(net);
- ncm->netdev = net;
+ scoped_guard(mutex, &opts->lock)
+ opts->net = net;
}
spin_lock(&ncm->lock);
@@ -1366,14 +1374,16 @@ err:
static void ncm_disable(struct usb_function *f)
{
struct f_ncm *ncm = func_to_ncm(f);
+ struct f_ncm_opts *opts = func_to_ncm_opts(f);
struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "ncm deactivated\n");
- if (ncm->netdev) {
- ncm->netdev = NULL;
- gether_disconnect(&ncm->port);
- }
+ scoped_guard(mutex, &opts->lock)
+ if (opts->net) {
+ opts->net = NULL;
+ gether_disconnect(&ncm->port);
+ }
if (ncm->notify->enabled) {
usb_ep_disable(ncm->notify);
@@ -1433,39 +1443,44 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_ncm *ncm = func_to_ncm(f);
+ struct f_ncm_opts *ncm_opts = func_to_ncm_opts(f);
struct usb_string *us;
int status = 0;
struct usb_ep *ep;
- struct f_ncm_opts *ncm_opts;
struct usb_os_desc_table *os_desc_table __free(kfree) = NULL;
+ struct net_device *netdev __free(free_gether_netdev) = NULL;
struct usb_request *request __free(free_usb_request) = NULL;
if (!can_support_ecm(cdev->gadget))
return -EINVAL;
- ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
-
if (cdev->use_os_string) {
os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL);
if (!os_desc_table)
return -ENOMEM;
}
- mutex_lock(&ncm_opts->lock);
- gether_set_gadget(ncm_opts->net, cdev->gadget);
- if (!ncm_opts->bound) {
- ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN);
- status = gether_register_netdev(ncm_opts->net);
+ netdev = gether_setup_default();
+ if (IS_ERR(netdev))
+ return -ENOMEM;
+
+ scoped_guard(mutex, &ncm_opts->lock) {
+ gether_apply_opts(netdev, &ncm_opts->net_opts);
+ netdev->mtu = ncm_opts->max_segment_size - ETH_HLEN;
}
- mutex_unlock(&ncm_opts->lock);
+ gether_set_gadget(netdev, cdev->gadget);
+ status = gether_register_netdev(netdev);
if (status)
return status;
- ncm_opts->bound = true;
-
- ncm_string_defs[1].s = ncm->ethaddr;
+ /* export host's Ethernet address in CDC format */
+ status = gether_get_host_addr_cdc(netdev, ncm->ethaddr,
+ sizeof(ncm->ethaddr));
+ if (status < 12)
+ return -EINVAL;
+ ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
us = usb_gstrings_attach(cdev, ncm_strings,
ARRAY_SIZE(ncm_string_defs));
@@ -1563,6 +1578,8 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
f->os_desc_n = 1;
}
ncm->notify_req = no_free_ptr(request);
+ ncm->netdev = no_free_ptr(netdev);
+ ncm->port.ioport = netdev_priv(ncm->netdev);
DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n",
ncm->port.in_ep->name, ncm->port.out_ep->name,
@@ -1577,19 +1594,19 @@ static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
}
/* f_ncm_item_ops */
-USB_ETHERNET_CONFIGFS_ITEM(ncm);
+USB_ETHER_OPTS_ITEM(ncm);
/* f_ncm_opts_dev_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm);
+USB_ETHER_OPTS_ATTR_DEV_ADDR(ncm);
/* f_ncm_opts_host_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm);
+USB_ETHER_OPTS_ATTR_HOST_ADDR(ncm);
/* f_ncm_opts_qmult */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm);
+USB_ETHER_OPTS_ATTR_QMULT(ncm);
/* f_ncm_opts_ifname */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm);
+USB_ETHER_OPTS_ATTR_IFNAME(ncm);
static ssize_t ncm_opts_max_segment_size_show(struct config_item *item,
char *page)
@@ -1655,34 +1672,27 @@ static void ncm_free_inst(struct usb_function_instance *f)
struct f_ncm_opts *opts;
opts = container_of(f, struct f_ncm_opts, func_inst);
- if (opts->bound)
- gether_cleanup(netdev_priv(opts->net));
- else
- free_netdev(opts->net);
kfree(opts->ncm_interf_group);
kfree(opts);
}
static struct usb_function_instance *ncm_alloc_inst(void)
{
- struct f_ncm_opts *opts;
+ struct usb_function_instance *ret;
struct usb_os_desc *descs[1];
char *names[1];
struct config_group *ncm_interf_group;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ struct f_ncm_opts *opts __free(kfree) = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
+
+ opts->net = NULL;
opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id;
+ gether_setup_opts_default(&opts->net_opts, "usb");
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = ncm_free_inst;
- opts->net = gether_setup_default();
- if (IS_ERR(opts->net)) {
- struct net_device *net = opts->net;
- kfree(opts);
- return ERR_CAST(net);
- }
opts->max_segment_size = ETH_FRAME_LEN;
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
@@ -1693,26 +1703,22 @@ static struct usb_function_instance *ncm_alloc_inst(void)
ncm_interf_group =
usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
names, THIS_MODULE);
- if (IS_ERR(ncm_interf_group)) {
- ncm_free_inst(&opts->func_inst);
+ if (IS_ERR(ncm_interf_group))
return ERR_CAST(ncm_interf_group);
- }
opts->ncm_interf_group = ncm_interf_group;
- return &opts->func_inst;
+ ret = &opts->func_inst;
+ retain_and_null_ptr(opts);
+ return ret;
}
static void ncm_free(struct usb_function *f)
{
- struct f_ncm *ncm;
- struct f_ncm_opts *opts;
+ struct f_ncm_opts *opts = func_to_ncm_opts(f);
- ncm = func_to_ncm(f);
- opts = container_of(f->fi, struct f_ncm_opts, func_inst);
- kfree(ncm);
- mutex_lock(&opts->lock);
- opts->refcnt--;
- mutex_unlock(&opts->lock);
+ scoped_guard(mutex, &opts->lock)
+ opts->refcnt--;
+ kfree(func_to_ncm(f));
}
static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
@@ -1736,13 +1742,15 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(ncm->notify_req->buf);
usb_ep_free_request(ncm->notify, ncm->notify_req);
+
+ ncm->port.ioport = NULL;
+ gether_cleanup(netdev_priv(ncm->netdev));
}
static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
{
struct f_ncm *ncm;
struct f_ncm_opts *opts;
- int status;
/* allocate and initialize one new instance */
ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
@@ -1750,22 +1758,12 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
return ERR_PTR(-ENOMEM);
opts = container_of(fi, struct f_ncm_opts, func_inst);
- mutex_lock(&opts->lock);
- opts->refcnt++;
- /* export host's Ethernet address in CDC format */
- status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
- sizeof(ncm->ethaddr));
- if (status < 12) { /* strlen("01234567890a") */
- kfree(ncm);
- mutex_unlock(&opts->lock);
- return ERR_PTR(-EINVAL);
- }
+ scoped_guard(mutex, &opts->lock)
+ opts->refcnt++;
spin_lock_init(&ncm->lock);
ncm_reset_values(ncm);
- ncm->port.ioport = netdev_priv(opts->net);
- mutex_unlock(&opts->lock);
ncm->port.is_fixed = true;
ncm->port.supports_multi_frame = true;
diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c
index 1305e2326cdf..6d498f63183e 100644
--- a/drivers/usb/gadget/function/f_obex.c
+++ b/drivers/usb/gadget/function/f_obex.c
@@ -390,7 +390,7 @@ static void obex_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations obex_item_ops = {
+static const struct configfs_item_operations obex_item_ops = {
.release = obex_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c
index 0aa9e8224cae..d644d5495fdc 100644
--- a/drivers/usb/gadget/function/f_phonet.c
+++ b/drivers/usb/gadget/function/f_phonet.c
@@ -585,7 +585,7 @@ static void phonet_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations phonet_item_ops = {
+static const struct configfs_item_operations phonet_item_ops = {
.release = phonet_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index d295ade8fa67..b2fa2b56c37e 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -1220,7 +1220,7 @@ static void printer_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations printer_item_ops = {
+static const struct configfs_item_operations printer_item_ops = {
.release = printer_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index 0f266bc067f5..e6b412e0e045 100644
--- a/drivers/usb/gadget/function/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -260,7 +260,7 @@ static void serial_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations serial_item_ops = {
+static const struct configfs_item_operations serial_item_ops = {
.release = serial_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index ec5fd25020fd..22104e9c6cab 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -46,6 +46,7 @@ struct f_sourcesink {
unsigned isoc_mult;
unsigned isoc_maxburst;
unsigned buflen;
+ unsigned bulk_maxburst;
unsigned bulk_qlen;
unsigned iso_qlen;
};
@@ -328,6 +329,12 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
source_sink_intf_alt0.bInterfaceNumber = id;
source_sink_intf_alt1.bInterfaceNumber = id;
+ if (ss->bulk_maxburst > 15)
+ ss->bulk_maxburst = 15;
+
+ ss_source_comp_desc.bMaxBurst = ss->bulk_maxburst;
+ ss_sink_comp_desc.bMaxBurst = ss->bulk_maxburst;
+
/* allocate bulk endpoints */
ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
if (!ss->in_ep) {
@@ -853,6 +860,7 @@ static struct usb_function *source_sink_alloc_func(
ss->isoc_mult = ss_opts->isoc_mult;
ss->isoc_maxburst = ss_opts->isoc_maxburst;
ss->buflen = ss_opts->bulk_buflen;
+ ss->bulk_maxburst = ss_opts->bulk_maxburst;
ss->bulk_qlen = ss_opts->bulk_qlen;
ss->iso_qlen = ss_opts->iso_qlen;
@@ -882,7 +890,7 @@ static void ss_attr_release(struct config_item *item)
usb_put_function_instance(&ss_opts->func_inst);
}
-static struct configfs_item_operations ss_item_ops = {
+static const struct configfs_item_operations ss_item_ops = {
.release = ss_attr_release,
};
@@ -1101,6 +1109,49 @@ end:
CONFIGFS_ATTR(f_ss_opts_, isoc_maxburst);
+static ssize_t f_ss_opts_bulk_maxburst_show(struct config_item *item, char *page)
+{
+ struct f_ss_opts *opts = to_f_ss_opts(item);
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sysfs_emit(page, "%u\n", opts->bulk_maxburst);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_ss_opts_bulk_maxburst_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct f_ss_opts *opts = to_f_ss_opts(item);
+ int ret;
+ u8 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou8(page, 0, &num);
+ if (ret)
+ goto end;
+
+ if (num > 15) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ opts->bulk_maxburst = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+CONFIGFS_ATTR(f_ss_opts_, bulk_maxburst);
+
static ssize_t f_ss_opts_bulk_buflen_show(struct config_item *item, char *page)
{
struct f_ss_opts *opts = to_f_ss_opts(item);
@@ -1222,6 +1273,7 @@ static struct configfs_attribute *ss_attrs[] = {
&f_ss_opts_attr_isoc_mult,
&f_ss_opts_attr_isoc_maxburst,
&f_ss_opts_attr_bulk_buflen,
+ &f_ss_opts_attr_bulk_maxburst,
&f_ss_opts_attr_bulk_qlen,
&f_ss_opts_attr_iso_qlen,
NULL,
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index 6e8804f04baa..efb94fd82533 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -1227,7 +1227,7 @@ static void usbg_submit_cmd(struct usbg_cmd *cmd)
goto out;
target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess, cmd->cmd_buf,
- cmd->sense_iu.sense, cmd->unpacked_lun, 0,
+ cmd->sense_iu.sense, cmd->unpacked_lun, cmd->data_len,
cmd->prio_attr, dir, flags);
return;
@@ -1389,6 +1389,7 @@ static int usbg_submit_command(struct f_uas *fu, struct usb_request *req)
cmd->tmr_func = 0;
cmd->tmr_rsp = RC_RESPONSE_UNKNOWN;
cmd->flags = 0;
+ cmd->data_len = 0;
cmd_iu = (struct command_iu *)iu;
@@ -2446,7 +2447,7 @@ static void tcm_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations tcm_item_ops = {
+static const struct configfs_item_operations tcm_item_ops = {
.release = tcm_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 9da9fb4e1239..efe9f270b02d 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -1512,7 +1512,7 @@ static void f_uac1_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations f_uac1_item_ops = {
+static const struct configfs_item_operations f_uac1_item_ops = {
.release = f_uac1_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c
index 49cf5aae90ca..8fc452b4b39a 100644
--- a/drivers/usb/gadget/function/f_uac1_legacy.c
+++ b/drivers/usb/gadget/function/f_uac1_legacy.c
@@ -812,7 +812,7 @@ static void f_uac1_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations f_uac1_item_ops = {
+static const struct configfs_item_operations f_uac1_item_ops = {
.release = f_uac1_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index dd252ff2fb4e..98f0f50dc7a8 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -1874,7 +1874,7 @@ static void f_uac2_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations f_uac2_item_ops = {
+static const struct configfs_item_operations f_uac2_item_ops = {
.release = f_uac2_attr_release,
};
diff --git a/drivers/usb/gadget/function/g_zero.h b/drivers/usb/gadget/function/g_zero.h
index 98b8462ad538..7bd66004821f 100644
--- a/drivers/usb/gadget/function/g_zero.h
+++ b/drivers/usb/gadget/function/g_zero.h
@@ -34,6 +34,7 @@ struct f_ss_opts {
unsigned isoc_mult;
unsigned isoc_maxburst;
unsigned bulk_buflen;
+ unsigned bulk_maxburst;
unsigned bulk_qlen;
unsigned iso_qlen;
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index c47965d850d4..338f6e2a85a9 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -1040,6 +1040,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;
@@ -1096,6 +1126,21 @@ void gether_cleanup(struct eth_dev *dev)
}
EXPORT_SYMBOL_GPL(gether_cleanup);
+void gether_unregister_free_netdev(struct net_device *net)
+{
+ if (!net)
+ return;
+
+ struct eth_dev *dev = netdev_priv(net);
+
+ if (net->reg_state == NETREG_REGISTERED) {
+ unregister_netdev(net);
+ flush_work(&dev->work);
+ }
+ free_netdev(net);
+}
+EXPORT_SYMBOL_GPL(gether_unregister_free_netdev);
+
/**
* gether_connect - notify network layer that USB link is active
* @link: the USB link, set up with endpoints, descriptors matching
diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h
index 34be220cef77..a212a8ec5eb1 100644
--- a/drivers/usb/gadget/function/u_ether.h
+++ b/drivers/usb/gadget/function/u_ether.h
@@ -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
@@ -258,6 +283,11 @@ int gether_get_ifname(struct net_device *net, char *name, int len);
int gether_set_ifname(struct net_device *net, const char *name, int len);
void gether_cleanup(struct eth_dev *dev);
+void gether_unregister_free_netdev(struct net_device *net);
+DEFINE_FREE(free_gether_netdev, struct net_device *, gether_unregister_free_netdev(_T));
+
+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);
diff --git a/drivers/usb/gadget/function/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h
index f558c3139ebe..217990a266b2 100644
--- a/drivers/usb/gadget/function/u_ether_configfs.h
+++ b/drivers/usb/gadget/function/u_ether_configfs.h
@@ -13,6 +13,13 @@
#ifndef __U_ETHER_CONFIGFS_H
#define __U_ETHER_CONFIGFS_H
+#include <linux/cleanup.h>
+#include <linux/hex.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) \
{ \
@@ -21,7 +28,7 @@
usb_put_function_instance(&opts->func_inst); \
} \
\
- static struct configfs_item_operations _f_##_item_ops = { \
+ static const struct configfs_item_operations _f_##_item_ops = { \
.release = _f_##_attr_release, \
}
@@ -197,4 +204,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 */
diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h
index 2e400b495cb8..41cb8aa73f09 100644
--- a/drivers/usb/gadget/function/u_midi.h
+++ b/drivers/usb/gadget/function/u_midi.h
@@ -19,7 +19,7 @@ struct f_midi_opts {
struct usb_function_instance func_inst;
int index;
char *id;
- bool id_allocated;
+ char *interface_string;
unsigned int in_ports;
unsigned int out_ports;
unsigned int buflen;
diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h
index 49ec095cdb4b..d99330fe31e8 100644
--- a/drivers/usb/gadget/function/u_ncm.h
+++ b/drivers/usb/gadget/function/u_ncm.h
@@ -15,11 +15,13 @@
#include <linux/usb/composite.h>
+#include "u_ether.h"
+
struct f_ncm_opts {
struct usb_function_instance func_inst;
struct net_device *net;
- bool bound;
+ struct gether_opts net_opts;
struct config_group *ncm_interf_group;
struct usb_os_desc ncm_os_desc;
char ncm_ext_compat_id[16];
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index 5a87516ddb31..4cec6c586d67 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -128,7 +128,7 @@ static void uvcg_config_item_release(struct config_item *item)
kfree(group);
}
-static struct configfs_item_operations uvcg_config_item_ops = {
+static const struct configfs_item_operations uvcg_config_item_ops = {
.release = uvcg_config_item_release,
};
@@ -285,7 +285,7 @@ static struct config_item *uvcg_control_header_make(struct config_group *group,
return &h->item;
}
-static struct configfs_group_operations uvcg_control_header_grp_ops = {
+static const struct configfs_group_operations uvcg_control_header_grp_ops = {
.make_item = uvcg_control_header_make,
};
@@ -1233,7 +1233,7 @@ static void uvcg_extension_drop_link(struct config_item *src, struct config_item
mutex_unlock(su_mutex);
}
-static struct configfs_item_operations uvcg_extension_item_ops = {
+static const struct configfs_item_operations uvcg_extension_item_ops = {
.release = uvcg_extension_release,
.allow_link = uvcg_extension_allow_link,
.drop_link = uvcg_extension_drop_link,
@@ -1298,7 +1298,7 @@ static struct config_item *uvcg_extension_make(struct config_group *group, const
return &xu->item;
}
-static struct configfs_group_operations uvcg_extensions_grp_ops = {
+static const struct configfs_group_operations uvcg_extensions_grp_ops = {
.make_item = uvcg_extension_make,
.drop_item = uvcg_extension_drop,
};
@@ -1414,7 +1414,7 @@ out:
mutex_unlock(su_mutex);
}
-static struct configfs_item_operations uvcg_control_class_item_ops = {
+static const struct configfs_item_operations uvcg_control_class_item_ops = {
.release = uvcg_config_item_release,
.allow_link = uvcg_control_class_allow_link,
.drop_link = uvcg_control_class_drop_link,
@@ -1664,7 +1664,7 @@ static void uvcg_format_drop_link(struct config_item *src, struct config_item *t
mutex_unlock(su_mutex);
}
-static struct configfs_item_operations uvcg_format_item_operations = {
+static const struct configfs_item_operations uvcg_format_item_operations = {
.release = uvcg_config_item_release,
.allow_link = uvcg_format_allow_link,
.drop_link = uvcg_format_drop_link,
@@ -1840,7 +1840,7 @@ out:
mutex_unlock(su_mutex);
}
-static struct configfs_item_operations uvcg_streaming_header_item_ops = {
+static const struct configfs_item_operations uvcg_streaming_header_item_ops = {
.release = uvcg_config_item_release,
.allow_link = uvcg_streaming_header_allow_link,
.drop_link = uvcg_streaming_header_drop_link,
@@ -1914,7 +1914,7 @@ static struct config_item
return &h->item;
}
-static struct configfs_group_operations uvcg_streaming_header_grp_ops = {
+static const struct configfs_group_operations uvcg_streaming_header_grp_ops = {
.make_item = uvcg_streaming_header_make,
};
@@ -2261,7 +2261,7 @@ static void uvcg_format_set_indices(struct config_group *fmt)
* streaming/uncompressed/<NAME>
*/
-static struct configfs_group_operations uvcg_uncompressed_group_ops = {
+static const struct configfs_group_operations uvcg_uncompressed_group_ops = {
.make_item = uvcg_frame_make,
.drop_item = uvcg_frame_drop,
};
@@ -2508,7 +2508,7 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group,
return &h->fmt.group;
}
-static struct configfs_group_operations uvcg_uncompressed_grp_ops = {
+static const struct configfs_group_operations uvcg_uncompressed_grp_ops = {
.make_group = uvcg_uncompressed_make,
};
@@ -2525,7 +2525,7 @@ static const struct uvcg_config_group_type uvcg_uncompressed_grp_type = {
* streaming/mjpeg/<NAME>
*/
-static struct configfs_group_operations uvcg_mjpeg_group_ops = {
+static const struct configfs_group_operations uvcg_mjpeg_group_ops = {
.make_item = uvcg_frame_make,
.drop_item = uvcg_frame_drop,
};
@@ -2698,7 +2698,7 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group,
return &h->fmt.group;
}
-static struct configfs_group_operations uvcg_mjpeg_grp_ops = {
+static const struct configfs_group_operations uvcg_mjpeg_grp_ops = {
.make_group = uvcg_mjpeg_make,
};
@@ -2715,7 +2715,7 @@ static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = {
* streaming/framebased/<NAME>
*/
-static struct configfs_group_operations uvcg_framebased_group_ops = {
+static const struct configfs_group_operations uvcg_framebased_group_ops = {
.make_item = uvcg_frame_make,
.drop_item = uvcg_frame_drop,
};
@@ -2953,7 +2953,7 @@ static struct config_group *uvcg_framebased_make(struct config_group *group,
return &h->fmt.group;
}
-static struct configfs_group_operations uvcg_framebased_grp_ops = {
+static const struct configfs_group_operations uvcg_framebased_grp_ops = {
.make_group = uvcg_framebased_make,
};
@@ -3056,7 +3056,7 @@ static void uvcg_color_matching_release(struct config_item *item)
kfree(color_match);
}
-static struct configfs_item_operations uvcg_color_matching_item_ops = {
+static const struct configfs_item_operations uvcg_color_matching_item_ops = {
.release = uvcg_color_matching_release,
};
@@ -3089,7 +3089,7 @@ static struct config_group *uvcg_color_matching_make(struct config_group *group,
return &color_match->group;
}
-static struct configfs_group_operations uvcg_color_matching_grp_group_ops = {
+static const struct configfs_group_operations uvcg_color_matching_grp_group_ops = {
.make_group = uvcg_color_matching_make,
};
@@ -3530,7 +3530,7 @@ out:
mutex_unlock(su_mutex);
}
-static struct configfs_item_operations uvcg_streaming_class_item_ops = {
+static const struct configfs_item_operations uvcg_streaming_class_item_ops = {
.release = uvcg_config_item_release,
.allow_link = uvcg_streaming_class_allow_link,
.drop_link = uvcg_streaming_class_drop_link,
@@ -3698,7 +3698,7 @@ static void uvc_func_drop_link(struct config_item *src, struct config_item *tgt)
mutex_unlock(&opts->lock);
}
-static struct configfs_item_operations uvc_func_item_ops = {
+static const struct configfs_item_operations uvc_func_item_ops = {
.release = uvc_func_item_release,
.allow_link = uvc_func_allow_link,
.drop_link = uvc_func_drop_link,