diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index c87e74afc881..eeedcfa61fa1 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -19,6 +19,7 @@
 
 #include "u_audio.h"
 #include "u_uac1.h"
+#include "u_uac_utils.h"
 
 /* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */
 #define UAC1_CHANNEL_MASK 0x0FFF
@@ -1516,151 +1517,18 @@ static struct configfs_item_operations f_uac1_item_ops = {
 	.release	= f_uac1_attr_release,
 };
 
-#define uac1_kstrtou32			kstrtou32
-#define uac1_kstrtos16			kstrtos16
-#define uac1_kstrtobool(s, base, res)	kstrtobool((s), (res))
-
-static const char *u32_fmt = "%u\n";
-static const char *s16_fmt = "%hd\n";
-static const char *bool_fmt = "%u\n";
-
+#define UAC1_ATTR_TO_OPTS struct f_uac1_opts *opts = to_f_uac1_opts(item)
 #define UAC1_ATTRIBUTE(type, name)					\
-static ssize_t f_uac1_opts_##name##_show(				\
-					  struct config_item *item,	\
-					  char *page)			\
-{									\
-	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
-	int result;							\
-									\
-	mutex_lock(&opts->lock);					\
-	result = sprintf(page, type##_fmt, opts->name);			\
-	mutex_unlock(&opts->lock);					\
-									\
-	return result;							\
-}									\
-									\
-static ssize_t f_uac1_opts_##name##_store(				\
-					  struct config_item *item,	\
-					  const char *page, size_t len)	\
-{									\
-	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
-	int ret;							\
-	type num;							\
-									\
-	mutex_lock(&opts->lock);					\
-	if (opts->refcnt) {						\
-		ret = -EBUSY;						\
-		goto end;						\
-	}								\
-									\
-	ret = uac1_kstrto##type(page, 0, &num);				\
-	if (ret)							\
-		goto end;						\
-									\
-	opts->name = num;						\
-	ret = len;							\
-									\
-end:									\
-	mutex_unlock(&opts->lock);					\
-	return ret;							\
-}									\
-									\
-CONFIGFS_ATTR(f_uac1_opts_, name)
+	UAC_ATTRIBUTE(f_uac1_opts, UAC1_ATTR_TO_OPTS, opts,		\
+		      opts->lock, opts->refcnt, type, name)
 
 #define UAC1_RATE_ATTRIBUTE(name)					\
-static ssize_t f_uac1_opts_##name##_show(struct config_item *item,	\
-					 char *page)			\
-{									\
-	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
-	int result = 0;							\
-	int i;								\
-									\
-	mutex_lock(&opts->lock);					\
-	page[0] = '\0';							\
-	for (i = 0; i < UAC_MAX_RATES; i++) {				\
-		if (opts->name##s[i] == 0)				\
-			break;						\
-		result += sprintf(page + strlen(page), "%u,",		\
-				opts->name##s[i]);			\
-	}								\
-	if (strlen(page) > 0)						\
-		page[strlen(page) - 1] = '\n';				\
-	mutex_unlock(&opts->lock);					\
-									\
-	return result;							\
-}									\
-									\
-static ssize_t f_uac1_opts_##name##_store(struct config_item *item,	\
-					  const char *page, size_t len)	\
-{									\
-	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
-	char *split_page = NULL;					\
-	int ret = -EINVAL;						\
-	char *token;							\
-	u32 num;							\
-	int i;								\
-									\
-	mutex_lock(&opts->lock);					\
-	if (opts->refcnt) {						\
-		ret = -EBUSY;						\
-		goto end;						\
-	}								\
-									\
-	i = 0;								\
-	memset(opts->name##s, 0x00, sizeof(opts->name##s));		\
-	split_page = kstrdup(page, GFP_KERNEL);				\
-	while ((token = strsep(&split_page, ",")) != NULL) {		\
-		ret = kstrtou32(token, 0, &num);			\
-		if (ret)						\
-			goto end;					\
-									\
-		opts->name##s[i++] = num;				\
-		ret = len;						\
-	};								\
-									\
-end:									\
-	kfree(split_page);						\
-	mutex_unlock(&opts->lock);					\
-	return ret;							\
-}									\
-									\
-CONFIGFS_ATTR(f_uac1_opts_, name)
+	UAC_RATE_ATTRIBUTE(f_uac1_opts, UAC1_ATTR_TO_OPTS, opts,	\
+			   opts->lock, opts->refcnt, name)
 
 #define UAC1_ATTRIBUTE_STRING(name)					\
-static ssize_t f_uac1_opts_##name##_show(struct config_item *item,	\
-					 char *page)			\
-{									\
-	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
-	int result;							\
-									\
-	mutex_lock(&opts->lock);					\
-	result = scnprintf(page, sizeof(opts->name), "%s", opts->name);	\
-	mutex_unlock(&opts->lock);					\
-									\
-	return result;							\
-}									\
-									\
-static ssize_t f_uac1_opts_##name##_store(struct config_item *item,	\
-					  const char *page, size_t len)	\
-{									\
-	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
-	int ret = 0;							\
-									\
-	mutex_lock(&opts->lock);					\
-	if (opts->refcnt) {						\
-		ret = -EBUSY;						\
-		goto end;						\
-	}								\
-									\
-	ret = scnprintf(opts->name, min(sizeof(opts->name), len),	\
-			"%s", page);					\
-									\
-end:									\
-	mutex_unlock(&opts->lock);					\
-	return ret;							\
-}									\
-									\
-CONFIGFS_ATTR(f_uac1_opts_, name)
+	UAC_ATTRIBUTE_STRING(f_uac1_opts, UAC1_ATTR_TO_OPTS, opts,	\
+			     opts->lock, opts->refcnt, name)
 
 UAC1_ATTRIBUTE(u32, c_chmask);
 UAC1_RATE_ATTRIBUTE(c_srate);
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 1cdda44455b3..2142a0951f71 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -17,6 +17,7 @@
 #include "u_audio.h"
 
 #include "u_uac2.h"
+#include "u_uac_utils.h"
 
 /* UAC2 spec: 4.1 Audio Channel Cluster Descriptor */
 #define UAC2_CHANNEL_MASK 0x07FFFFFF
@@ -1877,210 +1878,22 @@ static struct configfs_item_operations f_uac2_item_ops = {
 	.release	= f_uac2_attr_release,
 };
 
-#define uac2_kstrtou8 kstrtou8
-#define uac2_kstrtou32 kstrtou32
-#define uac2_kstrtos16 kstrtos16
-#define uac2_kstrtobool(s, base, res) kstrtobool((s), (res))
-
-static const char *u8_fmt = "%u\n";
-static const char *u32_fmt = "%u\n";
-static const char *s16_fmt = "%hd\n";
-static const char *bool_fmt = "%u\n";
-
+#define UAC2_ATTR_TO_OPTS struct f_uac2_opts *opts = to_f_uac2_opts(item)
 #define UAC2_ATTRIBUTE(type, name)					\
-static ssize_t f_uac2_opts_##name##_show(struct config_item *item,	\
-					 char *page)			\
-{									\
-	struct f_uac2_opts *opts = to_f_uac2_opts(item);		\
-	int result;							\
-									\
-	mutex_lock(&opts->lock);					\
-	result = sprintf(page, type##_fmt, opts->name);			\
-	mutex_unlock(&opts->lock);					\
-									\
-	return result;							\
-}									\
-									\
-static ssize_t f_uac2_opts_##name##_store(struct config_item *item,	\
-					  const char *page, size_t len)	\
-{									\
-	struct f_uac2_opts *opts = to_f_uac2_opts(item);		\
-	int ret;							\
-	type num;							\
-									\
-	mutex_lock(&opts->lock);					\
-	if (opts->refcnt) {						\
-		ret = -EBUSY;						\
-		goto end;						\
-	}								\
-									\
-	ret = uac2_kstrto##type(page, 0, &num);				\
-	if (ret)							\
-		goto end;						\
-									\
-	opts->name = num;						\
-	ret = len;							\
-									\
-end:									\
-	mutex_unlock(&opts->lock);					\
-	return ret;							\
-}									\
-									\
-CONFIGFS_ATTR(f_uac2_opts_, name)
+	UAC_ATTRIBUTE(f_uac2_opts, UAC2_ATTR_TO_OPTS, opts,		\
+		      opts->lock, opts->refcnt, type, name)
 
 #define UAC2_ATTRIBUTE_SYNC(name)					\
-static ssize_t f_uac2_opts_##name##_show(struct config_item *item,	\
-					 char *page)			\
-{									\
-	struct f_uac2_opts *opts = to_f_uac2_opts(item);		\
-	int result;							\
-	char *str;							\
-									\
-	mutex_lock(&opts->lock);					\
-	switch (opts->name) {						\
-	case USB_ENDPOINT_SYNC_ASYNC:					\
-		str = "async";						\
-		break;							\
-	case USB_ENDPOINT_SYNC_ADAPTIVE:				\
-		str = "adaptive";					\
-		break;							\
-	default:							\
-		str = "unknown";					\
-		break;							\
-	}								\
-	result = sprintf(page, "%s\n", str);				\
-	mutex_unlock(&opts->lock);					\
-									\
-	return result;							\
-}									\
-									\
-static ssize_t f_uac2_opts_##name##_store(struct config_item *item,	\
-					  const char *page, size_t len)	\
-{									\
-	struct f_uac2_opts *opts = to_f_uac2_opts(item);		\
-	int ret = 0;							\
-									\
-	mutex_lock(&opts->lock);					\
-	if (opts->refcnt) {						\
-		ret = -EBUSY;						\
-		goto end;						\
-	}								\
-									\
-	if (!strncmp(page, "async", 5))					\
-		opts->name = USB_ENDPOINT_SYNC_ASYNC;			\
-	else if (!strncmp(page, "adaptive", 8))				\
-		opts->name = USB_ENDPOINT_SYNC_ADAPTIVE;		\
-	else {								\
-		ret = -EINVAL;						\
-		goto end;						\
-	}								\
-									\
-	ret = len;							\
-									\
-end:									\
-	mutex_unlock(&opts->lock);					\
-	return ret;							\
-}									\
-									\
-CONFIGFS_ATTR(f_uac2_opts_, name)
+	UAC_ATTRIBUTE_SYNC(f_uac2_opts, UAC2_ATTR_TO_OPTS, opts,	\
+			   opts->lock, opts->refcnt, name)
 
 #define UAC2_RATE_ATTRIBUTE(name)					\
-static ssize_t f_uac2_opts_##name##_show(struct config_item *item,	\
-					 char *page)			\
-{									\
-	struct f_uac2_opts *opts = to_f_uac2_opts(item);		\
-	int result = 0;							\
-	int i;								\
-									\
-	mutex_lock(&opts->lock);					\
-	page[0] = '\0';							\
-	for (i = 0; i < UAC_MAX_RATES; i++) {				\
-		if (opts->name##s[i] == 0)				\
-			break;						\
-		result += sprintf(page + strlen(page), "%u,",		\
-				opts->name##s[i]);			\
-	}								\
-	if (strlen(page) > 0)						\
-		page[strlen(page) - 1] = '\n';				\
-	mutex_unlock(&opts->lock);					\
-									\
-	return result;							\
-}									\
-									\
-static ssize_t f_uac2_opts_##name##_store(struct config_item *item,	\
-					  const char *page, size_t len)	\
-{									\
-	struct f_uac2_opts *opts = to_f_uac2_opts(item);		\
-	char *split_page = NULL;					\
-	int ret = -EINVAL;						\
-	char *token;							\
-	u32 num;							\
-	int i;								\
-									\
-	mutex_lock(&opts->lock);					\
-	if (opts->refcnt) {						\
-		ret = -EBUSY;						\
-		goto end;						\
-	}								\
-									\
-	i = 0;								\
-	memset(opts->name##s, 0x00, sizeof(opts->name##s));		\
-	split_page = kstrdup(page, GFP_KERNEL);				\
-	while ((token = strsep(&split_page, ",")) != NULL) {		\
-		ret = kstrtou32(token, 0, &num);			\
-		if (ret)						\
-			goto end;					\
-									\
-		opts->name##s[i++] = num;				\
-		ret = len;						\
-	};								\
-									\
-end:									\
-	kfree(split_page);						\
-	mutex_unlock(&opts->lock);					\
-	return ret;							\
-}									\
-									\
-CONFIGFS_ATTR(f_uac2_opts_, name)
+	UAC_RATE_ATTRIBUTE(f_uac2_opts, UAC2_ATTR_TO_OPTS, opts,	\
+			   opts->lock, opts->refcnt, name)
 
 #define UAC2_ATTRIBUTE_STRING(name)					\
-static ssize_t f_uac2_opts_##name##_show(struct config_item *item,	\
-					 char *page)			\
-{									\
-	struct f_uac2_opts *opts = to_f_uac2_opts(item);		\
-	int result;							\
-									\
-	mutex_lock(&opts->lock);					\
-	result = scnprintf(page, sizeof(opts->name), "%s", opts->name);	\
-	mutex_unlock(&opts->lock);					\
-									\
-	return result;							\
-}									\
-									\
-static ssize_t f_uac2_opts_##name##_store(struct config_item *item,	\
-					  const char *page, size_t len)	\
-{									\
-	struct f_uac2_opts *opts = to_f_uac2_opts(item);		\
-	int ret = 0;							\
-									\
-	mutex_lock(&opts->lock);					\
-	if (opts->refcnt) {						\
-		ret = -EBUSY;						\
-		goto end;						\
-	}								\
-									\
-	if (len && page[len - 1] == '\n')				\
-		len--;							\
-									\
-	ret = scnprintf(opts->name, min(sizeof(opts->name), len + 1),	\
-			"%s", page);					\
-									\
-end:									\
-	mutex_unlock(&opts->lock);					\
-	return ret;							\
-}									\
-									\
-CONFIGFS_ATTR(f_uac2_opts_, name)
+	UAC_ATTRIBUTE_STRING(f_uac2_opts, UAC2_ATTR_TO_OPTS, opts,	\
+			     opts->lock, opts->refcnt, name)
 
 UAC2_ATTRIBUTE(u32, p_chmask);
 UAC2_RATE_ATTRIBUTE(p_srate);
diff --git a/drivers/usb/gadget/function/u_uac_utils.h b/drivers/usb/gadget/function/u_uac_utils.h
new file mode 100644
index 000000000000..5f88e31103dd
--- /dev/null
+++ b/drivers/usb/gadget/function/u_uac_utils.h
@@ -0,0 +1,217 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * u_uac_utils.h -- Utilities for UAC1/2 function driver
+ *
+ * Copyright (C) 2024
+ * Author: Chris Wulff <crwulff@gmail.com>
+ */
+
+#ifndef __U_UAC_UTILS_H
+#define __U_UAC_UTILS_H
+
+#define uac_kstrtou8			kstrtou8
+#define uac_kstrtos16			kstrtos16
+#define uac_kstrtou32			kstrtou32
+#define uac_kstrtobool(s, base, res)	kstrtobool((s), (res))
+
+#define u8_FMT "%u\n"
+#define u32_FMT "%u\n"
+#define s16_FMT "%hd\n"
+#define bool_FMT "%u\n"
+
+#define UAC_ATTRIBUTE(prefix, to_struct, var, lock, refcnt, type, name) \
+static ssize_t prefix##_##name##_show(struct config_item *item,		\
+				      char *page)			\
+{									\
+	to_struct;							\
+	int result;							\
+									\
+	mutex_lock(&lock);						\
+	result = sprintf(page, type##_FMT, var->name);			\
+	mutex_unlock(&lock);						\
+									\
+	return result;							\
+}									\
+									\
+static ssize_t prefix##_##name##_store(struct config_item *item,	\
+				       const char *page, size_t len)	\
+{									\
+	to_struct;							\
+	int ret;							\
+	type num;							\
+									\
+	mutex_lock(&lock);						\
+	if (refcnt) {							\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = uac_kstrto##type(page, 0, &num);				\
+	if (ret)							\
+		goto end;						\
+									\
+	var->name = num;						\
+	ret = len;							\
+									\
+end:									\
+	mutex_unlock(&lock);						\
+	return ret;							\
+}									\
+									\
+CONFIGFS_ATTR(prefix##_, name)
+
+#define UAC_RATE_ATTRIBUTE(prefix, to_struct, var, lock, refcnt, name)	\
+static ssize_t prefix##_##name##_show(struct config_item *item,		\
+				      char *page)			\
+{									\
+	to_struct;							\
+	int result = 0;							\
+	int i;								\
+									\
+	mutex_lock(&lock);						\
+	page[0] = '\0';							\
+	for (i = 0; i < UAC_MAX_RATES; i++) {				\
+		if (var->name##s[i] == 0)				\
+			break;						\
+		result += sprintf(page + strlen(page), "%u,",		\
+				var->name##s[i]);			\
+	}								\
+	if (strlen(page) > 0)						\
+		page[strlen(page) - 1] = '\n';				\
+	mutex_unlock(&lock);						\
+									\
+	return result;							\
+}									\
+									\
+static ssize_t prefix##_##name##_store(struct config_item *item,	\
+				       const char *page, size_t len)	\
+{									\
+	to_struct;							\
+	char *split_page = NULL;					\
+	int ret = -EINVAL;						\
+	char *token;							\
+	u32 num;							\
+	int i;								\
+									\
+	mutex_lock(&lock);						\
+	if (refcnt) {							\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	i = 0;								\
+	memset(var->name##s, 0x00, sizeof(var->name##s));		\
+	split_page = kstrdup(page, GFP_KERNEL);				\
+	while ((token = strsep(&split_page, ",")) != NULL) {		\
+		ret = kstrtou32(token, 0, &num);			\
+		if (ret)						\
+			goto end;					\
+									\
+		var->name##s[i++] = num;				\
+		ret = len;						\
+	};								\
+									\
+end:									\
+	kfree(split_page);						\
+	mutex_unlock(&lock);						\
+	return ret;							\
+}									\
+									\
+CONFIGFS_ATTR(prefix##_, name)
+
+#define UAC_ATTRIBUTE_STRING(prefix, to_struct, var, lock, refcnt, name) \
+static ssize_t prefix##_##name##_show(struct config_item *item,		\
+				      char *page)			\
+{									\
+	to_struct;							\
+	int result;							\
+									\
+	mutex_lock(&lock);						\
+	result = scnprintf(page, sizeof(var->name), "%s", var->name);	\
+	mutex_unlock(&lock);						\
+									\
+	return result;							\
+}									\
+									\
+static ssize_t prefix##_##name##_store(struct config_item *item,	\
+				       const char *page, size_t len)	\
+{									\
+	to_struct;							\
+	int ret = 0;							\
+									\
+	mutex_lock(&lock);						\
+	if (refcnt) {							\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	if (len && page[len - 1] == '\n')				\
+		len--;							\
+									\
+	ret = scnprintf(var->name, min(sizeof(var->name), len + 1),	\
+			"%s", page);					\
+									\
+end:									\
+	mutex_unlock(&lock);						\
+	return ret;							\
+}									\
+									\
+CONFIGFS_ATTR(prefix##_, name)
+
+#define UAC_ATTRIBUTE_SYNC(prefix, to_struct, var, lock, refcnt, name)	\
+static ssize_t prefix##_##name##_show(struct config_item *item,		\
+				      char *page)			\
+{									\
+	to_struct;							\
+	int result;							\
+	char *str;							\
+									\
+	mutex_lock(&lock);						\
+	switch (var->name) {						\
+	case USB_ENDPOINT_SYNC_ASYNC:					\
+		str = "async";						\
+		break;							\
+	case USB_ENDPOINT_SYNC_ADAPTIVE:				\
+		str = "adaptive";					\
+		break;							\
+	default:							\
+		str = "unknown";					\
+		break;							\
+	}								\
+	result = sprintf(page, "%s\n", str);				\
+	mutex_unlock(&lock);						\
+									\
+	return result;							\
+}									\
+									\
+static ssize_t prefix##_##name##_store(struct config_item *item,	\
+				       const char *page, size_t len)	\
+{									\
+	to_struct;							\
+	int ret = 0;							\
+									\
+	mutex_lock(&lock);						\
+	if (refcnt) {							\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	if (!strncmp(page, "async", 5))					\
+		var->name = USB_ENDPOINT_SYNC_ASYNC;			\
+	else if (!strncmp(page, "adaptive", 8))				\
+		var->name = USB_ENDPOINT_SYNC_ADAPTIVE;			\
+	else {								\
+		ret = -EINVAL;						\
+		goto end;						\
+	}								\
+									\
+	ret = len;							\
+									\
+end:									\
+	mutex_unlock(&lock);						\
+	return ret;							\
+}									\
+									\
+CONFIGFS_ATTR(prefix##_, name)
+
+#endif	/* __U_UAC_UTILS_H */
