usb: device_next: Introduce speed specific configurations

USB High-Speed devices must be able to operate at both High-Speed and
Full-Speed. The USB specification allows the device to have different
configurations depending on connection speed. Modify the API to reflect
USB Specification requirements on what can (e.g. configurations) and
what cannot (e.g. VID, PID) be speed dependent.

While the class configurations for different speeds are completely
independent, the actual class instances are shared between operating
speeds (because only one speed can be active at a time). Classes are
free to provide different number of interfaces and/or endpoints for
different speeds. The endpoints are assigned for all operating speeds
during initialization.

Signed-off-by: Tomasz Moń <tomasz.mon@nordicsemi.no>
diff --git a/cmake/linker_script/common/common-ram.cmake b/cmake/linker_script/common/common-ram.cmake
index f5bee7e..f2ac29f 100644
--- a/cmake/linker_script/common/common-ram.cmake
+++ b/cmake/linker_script/common/common-ram.cmake
@@ -121,7 +121,8 @@
 if(CONFIG_USB_DEVICE_STACK OR CONFIG_USB_DEVICE_STACK_NEXT)
   zephyr_iterable_section(NAME usb_cfg_data GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4)
   zephyr_iterable_section(NAME usbd_contex GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4)
-  zephyr_iterable_section(NAME usbd_class_iter GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4)
+  zephyr_iterable_section(NAME usbd_class_fs GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4)
+  zephyr_iterable_section(NAME usbd_class_hs GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4)
 endif()
 
 if(CONFIG_USB_HOST_STACK)
diff --git a/include/zephyr/usb/usbd.h b/include/zephyr/usb/usbd.h
index 7aac5df..babf6b5 100644
--- a/include/zephyr/usb/usbd.h
+++ b/include/zephyr/usb/usbd.h
@@ -188,12 +188,16 @@
 	struct usbd_ch9_data ch9_data;
 	/** slist to manage descriptors like string, bos */
 	sys_dlist_t descriptors;
-	/** slist to manage device configurations */
-	sys_slist_t configs;
+	/** slist to manage Full-Speed device configurations */
+	sys_slist_t fs_configs;
+	/** slist to manage High-Speed device configurations */
+	sys_slist_t hs_configs;
 	/** Status of the USB device support */
 	struct usbd_status status;
-	/** Pointer to device descriptor */
-	void *desc;
+	/** Pointer to Full-Speed device descriptor */
+	void *fs_desc;
+	/** Pointer to High-Speed device descriptor */
+	void *hs_desc;
 };
 
 /**
@@ -349,7 +353,7 @@
 
 #define USBD_DEVICE_DEFINE(device_name, uhc_dev, vid, pid)		\
 	static struct usb_device_descriptor				\
-	desc_##device_name = {						\
+	fs_desc_##device_name = {					\
 		.bLength = sizeof(struct usb_device_descriptor),	\
 		.bDescriptorType = USB_DESC_DEVICE,			\
 		.bcdUSB = sys_cpu_to_le16(USB_SRN_2_0),			\
@@ -365,10 +369,28 @@
 		.iSerialNumber = 0,					\
 		.bNumConfigurations = 0,				\
 	};								\
+	static struct usb_device_descriptor				\
+	hs_desc_##device_name = {					\
+		.bLength = sizeof(struct usb_device_descriptor),	\
+		.bDescriptorType = USB_DESC_DEVICE,			\
+		.bcdUSB = sys_cpu_to_le16(USB_SRN_2_0),			\
+		.bDeviceClass = USB_BCC_MISCELLANEOUS,			\
+		.bDeviceSubClass = 2,					\
+		.bDeviceProtocol = 1,					\
+		.bMaxPacketSize0 = 64,					\
+		.idVendor = vid,					\
+		.idProduct = pid,					\
+		.bcdDevice = sys_cpu_to_le16(USB_BCD_DRN),		\
+		.iManufacturer = 0,					\
+		.iProduct = 0,						\
+		.iSerialNumber = 0,					\
+		.bNumConfigurations = 0,				\
+	};								\
 	static STRUCT_SECTION_ITERABLE(usbd_contex, device_name) = {	\
 		.name = STRINGIFY(device_name),				\
 		.dev = uhc_dev,						\
-		.desc = &desc_##device_name,				\
+		.fs_desc = &fs_desc_##device_name,			\
+		.hs_desc = &hs_desc_##device_name,			\
 	}
 
 #define USBD_CONFIGURATION_DEFINE(name, attrib, power)			\
@@ -474,14 +496,19 @@
 #define USBD_DESC_SERIAL_NUMBER_DEFINE(d_name, d_string)		\
 	USBD_DESC_STRING_DEFINE(d_name, d_string, USBD_DUT_STRING_SERIAL_NUMBER)
 
-#define USBD_DEFINE_CLASS(class_name, class_api, class_data)			\
-	static struct usbd_class_node class_name = {				\
-		.name = STRINGIFY(class_name),					\
-		.api = class_api,						\
-		.data = class_data,						\
-	};									\
-	static STRUCT_SECTION_ITERABLE(usbd_class_iter, class_name##_iter) = {	\
-		.c_nd = &class_name,						\
+#define USBD_DEFINE_CLASS(class_name, class_api, class_data)		\
+	static struct usbd_class_node class_name = {			\
+		.name = STRINGIFY(class_name),				\
+		.api = class_api,					\
+		.data = class_data,					\
+	};								\
+	static STRUCT_SECTION_ITERABLE_ALTERNATE(			\
+		usbd_class_fs, usbd_class_iter, class_name##_fs) = {	\
+		.c_nd = &class_name,					\
+	};								\
+	static STRUCT_SECTION_ITERABLE_ALTERNATE(			\
+		usbd_class_hs, usbd_class_iter, class_name##_hs) = {	\
+		.c_nd = &class_name,					\
 	}
 
 /** @brief Helper to declare request table of usbd_cctx_vendor_req
@@ -521,11 +548,13 @@
  * @brief Add a USB device configuration
  *
  * @param[in] uds_ctx Pointer to USB device support context
+ * @param[in] speed   Speed at which this configuration operates
  * @param[in] cd      Pointer to USB configuration node
  *
  * @return 0 on success, other values on fail.
  */
 int usbd_add_configuration(struct usbd_contex *uds_ctx,
+			   const enum usbd_speed speed,
 			   struct usbd_config_node *cd);
 
 /**
@@ -544,13 +573,14 @@
  *
  * @param[in] uds_ctx Pointer to USB device support context
  * @param[in] name    Class instance name
+ * @param[in] speed   Configuration speed
  * @param[in] cfg     Configuration value (similar to bConfigurationValue)
  *
  * @return 0 on success, other values on fail.
  */
 int usbd_register_class(struct usbd_contex *uds_ctx,
 			const char *name,
-			uint8_t cfg);
+			const enum usbd_speed speed, uint8_t cfg);
 
 /**
  * @brief Unregister an USB class instance
@@ -561,13 +591,14 @@
  *
  * @param[in] uds_ctx Pointer to USB device support context
  * @param[in] name    Class instance name
+ * @param[in] speed   Configuration speed
  * @param[in] cfg     Configuration value (similar to bConfigurationValue)
  *
  * @return 0 on success, other values on fail.
  */
 int usbd_unregister_class(struct usbd_contex *uds_ctx,
 			  const char *name,
-			  uint8_t cfg);
+			  const enum usbd_speed speed, uint8_t cfg);
 
 /**
  * @brief Register USB notification message callback
@@ -772,12 +803,13 @@
  * @brief Set USB device descriptor value bcdUSB
  *
  * @param[in] uds_ctx Pointer to USB device support context
+ * @param[in] speed   Speed for which the bcdUSB should be set
  * @param[in] bcd     bcdUSB value
  *
  * @return 0 on success, other values on fail.
  */
 int usbd_device_set_bcd(struct usbd_contex *const uds_ctx,
-			 const uint16_t bcd);
+			const enum usbd_speed speed, const uint16_t bcd);
 
 /**
  * @brief Set USB device descriptor value idVendor
@@ -805,6 +837,7 @@
  * @brief Set USB device descriptor code triple Base Class, SubClass, and Protocol
  *
  * @param[in] uds_ctx    Pointer to USB device support context
+ * @param[in] speed      Speed for which the code triple should be set
  * @param[in] base_class bDeviceClass value
  * @param[in] subclass   bDeviceSubClass value
  * @param[in] protocol   bDeviceProtocol value
@@ -812,6 +845,7 @@
  * @return 0 on success, other values on fail.
  */
 int usbd_device_set_code_triple(struct usbd_contex *const uds_ctx,
+				const enum usbd_speed speed,
 				const uint8_t base_class,
 				const uint8_t subclass, const uint8_t protocol);
 
@@ -819,36 +853,42 @@
  * @brief Setup USB device configuration attribute Remote Wakeup
  *
  * @param[in] uds_ctx Pointer to USB device support context
+ * @param[in] speed   Configuration speed
  * @param[in] cfg     Configuration number
  * @param[in] enable  Sets attribute if true, clears it otherwise
  *
  * @return 0 on success, other values on fail.
  */
 int usbd_config_attrib_rwup(struct usbd_contex *const uds_ctx,
+			    const enum usbd_speed speed,
 			    const uint8_t cfg, const bool enable);
 
 /**
  * @brief Setup USB device configuration attribute Self-powered
  *
  * @param[in] uds_ctx Pointer to USB device support context
+ * @param[in] speed   Configuration speed
  * @param[in] cfg     Configuration number
  * @param[in] enable  Sets attribute if true, clears it otherwise
  *
  * @return 0 on success, other values on fail.
  */
 int usbd_config_attrib_self(struct usbd_contex *const uds_ctx,
+			    const enum usbd_speed speed,
 			    const uint8_t cfg, const bool enable);
 
 /**
  * @brief Setup USB device configuration power consumption
  *
  * @param[in] uds_ctx Pointer to USB device support context
+ * @param[in] speed   Configuration speed
  * @param[in] cfg     Configuration number
  * @param[in] power   Maximum power consumption value (bMaxPower)
  *
  * @return 0 on success, other values on fail.
  */
 int usbd_config_maxpower(struct usbd_contex *const uds_ctx,
+			 const enum usbd_speed speed,
 			 const uint8_t cfg, const uint8_t power);
 /**
  * @}
diff --git a/samples/subsys/usb/common/sample_usbd_init.c b/samples/subsys/usb/common/sample_usbd_init.c
index 6bfe2d6..a9a4974 100644
--- a/samples/subsys/usb/common/sample_usbd_init.c
+++ b/samples/subsys/usb/common/sample_usbd_init.c
@@ -29,10 +29,94 @@
 				  (IS_ENABLED(CONFIG_SAMPLE_USBD_REMOTE_WAKEUP) ?
 				   USB_SCD_REMOTE_WAKEUP : 0);
 
-USBD_CONFIGURATION_DEFINE(sample_config,
+USBD_CONFIGURATION_DEFINE(sample_fs_config,
 			  attributes,
 			  CONFIG_SAMPLE_USBD_MAX_POWER);
 
+USBD_CONFIGURATION_DEFINE(sample_hs_config,
+			  attributes,
+			  CONFIG_SAMPLE_USBD_MAX_POWER);
+
+static int register_fs_classes(struct usbd_contex *uds_ctx)
+{
+	int err = 0;
+
+	STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_iter, iter) {
+		/* Pull everything that is enabled in our configuration. */
+		err = usbd_register_class(uds_ctx, iter->c_nd->name,
+					  USBD_SPEED_FS, 1);
+		if (err) {
+			LOG_ERR("Failed to register FS %s (%d)",
+				iter->c_nd->name, err);
+			return err;
+		}
+
+		LOG_DBG("Register FS %s", iter->c_nd->name);
+	}
+
+	return err;
+}
+
+static int register_hs_classes(struct usbd_contex *uds_ctx)
+{
+	int err = 0;
+
+	STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_iter, iter) {
+		/* Pull everything that is enabled in our configuration. */
+		err = usbd_register_class(uds_ctx, iter->c_nd->name,
+					  USBD_SPEED_HS, 1);
+		if (err) {
+			LOG_ERR("Failed to register HS %s (%d)",
+				iter->c_nd->name, err);
+			return err;
+		}
+
+		LOG_DBG("Register HS %s", iter->c_nd->name);
+	}
+
+	return err;
+}
+
+static int sample_add_configuration(struct usbd_contex *uds_ctx,
+				    const enum usbd_speed speed,
+				    struct usbd_config_node *config)
+{
+	int err;
+
+	err = usbd_add_configuration(uds_ctx, speed, config);
+	if (err) {
+		LOG_ERR("Failed to add configuration (%d)", err);
+		return err;
+	}
+
+	if (speed == USBD_SPEED_FS) {
+		err = register_fs_classes(uds_ctx);
+	} else if (speed == USBD_SPEED_HS) {
+		err = register_hs_classes(uds_ctx);
+	}
+
+	if (err) {
+		return err;
+	}
+
+	/* Always use class code information from Interface Descriptors */
+	if (IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS) ||
+	    IS_ENABLED(CONFIG_USBD_CDC_ECM_CLASS) ||
+	    IS_ENABLED(CONFIG_USBD_AUDIO2_CLASS)) {
+		/*
+		 * Class with multiple interfaces have an Interface
+		 * Association Descriptor available, use an appropriate triple
+		 * to indicate it.
+		 */
+		usbd_device_set_code_triple(uds_ctx, speed,
+					    USB_BCC_MISCELLANEOUS, 0x02, 0x01);
+	} else {
+		usbd_device_set_code_triple(uds_ctx, speed, 0, 0, 0);
+	}
+
+	return 0;
+}
+
 struct usbd_contex *sample_usbd_init_device(usbd_msg_cb_t msg_cb)
 {
 	int err;
@@ -61,37 +145,20 @@
 		return NULL;
 	}
 
-	err = usbd_add_configuration(&sample_usbd, &sample_config);
-	if (err) {
-		LOG_ERR("Failed to add configuration (%d)", err);
-		return NULL;
-	}
-
-	STRUCT_SECTION_FOREACH(usbd_class_iter, iter) {
-		/* Pull everything that is enabled in our configuration. */
-		err = usbd_register_class(&sample_usbd, iter->c_nd->name, 1);
+	if (usbd_caps_speed(&sample_usbd) == USBD_SPEED_HS) {
+		err = sample_add_configuration(&sample_usbd, USBD_SPEED_HS,
+					       &sample_hs_config);
 		if (err) {
-			LOG_ERR("Failed to register %s (%d)",
-				iter->c_nd->name, err);
+			LOG_ERR("Failed to add High-Speed configuration");
 			return NULL;
 		}
-
-		LOG_DBG("Register %s", iter->c_nd->name);
 	}
 
-	/* Always use class code information from Interface Descriptors */
-	if (IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS) ||
-	    IS_ENABLED(CONFIG_USBD_CDC_ECM_CLASS) ||
-	    IS_ENABLED(CONFIG_USBD_AUDIO2_CLASS)) {
-		/*
-		 * Class with multiple interfaces have an Interface
-		 * Association Descriptor available, use an appropriate triple
-		 * to indicate it.
-		 */
-		usbd_device_set_code_triple(&sample_usbd,
-					    USB_BCC_MISCELLANEOUS, 0x02, 0x01);
-	} else {
-		usbd_device_set_code_triple(&sample_usbd, 0, 0, 0);
+	err = sample_add_configuration(&sample_usbd, USBD_SPEED_FS,
+				       &sample_fs_config);
+	if (err) {
+		LOG_ERR("Failed to add Full-Speed configuration");
+		return NULL;
 	}
 
 	if (msg_cb != NULL) {
diff --git a/subsys/usb/device_next/class/bt_hci.c b/subsys/usb/device_next/class/bt_hci.c
index 99b9bbc..dc45a1f 100644
--- a/subsys/usb/device_next/class/bt_hci.c
+++ b/subsys/usb/device_next/class/bt_hci.c
@@ -460,16 +460,10 @@
 static int bt_hci_init(struct usbd_class_node *const c_nd)
 {
 
-	struct usbd_contex *uds_ctx = usbd_class_get_ctx(c_nd);
 	struct bt_hci_data *data = usbd_class_get_private(c_nd);
 	struct usbd_bt_hci_desc *desc = data->desc;
 
 	desc->iad.bFirstInterface = desc->if0.bInterfaceNumber;
-	if (usbd_caps_speed(uds_ctx) == USBD_SPEED_HS) {
-		LOG_INF("FS endpoint descriptor needs to be updated");
-		desc->if0_in_ep.bEndpointAddress = desc->if0_hs_in_ep.bEndpointAddress;
-		desc->if0_out_ep.bEndpointAddress = desc->if0_hs_out_ep.bEndpointAddress;
-	}
 
 	return 0;
 }
diff --git a/subsys/usb/device_next/class/loopback.c b/subsys/usb/device_next/class/loopback.c
index bfcfc95..6bffa63 100644
--- a/subsys/usb/device_next/class/loopback.c
+++ b/subsys/usb/device_next/class/loopback.c
@@ -135,16 +135,10 @@
 
 static int lb_init(struct usbd_class_node *c_nd)
 {
-	struct usbd_contex *uds_ctx = usbd_class_get_ctx(c_nd);
 	struct lb_data *data = usbd_class_get_private(c_nd);
 	struct loopback_desc *desc = data->desc;
 
 	desc->iad.bFirstInterface = desc->if0.bInterfaceNumber;
-	if (usbd_caps_speed(uds_ctx) == USBD_SPEED_HS) {
-		LOG_INF("FS endpoint descriptor needs to be updated");
-		desc->if0_in_ep.bEndpointAddress = desc->if0_hs_in_ep.bEndpointAddress;
-		desc->if0_out_ep.bEndpointAddress = desc->if0_hs_out_ep.bEndpointAddress;
-	}
 
 	LOG_DBG("Init class instance %p", c_nd);
 
diff --git a/subsys/usb/device_next/class/usbd_cdc_acm.c b/subsys/usb/device_next/class/usbd_cdc_acm.c
index 59a342d..c7d4e44 100644
--- a/subsys/usb/device_next/class/usbd_cdc_acm.c
+++ b/subsys/usb/device_next/class/usbd_cdc_acm.c
@@ -444,7 +444,6 @@
 
 static int usbd_cdc_acm_init(struct usbd_class_node *const c_nd)
 {
-	struct usbd_contex *uds_ctx = usbd_class_get_ctx(c_nd);
 	const struct device *dev = usbd_class_get_private(c_nd);
 	struct cdc_acm_uart_data *data = dev->data;
 	struct usbd_cdc_acm_desc *desc = data->desc;
@@ -453,13 +452,6 @@
 	desc->if0_union.bControlInterface = desc->if0.bInterfaceNumber;
 	desc->if0_union.bSubordinateInterface0 = desc->if1.bInterfaceNumber;
 
-	if (usbd_caps_speed(uds_ctx) == USBD_SPEED_HS) {
-		LOG_INF("FS endpoint descriptor needs to be updated");
-		desc->if0_int_ep.bEndpointAddress = desc->if0_hs_int_ep.bEndpointAddress;
-		desc->if1_in_ep.bEndpointAddress = desc->if1_hs_in_ep.bEndpointAddress;
-		desc->if1_out_ep.bEndpointAddress = desc->if1_hs_out_ep.bEndpointAddress;
-	}
-
 	return 0;
 }
 
diff --git a/subsys/usb/device_next/class/usbd_cdc_ecm.c b/subsys/usb/device_next/class/usbd_cdc_ecm.c
index cc8d676..39895ae 100644
--- a/subsys/usb/device_next/class/usbd_cdc_ecm.c
+++ b/subsys/usb/device_next/class/usbd_cdc_ecm.c
@@ -454,18 +454,6 @@
 	desc->if0_union.bSubordinateInterface0 = if_num + 1;
 	LOG_DBG("CDC ECM class initialized");
 
-	/* Core device support configures the instance's endpoint addresses,
-	 * but only for the highest supported speed descriptor set. Fix this
-	 * for the case where a high-speed capable controller is connected to
-	 * the full-speed bus.
-	 */
-	if (usbd_caps_speed(uds_ctx) == USBD_SPEED_HS) {
-		LOG_INF("FS endpoint descriptor needs to be updated");
-		desc->if0_int_ep.bEndpointAddress = desc->if0_hs_int_ep.bEndpointAddress;
-		desc->if1_1_in_ep.bEndpointAddress = desc->if1_1_hs_in_ep.bEndpointAddress;
-		desc->if1_1_out_ep.bEndpointAddress = desc->if1_1_hs_out_ep.bEndpointAddress;
-	}
-
 	if (usbd_add_descriptor(c_nd->data->uds_ctx, data->mac_desc_nd)) {
 		LOG_ERR("Failed to add iMACAddress string descriptor");
 	} else {
diff --git a/subsys/usb/device_next/class/usbd_msc.c b/subsys/usb/device_next/class/usbd_msc.c
index 592de73..c709048 100644
--- a/subsys/usb/device_next/class/usbd_msc.c
+++ b/subsys/usb/device_next/class/usbd_msc.c
@@ -766,9 +766,7 @@
 /* Initialization of the class implementation */
 static int msc_bot_init(struct usbd_class_node *const node)
 {
-	struct usbd_contex *uds_ctx = usbd_class_get_ctx(node);
 	struct msc_bot_ctx *ctx = usbd_class_get_private(node);
-	struct msc_bot_desc *desc = ctx->desc;
 
 	ctx->class_node = node;
 	ctx->state = MSC_BBB_EXPECT_CBW;
@@ -784,12 +782,6 @@
 			  lun->vendor, lun->product, lun->revision);
 	}
 
-	if (usbd_caps_speed(uds_ctx) == USBD_SPEED_HS) {
-		LOG_INF("FS endpoint descriptor needs to be updated");
-		desc->if0_in_ep.bEndpointAddress = desc->if0_hs_in_ep.bEndpointAddress;
-		desc->if0_out_ep.bEndpointAddress = desc->if0_hs_out_ep.bEndpointAddress;
-	}
-
 	return 0;
 }
 
diff --git a/subsys/usb/device_next/usbd_ch9.c b/subsys/usb/device_next/usbd_ch9.c
index 71d3c49..e7e032f 100644
--- a/subsys/usb/device_next/usbd_ch9.c
+++ b/subsys/usb/device_next/usbd_ch9.c
@@ -122,6 +122,7 @@
 static int sreq_set_configuration(struct usbd_contex *const uds_ctx)
 {
 	struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
+	const enum usbd_speed speed = usbd_bus_speed(uds_ctx);
 	int ret;
 
 	LOG_INF("Set Configuration Request value %u", setup->wValue);
@@ -142,7 +143,7 @@
 		return 0;
 	}
 
-	if (setup->wValue && !usbd_config_exist(uds_ctx, setup->wValue)) {
+	if (setup->wValue && !usbd_config_exist(uds_ctx, speed, setup->wValue)) {
 		errno = -EPERM;
 		return 0;
 	}
@@ -467,7 +468,17 @@
 		return 0;
 	}
 
-	cfg_nd = usbd_config_get(uds_ctx, idx + 1);
+	if (other_cfg) {
+		if (speed == USBD_SPEED_FS) {
+			get_desc_speed = USBD_SPEED_HS;
+		} else {
+			get_desc_speed = USBD_SPEED_FS;
+		}
+	} else {
+		get_desc_speed = speed;
+	}
+
+	cfg_nd = usbd_config_get(uds_ctx, get_desc_speed, idx + 1);
 	if (cfg_nd == NULL) {
 		LOG_ERR("Configuration descriptor %u not found", idx + 1);
 		errno = -ENOTSUP;
@@ -475,29 +486,12 @@
 	}
 
 	if (other_cfg) {
-		/*
-		 * Because the structure of the other-speed-configuration is
-		 * the same as a configuration descriptor, and the other speed
-		 * function collection has the same length and the number of
-		 * interfaces and endpoints, we simply copy the configuration
-		 * descriptor and update the type.
-		 * If at some point the number of interfaces or endpoints for
-		 * full and high speed descritpors in a class implementation
-		 * becomes different, we need to revisit this and compute the
-		 * configuration descriptor properties on the fly.
-		 */
+		/* Copy the configuration descriptor and update the type */
 		memcpy(&other_desc, cfg_nd->desc, sizeof(other_desc));
 		other_desc.bDescriptorType = USB_DESC_OTHER_SPEED;
-
 		cfg_desc = &other_desc;
-		if (speed != USBD_SPEED_HS) {
-			get_desc_speed = USBD_SPEED_HS;
-		} else {
-			get_desc_speed = USBD_SPEED_FS;
-		}
 	} else {
 		cfg_desc = cfg_nd->desc;
-		get_desc_speed = speed;
 	}
 
 	net_buf_add_mem(buf, cfg_desc, MIN(net_buf_tailroom(buf), cfg_desc->bLength));
@@ -535,7 +529,17 @@
 	size_t len;
 
 	if (type == USB_DESC_DEVICE) {
-		head = uds_ctx->desc;
+		switch (usbd_bus_speed(uds_ctx)) {
+		case USBD_SPEED_FS:
+			head = uds_ctx->fs_desc;
+			break;
+		case USBD_SPEED_HS:
+			head = uds_ctx->hs_desc;
+			break;
+		default:
+			errno = -ENOTSUP;
+			return 0;
+		}
 	} else {
 		head = usbd_get_descriptor(uds_ctx, type, idx);
 	}
@@ -555,7 +559,10 @@
 				  struct net_buf *const buf)
 {
 	struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
-	struct usb_device_descriptor *d_desc = uds_ctx->desc;
+	/* At Full-Speed we want High-Speed descriptor and vice versa */
+	struct usb_device_descriptor *d_desc =
+		usbd_bus_speed(uds_ctx) == USBD_SPEED_FS ?
+		uds_ctx->hs_desc : uds_ctx->fs_desc;
 	struct usb_device_qualifier_descriptor q_desc = {
 		.bLength = sizeof(struct usb_device_qualifier_descriptor),
 		.bDescriptorType = USB_DESC_DEVICE_QUALIFIER,
diff --git a/subsys/usb/device_next/usbd_class.c b/subsys/usb/device_next/usbd_class.c
index 7dc8e48..d8400b8 100644
--- a/subsys/usb/device_next/usbd_class.c
+++ b/subsys/usb/device_next/usbd_class.c
@@ -45,13 +45,14 @@
 
 struct usbd_class_iter *
 usbd_class_get_by_config(struct usbd_contex *const uds_ctx,
+			 const enum usbd_speed speed,
 			 const uint8_t cnum,
 			 const uint8_t inum)
 {
 	struct usbd_class_iter *iter;
 	struct usbd_config_node *cfg_nd;
 
-	cfg_nd = usbd_config_get(uds_ctx, cnum);
+	cfg_nd = usbd_config_get(uds_ctx, speed, cnum);
 	if (cfg_nd == NULL) {
 		return NULL;
 	}
@@ -141,6 +142,7 @@
 {
 	struct usbd_class_iter *iter;
 	struct usbd_config_node *cfg_nd;
+	enum usbd_speed speed;
 	uint8_t ep_idx = USB_EP_GET_IDX(ep);
 	uint8_t cfg;
 	uint32_t ep_bm;
@@ -157,7 +159,8 @@
 	}
 
 	cfg = usbd_get_config_value(uds_ctx);
-	cfg_nd = usbd_config_get(uds_ctx, cfg);
+	speed = usbd_bus_speed(uds_ctx);
+	cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
 	if (cfg_nd == NULL) {
 		return NULL;
 	}
@@ -202,11 +205,22 @@
 	return NULL;
 }
 
-static struct usbd_class_iter *usbd_class_iter_get(const char *name)
+static struct usbd_class_iter *
+usbd_class_iter_get(const char *name, const enum usbd_speed speed)
 {
-	STRUCT_SECTION_FOREACH(usbd_class_iter, iter) {
-		if (strcmp(name, iter->c_nd->name) == 0) {
-			return iter;
+	if (speed == USBD_SPEED_FS) {
+		STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs,
+						 usbd_class_iter, iter) {
+			if (strcmp(name, iter->c_nd->name) == 0) {
+				return iter;
+			}
+		}
+	} else if (speed == USBD_SPEED_HS) {
+		STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs,
+						 usbd_class_iter, iter) {
+			if (strcmp(name, iter->c_nd->name) == 0) {
+				return iter;
+			}
 		}
 	}
 
@@ -217,11 +231,12 @@
 
 static int usbd_class_append(struct usbd_contex *const uds_ctx,
 			     struct usbd_class_iter *const iter,
+			     const enum usbd_speed speed,
 			     const uint8_t cfg)
 {
 	struct usbd_config_node *cfg_nd;
 
-	cfg_nd = usbd_config_get(uds_ctx, cfg);
+	cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
 	if (cfg_nd == NULL) {
 		return -ENODATA;
 	}
@@ -233,11 +248,12 @@
 
 static int usbd_class_remove(struct usbd_contex *const uds_ctx,
 			     struct usbd_class_iter *const iter,
+			     const enum usbd_speed speed,
 			     const uint8_t cfg)
 {
 	struct usbd_config_node *cfg_nd;
 
-	cfg_nd = usbd_config_get(uds_ctx, cfg);
+	cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
 	if (cfg_nd == NULL) {
 		return -ENODATA;
 	}
@@ -250,13 +266,14 @@
 }
 
 int usbd_class_remove_all(struct usbd_contex *const uds_ctx,
+			  const enum usbd_speed speed,
 			  const uint8_t cfg)
 {
 	struct usbd_config_node *cfg_nd;
 	struct usbd_class_iter *iter;
 	sys_snode_t *node;
 
-	cfg_nd = usbd_config_get(uds_ctx, cfg);
+	cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
 	if (cfg_nd == NULL) {
 		return -ENODATA;
 	}
@@ -277,13 +294,13 @@
 
 int usbd_register_class(struct usbd_contex *const uds_ctx,
 			const char *name,
-			const uint8_t cfg)
+			const enum usbd_speed speed, const uint8_t cfg)
 {
 	struct usbd_class_iter *iter;
 	struct usbd_class_data *data;
 	int ret;
 
-	iter = usbd_class_iter_get(name);
+	iter = usbd_class_iter_get(name, speed);
 	if (iter == NULL) {
 		return -ENODEV;
 	}
@@ -305,7 +322,13 @@
 		goto register_class_error;
 	}
 
-	ret = usbd_class_append(uds_ctx, iter, cfg);
+	if ((data->uds_ctx != NULL) && (data->uds_ctx != uds_ctx)) {
+		LOG_ERR("Class registered to other context at different speed");
+		ret = -EBUSY;
+		goto register_class_error;
+	}
+
+	ret = usbd_class_append(uds_ctx, iter, speed, cfg);
 	if (ret == 0) {
 		/* Initialize pointer back to the device struct */
 		atomic_set_bit(&iter->state, USBD_CCTX_REGISTERED);
@@ -319,13 +342,14 @@
 
 int usbd_unregister_class(struct usbd_contex *const uds_ctx,
 			  const char *name,
-			  const uint8_t cfg)
+			  const enum usbd_speed speed, const uint8_t cfg)
 {
 	struct usbd_class_iter *iter;
 	struct usbd_class_data *data;
+	bool can_release_data = true;
 	int ret;
 
-	iter = usbd_class_iter_get(name);
+	iter = usbd_class_iter_get(name, speed);
 	if (iter == NULL) {
 		return -ENODEV;
 	}
@@ -346,11 +370,37 @@
 		goto unregister_class_error;
 	}
 
-	ret = usbd_class_remove(uds_ctx, iter, cfg);
+	/* TODO: The use of atomic here does not make this code thread safe.
+	 * The atomic should be changed to something else.
+	 */
+	if (speed == USBD_SPEED_HS) {
+		STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs,
+						 usbd_class_iter, i) {
+			if ((i->c_nd == iter->c_nd) &&
+			    atomic_test_bit(&i->state, USBD_CCTX_REGISTERED)) {
+				can_release_data = false;
+				break;
+			}
+		}
+	} else {
+		STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs,
+						 usbd_class_iter, i) {
+			if ((i->c_nd == iter->c_nd) &&
+			    atomic_test_bit(&i->state, USBD_CCTX_REGISTERED)) {
+				can_release_data = false;
+				break;
+			}
+		}
+	}
+
+	ret = usbd_class_remove(uds_ctx, iter, speed, cfg);
 	if (ret == 0) {
 		atomic_clear_bit(&iter->state, USBD_CCTX_REGISTERED);
 		usbd_class_shutdown(iter->c_nd);
-		data->uds_ctx = NULL;
+
+		if (can_release_data) {
+			data->uds_ctx = NULL;
+		}
 	}
 
 unregister_class_error:
diff --git a/subsys/usb/device_next/usbd_class.h b/subsys/usb/device_next/usbd_class.h
index 827342d..469433e 100644
--- a/subsys/usb/device_next/usbd_class.h
+++ b/subsys/usb/device_next/usbd_class.h
@@ -54,12 +54,14 @@
  * @brief Get class context by configuration and interface number
  *
  * @param[in] uds_ctx Pointer to device context
+ * @param[in] speed   Speed the configuration number refers to
  * @param[in] cnum    Configuration number
  * @param[in] inum    Interface number
  *
  * @return Class iter pointer or NULL
  */
 struct usbd_class_iter *usbd_class_get_by_config(struct usbd_contex *uds_ctx,
+						 const enum usbd_speed speed,
 						 uint8_t cnum,
 						 uint8_t inum);
 
@@ -97,11 +99,13 @@
  * @brief Remove all registered class instances from a configuration
  *
  * @param[in] uds_ctx Pointer to device context
+ * @param[in] speed   Speed the configuration number applies to
  * @param[in] cfg     Configuration number (bConfigurationValue)
  *
  * @return 0 on success, other values on fail.
  */
 int usbd_class_remove_all(struct usbd_contex *const uds_ctx,
+			  const enum usbd_speed speed,
 			  const uint8_t cfg);
 
 #endif /* ZEPHYR_INCLUDE_USBD_CLASS_H */
diff --git a/subsys/usb/device_next/usbd_config.c b/subsys/usb/device_next/usbd_config.c
index 0efa6e0..f9f37c9 100644
--- a/subsys/usb/device_next/usbd_config.c
+++ b/subsys/usb/device_next/usbd_config.c
@@ -16,12 +16,26 @@
 #include <zephyr/logging/log.h>
 LOG_MODULE_REGISTER(usbd_cfg, CONFIG_USBD_LOG_LEVEL);
 
+static sys_slist_t *usbd_configs(struct usbd_contex *uds_ctx,
+				 const enum usbd_speed speed)
+{
+	switch (speed) {
+	case USBD_SPEED_FS:
+		return &uds_ctx->fs_configs;
+	case USBD_SPEED_HS:
+		return &uds_ctx->hs_configs;
+	default:
+		return NULL;
+	}
+}
+
 struct usbd_config_node *usbd_config_get(struct usbd_contex *const uds_ctx,
+					 const enum usbd_speed speed,
 					 const uint8_t cfg)
 {
 	struct usbd_config_node *cfg_nd;
 
-	SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->configs, cfg_nd, node) {
+	SYS_SLIST_FOR_EACH_CONTAINER(usbd_configs(uds_ctx, speed), cfg_nd, node) {
 		if (usbd_config_get_value(cfg_nd) == cfg) {
 			return cfg_nd;
 		}
@@ -38,7 +52,8 @@
 		return NULL;
 	}
 
-	return usbd_config_get(uds_ctx, usbd_get_config_value(uds_ctx));
+	return usbd_config_get(uds_ctx, usbd_bus_speed(uds_ctx),
+			       usbd_get_config_value(uds_ctx));
 }
 
 static void usbd_config_classes_enable(struct usbd_config_node *const cfg_nd,
@@ -78,11 +93,12 @@
 }
 
 bool usbd_config_exist(struct usbd_contex *const uds_ctx,
+		       const enum usbd_speed speed,
 		       const uint8_t cfg)
 {
 	struct usbd_config_node *config;
 
-	config = usbd_config_get(uds_ctx, cfg);
+	config = usbd_config_get(uds_ctx, speed, cfg);
 
 	return (config != NULL) ? true : false;
 }
@@ -91,6 +107,7 @@
 		    const uint8_t new_cfg)
 {
 	struct usbd_config_node *cfg_nd;
+	const enum usbd_speed speed = usbd_bus_speed(uds_ctx);
 	int ret;
 
 	if (usbd_get_config_value(uds_ctx) != 0) {
@@ -106,12 +123,12 @@
 		return 0;
 	}
 
-	cfg_nd = usbd_config_get(uds_ctx, new_cfg);
+	cfg_nd = usbd_config_get(uds_ctx, speed, new_cfg);
 	if (cfg_nd == NULL) {
 		return -ENODATA;
 	}
 
-	ret = usbd_interface_default(uds_ctx, cfg_nd);
+	ret = usbd_interface_default(uds_ctx, speed, cfg_nd);
 	if (ret) {
 		return ret;
 	}
@@ -127,6 +144,7 @@
  */
 
 int usbd_config_attrib_rwup(struct usbd_contex *const uds_ctx,
+			    const enum usbd_speed speed,
 			    const uint8_t cfg, const bool enable)
 {
 	struct usbd_config_node *cfg_nd;
@@ -148,7 +166,7 @@
 		goto attrib_rwup_exit;
 	}
 
-	cfg_nd = usbd_config_get(uds_ctx, cfg);
+	cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
 	if (cfg_nd == NULL) {
 		LOG_INF("Configuration %u not found", cfg);
 		ret = -ENODATA;
@@ -168,6 +186,7 @@
 }
 
 int usbd_config_attrib_self(struct usbd_contex *const uds_ctx,
+			    const enum usbd_speed speed,
 			    const uint8_t cfg, const bool enable)
 {
 	struct usbd_config_node *cfg_nd;
@@ -181,7 +200,7 @@
 		goto attrib_self_exit;
 	}
 
-	cfg_nd = usbd_config_get(uds_ctx, cfg);
+	cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
 	if (cfg_nd == NULL) {
 		LOG_INF("Configuration %u not found", cfg);
 		ret = -ENODATA;
@@ -201,6 +220,7 @@
 }
 
 int usbd_config_maxpower(struct usbd_contex *const uds_ctx,
+			 const enum usbd_speed speed,
 			 const uint8_t cfg, const uint8_t power)
 {
 	struct usbd_config_node *cfg_nd;
@@ -214,7 +234,7 @@
 		goto maxpower_exit;
 	}
 
-	cfg_nd = usbd_config_get(uds_ctx, cfg);
+	cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
 	if (cfg_nd == NULL) {
 		LOG_INF("Configuration %u not found", cfg);
 		ret = -ENODATA;
@@ -230,9 +250,12 @@
 }
 
 int usbd_add_configuration(struct usbd_contex *const uds_ctx,
+			   const enum usbd_speed speed,
 			   struct usbd_config_node *const cfg_nd)
 {
 	struct usb_cfg_descriptor *desc = cfg_nd->desc;
+	sys_slist_t *configs;
+	sys_snode_t *node;
 	int ret = 0;
 
 	usbd_device_lock(uds_ctx);
@@ -243,6 +266,13 @@
 		goto add_configuration_exit;
 	}
 
+	if (speed == USBD_SPEED_HS &&
+	    usbd_caps_speed(uds_ctx) == USBD_SPEED_FS) {
+		LOG_ERR("Controller doesn't support HS");
+		ret = -ENOTSUP;
+		goto add_configuration_exit;
+	}
+
 	if (desc->bmAttributes & USB_SCD_REMOTE_WAKEUP) {
 		struct udc_device_caps caps = udc_caps(uds_ctx->dev);
 
@@ -253,15 +283,43 @@
 		}
 	}
 
-	if (sys_slist_find_and_remove(&uds_ctx->configs, &cfg_nd->node)) {
+	configs = usbd_configs(uds_ctx, speed);
+	switch (speed) {
+	case USBD_SPEED_HS:
+		SYS_SLIST_FOR_EACH_NODE(&uds_ctx->fs_configs, node) {
+			if (node == &cfg_nd->node) {
+				LOG_ERR("HS config already on FS list");
+				ret = -EINVAL;
+				goto add_configuration_exit;
+			}
+		}
+		break;
+	case USBD_SPEED_FS:
+		SYS_SLIST_FOR_EACH_NODE(&uds_ctx->hs_configs, node) {
+			if (node == &cfg_nd->node) {
+				LOG_ERR("FS config already on HS list");
+				ret = -EINVAL;
+				goto add_configuration_exit;
+			}
+		}
+		break;
+	default:
+		LOG_ERR("Unsupported configuration speed");
+		ret = -ENOTSUP;
+		goto add_configuration_exit;
+	}
+
+	if (sys_slist_find_and_remove(configs, &cfg_nd->node)) {
 		LOG_WRN("Configuration %u re-inserted",
 			usbd_config_get_value(cfg_nd));
 	} else {
-		usbd_config_set_value(cfg_nd, usbd_get_num_configs(uds_ctx) + 1);
-		usbd_set_num_configs(uds_ctx, usbd_get_num_configs(uds_ctx) + 1);
+		uint8_t num = usbd_get_num_configs(uds_ctx, speed) + 1;
+
+		usbd_config_set_value(cfg_nd, num);
+		usbd_set_num_configs(uds_ctx, speed, num);
 	}
 
-	sys_slist_append(&uds_ctx->configs, &cfg_nd->node);
+	sys_slist_append(configs, &cfg_nd->node);
 
 	usbd_device_unlock(uds_ctx);
 
diff --git a/subsys/usb/device_next/usbd_config.h b/subsys/usb/device_next/usbd_config.h
index b3e8794..f042446 100644
--- a/subsys/usb/device_next/usbd_config.h
+++ b/subsys/usb/device_next/usbd_config.h
@@ -42,11 +42,13 @@
  * Get configuration node with desired configuration number.
  *
  * @param[in] ctx    Pointer to USB device support context
+ * @param[in] speed  Speed the configuration number applies to
  * @param[in] cfg    Configuration number (bConfigurationValue)
  *
  * @return pointer to configuration node or NULL if does not exist
  */
 struct usbd_config_node *usbd_config_get(struct usbd_contex *uds_ctx,
+					 const enum usbd_speed speed,
 					 uint8_t cfg);
 
 /**
@@ -64,11 +66,13 @@
  * @brief Check whether a configuration exist
  *
  * @param[in] ctx    Pointer to USB device support context
+ * @param[in] speed  Speed at which the configuration should be checked
  * @param[in] cfg    Configuration number (bConfigurationValue)
  *
  * @return True if a configuration exist.
  */
 bool usbd_config_exist(struct usbd_contex *const uds_ctx,
+		       const enum usbd_speed speed,
 		       const uint8_t cfg);
 
 /**
diff --git a/subsys/usb/device_next/usbd_core.c b/subsys/usb/device_next/usbd_core.c
index a5968d1..0fffac2 100644
--- a/subsys/usb/device_next/usbd_core.c
+++ b/subsys/usb/device_next/usbd_core.c
@@ -226,10 +226,19 @@
 	struct usbd_config_node *cfg_nd;
 	int ret;
 
-	SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->configs, cfg_nd, node) {
+	SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->hs_configs, cfg_nd, node) {
 		uint8_t cfg_value = usbd_config_get_value(cfg_nd);
 
-		ret = usbd_class_remove_all(uds_ctx, cfg_value);
+		ret = usbd_class_remove_all(uds_ctx, USBD_SPEED_HS, cfg_value);
+		if (ret) {
+			LOG_ERR("Failed to cleanup registered classes, %d", ret);
+		}
+	}
+
+	SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->fs_configs, cfg_nd, node) {
+		uint8_t cfg_value = usbd_config_get_value(cfg_nd);
+
+		ret = usbd_class_remove_all(uds_ctx, USBD_SPEED_FS, cfg_value);
 		if (ret) {
 			LOG_ERR("Failed to cleanup registered classes, %d", ret);
 		}
@@ -254,7 +263,11 @@
 	k_thread_name_set(&usbd_thread_data, "usbd");
 
 	LOG_DBG("Available USB class iterators:");
-	STRUCT_SECTION_FOREACH(usbd_class_iter, iter) {
+	STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_iter, iter) {
+		atomic_set(&iter->state, 0);
+		LOG_DBG("\t%p->%p, name %s", iter, iter->c_nd, iter->c_nd->name);
+	}
+	STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_iter, iter) {
 		atomic_set(&iter->state, 0);
 		LOG_DBG("\t%p->%p, name %s", iter, iter->c_nd, iter->c_nd->name);
 	}
diff --git a/subsys/usb/device_next/usbd_data.ld b/subsys/usb/device_next/usbd_data.ld
index ab2c754..4038203 100644
--- a/subsys/usb/device_next/usbd_data.ld
+++ b/subsys/usb/device_next/usbd_data.ld
@@ -1,4 +1,5 @@
 #include <zephyr/linker/iterable_sections.h>
 
 ITERABLE_SECTION_RAM(usbd_contex, Z_LINK_ITERABLE_SUBALIGN)
-ITERABLE_SECTION_RAM(usbd_class_iter, Z_LINK_ITERABLE_SUBALIGN)
+ITERABLE_SECTION_RAM(usbd_class_fs, Z_LINK_ITERABLE_SUBALIGN)
+ITERABLE_SECTION_RAM(usbd_class_hs, Z_LINK_ITERABLE_SUBALIGN)
diff --git a/subsys/usb/device_next/usbd_desc.c b/subsys/usb/device_next/usbd_desc.c
index 825d1b9..b5d086d 100644
--- a/subsys/usb/device_next/usbd_desc.c
+++ b/subsys/usb/device_next/usbd_desc.c
@@ -203,13 +203,15 @@
 int usbd_add_descriptor(struct usbd_contex *const uds_ctx,
 			struct usbd_desc_node *const desc_nd)
 {
-	struct usb_device_descriptor *dev_desc = uds_ctx->desc;
+	struct usb_device_descriptor *hs_desc, *fs_desc;
 	struct usb_desc_header *head;
 	int ret = 0;
 
 	usbd_device_lock(uds_ctx);
 
-	if (dev_desc == NULL || usbd_is_initialized(uds_ctx)) {
+	hs_desc = uds_ctx->hs_desc;
+	fs_desc = uds_ctx->fs_desc;
+	if (!fs_desc || !hs_desc || usbd_is_initialized(uds_ctx)) {
 		ret = -EPERM;
 		goto add_descriptor_error;
 	}
@@ -237,10 +239,12 @@
 		case USBD_DUT_STRING_LANG:
 			break;
 		case USBD_DUT_STRING_MANUFACTURER:
-			dev_desc->iManufacturer = desc_nd->idx;
+			hs_desc->iManufacturer = desc_nd->idx;
+			fs_desc->iManufacturer = desc_nd->idx;
 			break;
 		case USBD_DUT_STRING_PRODUCT:
-			dev_desc->iProduct = desc_nd->idx;
+			hs_desc->iProduct = desc_nd->idx;
+			fs_desc->iProduct = desc_nd->idx;
 			break;
 		case USBD_DUT_STRING_SERIAL_NUMBER:
 			if (!desc_nd->custom_sn) {
@@ -248,7 +252,8 @@
 				desc_nd->utf16le = false;
 			}
 
-			dev_desc->iSerialNumber = desc_nd->idx;
+			hs_desc->iSerialNumber = desc_nd->idx;
+			fs_desc->iSerialNumber = desc_nd->idx;
 			break;
 		default:
 			break;
diff --git a/subsys/usb/device_next/usbd_device.c b/subsys/usb/device_next/usbd_device.c
index f45f55b..f13c126 100644
--- a/subsys/usb/device_next/usbd_device.c
+++ b/subsys/usb/device_next/usbd_device.c
@@ -37,10 +37,25 @@
 	return USBD_SPEED_FS;
 }
 
-int usbd_device_set_bcd(struct usbd_contex *const uds_ctx,
-			const uint16_t bcd)
+static struct usb_device_descriptor *
+get_device_descriptor(struct usbd_contex *const uds_ctx,
+		      const enum usbd_speed speed)
 {
-	struct usb_device_descriptor *desc = uds_ctx->desc;
+	switch (speed) {
+	case USBD_SPEED_FS:
+		return uds_ctx->fs_desc;
+	case USBD_SPEED_HS:
+		return uds_ctx->hs_desc;
+	default:
+		__ASSERT(false, "Not supported speed");
+		return NULL;
+	}
+}
+
+int usbd_device_set_bcd(struct usbd_contex *const uds_ctx,
+			const enum usbd_speed speed, const uint16_t bcd)
+{
+	struct usb_device_descriptor *desc;
 	int ret = 0;
 
 	usbd_device_lock(uds_ctx);
@@ -50,6 +65,7 @@
 		goto set_bcd_exit;
 	}
 
+	desc = get_device_descriptor(uds_ctx, speed);
 	desc->bcdUSB = sys_cpu_to_le16(bcd);
 
 set_bcd_exit:
@@ -60,7 +76,7 @@
 int usbd_device_set_vid(struct usbd_contex *const uds_ctx,
 			 const uint16_t vid)
 {
-	struct usb_device_descriptor *desc = uds_ctx->desc;
+	struct usb_device_descriptor *fs_desc, *hs_desc;
 	int ret = 0;
 
 	usbd_device_lock(uds_ctx);
@@ -70,7 +86,11 @@
 		goto set_vid_exit;
 	}
 
-	desc->idVendor = sys_cpu_to_le16(vid);
+	fs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_FS);
+	fs_desc->idVendor = sys_cpu_to_le16(vid);
+
+	hs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_HS);
+	hs_desc->idVendor = sys_cpu_to_le16(vid);
 
 set_vid_exit:
 	usbd_device_unlock(uds_ctx);
@@ -80,7 +100,7 @@
 int usbd_device_set_pid(struct usbd_contex *const uds_ctx,
 			 const uint16_t pid)
 {
-	struct usb_device_descriptor *desc = uds_ctx->desc;
+	struct usb_device_descriptor *fs_desc, *hs_desc;
 	int ret = 0;
 
 	usbd_device_lock(uds_ctx);
@@ -90,7 +110,11 @@
 		goto set_pid_exit;
 	}
 
-	desc->idProduct = sys_cpu_to_le16(pid);
+	fs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_FS);
+	fs_desc->idProduct = sys_cpu_to_le16(pid);
+
+	hs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_HS);
+	hs_desc->idProduct = sys_cpu_to_le16(pid);
 
 set_pid_exit:
 	usbd_device_unlock(uds_ctx);
@@ -98,10 +122,11 @@
 }
 
 int usbd_device_set_code_triple(struct usbd_contex *const uds_ctx,
+				const enum usbd_speed speed,
 				const uint8_t base_class,
 				const uint8_t subclass, const uint8_t protocol)
 {
-	struct usb_device_descriptor *desc = uds_ctx->desc;
+	struct usb_device_descriptor *desc;
 	int ret = 0;
 
 	usbd_device_lock(uds_ctx);
@@ -111,6 +136,7 @@
 		goto set_code_triple_exit;
 	}
 
+	desc = get_device_descriptor(uds_ctx, speed);
 	desc->bDeviceClass = base_class;
 	desc->bDeviceSubClass = subclass;
 	desc->bDeviceProtocol = protocol;
diff --git a/subsys/usb/device_next/usbd_device.h b/subsys/usb/device_next/usbd_device.h
index 6c79b85..f125345 100644
--- a/subsys/usb/device_next/usbd_device.h
+++ b/subsys/usb/device_next/usbd_device.h
@@ -14,12 +14,22 @@
  * @brief Get device descriptor bNumConfigurations value
  *
  * @param[in] uds_ctx Pointer to a device context
+ * @param[in] speed   Speed for which the bNumConfigurations should be returned
  *
  * @return bNumConfigurations value
  */
-static inline uint8_t usbd_get_num_configs(const struct usbd_contex *const uds_ctx)
+static inline uint8_t usbd_get_num_configs(const struct usbd_contex *const uds_ctx,
+					   const enum usbd_speed speed)
 {
-	struct usb_device_descriptor *desc = uds_ctx->desc;
+	struct usb_device_descriptor *desc;
+
+	if (speed == USBD_SPEED_FS) {
+		desc = uds_ctx->fs_desc;
+	} else if (speed == USBD_SPEED_HS) {
+		desc = uds_ctx->hs_desc;
+	} else {
+		return 0;
+	}
 
 	return desc->bNumConfigurations;
 }
@@ -29,12 +39,22 @@
  * @brief Set device descriptor bNumConfigurations value
  *
  * @param[in] uds_ctx Pointer to a device context
+ * @param[in] speed   Speed for which the bNumConfigurations should be set
  * @param[in] value   new bNumConfigurations value
  */
 static inline void usbd_set_num_configs(struct usbd_contex *const uds_ctx,
+					const enum usbd_speed speed,
 					const uint8_t value)
 {
-	struct usb_device_descriptor *desc = uds_ctx->desc;
+	struct usb_device_descriptor *desc;
+
+	if (speed == USBD_SPEED_FS) {
+		desc = uds_ctx->fs_desc;
+	} else if (speed == USBD_SPEED_HS) {
+		desc = uds_ctx->hs_desc;
+	} else {
+		return;
+	}
 
 	desc->bNumConfigurations = value;
 }
diff --git a/subsys/usb/device_next/usbd_init.c b/subsys/usb/device_next/usbd_init.c
index 61b1a58..cfa3a06 100644
--- a/subsys/usb/device_next/usbd_init.c
+++ b/subsys/usb/device_next/usbd_init.c
@@ -109,6 +109,7 @@
  * USB device configuration.
  */
 static int init_configuration_inst(struct usbd_contex *const uds_ctx,
+				   const enum usbd_speed speed,
 				   struct usbd_class_iter *const iter,
 				   uint32_t *const config_ep_bm,
 				   uint8_t *const nif)
@@ -116,27 +117,11 @@
 	struct usb_desc_header **dhp;
 	struct usb_if_descriptor *ifd = NULL;
 	struct usb_ep_descriptor *ed;
-	enum usbd_speed speed;
 	uint32_t class_ep_bm = 0;
 	uint8_t tmp_nif;
 	int ret;
 
-	/*
-	 * Read the highest speed supported by the controller and use it to get
-	 * the appropriate function descriptor from the instance. If the
-	 * controller only supports full speed, the code below will configure
-	 * the function descriptor for full speed only, and high speed will
-	 * never be used after the device instance is initialized. If the
-	 * controller supports high speed, the code will only configure the
-	 * function's high speed descriptor, and the function implementation
-	 * must update the full speed descriptor during the init callback
-	 * processing, which is required to properly respond to
-	 * other-speed-configuration descriptor requests and for the unlikely
-	 * case where the high speed controller is connected to a full speed bus.
-	 */
-
-	speed = usbd_caps_speed(uds_ctx);
-	LOG_DBG("Highest speed supported by the controller is %u", speed);
+	LOG_DBG("Initializing configuration for %u speed", speed);
 	dhp = usbd_class_get_desc(iter->c_nd, speed);
 	if (dhp == NULL) {
 		return 0;
@@ -207,6 +192,7 @@
  * Iterate on a list of all classes in a configuration
  */
 static int init_configuration(struct usbd_contex *const uds_ctx,
+			      const enum usbd_speed speed,
 			      struct usbd_config_node *const cfg_nd)
 {
 	struct usb_cfg_descriptor *cfg_desc = cfg_nd->desc;
@@ -218,7 +204,7 @@
 
 	SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, iter, node) {
 
-		ret = init_configuration_inst(uds_ctx, iter,
+		ret = init_configuration_inst(uds_ctx, speed, iter,
 					      &config_ep_bm, &nif);
 		if (ret != 0) {
 			LOG_ERR("Failed to assign endpoint addresses");
@@ -231,9 +217,9 @@
 			return ret;
 		}
 
-		LOG_INF("Init class node %p, descriptor length %zu", iter->c_nd,
-			usbd_class_desc_len(iter->c_nd, usbd_caps_speed(uds_ctx)));
-		cfg_len += usbd_class_desc_len(iter->c_nd, usbd_caps_speed(uds_ctx));
+		LOG_INF("Init class node %p, descriptor length %zu",
+			iter->c_nd, usbd_class_desc_len(iter->c_nd, speed));
+		cfg_len += usbd_class_desc_len(iter->c_nd, speed);
 	}
 
 	/* Update wTotalLength and bNumInterfaces of configuration descriptor */
@@ -257,10 +243,10 @@
 	return 0;
 }
 
-static void usbd_init_update_mps0(struct usbd_contex *const uds_ctx)
+static void usbd_init_update_fs_mps0(struct usbd_contex *const uds_ctx)
 {
 	struct udc_device_caps caps = udc_caps(uds_ctx->dev);
-	struct usb_device_descriptor *desc = uds_ctx->desc;
+	struct usb_device_descriptor *desc = uds_ctx->fs_desc;
 
 	switch (caps.mps0) {
 	case UDC_MPS0_8:
@@ -282,20 +268,34 @@
 {
 	struct usbd_config_node *cfg_nd;
 
-	usbd_init_update_mps0(uds_ctx);
+	usbd_init_update_fs_mps0(uds_ctx);
 
-	SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->configs, cfg_nd, node) {
+	SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->hs_configs, cfg_nd, node) {
 		int ret;
 
-		ret = init_configuration(uds_ctx, cfg_nd);
+		ret = init_configuration(uds_ctx, USBD_SPEED_HS, cfg_nd);
 		if (ret) {
-			LOG_ERR("Failed to init configuration %u",
+			LOG_ERR("Failed to init HS configuration %u",
 				usbd_config_get_value(cfg_nd));
 			return ret;
 		}
 
-		LOG_INF("bNumConfigurations %u",
-			usbd_get_num_configs(uds_ctx));
+		LOG_INF("HS bNumConfigurations %u",
+			usbd_get_num_configs(uds_ctx, USBD_SPEED_HS));
+	}
+
+	SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->fs_configs, cfg_nd, node) {
+		int ret;
+
+		ret = init_configuration(uds_ctx, USBD_SPEED_FS, cfg_nd);
+		if (ret) {
+			LOG_ERR("Failed to init FS configuration %u",
+				usbd_config_get_value(cfg_nd));
+			return ret;
+		}
+
+		LOG_INF("FS bNumConfigurations %u",
+			usbd_get_num_configs(uds_ctx, USBD_SPEED_FS));
 	}
 
 	return 0;
diff --git a/subsys/usb/device_next/usbd_interface.c b/subsys/usb/device_next/usbd_interface.c
index 49fc3f4..9859dd5 100644
--- a/subsys/usb/device_next/usbd_interface.c
+++ b/subsys/usb/device_next/usbd_interface.c
@@ -140,6 +140,7 @@
 }
 
 int usbd_interface_default(struct usbd_contex *const uds_ctx,
+			   const enum usbd_speed speed,
 			   struct usbd_config_node *const cfg_nd)
 {
 	struct usb_cfg_descriptor *desc = cfg_nd->desc;
@@ -150,7 +151,7 @@
 		struct usbd_class_iter *class;
 		int ret;
 
-		class = usbd_class_get_by_config(uds_ctx, new_cfg, i);
+		class = usbd_class_get_by_config(uds_ctx, speed, new_cfg, i);
 		if (class == NULL) {
 			return -ENODATA;
 		}
diff --git a/subsys/usb/device_next/usbd_interface.h b/subsys/usb/device_next/usbd_interface.h
index e335554..bbeaf6f 100644
--- a/subsys/usb/device_next/usbd_interface.h
+++ b/subsys/usb/device_next/usbd_interface.h
@@ -26,11 +26,13 @@
  * @note Used only for configuration change.
  *
  * @param[in] uds_ctx Pointer to USB device support context
+ * @param[in] speed   Configuration speed
  * @param[in] cfg_nd  Pointer to configuration node
  *
  * @return 0 on success, other values on fail.
  */
 int usbd_interface_default(struct usbd_contex *const uds_ctx,
+			   const enum usbd_speed speed,
 			   struct usbd_config_node *const cfg_nd);
 
 /**
diff --git a/subsys/usb/device_next/usbd_shell.c b/subsys/usb/device_next/usbd_shell.c
index fd27c5d..d3e771d 100644
--- a/subsys/usb/device_next/usbd_shell.c
+++ b/subsys/usb/device_next/usbd_shell.c
@@ -28,6 +28,8 @@
 		   0x2fe3, 0xffff);
 
 static struct usbd_contex *my_uds_ctx = &sh_uds_ctx;
+/* TODO: Rework commands to allow specifying speed */
+static enum usbd_speed speed = USBD_SPEED_FS;
 
 int cmd_wakeup_request(const struct shell *sh,
 		       size_t argc, char **argv)
@@ -51,7 +53,7 @@
 	int ret;
 
 	cfg = strtol(argv[2], NULL, 10);
-	ret = usbd_register_class(my_uds_ctx, argv[1], cfg);
+	ret = usbd_register_class(my_uds_ctx, argv[1], speed, cfg);
 
 	if (ret) {
 		shell_error(sh,
@@ -73,7 +75,7 @@
 	int ret;
 
 	cfg = strtol(argv[2], NULL, 10);
-	ret = usbd_unregister_class(my_uds_ctx, argv[1], cfg);
+	ret = usbd_unregister_class(my_uds_ctx, argv[1], speed, cfg);
 	if (ret) {
 		shell_error(sh,
 			    "dev: failed to remove USB class %s from configuration %u",
@@ -101,13 +103,13 @@
 		shell_error(sh, "dev: Failed to initialize descriptors, %d", err);
 	}
 
-	err = usbd_add_configuration(my_uds_ctx, &config_foo);
+	err = usbd_add_configuration(my_uds_ctx, speed, &config_foo);
 	if (err) {
 		shell_error(sh, "dev: Failed to add configuration");
 	}
 
 	if (IS_ENABLED(CONFIG_USBD_LOOPBACK_CLASS)) {
-		err = usbd_register_class(my_uds_ctx, "loopback_0", 1);
+		err = usbd_register_class(my_uds_ctx, "loopback_0", speed, 1);
 		if (err) {
 			shell_error(sh, "dev: Failed to add loopback_0 class");
 		}
@@ -242,7 +244,7 @@
 	int ret;
 
 	bcd = strtol(argv[1], NULL, 16);
-	ret = usbd_device_set_bcd(my_uds_ctx, bcd);
+	ret = usbd_device_set_bcd(my_uds_ctx, speed, bcd);
 	if (ret) {
 		shell_error(sh, "dev: failed to set device bcdUSB to %x", bcd);
 	}
@@ -289,7 +291,8 @@
 	class = strtol(argv[1], NULL, 16);
 	subclass = strtol(argv[2], NULL, 16);
 	protocol = strtol(argv[3], NULL, 16);
-	ret = usbd_device_set_code_triple(my_uds_ctx, class, subclass, protocol);
+	ret = usbd_device_set_code_triple(my_uds_ctx, speed,
+					  class, subclass, protocol);
 	if (ret) {
 		shell_error(sh, "dev: failed to set device code triple to %x %x %x",
 			    class, subclass, protocol);
@@ -307,9 +310,9 @@
 	cfg = strtol(argv[1], NULL, 10);
 
 	if (cfg == 1) {
-		ret = usbd_add_configuration(my_uds_ctx, &config_foo);
+		ret = usbd_add_configuration(my_uds_ctx, speed, &config_foo);
 	} else if (cfg == 2) {
-		ret = usbd_add_configuration(my_uds_ctx, &config_baz);
+		ret = usbd_add_configuration(my_uds_ctx, speed, &config_baz);
 	} else {
 		shell_error(sh, "dev: Configuration %u not available", cfg);
 		return -EINVAL;
@@ -336,7 +339,7 @@
 		self = false;
 	}
 
-	ret = usbd_config_attrib_self(my_uds_ctx, cfg, self);
+	ret = usbd_config_attrib_self(my_uds_ctx, speed, cfg, self);
 	if (ret) {
 		shell_error(sh,
 			    "dev: failed to set attribute self powered to %u",
@@ -360,7 +363,7 @@
 		rwup = false;
 	}
 
-	ret = usbd_config_attrib_rwup(my_uds_ctx, cfg, rwup);
+	ret = usbd_config_attrib_rwup(my_uds_ctx, speed, cfg, rwup);
 	if (ret) {
 		shell_error(sh,
 			    "dev: failed to set attribute remote wakeup to %x",
@@ -380,7 +383,7 @@
 	cfg = strtol(argv[1], NULL, 10);
 	power = strtol(argv[1], NULL, 10);
 
-	ret = usbd_config_maxpower(my_uds_ctx, cfg, power);
+	ret = usbd_config_maxpower(my_uds_ctx, speed, cfg, power);
 	if (ret) {
 		shell_error(sh, "dev: failed to set bMaxPower value to %u", cfg);
 	}
@@ -397,7 +400,7 @@
 	entry->help = NULL;
 	entry->subcmd = NULL;
 
-	STRUCT_SECTION_FOREACH(usbd_class_iter, iter) {
+	STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_iter, iter) {
 		if ((iter->c_nd->name != NULL) &&
 		    (strlen(iter->c_nd->name) != 0)) {
 			if (match_idx == idx) {
diff --git a/tests/subsys/usb/device_next/src/main.c b/tests/subsys/usb/device_next/src/main.c
index 50c20c2..5d6a9da 100644
--- a/tests/subsys/usb/device_next/src/main.c
+++ b/tests/subsys/usb/device_next/src/main.c
@@ -17,10 +17,15 @@
 #define TEST_DEFAULT_INTERFACE		0
 #define TEST_DEFAULT_ALTERNATE		1
 
-USBD_CONFIGURATION_DEFINE(test_config,
+USBD_CONFIGURATION_DEFINE(test_fs_config,
 			  USB_SCD_SELF_POWERED | USB_SCD_REMOTE_WAKEUP,
 			  200);
 
+USBD_CONFIGURATION_DEFINE(test_hs_config,
+			  USB_SCD_SELF_POWERED | USB_SCD_REMOTE_WAKEUP,
+			  200);
+
+
 USBD_DESC_LANG_DEFINE(test_lang);
 USBD_DESC_STRING_DEFINE(test_mfg, "ZEPHYR", 1);
 USBD_DESC_STRING_DEFINE(test_product, "Zephyr USB Test", 2);
@@ -120,10 +125,20 @@
 	err = usbd_add_descriptor(&test_usbd, &test_sn);
 	zassert_equal(err, 0, "Failed to initialize descriptor (%d)", err);
 
-	err = usbd_add_configuration(&test_usbd, &test_config);
+	if (usbd_caps_speed(&test_usbd) == USBD_SPEED_HS) {
+		err = usbd_add_configuration(&test_usbd, USBD_SPEED_HS, &test_hs_config);
+		zassert_equal(err, 0, "Failed to add configuration (%d)");
+	}
+
+	err = usbd_add_configuration(&test_usbd, USBD_SPEED_FS, &test_fs_config);
 	zassert_equal(err, 0, "Failed to add configuration (%d)");
 
-	err = usbd_register_class(&test_usbd, "loopback_0", 1);
+	if (usbd_caps_speed(&test_usbd) == USBD_SPEED_HS) {
+		err = usbd_register_class(&test_usbd, "loopback_0", USBD_SPEED_HS, 1);
+		zassert_equal(err, 0, "Failed to register loopback_0 class (%d)");
+	}
+
+	err = usbd_register_class(&test_usbd, "loopback_0", USBD_SPEED_FS, 1);
 	zassert_equal(err, 0, "Failed to register loopback_0 class (%d)");
 
 	err = usbd_init(&test_usbd);