From 303df7d4722e0b53b137c3ebd749902a2687eaf8 Mon Sep 17 00:00:00 2001 From: Eric Ocasio Date: Fri, 3 Oct 2025 14:46:03 -0500 Subject: [PATCH 1/2] usb: pmci: mctp: samples: Add MCTP USB device class, MCTP over USB binding Add support for MCTP over USB by providing a new USBD class, device tree binding and sample application. The USB binding for libmctp follows existing design from other transports. The USBD class is a basic implementation, as defined in the DMTF spec, and is solely providing transmit and receive data pipes for a higher-level MCTP transport and protocol manager. The USB-specific wrapping of MCTP data is added and parsed to/from the raw USB data in the MCTP USB binding (libmctp in this case), not in the USBD class. The goal of this design is to provide flexibility for alternative MCTP implementations. Signed-off-by: Eric Ocasio --- .../usb/device_next/api/index.rst | 1 + .../usb/device_next/api/usbd_mctp.rst | 14 + .../usb/device_next/usb_device.rst | 4 + dts/bindings/pmci/mctp/usb.yaml | 13 + include/zephyr/pmci/mctp/mctp_usb.h | 79 ++++ include/zephyr/usb/class/usbd_mctp.h | 93 +++++ include/zephyr/usb/usb_ch9.h | 1 + .../pmci/mctp/usb_endpoint/CMakeLists.txt | 9 + .../modules/pmci/mctp/usb_endpoint/Kconfig | 9 + .../modules/pmci/mctp/usb_endpoint/README.rst | 56 +++ .../pmci/mctp/usb_endpoint/app.overlay | 12 + .../modules/pmci/mctp/usb_endpoint/prj.conf | 11 + .../pmci/mctp/usb_endpoint/requirements.txt | 2 + .../modules/pmci/mctp/usb_endpoint/src/main.c | 110 ++++++ .../pmci/mctp/usb_endpoint/usb_host_tester.py | 92 +++++ subsys/pmci/mctp/CMakeLists.txt | 1 + subsys/pmci/mctp/Kconfig | 8 + subsys/pmci/mctp/Kconfig.usb | 12 + subsys/pmci/mctp/mctp_usb.c | 159 ++++++++ subsys/usb/device_next/CMakeLists.txt | 10 + subsys/usb/device_next/class/Kconfig | 1 + subsys/usb/device_next/class/Kconfig.mctp | 19 + subsys/usb/device_next/class/usbd_mctp.c | 369 ++++++++++++++++++ subsys/usb/device_next/class/usbd_mctp.ld | 3 + 24 files changed, 1088 insertions(+) create mode 100644 doc/connectivity/usb/device_next/api/usbd_mctp.rst create mode 100644 dts/bindings/pmci/mctp/usb.yaml create mode 100644 include/zephyr/pmci/mctp/mctp_usb.h create mode 100644 include/zephyr/usb/class/usbd_mctp.h create mode 100644 samples/modules/pmci/mctp/usb_endpoint/CMakeLists.txt create mode 100644 samples/modules/pmci/mctp/usb_endpoint/Kconfig create mode 100644 samples/modules/pmci/mctp/usb_endpoint/README.rst create mode 100644 samples/modules/pmci/mctp/usb_endpoint/app.overlay create mode 100644 samples/modules/pmci/mctp/usb_endpoint/prj.conf create mode 100644 samples/modules/pmci/mctp/usb_endpoint/requirements.txt create mode 100644 samples/modules/pmci/mctp/usb_endpoint/src/main.c create mode 100644 samples/modules/pmci/mctp/usb_endpoint/usb_host_tester.py create mode 100644 subsys/pmci/mctp/Kconfig.usb create mode 100644 subsys/pmci/mctp/mctp_usb.c create mode 100644 subsys/usb/device_next/class/Kconfig.mctp create mode 100644 subsys/usb/device_next/class/usbd_mctp.c create mode 100644 subsys/usb/device_next/class/usbd_mctp.ld diff --git a/doc/connectivity/usb/device_next/api/index.rst b/doc/connectivity/usb/device_next/api/index.rst index 34a6e068a3199..084084729c187 100644 --- a/doc/connectivity/usb/device_next/api/index.rst +++ b/doc/connectivity/usb/device_next/api/index.rst @@ -13,3 +13,4 @@ USB device support APIs usbd_msc_device.rst usbd_midi2.rst usbd_dfu.rst + usbd_mctp.rst diff --git a/doc/connectivity/usb/device_next/api/usbd_mctp.rst b/doc/connectivity/usb/device_next/api/usbd_mctp.rst new file mode 100644 index 0000000000000..c825e34b7e386 --- /dev/null +++ b/doc/connectivity/usb/device_next/api/usbd_mctp.rst @@ -0,0 +1,14 @@ +.. _usbd_mctp: + +MCTP (Management Component Transport Protocol) USB device API +############################################################# + +MCTP USB device specific API defined in :zephyr_file:`include/zephyr/usb/class/usbd_mctp.h`. + +The implementation of this device class is specified by DMTF and the spec is found `here +`__. + +API Reference +************* + +.. doxygengroup:: usbd_mctp diff --git a/doc/connectivity/usb/device_next/usb_device.rst b/doc/connectivity/usb/device_next/usb_device.rst index 3e81252602dbd..2e76bd92f2151 100644 --- a/doc/connectivity/usb/device_next/usb_device.rst +++ b/doc/connectivity/usb/device_next/usb_device.rst @@ -41,6 +41,8 @@ Samples * :zephyr:code-sample:`usb-hid-mouse` +* :zephyr:code-sample:`mctp-usb-endpoint` + * :zephyr:code-sample:`zperf` To build the sample for the device support, set the configuration overlay file ``-DDEXTRA_CONF_FILE=overlay-usbd_next_ecm.conf`` and devicetree overlay file @@ -217,6 +219,8 @@ instance (``n``) and is used as an argument to the :c:func:`usbd_register_class` +-----------------------------------+-------------------------+-------------------------+ | USB Video Class (UVC) | Video device | :samp:`uvc_{n}` | +-----------------------------------+-------------------------+-------------------------+ +| MCTP over USB Endpoint class | :ref:`usbd_mctp` | :samp:`mctp_{n}` | ++-----------------------------------+-------------------------+-------------------------+ CDC ACM UART ============ diff --git a/dts/bindings/pmci/mctp/usb.yaml b/dts/bindings/pmci/mctp/usb.yaml new file mode 100644 index 0000000000000..c706af125574e --- /dev/null +++ b/dts/bindings/pmci/mctp/usb.yaml @@ -0,0 +1,13 @@ +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: | + A configuration for MCTP binding to a USB target. + +compatible: "zephyr,mctp-usb" + +properties: + endpoint-id: + type: int + description: | + MCTP Endpoint ID diff --git a/include/zephyr/pmci/mctp/mctp_usb.h b/include/zephyr/pmci/mctp/mctp_usb.h new file mode 100644 index 0000000000000..a795d5e76b2c3 --- /dev/null +++ b/include/zephyr/pmci/mctp/mctp_usb.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024 Intel Corporation + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#ifndef ZEPHYR_MCTP_USB_H_ +#define ZEPHYR_MCTP_USB_H_ + +#include +#include +#include +#include +#include + +#define MCTP_USB_HEADER_SIZE 4 +#define MCTP_USB_MAX_PACKET_LENGTH 255 + +/** + * @brief An MCTP binding for Zephyr's USB device stack + */ +struct mctp_binding_usb { + /** @cond INTERNAL_HIDDEN */ + struct mctp_binding binding; + const struct usbd_mctp_inst *usb_inst; + uint8_t endpoint_id; + uint8_t tx_buf[MCTP_USB_HEADER_SIZE + MCTP_USB_MAX_PACKET_LENGTH]; + struct k_sem tx_lock; + struct mctp_pktbuf *rx_pkt; + uint8_t rx_data_idx; + enum { + STATE_WAIT_HDR_DMTF0, + STATE_WAIT_HDR_DMTF1, + STATE_WAIT_HDR_RSVD0, + STATE_WAIT_HDR_LEN, + STATE_DATA + } rx_state; + /** @endcond INTERNAL_HIDDEN */ +}; + +/** @cond INTERNAL_HIDDEN */ +void mctp_usb_data_recv_cb(const void *const buf, const uint16_t size, const void *const priv); +void mctp_usb_error_cb(const int err, const uint8_t ep, const void *const priv); +int mctp_usb_start(struct mctp_binding *binding); +int mctp_usb_tx(struct mctp_binding *binding, struct mctp_pktbuf *pkt); +/** @endcond INTERNAL_HIDDEN */ + +/** + * @brief Define a MCTP bus binding for USB + * + * @param _name Symbolic name of the bus binding variable + * @param _dev DeviceTree Node containing the configuration of this MCTP binding + */ +#define MCTP_USB_DEFINE(_name, _dev, _subclass, _protocol) \ + extern const struct usbd_mctp_inst usbd_mctp_inst_##_name; \ + \ + struct mctp_binding_usb _name = { \ + .binding = { \ + .name = STRINGIFY(_name), \ + .version = 1, \ + .pkt_size = MCTP_PACKET_SIZE(MCTP_USB_MAX_PACKET_LENGTH), \ + .pkt_header = 0, \ + .pkt_trailer = 0, \ + .start = mctp_usb_start, \ + .tx = mctp_usb_tx \ + }, \ + .usb_inst = &usbd_mctp_inst_##_name, \ + .endpoint_id = DT_PROP(_dev, endpoint_id), \ + .rx_pkt = NULL, \ + .rx_data_idx = 0, \ + .rx_state = STATE_WAIT_HDR_DMTF0 \ + }; \ + \ + USBD_DEFINE_MCTP_INSTANCE(_name, _subclass, _protocol, &_name, mctp_usb_data_recv_cb, \ + mctp_usb_error_cb); + +#endif /* ZEPHYR_MCTP_USB_H_ */ diff --git a/include/zephyr/usb/class/usbd_mctp.h b/include/zephyr/usb/class/usbd_mctp.h new file mode 100644 index 0000000000000..5d41dbc9144cc --- /dev/null +++ b/include/zephyr/usb/class/usbd_mctp.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief MCTP over USB Protocol Endpoint Deivce Class public header + * + * This API is currently considered experimental. + */ + +#include + +#ifndef ZEPHYR_INCLUDE_USB_CLASS_USBD_MCTP_H_ +#define ZEPHYR_INCLUDE_USB_CLASS_USBD_MCTP_H_ + +/** + * @brief USB MCTP device API + * @defgroup usbd_mctp USB MCTP device API + * @ingroup usb + * @since 4.2 + * @version 0.1.0 + * @{ + */ + +/* MCTP class subclass options */ +#define USBD_MCTP_SUBCLASS_MANAGEMENT_CONTROLLER 0 +#define USBD_MCTP_SUBCLASS_MANAGED_DEVICE_ENDPOINT 0 +#define USBD_MCTP_SUBCLASS_HOST_INTERFACE_ENDPOINT 1 + +/* MCTP class protocol options */ +#define USBD_MCTP_PROTOCOL_1_X 1 +#define USBD_MCTP_PROTOCOL_2_X 2 + +struct usbd_mctp_inst { + const char *name; + uint8_t sublcass; + uint8_t mctp_protocol; + void *priv; + void (*data_recv_cb)(const void *const buf, + const uint16_t size, + const void *const priv); + void (*error_cb)(const int err, + const uint8_t ep, + const void *const priv); +}; + +/** + * @brief Define USB MCTP instance + * + * Use this macro to create set the parameters of an MCTP instance. + * + * @param id Identifier by which the linker sorts registered instances + * @param subclass MCTP subclass used in the USB interfce descriptor + * @param protocol MCTP protocol used in the USB interface descriptor + * @param private Opaque private/user data + * @param recv_cb Data received callback + * @param err_cb Error callback + */ +#define USBD_DEFINE_MCTP_INSTANCE(id, subclass, protocol, private, recv_cb, err_cb) \ + const STRUCT_SECTION_ITERABLE(usbd_mctp_inst, usbd_mctp_inst_##id) = { \ + .name = STRINGIFY(id), \ + .sublcass = subclass, \ + .mctp_protocol = protocol, \ + .priv = private, \ + .data_recv_cb = recv_cb, \ + .error_cb = err_cb \ + } + +/** + * @brief Send data to the MCTP bus owner. + * + * @note Buffer ownership is transferred to the stack in case of success, in + * case of an error the caller retains the ownership of the buffer. + * + * @param inst USBD MCTP instance to send data through + * @param buf Buffer containing outgoing data + * @param size Number of bytes to send + * + * @return 0 on success, negative value on error + */ +int usbd_mctp_send(const struct usbd_mctp_inst *const inst, + const void *const buf, + const uint16_t size); + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_USB_CLASS_USBD_MCTP_H_ */ diff --git a/include/zephyr/usb/usb_ch9.h b/include/zephyr/usb/usb_ch9.h index 0c4a3ac06086a..67af0047cc11c 100644 --- a/include/zephyr/usb/usb_ch9.h +++ b/include/zephyr/usb/usb_ch9.h @@ -267,6 +267,7 @@ struct usb_association_descriptor { #define USB_BCC_MASS_STORAGE 0x08 #define USB_BCC_CDC_DATA 0x0A #define USB_BCC_VIDEO 0x0E +#define USB_BCC_MCTP 0x14 #define USB_BCC_WIRELESS_CONTROLLER 0xE0 #define USB_BCC_MISCELLANEOUS 0xEF #define USB_BCC_APPLICATION 0xFE diff --git a/samples/modules/pmci/mctp/usb_endpoint/CMakeLists.txt b/samples/modules/pmci/mctp/usb_endpoint/CMakeLists.txt new file mode 100644 index 0000000000000..efbc4cb50f8c6 --- /dev/null +++ b/samples/modules/pmci/mctp/usb_endpoint/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(mctp_usb_endpoint) + +include(${ZEPHYR_BASE}/samples/subsys/usb/common/common.cmake) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/modules/pmci/mctp/usb_endpoint/Kconfig b/samples/modules/pmci/mctp/usb_endpoint/Kconfig new file mode 100644 index 0000000000000..96c5455894806 --- /dev/null +++ b/samples/modules/pmci/mctp/usb_endpoint/Kconfig @@ -0,0 +1,9 @@ +# Copyright (c) 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +# Source common USB sample options used to initialize new experimental USB +# device stack. The scope of these options is limited to USB samples in project +# tree, you cannot use them in your own application. +source "samples/subsys/usb/common/Kconfig.sample_usbd" + +source "Kconfig.zephyr" diff --git a/samples/modules/pmci/mctp/usb_endpoint/README.rst b/samples/modules/pmci/mctp/usb_endpoint/README.rst new file mode 100644 index 0000000000000..fa8ca6099295a --- /dev/null +++ b/samples/modules/pmci/mctp/usb_endpoint/README.rst @@ -0,0 +1,56 @@ +.. zephyr:code-sample:: mctp-usb-endpoint + :name: MCTP USB Endpoint Node Sample + :relevant-api: usbd_api + + Create an MCTP endpoint node using the USB device interface. + +Overview +******** +Sets up an MCTP endpoint node that listens for messages from the MCTP bus owner with EID 20. +Responds with "Hello, bus owner" message. + +Requirements +************ +A board and SoC that provide support for USB device capability (either FS or HS). Testing requires +a USB host that has the ability to enumerate the MCTP interface and interact with the board using +the MCTP protocol. An easy way to do this is a Python script. + +An example script to test this interface is provided with this sample application. To run the +script, you must first install pyusb. + +.. code-block:: console + + pip install requirements.txt + python usb_host_tester.py + +If the test is successful, you will see the following output from the script: + +.. code-block:: console + + Sending message with size smaller than USB FS MPS (<64b) + Received: b'\x1a\xb4\x00\x15\x01\x14\n\xc0Hello, bus owner\x00' + Sending message spanning two USB FS packets (>64b) + Received: b'\x1a\xb4\x00\x15\x01\x14\n\xc0Hello, bus owner\x00' + Sending message with two MCTP messages in a single USB FS packet + Received: b'\x1a\xb4\x00\x15\x01\x14\n\xc0Hello, bus owner\x00' + Received: b'\x1a\xb4\x00\x15\x01\x14\n\xc0Hello, bus owner\x00' + +Wiring +****** +Connect a USB cable between the host (likely a PC) and the board. The type of cable (mini, micro, +type C) depends on the host and board connectors. + +Building and Running +******************** + +.. zephyr-app-commands:: + :zephyr-app: samples/modules/pmci/mctp/usb_endpoint + :host-os: unix + :board: frdm_mcxn947_mcxn947_cpu0 + :goals: run + :compact: + +References +********** + +`MCTP Base Specification 2019 `_ diff --git a/samples/modules/pmci/mctp/usb_endpoint/app.overlay b/samples/modules/pmci/mctp/usb_endpoint/app.overlay new file mode 100644 index 0000000000000..5cca7e255447b --- /dev/null +++ b/samples/modules/pmci/mctp/usb_endpoint/app.overlay @@ -0,0 +1,12 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + mctp_usb0: mctp_usb0 { + compatible = "zephyr,mctp-usb"; + endpoint-id = <10>; + }; +}; diff --git a/samples/modules/pmci/mctp/usb_endpoint/prj.conf b/samples/modules/pmci/mctp/usb_endpoint/prj.conf new file mode 100644 index 0000000000000..3a0e9c7dc2d69 --- /dev/null +++ b/samples/modules/pmci/mctp/usb_endpoint/prj.conf @@ -0,0 +1,11 @@ +CONFIG_USB_DEVICE_STACK_NEXT=y +CONFIG_USBD_MCTP_CLASS=y + +CONFIG_MCTP=y +CONFIG_MCTP_USB=y + +CONFIG_LOG=y +CONFIG_MCTP_LOG_LEVEL_ERR=y +CONFIG_USBD_MCTP_LOG_LEVEL_DBG=y +CONFIG_USBD_LOG_LEVEL_ERR=y +CONFIG_UDC_DRIVER_LOG_LEVEL_ERR=y diff --git a/samples/modules/pmci/mctp/usb_endpoint/requirements.txt b/samples/modules/pmci/mctp/usb_endpoint/requirements.txt new file mode 100644 index 0000000000000..feec2b980afdb --- /dev/null +++ b/samples/modules/pmci/mctp/usb_endpoint/requirements.txt @@ -0,0 +1,2 @@ +# Needed to run USB host test script. +pyusb diff --git a/samples/modules/pmci/mctp/usb_endpoint/src/main.c b/samples/modules/pmci/mctp/usb_endpoint/src/main.c new file mode 100644 index 0000000000000..271c1a1f4f5e9 --- /dev/null +++ b/samples/modules/pmci/mctp/usb_endpoint/src/main.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2024 Intel Corporation + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(mctp_endpoint); + +#define BUS_OWNER_ID 20 + +K_SEM_DEFINE(mctp_rx, 0, 1); +MCTP_USB_DEFINE(mctp0, DT_NODELABEL(mctp_usb0), USBD_MCTP_SUBCLASS_MANAGED_DEVICE_ENDPOINT, + USBD_MCTP_PROTOCOL_1_X); + +static struct mctp *mctp_ctx; +static bool usb_configured; + +static void sample_msg_cb(struct usbd_context *const ctx, const struct usbd_msg *msg) +{ + LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type)); + + if (msg->type == USBD_MSG_CONFIGURATION) { + usb_configured = true; + } +} + +static int enable_usb_device_next(void) +{ + static struct usbd_context *mctp_poc_usbd; + int err; + + mctp_poc_usbd = sample_usbd_init_device(sample_msg_cb); + if (mctp_poc_usbd == NULL) { + LOG_ERR("Failed to initialize USB device"); + return -ENODEV; + } + + if (!usbd_can_detect_vbus(mctp_poc_usbd)) { + err = usbd_enable(mctp_poc_usbd); + if (err) { + LOG_ERR("Failed to enable device support"); + return err; + } + } + + LOG_INF("USB device support enabled"); + + return 0; +} + +static void rx_message(uint8_t eid, bool tag_owner, uint8_t msg_tag, void *data, void *msg, + size_t len) +{ + switch (eid) { + case BUS_OWNER_ID: { + LOG_INF("Received MCTP message \"%s\" from EID %d", (char *)msg, eid); + k_sem_give(&mctp_rx); + break; + } + default: { + LOG_INF("Unknown endpoint %d", eid); + break; + } + } +} + +int main(void) +{ + LOG_INF("MCTP Endpoint EID: %d on %s", mctp0.endpoint_id, CONFIG_BOARD_TARGET); + + int ret = enable_usb_device_next(); + + if (ret != 0) { + LOG_ERR("Failed to enable USB device support"); + return 0; + } + + while (!usb_configured) { + k_msleep(5); + } + + mctp_set_alloc_ops(malloc, free, realloc); + mctp_ctx = mctp_init(); + __ASSERT_NO_MSG(mctp_ctx != NULL); + + mctp_register_bus(mctp_ctx, &mctp0.binding, mctp0.endpoint_id); + mctp_set_rx_all(mctp_ctx, rx_message, NULL); + + while (1) { + k_sem_take(&mctp_rx, K_FOREVER); + mctp_message_tx(mctp_ctx, BUS_OWNER_ID, false, 0, "Hello, bus owner", + sizeof("Hello, bus owner")); + } + + return 0; +} diff --git a/samples/modules/pmci/mctp/usb_endpoint/usb_host_tester.py b/samples/modules/pmci/mctp/usb_endpoint/usb_host_tester.py new file mode 100644 index 0000000000000..d1e4c9ae8928c --- /dev/null +++ b/samples/modules/pmci/mctp/usb_endpoint/usb_host_tester.py @@ -0,0 +1,92 @@ +import usb.core +import usb.util + + +class USBDevice: + def __init__(self, vid, pid): + self.vid = vid + self.pid = pid + self.device = None + self.endpoint_in = None + self.endpoint_out = None + self.interface = None + + def connect(self): + # Find the device + self.device = usb.core.find(idVendor=self.vid, idProduct=self.pid) + if self.device is None: + raise ValueError( + "Device not found. Make sure the board running the MCTP USB endpoint \ + sample is connected." + ) + + # Set the active configuration + self.device.set_configuration() + cfg = self.device.get_active_configuration() + self.interface = cfg[(0, 0)] + + # Claim the interface + usb.util.claim_interface(self.device, self.interface.bInterfaceNumber) + + # Find bulk IN and OUT endpoints + for ep in self.interface: + if usb.util.endpoint_direction(ep.bEndpointAddress) == usb.util.ENDPOINT_IN: + self.endpoint_in = ep + elif usb.util.endpoint_direction(ep.bEndpointAddress) == usb.util.ENDPOINT_OUT: + self.endpoint_out = ep + + if not self.endpoint_in or not self.endpoint_out: + raise ValueError("Bulk IN/OUT endpoints not found") + + # Print descriptor information (for debug purposes) + for cfg in self.device: + print(cfg) + + def send_data(self, data): + if not self.endpoint_out: + raise RuntimeError("OUT endpoint not initialized") + self.endpoint_out.write(data) + + def receive_data(self, size=64, timeout=1000): + if not self.endpoint_in: + raise RuntimeError("IN endpoint not initialized") + return self.endpoint_in.read(size, timeout) + + def disconnect(self): + usb.util.release_interface(self.device, self.interface.bInterfaceNumber) + usb.util.dispose_resources(self.device) + + +if __name__ == "__main__": + usb_dev = USBDevice(0x2FE3, 0x0001) + try: + usb_dev.connect() + + print("Sending message with size smaller than USB FS MPS (<64b)") + usb_dev.send_data( + b'\x1a\xb4\x00\x0f\x01\x0a\x14\xc0\x48\x65\x6c\x6c\x6f\x2c\x20\x4d\x43\x58\x00' + ) + response = usb_dev.receive_data() + print("Received:", bytes(response)) + + print("Sending message spanning two USB FS packets (>64b)") + usb_dev.send_data( + b'\x1a\xb4\x00\x4c\x01\x0a\x14\xc0\x48\x65\x6c\x6c\x6f\x2c\x20\x4d\x43\x58\x2e\x20' + b'\x4c\x65\x74\x27\x73\x20\x74\x65\x73\x74\x20\x61\x20\x6d\x75\x6c\x74\x69\x2d\x70' + b'\x61\x63\x6b\x65\x74\x20\x6d\x65\x73\x73\x61\x67\x65\x20\x74\x6f\x20\x73\x65\x65' + b'\x20\x68\x6f\x77\x20\x79\x6f\x75\x20\x68\x61\x6e\x64\x6c\x65\x20\x69\x74\x2e\x00' + ) + response = usb_dev.receive_data() + print("Received:", bytes(response)) + + print("Sending message with two MCTP messages in a single USB FS packet") + usb_dev.send_data( + b'\x1a\xb4\x00\x12\x01\x0a\x14\xc0\x48\x65\x6c\x6c\x6f\x2c\x20\x4d\x43\x58\x2c\x20' + b'\x31\x00\x1a\xb4\x00\x12\x01\x0a\x14\xc0\x48\x65\x6c\x6c\x6f\x2c\x20\x4d\x43\x58' + b'\x2c\x20\x32\x00' + ) + response = usb_dev.receive_data() + print("Received:", bytes(response)) + print("Received:", bytes(response)) + finally: + usb_dev.disconnect() diff --git a/subsys/pmci/mctp/CMakeLists.txt b/subsys/pmci/mctp/CMakeLists.txt index aa32c208f00c9..9d9fc9cbb3d3a 100644 --- a/subsys/pmci/mctp/CMakeLists.txt +++ b/subsys/pmci/mctp/CMakeLists.txt @@ -3,3 +3,4 @@ zephyr_library_sources(mctp_memory.c) zephyr_library_sources_ifdef(CONFIG_MCTP_UART mctp_uart.c) zephyr_library_sources_ifdef(CONFIG_MCTP_I2C_GPIO_CONTROLLER mctp_i2c_gpio_controller.c) zephyr_library_sources_ifdef(CONFIG_MCTP_I2C_GPIO_TARGET mctp_i2c_gpio_target.c) +zephyr_library_sources_ifdef(CONFIG_MCTP_USB mctp_usb.c) diff --git a/subsys/pmci/mctp/Kconfig b/subsys/pmci/mctp/Kconfig index 1d70ccc7b240d..7f2f9dc11d2c8 100644 --- a/subsys/pmci/mctp/Kconfig +++ b/subsys/pmci/mctp/Kconfig @@ -1,4 +1,5 @@ # Copyright (c) 2024 Intel Corporation +# Copyright 2025 NXP # SPDX-License-Identifier: Apache-2.0 menuconfig MCTP @@ -42,9 +43,16 @@ config MCTP_I2C_GPIO_TARGET Build the MCTP I2C+GPIO target binding to use MCTP over Zephyr's I2C target interface and GPIO to signal writes to the bus controller. +config MCTP_USB + bool "MCTP USB Binding" + depends on USBD_MCTP_CLASS + help + Build the MCTP USB binding to use MCTP over Zephyr's USBD "next" interface. module = MCTP module-str = MCTP source "subsys/logging/Kconfig.template.log_config" +rsource "Kconfig.usb" + endif diff --git a/subsys/pmci/mctp/Kconfig.usb b/subsys/pmci/mctp/Kconfig.usb new file mode 100644 index 0000000000000..9fba1affae5bc --- /dev/null +++ b/subsys/pmci/mctp/Kconfig.usb @@ -0,0 +1,12 @@ +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +if MCTP_USB + +config MCTP_USB_TX_TIMEOUT + int "MCTP USB transmit timeout (ms)" + default 1000 + help + Set the MCTP USB transmit timeout, in milliseconds. + +endif diff --git a/subsys/pmci/mctp/mctp_usb.c b/subsys/pmci/mctp/mctp_usb.c new file mode 100644 index 0000000000000..65ca1f06022b5 --- /dev/null +++ b/subsys/pmci/mctp/mctp_usb.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2024 Intel Corporation + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#include +#include +#include + +#include +LOG_MODULE_REGISTER(mctp_usb, CONFIG_MCTP_LOG_LEVEL); + +#include "libmctp-alloc.h" + +#define MCTP_USB_DMTF_0 0x1A +#define MCTP_USB_DMTF_1 0xB4 + +static void mctp_usb_reset_rx_state(struct mctp_binding_usb *usb) +{ + if (usb->rx_pkt != NULL) { + mctp_pktbuf_free(usb->rx_pkt); + } + + usb->rx_data_idx = 0; + usb->rx_state = STATE_WAIT_HDR_DMTF0; +} + +void mctp_usb_data_recv_cb(const void *const buf, const uint16_t size, const void *const priv) +{ + struct mctp_binding_usb *usb = (struct mctp_binding_usb *)priv; + uint8_t *rx_buf = (uint8_t *)buf; + + LOG_DBG("size=%d", size); + LOG_HEXDUMP_DBG(buf, size, "buf = "); + + for (int i = 0; i < size; i++) { + switch (usb->rx_state) { + case STATE_WAIT_HDR_DMTF0: { + if (rx_buf[i] == MCTP_USB_DMTF_0) { + usb->rx_state = STATE_WAIT_HDR_DMTF1; + } else { + LOG_ERR("Invalid DMTF0 %02X", rx_buf[i]); + goto recv_exit; + } + break; + } + case STATE_WAIT_HDR_DMTF1: { + if (rx_buf[i] == MCTP_USB_DMTF_1) { + usb->rx_state = STATE_WAIT_HDR_RSVD0; + } else { + LOG_ERR("Invalid DMTF1 %02X", rx_buf[i]); + usb->rx_state = STATE_WAIT_HDR_DMTF0; + goto recv_exit; + } + break; + } + case STATE_WAIT_HDR_RSVD0: { + if (rx_buf[i] == 0) { + usb->rx_state = STATE_WAIT_HDR_LEN; + } else { + LOG_ERR("Invalid RSVD0 %02X", rx_buf[i]); + usb->rx_state = STATE_WAIT_HDR_DMTF0; + goto recv_exit; + } + break; + } + case STATE_WAIT_HDR_LEN: { + if (rx_buf[i] > MCTP_USB_MAX_PACKET_LENGTH || rx_buf[i] == 0) { + LOG_ERR("Invalid LEN %02X", rx_buf[i]); + usb->rx_state = STATE_WAIT_HDR_DMTF0; + goto recv_exit; + } + + usb->rx_data_idx = 0; + usb->rx_pkt = mctp_pktbuf_alloc(&usb->binding, rx_buf[i]); + if (usb->rx_pkt == NULL) { + LOG_ERR("Could not allocate PKT buffer"); + mctp_usb_reset_rx_state(usb); + goto recv_exit; + } else { + usb->rx_state = STATE_DATA; + } + + LOG_DBG("Expecting LEN=%d", (int)rx_buf[i]); + + break; + } + case STATE_DATA: { + usb->rx_pkt->data[usb->rx_data_idx++] = rx_buf[i]; + + if (usb->rx_data_idx == usb->rx_pkt->end) { + LOG_DBG("Packet complete"); + mctp_bus_rx(&usb->binding, usb->rx_pkt); + mctp_usb_reset_rx_state(usb); + } + + break; + } + } + } + +recv_exit: +} + +void mctp_usb_error_cb(const int err, const uint8_t ep, const void *const priv) +{ + struct mctp_binding_usb *usb = (struct mctp_binding_usb *)priv; + + LOG_ERR("Encountered USB error %d, endpoint %d, on %s", err, ep, usb->binding.name); +} + +int mctp_usb_tx(struct mctp_binding *binding, struct mctp_pktbuf *pkt) +{ + struct mctp_binding_usb *usb = CONTAINER_OF(binding, struct mctp_binding_usb, binding); + size_t len = mctp_pktbuf_size(pkt); + int err; + + if (len > MCTP_USB_MAX_PACKET_LENGTH) { + return -E2BIG; + } + + err = k_sem_take(&usb->tx_lock, K_MSEC(CONFIG_MCTP_USB_TX_TIMEOUT)); + if (err != 0) { + LOG_ERR("Semaphore could not be obtained"); + return err; + } + + usb->tx_buf[0] = MCTP_USB_DMTF_0; + usb->tx_buf[1] = MCTP_USB_DMTF_1; + usb->tx_buf[2] = 0; + usb->tx_buf[3] = len; + + memcpy((void *)&usb->tx_buf[MCTP_USB_HEADER_SIZE], pkt->data, len); + + LOG_HEXDUMP_DBG(usb->tx_buf, len + 4, "buf = "); + + err = usbd_mctp_send(usb->usb_inst, (void *)usb->tx_buf, len + MCTP_USB_HEADER_SIZE); + if (err != 0) { + LOG_ERR("Failed to send MCTP data over USB"); + return err; + } + + k_sem_give(&usb->tx_lock); + + return 0; +} + +int mctp_usb_start(struct mctp_binding *binding) +{ + struct mctp_binding_usb *usb = CONTAINER_OF(binding, struct mctp_binding_usb, binding); + + k_sem_init(&usb->tx_lock, 1, 1); + mctp_binding_set_tx_enabled(binding, true); + + return 0; +} diff --git a/subsys/usb/device_next/CMakeLists.txt b/subsys/usb/device_next/CMakeLists.txt index 717cdb9da37b0..2c9f5d62c700d 100644 --- a/subsys/usb/device_next/CMakeLists.txt +++ b/subsys/usb/device_next/CMakeLists.txt @@ -104,4 +104,14 @@ zephyr_linker_sources_ifdef( SECTIONS class/usbd_dfu.ld ) +zephyr_library_sources_ifdef( + CONFIG_USBD_MCTP_CLASS + class/usbd_mctp.c +) + +zephyr_linker_sources_ifdef( + CONFIG_USBD_MCTP_CLASS + SECTIONS class/usbd_mctp.ld +) + zephyr_linker_sources(DATA_SECTIONS usbd_data.ld) diff --git a/subsys/usb/device_next/class/Kconfig b/subsys/usb/device_next/class/Kconfig index d3d9a946488e3..84eb20473fd6c 100644 --- a/subsys/usb/device_next/class/Kconfig +++ b/subsys/usb/device_next/class/Kconfig @@ -13,3 +13,4 @@ rsource "Kconfig.hid" rsource "Kconfig.midi2" rsource "Kconfig.dfu" rsource "Kconfig.uvc" +rsource "Kconfig.mctp" diff --git a/subsys/usb/device_next/class/Kconfig.mctp b/subsys/usb/device_next/class/Kconfig.mctp new file mode 100644 index 0000000000000..1aaed70deeaab --- /dev/null +++ b/subsys/usb/device_next/class/Kconfig.mctp @@ -0,0 +1,19 @@ +# Copyright 2025 NXP +# +# SPDX-License-Identifier: Apache-2.0 + +config USBD_MCTP_CLASS + bool "USB MCTP implementation" + default y + help + USB device MCTP class implementation. + +if USBD_MCTP_CLASS + +module = USBD_MCTP +module-str = usbd mctp +default-count = 1 +source "subsys/logging/Kconfig.template.log_config" + +rsource "Kconfig.template.instances_count" +endif diff --git a/subsys/usb/device_next/class/usbd_mctp.c b/subsys/usb/device_next/class/usbd_mctp.c new file mode 100644 index 0000000000000..d7567191e1af6 --- /dev/null +++ b/subsys/usb/device_next/class/usbd_mctp.c @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(usbd_mctp, CONFIG_USBD_MCTP_LOG_LEVEL); + +#define MCTP_ENABLED 0 +#define MCTP_NUM_INSTANCES CONFIG_USBD_MCTP_INSTANCES_COUNT + +UDC_BUF_POOL_DEFINE(mctp_ep_pool, MCTP_NUM_INSTANCES * 2, USBD_MAX_BULK_MPS, + sizeof(struct udc_buf_info), NULL); + +struct usbd_mctp_desc { + struct usb_if_descriptor if0; + struct usb_ep_descriptor if0_fs_out_ep; + struct usb_ep_descriptor if0_fs_in_ep; + struct usb_ep_descriptor if0_hs_out_ep; + struct usb_ep_descriptor if0_hs_in_ep; + struct usb_desc_header nil_desc; +}; + +struct usbd_mctp_ctx { + struct usbd_class_data *class_node; + struct usbd_mctp_desc *const desc; + const struct usb_desc_header **const fs_desc; + const struct usb_desc_header **const hs_desc; + uint8_t inst_idx; + struct usbd_mctp_inst *inst; + struct k_sem in_sem; + struct k_work out_work; + atomic_t state; +}; + +static struct { + const struct usbd_mctp_inst *inst; + struct usbd_class_data *c_data; +} usbd_mctp_inst_to_c_data[MCTP_NUM_INSTANCES]; + +static struct net_buf *mctp_buf_alloc(const uint8_t ep) +{ + struct net_buf *buf = NULL; + struct udc_buf_info *bi; + + buf = net_buf_alloc(&mctp_ep_pool, K_NO_WAIT); + if (!buf) { + return NULL; + } + + bi = udc_get_buf_info(buf); + bi->ep = ep; + + return buf; +} + +static uint8_t mctp_get_bulk_in(struct usbd_class_data *const c_data) +{ + struct usbd_mctp_ctx *ctx = usbd_class_get_private(c_data); + +#if USBD_SUPPORTS_HIGH_SPEED + if (usbd_bus_speed(usbd_class_get_ctx(ctx->class_node)) == USBD_SPEED_HS) { + return ctx->desc->if0_hs_in_ep.bEndpointAddress; + } +#endif + + return ctx->desc->if0_fs_in_ep.bEndpointAddress; +} + +static uint8_t mctp_get_bulk_out(struct usbd_class_data *const c_data) +{ + struct usbd_mctp_ctx *ctx = usbd_class_get_private(c_data); + +#if USBD_SUPPORTS_HIGH_SPEED + if (usbd_bus_speed(usbd_class_get_ctx(ctx->class_node)) == USBD_SPEED_HS) { + return ctx->desc->if0_hs_out_ep.bEndpointAddress; + } +#endif + + return ctx->desc->if0_fs_out_ep.bEndpointAddress; +} + +static void mctp_out_work(struct k_work *work) +{ + struct usbd_mctp_ctx *ctx = CONTAINER_OF(work, struct usbd_mctp_ctx, out_work); + struct net_buf *buf; + + if (!atomic_test_bit(&ctx->state, MCTP_ENABLED)) { + return; + } + + buf = mctp_buf_alloc(mctp_get_bulk_out(ctx->class_node)); + if (buf == NULL) { + if (ctx->inst->error_cb != NULL) { + ctx->inst->error_cb(-ENOBUFS, mctp_get_bulk_in(ctx->class_node), + ctx->inst->priv); + } + LOG_ERR("Failed to allocate OUT buffer"); + return; + } + + if (usbd_ep_enqueue(ctx->class_node, buf)) { + net_buf_unref(buf); + if (ctx->inst->error_cb != NULL) { + ctx->inst->error_cb(-EIO, mctp_get_bulk_in(ctx->class_node), + ctx->inst->priv); + } + LOG_ERR("Failed to enqueue OUT buffer"); + } +} + +static int usbd_mctp_request(struct usbd_class_data *const c_data, struct net_buf *buf, int err) +{ + struct usbd_mctp_ctx *ctx = usbd_class_get_private(c_data); + struct udc_buf_info *bi = udc_get_buf_info(buf); + + LOG_DBG("request for EP 0x%x", bi->ep); + + if (err) { + if (err == -ECONNABORTED) { + LOG_WRN("request ep 0x%02x, len %u cancelled", bi->ep, buf->len); + } else { + LOG_ERR("request ep 0x%02x, len %u failed", bi->ep, buf->len); + } + + if (ctx->inst->error_cb != NULL) { + ctx->inst->error_cb(err, bi->ep, ctx->inst->priv); + } + + goto ep_request_exit; + } + + if (bi->ep == mctp_get_bulk_out(c_data)) { + if (ctx->inst->data_recv_cb != NULL) { + ctx->inst->data_recv_cb(buf->data, buf->len, ctx->inst->priv); + } + + k_work_submit(&ctx->out_work); + } + + if (bi->ep == mctp_get_bulk_in(c_data)) { + k_sem_give(&ctx->in_sem); + } + +ep_request_exit: + net_buf_unref(buf); + return 0; +} + +static void *usbd_mctp_get_desc(struct usbd_class_data *const c_data, const enum usbd_speed speed) +{ + struct usbd_mctp_ctx *ctx = usbd_class_get_private(c_data); + + if (USBD_SUPPORTS_HIGH_SPEED && speed == USBD_SPEED_HS) { + return ctx->hs_desc; + } + + return ctx->fs_desc; +} + +static void usbd_mctp_enable(struct usbd_class_data *const c_data) +{ + struct usbd_mctp_ctx *ctx = usbd_class_get_private(c_data); + + if (!atomic_test_and_set_bit(&ctx->state, MCTP_ENABLED)) { + k_work_submit(&ctx->out_work); + } + + LOG_DBG("Enabled %s", c_data->name); +} + +static void usbd_mctp_disable(struct usbd_class_data *const c_data) +{ + struct usbd_mctp_ctx *ctx = usbd_class_get_private(c_data); + + atomic_clear_bit(&ctx->state, MCTP_ENABLED); + + LOG_DBG("Disabled %s", c_data->name); +} + +static int usbd_mctp_init(struct usbd_class_data *const c_data) +{ + struct usbd_mctp_ctx *ctx = usbd_class_get_private(c_data); + size_t num_instances = 0; + + STRUCT_SECTION_COUNT(usbd_mctp_inst, &num_instances); + + if (num_instances != MCTP_NUM_INSTANCES) { + LOG_ERR("The number of application instances (%d) does not match the number " + "specified by CONFIG_USBD_MCTP_INSTANCES_COUNT (%d)", + num_instances, MCTP_NUM_INSTANCES); + return -EINVAL; + } + + ctx->class_node = c_data; + ctx->state = 0; + + k_sem_init(&ctx->in_sem, 1, 1); + k_work_init(&ctx->out_work, mctp_out_work); + + STRUCT_SECTION_GET(usbd_mctp_inst, ctx->inst_idx, &ctx->inst); + usbd_mctp_inst_to_c_data[ctx->inst_idx].inst = ctx->inst; + usbd_mctp_inst_to_c_data[ctx->inst_idx].c_data = c_data; + + if (ctx->inst->sublcass == USBD_MCTP_SUBCLASS_MANAGEMENT_CONTROLLER || + ctx->inst->sublcass == USBD_MCTP_SUBCLASS_MANAGED_DEVICE_ENDPOINT || + ctx->inst->sublcass == USBD_MCTP_SUBCLASS_HOST_INTERFACE_ENDPOINT) { + ctx->desc->if0.bInterfaceSubClass = ctx->inst->sublcass; + } else { + LOG_ERR("Invalid USB MCTP sublcass"); + return -EINVAL; + } + + if (ctx->inst->mctp_protocol == USBD_MCTP_PROTOCOL_1_X || + ctx->inst->mctp_protocol == USBD_MCTP_PROTOCOL_2_X) { + ctx->desc->if0.bInterfaceProtocol = ctx->inst->mctp_protocol; + } else { + LOG_ERR("Invalid MCTP protocol"); + return -EINVAL; + } + + LOG_DBG("MCTP device %s initialized", ctx->inst->name); + + return 0; +} + +struct usbd_class_api usbd_mctp_api = { + .request = usbd_mctp_request, + .enable = usbd_mctp_enable, + .disable = usbd_mctp_disable, + .init = usbd_mctp_init, + .get_desc = usbd_mctp_get_desc, +}; + +int usbd_mctp_send(const struct usbd_mctp_inst *const inst, const void *const data, + const uint16_t size) +{ + struct usbd_class_data *c_data = NULL; + struct net_buf *buf = NULL; + int err; + + /* Get the class data associated with the MCTP instance */ + for (int i = 0; i < MCTP_NUM_INSTANCES; i++) { + if (usbd_mctp_inst_to_c_data[i].inst == inst) { + c_data = usbd_mctp_inst_to_c_data[i].c_data; + break; + } + } + + if (c_data == NULL) { + LOG_ERR("MCTP instance not found"); + return -ENODEV; + } + + struct usbd_mctp_ctx *ctx = usbd_class_get_private(c_data); + + if (!atomic_test_bit(&ctx->state, MCTP_ENABLED)) { + return -EPERM; + } + + k_sem_take(&ctx->in_sem, K_FOREVER); + + buf = mctp_buf_alloc(mctp_get_bulk_in(c_data)); + if (buf == NULL) { + k_sem_give(&ctx->in_sem); + LOG_ERR("Failed to allocate IN buffer"); + return -ENOMEM; + } + + net_buf_add_mem(buf, data, size); + + err = usbd_ep_enqueue(c_data, buf); + if (err) { + k_sem_give(&ctx->in_sem); + LOG_ERR("Failed to enqueue IN buffer"); + net_buf_unref(buf); + return err; + } + + return 0; +} + +#define DEFINE_MCTP_DESCRIPTORS(n, _) \ + static struct usbd_mctp_desc usbd_mctp_desc_##n = { \ + .if0 = { \ + .bLength = sizeof(struct usb_if_descriptor), \ + .bDescriptorType = USB_DESC_INTERFACE, \ + .bInterfaceNumber = 0, \ + .bAlternateSetting = 0, \ + .bNumEndpoints = 2, \ + .bInterfaceClass = USB_BCC_MCTP, \ + .bInterfaceSubClass = 0, \ + .bInterfaceProtocol = 1, \ + .iInterface = 0 \ + }, \ + .if0_fs_out_ep = { \ + .bLength = sizeof(struct usb_ep_descriptor), \ + .bDescriptorType = USB_DESC_ENDPOINT, \ + .bEndpointAddress = 0x01, \ + .bmAttributes = USB_EP_TYPE_BULK, \ + .wMaxPacketSize = sys_cpu_to_le16(64), \ + .bInterval = 1 \ + }, \ + .if0_fs_in_ep = { \ + .bLength = sizeof(struct usb_ep_descriptor), \ + .bDescriptorType = USB_DESC_ENDPOINT, \ + .bEndpointAddress = 0x81, \ + .bmAttributes = USB_EP_TYPE_BULK, \ + .wMaxPacketSize = sys_cpu_to_le16(64), \ + .bInterval = 1 \ + }, \ + .if0_hs_out_ep = { \ + .bLength = sizeof(struct usb_ep_descriptor), \ + .bDescriptorType = USB_DESC_ENDPOINT, \ + .bEndpointAddress = 0x01, \ + .bmAttributes = USB_EP_TYPE_BULK, \ + .wMaxPacketSize = sys_cpu_to_le16(512), \ + .bInterval = 1 \ + }, \ + .if0_hs_in_ep = { \ + .bLength = sizeof(struct usb_ep_descriptor), \ + .bDescriptorType = USB_DESC_ENDPOINT, \ + .bEndpointAddress = 0x81, \ + .bmAttributes = USB_EP_TYPE_BULK, \ + .wMaxPacketSize = sys_cpu_to_le16(512), \ + .bInterval = 1 \ + }, \ + .nil_desc = { \ + .bLength = 0, \ + .bDescriptorType = 0 \ + } \ + }; \ + \ + const static struct usb_desc_header *usbd_mctp_fs_desc_##n[] = { \ + (struct usb_desc_header *)&usbd_mctp_desc_##n.if0, \ + (struct usb_desc_header *)&usbd_mctp_desc_##n.if0_fs_in_ep, \ + (struct usb_desc_header *)&usbd_mctp_desc_##n.if0_fs_out_ep, \ + (struct usb_desc_header *)&usbd_mctp_desc_##n.nil_desc \ + }; \ + \ + const static struct usb_desc_header *usbd_mctp_hs_desc_##n[] = { \ + (struct usb_desc_header *)&usbd_mctp_desc_##n.if0, \ + (struct usb_desc_header *)&usbd_mctp_desc_##n.if0_hs_in_ep, \ + (struct usb_desc_header *)&usbd_mctp_desc_##n.if0_hs_out_ep, \ + (struct usb_desc_header *)&usbd_mctp_desc_##n.nil_desc \ + }; + +#define DEFINE_MCTP_CLASS_DATA(n, _) \ + static struct usbd_mctp_ctx usbd_mctp_ctx_##n = { \ + .desc = &usbd_mctp_desc_##n, \ + .fs_desc = usbd_mctp_fs_desc_##n, \ + .hs_desc = usbd_mctp_hs_desc_##n, \ + .inst_idx = n \ + }; \ + \ + USBD_DEFINE_CLASS(mctp_##n, &usbd_mctp_api, &usbd_mctp_ctx_##n, NULL); + +LISTIFY(MCTP_NUM_INSTANCES, DEFINE_MCTP_DESCRIPTORS, ()) +LISTIFY(MCTP_NUM_INSTANCES, DEFINE_MCTP_CLASS_DATA, ()) diff --git a/subsys/usb/device_next/class/usbd_mctp.ld b/subsys/usb/device_next/class/usbd_mctp.ld new file mode 100644 index 0000000000000..e0641324364ac --- /dev/null +++ b/subsys/usb/device_next/class/usbd_mctp.ld @@ -0,0 +1,3 @@ +#include + +ITERABLE_SECTION_ROM(usbd_mctp_inst, Z_LINK_ITERABLE_SUBALIGN) From c4093f5c3eb50e5a4c4b2d09f09042ef46b3d19b Mon Sep 17 00:00:00 2001 From: Eric Ocasio Date: Fri, 3 Oct 2025 16:22:56 -0500 Subject: [PATCH 2/2] usb: pmci: mctp: samples: Fix issues from CI checks Add license header to usb_host_tester.py, fix typo in usbd_mctp.h and rogue space in usbd_mctp.h. Signed-off-by: Eric Ocasio --- include/zephyr/usb/class/usbd_mctp.h | 2 +- samples/modules/pmci/mctp/usb_endpoint/usb_host_tester.py | 5 +++++ subsys/usb/device_next/class/usbd_mctp.c | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/zephyr/usb/class/usbd_mctp.h b/include/zephyr/usb/class/usbd_mctp.h index 5d41dbc9144cc..b37a24c63f3bc 100644 --- a/include/zephyr/usb/class/usbd_mctp.h +++ b/include/zephyr/usb/class/usbd_mctp.h @@ -7,7 +7,7 @@ /** * @file - * @brief MCTP over USB Protocol Endpoint Deivce Class public header + * @brief MCTP over USB Protocol Endpoint Device Class public header * * This API is currently considered experimental. */ diff --git a/samples/modules/pmci/mctp/usb_endpoint/usb_host_tester.py b/samples/modules/pmci/mctp/usb_endpoint/usb_host_tester.py index d1e4c9ae8928c..c8acb4c5659b4 100644 --- a/samples/modules/pmci/mctp/usb_endpoint/usb_host_tester.py +++ b/samples/modules/pmci/mctp/usb_endpoint/usb_host_tester.py @@ -1,3 +1,8 @@ +#!/usr/bin/env python3 + +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + import usb.core import usb.util diff --git a/subsys/usb/device_next/class/usbd_mctp.c b/subsys/usb/device_next/class/usbd_mctp.c index d7567191e1af6..0695e277d4596 100644 --- a/subsys/usb/device_next/class/usbd_mctp.c +++ b/subsys/usb/device_next/class/usbd_mctp.c @@ -360,7 +360,7 @@ int usbd_mctp_send(const struct usbd_mctp_inst *const inst, const void *const da .desc = &usbd_mctp_desc_##n, \ .fs_desc = usbd_mctp_fs_desc_##n, \ .hs_desc = usbd_mctp_hs_desc_##n, \ - .inst_idx = n \ + .inst_idx = n \ }; \ \ USBD_DEFINE_CLASS(mctp_##n, &usbd_mctp_api, &usbd_mctp_ctx_##n, NULL);