Skip to content

Commit 196a1da

Browse files
eric-ocasio-nxpcfriedt
authored andcommitted
pmci: mctp: samples: Add support for MCTP over USB.
Adds support for the USB device binding for MCTP. Binding created based on the DMTF specification for USB Transport Binding. The overall design of this binding follows the existing UART and I2C+GPIO bindings that already exist in Zephyr. Signed-off-by: Eric Ocasio <[email protected]>
1 parent fc2e630 commit 196a1da

File tree

14 files changed

+885
-0
lines changed

14 files changed

+885
-0
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright (c) 2024 Intel Corporation
3+
* Copyright 2025 NXP
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*
7+
*/
8+
9+
#ifndef ZEPHYR_MCTP_USB_H_
10+
#define ZEPHYR_MCTP_USB_H_
11+
12+
#include <zephyr/sys/iterable_sections.h>
13+
#include <libmctp.h>
14+
15+
/* MCTP class subclass options */
16+
#define USBD_MCTP_SUBCLASS_MANAGEMENT_CONTROLLER 0
17+
#define USBD_MCTP_SUBCLASS_MANAGED_DEVICE_ENDPOINT 0
18+
#define USBD_MCTP_SUBCLASS_HOST_INTERFACE_ENDPOINT 1
19+
20+
/* MCTP class protocol options */
21+
#define USBD_MCTP_PROTOCOL_1_X 1
22+
#define USBD_MCTP_PROTOCOL_2_X 2
23+
24+
/* Binding-specific defines, internal use */
25+
#define MCTP_USB_HEADER_SIZE 4
26+
#define MCTP_USB_MAX_PACKET_LENGTH 255
27+
28+
/**
29+
* @brief An MCTP binding for Zephyr's USB device stack
30+
*/
31+
struct mctp_binding_usb {
32+
/** @cond INTERNAL_HIDDEN */
33+
struct mctp_binding binding;
34+
struct usbd_class_data *usb_class_data;
35+
uint8_t tx_buf[MCTP_USB_HEADER_SIZE + MCTP_USB_MAX_PACKET_LENGTH];
36+
struct k_sem tx_lock;
37+
struct mctp_pktbuf *rx_pkt;
38+
uint8_t rx_data_idx;
39+
enum {
40+
STATE_WAIT_HDR_DMTF0,
41+
STATE_WAIT_HDR_DMTF1,
42+
STATE_WAIT_HDR_RSVD0,
43+
STATE_WAIT_HDR_LEN,
44+
STATE_DATA
45+
} rx_state;
46+
/** @endcond INTERNAL_HIDDEN */
47+
};
48+
49+
struct mctp_usb_class_inst {
50+
uint8_t sublcass;
51+
uint8_t mctp_protocol;
52+
struct mctp_binding_usb *mctp_binding;
53+
};
54+
55+
/** @cond INTERNAL_HIDDEN */
56+
int mctp_usb_start(struct mctp_binding *binding);
57+
int mctp_usb_tx(struct mctp_binding *binding, struct mctp_pktbuf *pkt);
58+
/** @endcond INTERNAL_HIDDEN */
59+
60+
/**
61+
* @brief Define a MCTP bus binding for USB
62+
*
63+
* @param _name Symbolic name of the bus binding variable
64+
* @param _subclass MCTP subclass used in the USB interfce descriptor
65+
* @param _protocol MCTP protocol used in the USB interface descriptor
66+
*/
67+
#define MCTP_USB_DEFINE(_name, _subclass, _protocol) \
68+
struct mctp_binding_usb _name = { \
69+
.binding = { \
70+
.name = STRINGIFY(_name), \
71+
.version = 1, \
72+
.pkt_size = MCTP_PACKET_SIZE(MCTP_USB_MAX_PACKET_LENGTH), \
73+
.pkt_header = 0, \
74+
.pkt_trailer = 0, \
75+
.start = mctp_usb_start, \
76+
.tx = mctp_usb_tx \
77+
}, \
78+
.usb_class_data = NULL, \
79+
.rx_pkt = NULL, \
80+
.rx_data_idx = 0, \
81+
.rx_state = STATE_WAIT_HDR_DMTF0 \
82+
}; \
83+
\
84+
const STRUCT_SECTION_ITERABLE(mctp_usb_class_inst, mctp_usb_class_inst_##_name) = { \
85+
.sublcass = _subclass, \
86+
.mctp_protocol = _protocol, \
87+
.mctp_binding = &_name, \
88+
};
89+
90+
#endif /* ZEPHYR_MCTP_USB_H_ */

include/zephyr/usb/usb_ch9.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ struct usb_association_descriptor {
267267
#define USB_BCC_MASS_STORAGE 0x08
268268
#define USB_BCC_CDC_DATA 0x0A
269269
#define USB_BCC_VIDEO 0x0E
270+
#define USB_BCC_MCTP 0x14
270271
#define USB_BCC_WIRELESS_CONTROLLER 0xE0
271272
#define USB_BCC_MISCELLANEOUS 0xEF
272273
#define USB_BCC_APPLICATION 0xFE
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
5+
project(mctp_usb_endpoint)
6+
7+
include(${ZEPHYR_BASE}/samples/subsys/usb/common/common.cmake)
8+
9+
target_sources(app PRIVATE src/main.c)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2023 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# Source common USB sample options used to initialize new experimental USB
5+
# device stack. The scope of these options is limited to USB samples in project
6+
# tree, you cannot use them in your own application.
7+
source "samples/subsys/usb/common/Kconfig.sample_usbd"
8+
9+
source "Kconfig.zephyr"
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
.. zephyr:code-sample:: mctp-usb-endpoint
2+
:name: MCTP USB Endpoint Node Sample
3+
:relevant-api: usbd_api
4+
5+
Create an MCTP endpoint node using the USB device interface.
6+
7+
Overview
8+
********
9+
Sets up an MCTP endpoint node that listens for messages from the MCTP bus owner with EID 20.
10+
Responds with "Hello, bus owner" message.
11+
12+
Requirements
13+
************
14+
A board and SoC that provide support for USB device capability (either FS or HS, though DMTF spec
15+
specifies only HS). Testing requires a USB host that has the ability to enumerate the MCTP interface
16+
and interact with the board using the MCTP protocol. An easy way to do this is a Python script.
17+
18+
An example script to test this interface is provided with this sample application. To run the
19+
script, you must first install pyusb.
20+
21+
.. code-block:: console
22+
23+
pip install requirements.txt
24+
python usb_host_tester.py
25+
26+
If the test is successful, you will see the following output from the script:
27+
28+
.. code-block:: console
29+
30+
Sending message with size smaller than USB FS MPS (<64b)
31+
Received: b'\x1a\xb4\x00\x15\x01\x14\n\xc0Hello, bus owner\x00'
32+
Sending message spanning two USB FS packets (>64b)
33+
Received: b'\x1a\xb4\x00\x15\x01\x14\n\xc0Hello, bus owner\x00'
34+
Sending message with two MCTP messages in a single USB FS packet
35+
Received: b'\x1a\xb4\x00\x15\x01\x14\n\xc0Hello, bus owner\x00'
36+
Received: b'\x1a\xb4\x00\x15\x01\x14\n\xc0Hello, bus owner\x00'
37+
38+
Wiring
39+
******
40+
Connect a USB cable between the host (likely a PC) and the board. The type of cable (mini, micro,
41+
type C) depends on the host and board connectors.
42+
43+
Building and Running
44+
********************
45+
46+
.. zephyr-app-commands::
47+
:zephyr-app: samples/subsys/pmci/mctp/usb_endpoint
48+
:host-os: unix
49+
:board: frdm_mcxn947_mcxn947_cpu0
50+
:goals: run
51+
:compact:
52+
53+
References
54+
**********
55+
56+
`MCTP Base Specification 2019 <https://www.dmtf.org/sites/default/files/standards/documents/DSP0236_1.3.1.pdf>`_
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CONFIG_USB_DEVICE_STACK_NEXT=y
2+
3+
CONFIG_MCTP=y
4+
CONFIG_MCTP_USB=y
5+
6+
CONFIG_LOG=y
7+
CONFIG_MCTP_LOG_LEVEL_ERR=y
8+
CONFIG_UDC_DRIVER_LOG_LEVEL_ERR=y
9+
CONFIG_USBD_LOG_LEVEL_ERR=y
10+
CONFIG_UDC_DRIVER_LOG_LEVEL_ERR=y
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Needed to run USB host test script.
2+
pyusb
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright (c) 2024 Intel Corporation
3+
* Copyright 2025 NXP
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#include <sample_usbd.h>
9+
10+
#include <stdlib.h>
11+
#include <stdio.h>
12+
#include <assert.h>
13+
#include <unistd.h>
14+
#include <zephyr/kernel.h>
15+
#include <zephyr/types.h>
16+
#include <zephyr/pmci/mctp/mctp_usb.h>
17+
#include <zephyr/usb/usbd.h>
18+
#include <libmctp.h>
19+
20+
#include <zephyr/logging/log.h>
21+
LOG_MODULE_REGISTER(mctp_endpoint);
22+
23+
#define SELF_ID 10
24+
#define BUS_OWNER_ID 20
25+
26+
K_SEM_DEFINE(mctp_rx, 0, 1);
27+
MCTP_USB_DEFINE(mctp0, USBD_MCTP_SUBCLASS_MANAGED_DEVICE_ENDPOINT, USBD_MCTP_PROTOCOL_1_X);
28+
29+
static struct mctp *mctp_ctx;
30+
static bool usb_configured;
31+
32+
static void sample_msg_cb(struct usbd_context *const ctx, const struct usbd_msg *msg)
33+
{
34+
LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type));
35+
36+
if (msg->type == USBD_MSG_CONFIGURATION) {
37+
usb_configured = true;
38+
}
39+
}
40+
41+
static int enable_usb_device_next(void)
42+
{
43+
static struct usbd_context *mctp_poc_usbd;
44+
int err;
45+
46+
mctp_poc_usbd = sample_usbd_init_device(sample_msg_cb);
47+
if (mctp_poc_usbd == NULL) {
48+
LOG_ERR("Failed to initialize USB device");
49+
return -ENODEV;
50+
}
51+
52+
if (!usbd_can_detect_vbus(mctp_poc_usbd)) {
53+
err = usbd_enable(mctp_poc_usbd);
54+
if (err) {
55+
LOG_ERR("Failed to enable device support");
56+
return err;
57+
}
58+
}
59+
60+
LOG_INF("USB device support enabled");
61+
62+
return 0;
63+
}
64+
65+
static void rx_message(uint8_t eid, bool tag_owner, uint8_t msg_tag, void *data, void *msg,
66+
size_t len)
67+
{
68+
switch (eid) {
69+
case BUS_OWNER_ID: {
70+
LOG_INF("Received MCTP message \"%s\" from EID %d", (char *)msg, eid);
71+
k_sem_give(&mctp_rx);
72+
break;
73+
}
74+
default: {
75+
LOG_INF("Unknown endpoint %d", eid);
76+
break;
77+
}
78+
}
79+
}
80+
81+
int main(void)
82+
{
83+
LOG_INF("MCTP Endpoint EID: %d on %s", SELF_ID, CONFIG_BOARD_TARGET);
84+
85+
int ret = enable_usb_device_next();
86+
87+
if (ret != 0) {
88+
LOG_ERR("Failed to enable USB device support");
89+
return 0;
90+
}
91+
92+
while (!usb_configured) {
93+
k_msleep(5);
94+
}
95+
96+
mctp_set_alloc_ops(malloc, free, realloc);
97+
mctp_ctx = mctp_init();
98+
__ASSERT_NO_MSG(mctp_ctx != NULL);
99+
100+
mctp_register_bus(mctp_ctx, &mctp0.binding, SELF_ID);
101+
mctp_set_rx_all(mctp_ctx, rx_message, NULL);
102+
103+
while (1) {
104+
k_sem_take(&mctp_rx, K_FOREVER);
105+
mctp_message_tx(mctp_ctx, BUS_OWNER_ID, false, 0, "Hello, bus owner",
106+
sizeof("Hello, bus owner"));
107+
}
108+
109+
return 0;
110+
}

0 commit comments

Comments
 (0)