-
Notifications
You must be signed in to change notification settings - Fork 8k
Add MCTP over USB Binding support #95347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,3 +13,4 @@ USB device support APIs | |
usbd_msc_device.rst | ||
usbd_midi2.rst | ||
usbd_dfu.rst | ||
usbd_mctp.rst |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
<https://www.dmtf.org/sites/default/files/standards/documents/DSP0283_1.0.1.pdf>`__. | ||
|
||
API Reference | ||
************* | ||
|
||
.. doxygengroup:: usbd_mctp |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,8 @@ Samples | |
|
||
* :zephyr:code-sample:`usb-hid-mouse` | ||
|
||
* :zephyr:code-sample:`mctp-usb-endpoint` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you suggest that I just update this file in a commit with the sample application, or do one 'super commit' with all of these feature/MCTP changes? I did it this way for human readability, but didn't fully understand the need for every commit to build. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Commits in a pull request should represent clear, logical units of change that are easy to review and maintain bisectability." https://docs.zephyrproject.org/latest/contribute/contributor_expectations.html#pr-requirements |
||
|
||
* :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 | ||
============ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <stdint.h> | ||
#include <zephyr/kernel.h> | ||
#include <zephyr/device.h> | ||
#include <zephyr/usb/class/usbd_mctp.h> | ||
#include <libmctp.h> | ||
|
||
#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_ */ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 Device Class public header | ||
* | ||
* This API is currently considered experimental. | ||
*/ | ||
|
||
#include <zephyr/sys/iterable_sections.h> | ||
|
||
#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_ */ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Copyright (c) 2023 Nordic Semiconductor ASA | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am confused, is PMCI/MCTP not part of the Zephyr but an external module? If so then I do not think you can add any dependency on it to the USB subsystem. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The way I looked at it is that MCTP is called out as a base class on usb.org, so the device class implementation can exists in Zephyr, even if the higher-level MCTP protocol management comes from an external module. Right now it comes from libmctp (https://github.com/openbmc/libmctp). I understand this concern. Is there something you think would create such a dependency? Again, I'm new to Zephyr, so maybe I don't fully understand something. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
mctp is not an external module, it is part of the main manifest and there are dependencies on it already. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Anyway, the implementation cannot be part of subsys/usb, and has to go to the modules. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The fact that MCTP is listed on usb.org doesn't matter then? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @teburd ok, I'll explore this. Regarding dependency in general, I want to clarify something since it may not be clear from this initial PR. The re-worked PR I have locally has cleaner lines of separation... Besides the reference to the MCTP sample in usb_device.rst, the USB device class has no dependency on libmctp. Yes, libmctp uses it, but any other MCTP implementation can as well. You can remove libmctp and the device class stands on its own. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you post the re-worked PR? Would that come as separate PR or a force-push here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, I'll try to find some time to get it done today. The plan was for force push. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
is this your issue? really? #96995 |
||
# 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" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <https://www.dmtf.org/sites/default/files/standards/documents/DSP0236_1.3.1.pdf>`_ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/* | ||
* Copyright 2025 NXP | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
/ { | ||
mctp_usb0: mctp_usb0 { | ||
compatible = "zephyr,mctp-usb"; | ||
endpoint-id = <10>; | ||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Needed to run USB host test script. | ||
Check warning on line 1 in samples/modules/pmci/mctp/usb_endpoint/requirements.txt
|
||
pyusb |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well no, it is specified by the "DMTF association", https://www.dmtf.org/sites/default/files/standards/documents/DSP0283_1.0.0.pdf
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's base class 0x14 in the USB spec, so I'm not sure what you mean here.
https://www.usb.org/defined-class-codes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please refer latest specification https://www.dmtf.org/sites/default/files/standards/documents/DSP0283_1.0.1.pdf