观察拒绝、借用、取消与排空¶
执行本 host 教程,观察真实公共 SPI API 与 Native provider 的行为。它说明软件所有权和失败路径,不建立物理 DMA 时序。完整可执行夹具是维护中的 GoogleTest 目标;模型控制仅替代外部 IRQ 与线上进度。GoogleTest/GoogleMock 和 C++17 仅用于 host;生产固件保持 C11。
构建并运行完整夹具¶
从仓库根目录执行,先具备 host 工具和锁定依赖:
python tools/dev/dev.py configure --preset native-debug
python tools/dev/dev.py build --preset native-debug --parallel 4 --target nexus_native_async_test nexus_native_factory_test nexus_uart_owner_mock_test
不加过滤器运行每个 binary,将 fresh XML 保存在新目录:
mkdir -p /tmp/nexus-io-tutorial
build/native-debug/bin/nexus_native_async_test --gtest_output=xml:/tmp/nexus-io-tutorial/async.xml
build/native-debug/bin/nexus_native_factory_test --gtest_output=xml:/tmp/nexus-io-tutorial/factory.xml
build/native-debug/bin/nexus_uart_owner_mock_test --gtest_output=xml:/tmp/nexus-io-tutorial/owner.xml
三个命令都必须退出零,并报告非零的通过用例数。这些完整目标执行属于教程范围,不是全部目标的 commit TDD 资格。保留所需报告后才能删除教程目录。
观察正常准入与拒绝路径¶
TEST_F(NativeAsync, SPIBorrowWaitsForWireCompletionAndOtherEndpointRejects) {
nx_native_spi_state_t controller = {};
nx_native_spi_endpoint_state_t first = {};
nx_native_spi_endpoint_state_t second = {};
const nx_spi_port_t bus = {&nx_native_spi_ops, &controller};
const nx_spi_endpoint_t first_api = {&nx_native_spi_endpoint_ops, &first};
const nx_spi_endpoint_t second_api = {&nx_native_spi_endpoint_ops, &second};
ASSERT_EQ(nx_native_spi_port_configure_instance(&controller), NX_SUCCESS);
ASSERT_EQ(nx_native_spi_configure_instance(&first, &controller, nullptr, 0),
NX_SUCCESS);
ASSERT_EQ(
nx_native_spi_configure_instance(&second, &controller, nullptr, 0),
NX_SUCCESS);
nx_native_spi_model_async_configure(&bus, false);
WakeFacts facts;
const nx_irq_wake_t wake = {&facts, record_wake, false};
ASSERT_EQ(nx_spi_port_attach_wake(&bus, &wake, 5), NX_SUCCESS);
uint8_t tx[] = {17, 29};
uint8_t rx[2] = {};
nx_spi_request_t request = {};
nx_spi_request_t rejected = {};
nx_request_initialize(&request.base);
nx_request_initialize(&rejected.base);
ASSERT_EQ(nx_spi_request_prepare(&request, tx, rx, 2, 100), NX_SUCCESS);
ASSERT_EQ(nx_spi_request_prepare(&rejected, tx, nullptr, 2, 100),
NX_SUCCESS);
ASSERT_EQ(nx_spi_endpoint_submit(&first_api, &request), NX_SUCCESS);
EXPECT_TRUE(nx_native_spi_model_cs_active(&first_api));
EXPECT_EQ(facts.calls, 0u);
EXPECT_EQ(nx_spi_endpoint_submit(&second_api, &rejected), NX_ERROR_BUSY);
EXPECT_EQ(nx_request_state(&rejected.base), NX_REQUEST_READY);
nx_native_spi_model_irq_step(&bus);
nx_native_spi_model_irq_step(&bus);
nx_spi_port_service(&bus);
EXPECT_EQ(nx_request_state(&request.base), NX_REQUEST_ACTIVE);
EXPECT_EQ(facts.calls, 0u);
EXPECT_TRUE(nx_native_spi_model_cs_active(&first_api));
nx_native_spi_model_irq_step(&bus);
nx_spi_port_service(&bus);
EXPECT_EQ(nx_request_state(&request.base), NX_REQUEST_SETTLED);
EXPECT_FALSE(nx_native_spi_model_cs_active(&first_api));
EXPECT_EQ(facts.calls, 1u);
EXPECT_FALSE(facts.masked);
EXPECT_EQ(rx[0], 17);
EXPECT_EQ(rx[1], 29);
tx[0] = 99;
nx_native_spi_model_irq_step(&bus);
EXPECT_EQ(rx[0], 17);
EXPECT_EQ(nx_spi_port_stop(&bus), NX_SUCCESS);
nx_native_spi_model_irq_step(&bus);
EXPECT_EQ(facts.calls, 1u);
}
第一个 endpoint 接受请求,调用方保持请求和缓冲区有效。第二个 endpoint 返回 NX_ERROR_BUSY,其请求仍为 READY;该拒绝不保留借用。模拟 payload 传输推进后,在模拟线上完成步骤之前不会发布 SETTLED。Service 与 IRQ 进度保持显式;等待客户端不能假设存在隐藏 worker。
观察取消与失败排空¶
TEST_F(NativeAsync, SPIQuarantineRetainsBorrowAndStopUntilDrain) {
nx_native_spi_state_t controller = {};
nx_native_spi_endpoint_state_t endpoint = {};
const nx_spi_port_t bus = {&nx_native_spi_ops, &controller};
const nx_spi_endpoint_t endpoint_api = {&nx_native_spi_endpoint_ops,
&endpoint};
ASSERT_EQ(nx_native_spi_port_configure_instance(&controller), NX_SUCCESS);
ASSERT_EQ(
nx_native_spi_configure_instance(&endpoint, &controller, nullptr, 0),
NX_SUCCESS);
nx_native_spi_model_async_configure(&bus, false);
nx_native_spi_model_drain_hold(&bus, true);
uint8_t tx[] = {1, 2};
nx_spi_request_t request = {};
nx_request_initialize(&request.base);
ASSERT_EQ(nx_spi_request_prepare(&request, tx, nullptr, 2, 100),
NX_SUCCESS);
ASSERT_EQ(nx_spi_endpoint_submit(&endpoint_api, &request), NX_SUCCESS);
nx_native_spi_model_irq_step(&bus);
EXPECT_EQ(nx_spi_port_cancel(&bus, &request), NX_SUCCESS);
nx_spi_port_service(&bus);
EXPECT_EQ(nx_request_state(&request.base), NX_REQUEST_QUARANTINED);
EXPECT_EQ(nx_spi_port_stop(&bus), NX_ERROR_BUSY);
EXPECT_EQ(nx_spi_request_prepare(&request, tx, nullptr, 2, 100),
NX_ERROR_STATE);
nx_native_spi_model_drain_hold(&bus, false);
nx_spi_port_service(&bus);
EXPECT_EQ(nx_request_state(&request.base), NX_REQUEST_SETTLED);
nx_result_t result = NX_SUCCESS;
size_t count = 0;
ASSERT_EQ(nx_request_result(&request.base, &result, &count), NX_SUCCESS);
EXPECT_EQ(result, NX_ERROR_CANCELLED);
EXPECT_EQ(count, 1u);
EXPECT_EQ(
nx_spi_endpoint_transfer(&endpoint_api, tx, nullptr, 1, 100, &count),
NX_ERROR_STATE);
EXPECT_EQ(nx_spi_port_stop(&bus), NX_SUCCESS);
}
Cancel 请求终止。故意保持排空阻塞时,请求进入 QUARANTINED,stop 返回 BUSY,prepare 拒绝复用。随后模型提供实际排空进度,service 发布 SETTLED,结果观察提供取消结果与已传输前缀。解除模型阻塞是测试控制,不是通用生产恢复 API。
生成的 factory 夹具也测试持有隔离 UART 借用时的平台 stop。UART owner 夹具通过 GoogleMock 依赖端口检查拒绝提交和 payload 保留。将请求存储移入应用前先阅读所有权。
FIG-06 表示当前 Core helper 转移和 provider 的证明义务。I/O 生命周期矩阵限定模型建立的范围。IRQ 提示、稳定 slot epoch、observer detach 和 join 需遵循 OS 停机指南。