feat: add Pigweed platform integration for MCTP service (Phase 6) - MCTP server main.rs with IPC dispatch loop (NoopSender for bring-up) - Wire IpcMctpClient::send_recv to syscall::channel_transact (pigweed feature) - Bazel BUILD files for all MCTP crates (api, server, client, transport-i2c) - system.json5 with I2C server + MCTP server + echo app - Echo binary as Pigweed userspace process (target/ast1060-evb/mctp/)
diff --git a/port-mctp-echo.md b/port-mctp-echo.md index bba4a31..8124f7e 100644 --- a/port-mctp-echo.md +++ b/port-mctp-echo.md
@@ -81,36 +81,37 @@ - [x] IPC client (`services/mctp/client/`) — `IpcMctpClient` implementing `MctpClient` via wire protocol + IPC - [x] Server-side IPC dispatch (`services/mctp/server/src/dispatch.rs`) — decodes wire requests, calls `Server` - [x] Wire-protocol dispatch integration test (`tests/dispatch.rs`) — full round-trip through wire encoding -- [ ] Echo binary as Pigweed userspace process (needs Phase 6 platform wiring) -- [ ] Wire up with server + I2C transport for an end-to-end demo +- [x] Echo binary as Pigweed userspace process (`target/ast1060-evb/mctp/mctp_echo.rs`) +- [ ] Wire up with server + I2C transport for an end-to-end demo (needs real I2C sender in server `main.rs`) -## Phase 6: Pigweed Platform Integration — NOT STARTED +## Phase 6: Pigweed Platform Integration — MOSTLY DONE Wire up the MCTP server as a Pigweed userspace process on the AST1060-EVB (`target/ast1060-evb/`). -- [ ] MCTP server `main.rs`: event loop driven by pw_kernel IPC/channels +- [x] MCTP server `main.rs`: event loop driven by pw_kernel IPC/channels - Replaces hubris `sys_recv_open` / notifications / `idol` IPC dispatch - Uses `dispatch_mctp_op` for IPC request handling - Follows the pattern established by `services/i2c/server/` -- [ ] Connect `IpcMctpClient::send_recv` to `syscall::channel_transact` -- [ ] Bazel BUILD files for each new crate (following `services/i2c/` pattern) -- [ ] `system.json5` entry for MCTP server + echo processes -- [ ] Integration with `target/ast1060-evb/` platform definition + - Uses NoopSender for initial bring-up; real I2cSender wiring is TODO +- [x] Connect `IpcMctpClient::send_recv` to `syscall::channel_transact` (gated behind `pigweed` feature) +- [x] Bazel BUILD files for each new crate (following `services/i2c/` pattern) +- [x] `system.json5` entry for MCTP server + echo processes (`target/ast1060-evb/mctp/`) +- [x] Integration with `target/ast1060-evb/` platform definition (`target.rs`, `BUILD.bazel`) ## Phase 7: Testing & Documentation — PARTIALLY DONE - [x] Wire protocol unit tests (7 tests in `api/src/wire.rs`) - [x] Echo integration tests with client/server partition (2 tests in `server/tests/echo.rs`) - [x] Wire-protocol dispatch integration tests (2 tests in `server/tests/dispatch.rs`) +- [x] README for each MCTP crate (`api/`, `server/`, `client/`, `transport-i2c/`) - [ ] QEMU-based end-to-end test (following `services/i2c/` test pattern) - [ ] Update `docs/src/specification/middleware/mctp.md` with implementation status -- [ ] README for `services/mctp/` --- ## Current Status -**Phases 1–3 complete, Phase 5 mostly done.** All library code is written: wire protocol, IPC client, server dispatch, and transport bindings. 11 tests pass. The remaining work is Phase 6 (Pigweed platform integration): server `main.rs`, Bazel BUILD files, and `system.json5`. +**Phases 1–3, 5, and 6 mostly complete.** All library code, IPC dispatch, Bazel BUILD files, system configuration, and echo binary are written. 11 tests pass. Remaining work: wire up real I2cSender in server main.rs (replace NoopSender), QEMU e2e test, and docs update. ## Key Dependencies
diff --git a/services/mctp/api/BUILD.bazel b/services/mctp/api/BUILD.bazel new file mode 100644 index 0000000..39b1944 --- /dev/null +++ b/services/mctp/api/BUILD.bazel
@@ -0,0 +1,20 @@ +# Licensed under the Apache-2.0 license + +load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") + +rust_library( + name = "mctp_api", + srcs = glob(["src/**/*.rs"]), + crate_name = "openprot_mctp_api", + edition = "2024", + visibility = ["//visibility:public"], + deps = [ + "@rust_crates//:heapless", + "@rust_crates//:zerocopy", + ], +) + +rust_test( + name = "mctp_api_test", + crate = ":mctp_api", +)
diff --git a/services/mctp/client/BUILD.bazel b/services/mctp/client/BUILD.bazel new file mode 100644 index 0000000..fb4cd8d --- /dev/null +++ b/services/mctp/client/BUILD.bazel
@@ -0,0 +1,15 @@ +# Licensed under the Apache-2.0 license + +load("@rules_rust//rust:defs.bzl", "rust_library") + +rust_library( + name = "mctp_client", + srcs = ["src/lib.rs"], + crate_name = "openprot_mctp_client", + edition = "2024", + visibility = ["//visibility:public"], + deps = [ + "//services/mctp/api:mctp_api", + "@pigweed//pw_kernel/userspace", + ], +)
diff --git a/services/mctp/client/Cargo.toml b/services/mctp/client/Cargo.toml index 7de8400..edaad9e 100644 --- a/services/mctp/client/Cargo.toml +++ b/services/mctp/client/Cargo.toml
@@ -7,5 +7,10 @@ description = "MCTP IPC client for OpenPRoT (Pigweed userspace)" license = "Apache-2.0" +[features] +default = [] +# Enable Pigweed IPC transport (Bazel builds only; requires `userspace` crate). +pigweed = [] + [dependencies] openprot-mctp-api = { path = "../api" }
diff --git a/services/mctp/client/src/lib.rs b/services/mctp/client/src/lib.rs index 4eeaa11..fa0af29 100644 --- a/services/mctp/client/src/lib.rs +++ b/services/mctp/client/src/lib.rs
@@ -85,19 +85,25 @@ Ok((header, resp_len)) } - /// Platform-specific send/receive. + /// Platform-specific send/receive via Pigweed IPC. /// - /// In a Pigweed build, this calls `syscall::channel_transact`. - /// For now, this is a stub that returns an error. + /// Uses `syscall::channel_transact` when built under Pigweed (Bazel). + /// Returns a stub error when built under Cargo (no `userspace` crate). + #[cfg(feature = "pigweed")] + fn send_recv(&self, req_len: usize) -> Result<usize, MctpError> { + let mut inner = self.inner.borrow_mut(); + userspace::syscall::channel_transact( + self.handle, + &inner.request_buf[..req_len], + &mut inner.response_buf, + userspace::time::Instant::MAX, + ) + .map_err(|_| MctpError::from_code(ResponseCode::InternalError)) + } + + /// Stub for non-Pigweed builds (Cargo workspace). + #[cfg(not(feature = "pigweed"))] fn send_recv(&self, _req_len: usize) -> Result<usize, MctpError> { - // TODO(Phase 6): Replace with: - // let mut inner = self.inner.borrow_mut(); - // syscall::channel_transact( - // self.handle, - // &inner.request_buf[..req_len], - // &mut inner.response_buf, - // Instant::MAX, - // ).map_err(|_| MctpError::from_code(ResponseCode::InternalError)) Err(MctpError::from_code(ResponseCode::InternalError)) } }
diff --git a/services/mctp/server/BUILD.bazel b/services/mctp/server/BUILD.bazel new file mode 100644 index 0000000..2374fe8 --- /dev/null +++ b/services/mctp/server/BUILD.bazel
@@ -0,0 +1,54 @@ +# Licensed under the Apache-2.0 license + +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test") +load("@pigweed//pw_kernel/tooling:app_package.bzl", "app_package") + +rust_library( + name = "mctp_server_lib", + srcs = [ + "src/dispatch.rs", + "src/lib.rs", + "src/server.rs", + ], + crate_name = "openprot_mctp_server", + edition = "2024", + visibility = ["//visibility:public"], + deps = [ + "//services/mctp/api:mctp_api", + "@rust_crates//:heapless", + "@rust_crates//:mctp", + "@rust_crates//:mctp-lib", + ], +) + +rust_test( + name = "mctp_server_test", + crate = ":mctp_server_lib", +) + +rust_binary( + name = "mctp_server", + srcs = ["src/main.rs"], + edition = "2024", + tags = ["kernel"], + visibility = ["//visibility:public"], + deps = [ + ":app_mctp_server", + ":mctp_server_lib", + "//services/mctp/api:mctp_api", + "@rust_crates//:mctp", + "@rust_crates//:mctp-lib", + "@pigweed//pw_kernel/syscall:syscall_user", + "@pigweed//pw_kernel/userspace", + "@pigweed//pw_log/rust:pw_log", + "@pigweed//pw_status/rust:pw_status", + ], +) + +app_package( + name = "app_mctp_server", + app_name = "mctp_server", + edition = "2024", + system_config = "//target/ast1060-evb/mctp:system_config", + tags = ["kernel"], +)
diff --git a/services/mctp/server/Cargo.toml b/services/mctp/server/Cargo.toml index 73a7acf..60e7209 100644 --- a/services/mctp/server/Cargo.toml +++ b/services/mctp/server/Cargo.toml
@@ -6,6 +6,8 @@ edition = "2021" description = "MCTP server implementation for OpenPRoT" license = "Apache-2.0" +# main.rs is a Pigweed binary built by Bazel only. +autobins = false [dependencies] openprot-mctp-api = { path = "../api" }
diff --git a/services/mctp/server/src/main.rs b/services/mctp/server/src/main.rs new file mode 100644 index 0000000..80242dc --- /dev/null +++ b/services/mctp/server/src/main.rs
@@ -0,0 +1,153 @@ +// Licensed under the Apache-2.0 license + +//! MCTP Server — IPC Dispatch Loop +//! +//! Userspace service that receives MCTP requests over a Pigweed IPC channel, +//! dispatches them to the MCTP server core, and responds with results. +//! +//! # Architecture +//! +//! ```text +//! ┌─ Client ──────────────────────────┐ +//! │ channel_transact(request) │ +//! └──────────────┬────────────────────┘ +//! │ IPC channel +//! ▼ +//! ┌─ This Server ─────────────────────┐ +//! │ object_wait(READABLE) │ +//! │ channel_read → MctpRequestHeader │ +//! │ dispatch_mctp_op(op, server) │ +//! │ channel_respond ← MctpRespHeader │ +//! └──────────────┬────────────────────┘ +//! │ mctp-stack Router +//! ▼ +//! ┌─ I2C Transport ──────────────────┐ +//! │ I2cSender → I2C Server IPC │ +//! └──────────────────────────────────┘ +//! ``` +//! +//! # IPC Pattern +//! +//! Follows the same loop as `services/i2c/server/src/main.rs`: +//! +//! 1. `object_wait(handle, READABLE)` — block until a client sends a request +//! 2. `channel_read(handle)` — read the raw request bytes +//! 3. Parse `MctpRequestHeader`, dispatch via `dispatch_mctp_op` +//! 4. `channel_respond(handle)` — send response header + data +//! +//! # Handle Binding +//! +//! The IPC handle is provided by the `app_package` Bazel rule, which generates +//! `app_mctp_server::handle::MCTP` from the system configuration. + +#![no_main] +#![no_std] + +use openprot_mctp_api::wire::{MctpRequestHeader, MAX_REQUEST_SIZE, MAX_RESPONSE_SIZE, MAX_PAYLOAD_SIZE}; +use openprot_mctp_api::ResponseCode; +use openprot_mctp_server::dispatch; + +use pw_status::Result; +use userspace::entry; +use userspace::syscall::{self, Signals}; +use userspace::time::Instant; + +use app_mctp_server::handle; + +// --------------------------------------------------------------------------- +// Server loop +// --------------------------------------------------------------------------- + +fn mctp_server_loop() -> Result<()> { + pw_log::info!("MCTP server starting"); + + // TODO(Phase 6): Initialize transport binding. + // The I2C sender needs an IpcI2cClient bound to the I2C server channel. + // For now, we create a stub sender that will be replaced with a real + // I2cSender<IpcI2cClient> once the I2C channel handle is wired up. + // + // let i2c_client = i2c_client::IpcI2cClient::new(handle::I2C); + // let sender = I2cSender::new(i2c_client, BusIndex::BUS_0, OWN_I2C_ADDR); + // let mut server = Server::new(Eid(OWN_EID), 0, sender); + + // For initial bring-up, use a no-op sender so the IPC dispatch loop + // can be tested without I2C hardware. + let mut server = openprot_mctp_server::Server::<NoopSender, 16>::new( + mctp::Eid(8), // default EID + 0, + NoopSender, + ); + + let mut request_buf = [0u8; MAX_REQUEST_SIZE]; + let mut response_buf = [0u8; MAX_RESPONSE_SIZE]; + let mut recv_buf = [0u8; MAX_PAYLOAD_SIZE]; + + loop { + // Block until a client sends a request + syscall::object_wait(handle::MCTP, Signals::READABLE, Instant::MAX)?; + + // Read the request + let len = syscall::channel_read(handle::MCTP, 0, &mut request_buf)?; + + if len < MctpRequestHeader::SIZE { + // Truncated request — respond with error + let resp = openprot_mctp_api::wire::MctpResponseHeader::error(ResponseCode::BadArgument); + response_buf[..openprot_mctp_api::wire::MctpResponseHeader::SIZE] + .copy_from_slice(&resp.to_bytes()); + syscall::channel_respond( + handle::MCTP, + &response_buf[..openprot_mctp_api::wire::MctpResponseHeader::SIZE], + )?; + continue; + } + + // Dispatch and respond + let response_len = dispatch::dispatch_mctp_op( + &request_buf[..len], + &mut response_buf, + &mut server, + &mut recv_buf, + ); + syscall::channel_respond(handle::MCTP, &response_buf[..response_len])?; + } +} + +// --------------------------------------------------------------------------- +// No-op sender for initial bring-up +// --------------------------------------------------------------------------- + +/// Stub sender that does nothing. Used for IPC dispatch testing before +/// the I2C transport is wired up. +struct NoopSender; + +impl mctp_stack::Sender for NoopSender { + fn send_vectored( + &mut self, + _fragmenter: mctp_stack::fragment::Fragmenter, + _payload: &[&[u8]], + ) -> mctp::Result<mctp::Tag> { + Err(mctp::Error::TxFailure) + } + + fn get_mtu(&self) -> usize { + 64 + } +} + +// --------------------------------------------------------------------------- +// Entry point +// --------------------------------------------------------------------------- + +#[entry] +fn entry() -> ! { + if let Err(e) = mctp_server_loop() { + pw_log::error!("MCTP server error: {}", e as u32); + let _ = syscall::debug_shutdown(Err(e)); + } + loop {} +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +}
diff --git a/services/mctp/transport-i2c/BUILD.bazel b/services/mctp/transport-i2c/BUILD.bazel new file mode 100644 index 0000000..dc7d780 --- /dev/null +++ b/services/mctp/transport-i2c/BUILD.bazel
@@ -0,0 +1,19 @@ +# Licensed under the Apache-2.0 license + +load("@rules_rust//rust:defs.bzl", "rust_library") + +rust_library( + name = "mctp_transport_i2c", + srcs = glob(["src/**/*.rs"]), + crate_name = "openprot_mctp_transport_i2c", + edition = "2024", + visibility = ["//visibility:public"], + deps = [ + "//services/i2c/api:i2c_api", + "//services/mctp/api:mctp_api", + "@rust_crates//:embedded-hal", + "@rust_crates//:heapless", + "@rust_crates//:mctp", + "@rust_crates//:mctp-lib", + ], +)
diff --git a/target/ast1060-evb/mctp/BUILD.bazel b/target/ast1060-evb/mctp/BUILD.bazel new file mode 100644 index 0000000..f813178 --- /dev/null +++ b/target/ast1060-evb/mctp/BUILD.bazel
@@ -0,0 +1,112 @@ +# Licensed under the Apache-2.0 license + +load("@pigweed//pw_kernel/tooling:app_package.bzl", "app_package") +load("@pigweed//pw_kernel/tooling:system_image.bzl", "system_image", "system_image_test") +load("@pigweed//pw_kernel/tooling:target_codegen.bzl", "target_codegen") +load("@pigweed//pw_kernel/tooling:target_linker_script.bzl", "target_linker_script") +load("@rules_rust//rust:defs.bzl", "rust_binary") +load("//target/ast1060-evb:defs.bzl", "TARGET_COMPATIBLE_WITH") +load("//target:uart_boot_image.bzl", "uart_boot_image") + +# --------------------------------------------------------------------------- +# MCTP echo binary (userspace process) +# --------------------------------------------------------------------------- + +rust_binary( + name = "mctp_echo", + srcs = ["mctp_echo.rs"], + edition = "2024", + tags = ["kernel"], + deps = [ + ":app_mctp_echo", + "//services/mctp/api:mctp_api", + "//services/mctp/client:mctp_client", + "@pigweed//pw_kernel/syscall:syscall_user", + "@pigweed//pw_kernel/userspace", + "@pigweed//pw_log/rust:pw_log", + "@pigweed//pw_status/rust:pw_status", + ], +) + +app_package( + name = "app_mctp_echo", + app_name = "mctp_echo", + edition = "2024", + system_config = ":system_config", + tags = ["kernel"], +) + +# --------------------------------------------------------------------------- +# System image: I2C server + MCTP server + echo client +# --------------------------------------------------------------------------- + +system_image( + name = "mctp", + apps = [ + ":mctp_echo", + "//services/i2c/server:i2c_server", + "//services/mctp/server:mctp_server", + ], + kernel = ":target", + platform = "//target/ast1060-evb", + system_config = ":system_config", + tags = ["kernel"], + target_compatible_with = TARGET_COMPATIBLE_WITH, + visibility = ["//visibility:public"], +) + +system_image_test( + name = "mctp_test", + image = ":mctp", + target_compatible_with = TARGET_COMPATIBLE_WITH, +) + +# Generate UART bootable image +uart_boot_image( + name = "mctp_uart", + src = ":mctp", + out = "mctp_uart.bin", +) + +filegroup( + name = "system_config", + srcs = ["system.json5"], + visibility = ["//visibility:public"], +) + +target_codegen( + name = "codegen", + arch = "@pigweed//pw_kernel/arch/arm_cortex_m:arch_arm_cortex_m", + system_config = ":system_config", + target_compatible_with = TARGET_COMPATIBLE_WITH, +) + +target_linker_script( + name = "linker_script", + system_config = ":system_config", + tags = ["kernel"], + target_compatible_with = TARGET_COMPATIBLE_WITH, + template = "//target/ast1060-evb:linker_script_template", +) + +rust_binary( + name = "target", + srcs = [ + "target.rs", + ], + edition = "2024", + tags = ["kernel"], + target_compatible_with = TARGET_COMPATIBLE_WITH, + deps = [ + ":codegen", + ":linker_script", + "//target/ast1060-evb:entry", + "//target/ast1060-evb:console_backend_uart", + "@rust_crates//:cortex-m-semihosting", + "@pigweed//pw_kernel/arch/arm_cortex_m:arch_arm_cortex_m", + "@pigweed//pw_kernel/kernel", + "@pigweed//pw_kernel/target:target_common", + "@pigweed//pw_kernel/userspace", + "@pigweed//pw_log/rust:pw_log", + ], +)
diff --git a/target/ast1060-evb/mctp/mctp_echo.rs b/target/ast1060-evb/mctp/mctp_echo.rs new file mode 100644 index 0000000..ff08843 --- /dev/null +++ b/target/ast1060-evb/mctp/mctp_echo.rs
@@ -0,0 +1,83 @@ +// Licensed under the Apache-2.0 license + +//! MCTP Echo Application +//! +//! Listens for MCTP type-1 (vendor-defined) messages and echoes the +//! payload back to the sender. This is a direct port of the Hubris +//! `task/mctp-echo/` task. +//! +//! # Architecture +//! +//! The echo app is a simple loop: +//! 1. Register a listener for MCTP message type 1 +//! 2. Receive a message (blocking) +//! 3. Send the payload back to the sender +//! 4. Repeat + +#![no_main] +#![no_std] + +use openprot_mctp_api::MctpClient; +use openprot_mctp_client::IpcMctpClient; + +use pw_status::Result; +use userspace::entry; +use userspace::syscall; + +use app_mctp_echo::handle; + +/// MCTP message type for echo (vendor-defined type 1). +const ECHO_MSG_TYPE: u8 = 1; + +fn mctp_echo_loop() -> Result<()> { + pw_log::info!("MCTP echo starting"); + + let client = IpcMctpClient::new(handle::MCTP); + + // Register a listener for type-1 messages + let listener = client + .listener(ECHO_MSG_TYPE) + .map_err(|_| pw_status::Error::Internal)?; + + let mut buf = [0u8; 1024]; + + loop { + // Block until a message arrives + let meta = client + .recv(listener, 0, &mut buf) + .map_err(|_| pw_status::Error::Internal)?; + + pw_log::info!( + "Echo: received {} bytes from EID {}", + meta.payload_size as u32, + meta.remote_eid as u32, + ); + + // Echo the payload back + let payload = &buf[..meta.payload_size]; + client + .send( + None, // no request handle (this is a response) + meta.msg_type, // same message type + Some(meta.remote_eid), // back to sender + Some(meta.msg_tag), // same tag + meta.msg_ic, // preserve integrity check + payload, + ) + .map_err(|_| pw_status::Error::Internal)?; + } +} + +#[entry] +fn entry() -> ! { + if let Err(e) = mctp_echo_loop() { + pw_log::error!("MCTP echo error: {}", e as u32); + let _ = syscall::debug_shutdown(Err(e)); + } + loop {} +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +}
diff --git a/target/ast1060-evb/mctp/system.json5 b/target/ast1060-evb/mctp/system.json5 new file mode 100644 index 0000000..b0a3b86 --- /dev/null +++ b/target/ast1060-evb/mctp/system.json5
@@ -0,0 +1,105 @@ +// Licensed under the Apache-2.0 license + +// AST1060-EVB MCTP Echo Server + Client Configuration +// ARM Cortex-M4 @ 200 MHz +// 768KB SRAM (640KB usable), executes from RAM +// +// Memory Layout (PMSAv7-friendly, power-of-2 aligned regions): +// 0x00000000 - 0x000004A0: Vector table + kernel annotations (1184 bytes) +// 0x000004A0 - 0x00020000: Kernel code (~126KB) +// 0x00020000 - 0x00080000: App flash (384KB: 3 apps × 128KB) +// 0x00080000 - 0x000B0000: App RAM (192KB: 3 apps × 64KB) +// 0x000B0000 - 0x000C0000: Kernel RAM (64KB) +// +// Total: ~768KB, fits within AST1060's 768KB SRAM. +{ + arch: { + type: "armv7m", + vector_table_start_address: 0x00000000, + vector_table_size_bytes: 1184, // 0x4A0 (272 vectors + thread/stack annotations) + }, + kernel: { + flash_start_address: 0x000004A0, // After vector table + annotations + flash_size_bytes: 129888, // ~126KB (ends at 0x00020000, power-of-2 boundary) + ram_start_address: 0x000B0000, // After all app RAM + ram_size_bytes: 65536, // 64KB + }, + apps: [ + // ──── I2C Server ──── + // Owns the AST1060 I2C controllers. + // The MCTP server uses I2C as its transport. + { + name: "i2c_server", + flash_size_bytes: 131072, // 128KB for server code + ram_size_bytes: 65536, // 64KB RAM (register maps + buffers) + process: { + name: "i2c server process", + objects: [ + { + name: "I2C", + type: "channel_handler", // Server side of client→server channel + }, + ], + threads: [ + { + name: "i2c server thread", + stack_size_bytes: 4096, // 4KB stack + }, + ], + }, + }, + // ──── MCTP Server ──── + // Runs the MCTP stack. Receives MCTP operations via IPC, + // uses I2C transport for on-the-wire communication. + { + name: "mctp_server", + flash_size_bytes: 131072, // 128KB for MCTP server code + ram_size_bytes: 65536, // 64KB RAM (Router state, buffers) + process: { + name: "mctp server process", + objects: [ + { + name: "MCTP", + type: "channel_handler", // Server side: echo client → MCTP server + }, + { + name: "I2C", + type: "channel_initiator", // Client side: MCTP server → I2C server + handler_app: "i2c_server", + handler_object_name: "I2C", + }, + ], + threads: [ + { + name: "mctp server thread", + stack_size_bytes: 8192, // 8KB stack (Router + fragmentation buffers) + }, + ], + }, + }, + // ──── MCTP Echo Client ──── + // Listens for MCTP type-1 messages and echoes the payload back. + { + name: "mctp_echo", + flash_size_bytes: 131072, // 128KB for echo app + ram_size_bytes: 65536, // 64KB RAM + process: { + name: "mctp echo process", + objects: [ + { + name: "MCTP", + type: "channel_initiator", // Client side: echo → MCTP server + handler_app: "mctp_server", + handler_object_name: "MCTP", + }, + ], + threads: [ + { + name: "mctp echo thread", + stack_size_bytes: 4096, // 4KB stack + }, + ], + }, + }, + ], +}
diff --git a/target/ast1060-evb/mctp/target.rs b/target/ast1060-evb/mctp/target.rs new file mode 100644 index 0000000..3cb0a2e --- /dev/null +++ b/target/ast1060-evb/mctp/target.rs
@@ -0,0 +1,38 @@ +// Licensed under the Apache-2.0 license + +//! AST1060-EVB MCTP Echo Target +//! +//! This target runs the I2C server, MCTP server, and MCTP echo application +//! as separate userspace processes communicating over IPC channels. + +#![no_std] +#![no_main] + +use cortex_m_semihosting::debug::{EXIT_FAILURE, EXIT_SUCCESS, exit}; +use target_common::{TargetInterface, declare_target}; +use {console_backend as _, entry as _}; + +pub struct Target {} + +impl TargetInterface for Target { + const NAME: &'static str = "AST1060-EVB MCTP Echo"; + + fn main() -> ! { + codegen::start(); + #[expect(clippy::empty_loop)] + loop {} + } + + fn shutdown(code: u32) -> ! { + pw_log::info!("Shutting down with code {}", code as u32); + let status = match code { + 0 => EXIT_SUCCESS, + _ => EXIT_FAILURE, + }; + exit(status); + #[expect(clippy::empty_loop)] + loop {} + } +} + +declare_target!(Target);