diff --git a/CMakeLists.txt b/CMakeLists.txt index d1cea21..fd2b210 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,12 +67,16 @@ dmod_add_library(${DMOD_MODULE_NAME} ${DMOD_MODULE_VERSION} src/dmspi.c ) +# dmgpio >= 1.1: its ioctl commands start at DMDRVI_IOCTL_CUSTOM_BASE - +# dmgpio_ioctl_cmd_set_pins_state (chip select) has a different value than +# in 1.0. Build-time only: dmspi reaches the GPIO through Dmod_Ioctl() on +# its node, so the board image must carry dmgpio >= 1.1 as well. dmod_link_modules(${DMOD_MODULE_NAME} - dmdrvi + dmdrvi@>=2.0 dmini dmhaman dm_sw_ring - dmgpio + dmgpio@>=1.1 ) target_include_directories(${DMOD_MODULE_NAME} PRIVATE diff --git a/docs/api-reference.md b/docs/api-reference.md index 38c50ab..d50a070 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -44,6 +44,13 @@ callable by name from another module (see [configuration.md](configuration.md)). | `dmdrvi_dmspi_read` | Receive bytes - drains the RX ring if configured, otherwise blocks clocking dummy bytes (master) or waiting for the external master (slave). | | `dmdrvi_dmspi_write` | Transmit bytes (full duplex at the wire level; received bytes are discarded). | | `dmdrvi_dmspi_ioctl` | Get/set configuration parameters, register an interrupt handler, run a full-duplex transfer, or reconfigure - see `dmspi_ioctl_cmd_t` in [include/dmspi_types.h](../include/dmspi_types.h). | + +`dmspi_ioctl_cmd_t` starts at `DMDRVI_IOCTL_CUSTOM_BASE` (0x1000, dmdrvi 2.0), +the range dmdrvi reserves for driver-specific commands. Any other command - +for example the class probes (`DMDRVI_IOCTL_BLOCK_GET_INFO`, +`DMDRVI_IOCTL_MONITOR_GET_POLICY`) dmdevfs sends to every node - returns +`-ENOTTY` without being logged. Before this numbering the commands started at 1; +modules sending dmspi ioctls must be rebuilt. | `dmdrvi_dmspi_flush` | Block until any in-progress transfer completes. | | `dmdrvi_dmspi_stat` | Report device metadata. | diff --git a/include/dmspi_types.h b/include/dmspi_types.h index abdc5f3..2e92d47 100644 --- a/include/dmspi_types.h +++ b/include/dmspi_types.h @@ -4,6 +4,7 @@ #include #include #include +#include "dmdrvi_ioctl.h" /** * @brief SPI instance type (1-based, e.g. 1 = SPI1) @@ -79,7 +80,10 @@ typedef enum */ typedef enum { - dmspi_ioctl_cmd_get_role = 1, /**< Get role (master/slave) */ + /* Private commands start at DMDRVI_IOCTL_CUSTOM_BASE: everything below + * it is a standard dmdrvi command (network, block, monitor) that dmdevfs + * and other generic code may send to any node - dmspi answers -ENOTTY. */ + dmspi_ioctl_cmd_get_role = DMDRVI_IOCTL_CUSTOM_BASE, /**< Get role (master/slave) */ dmspi_ioctl_cmd_set_role, /**< Set role (master/slave) */ dmspi_ioctl_cmd_get_baudrate, /**< Get requested baud rate */ dmspi_ioctl_cmd_set_baudrate, /**< Set requested baud rate */ diff --git a/src/dmspi.c b/src/dmspi.c index fa6c256..9cf0d4a 100644 --- a/src/dmspi.c +++ b/src/dmspi.c @@ -682,10 +682,11 @@ dmod_dmdrvi_dif_api_declaration(2.0, dmspi, int, _ioctl, ( dmdrvi_context_t cont return -EINVAL; } - if (command >= dmspi_ioctl_cmd_max) + /* Not a dmspi command - e.g. the class probes dmdevfs sends to every + * node: not implemented, as dmdrvi requires, and not worth a log line. */ + if (command < dmspi_ioctl_cmd_get_role || command >= dmspi_ioctl_cmd_max) { - DMOD_LOG_ERROR("Invalid ioctl command %d\n", command); - return -EINVAL; + return -ENOTTY; } if (command == dmspi_ioctl_cmd_reconfigure) diff --git a/src/port/CMakeLists.txt b/src/port/CMakeLists.txt index 1dde73b..b264dc2 100644 --- a/src/port/CMakeLists.txt +++ b/src/port/CMakeLists.txt @@ -37,10 +37,13 @@ dmod_add_library(${DMOD_MODULE_NAME} ${DMOD_MODULE_VERSION} ${COMMON_SOURCES} ) +# dmdrvi: headers only - dmspi_types.h numbers the ioctl commands from +# DMDRVI_IOCTL_CUSTOM_BASE. dmod_link_modules(${DMOD_MODULE_NAME} dm_sw_ring dmclk_port dmosi + dmdrvi@>=2.0 ) target_include_directories(${DMOD_MODULE_NAME} BEFORE PRIVATE diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fdbaa2e..30548c3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,6 +14,13 @@ target_include_directories(${DMOD_MODULE_NAME} PRIVATE target_link_libraries(${DMOD_MODULE_NAME} dmspi_if) +# dmdrvi: the DIF the ioctl test calls dmspi through (like dmdevfs); dmini: +# its configuration. +dmod_link_modules(${DMOD_MODULE_NAME} + dmdrvi@>=2.0 + dmini +) + # Register the test module with ctest - this replays the same two steps CI # runs (see .github/templates/ci.yml.template): install the test module's # dependencies with dmf-get, then execute it through dmod_loader. The test's diff --git a/tests/dmspi_test.c b/tests/dmspi_test.c index 946afca..5ac3e07 100644 --- a/tests/dmspi_test.c +++ b/tests/dmspi_test.c @@ -1,6 +1,10 @@ #define DMOD_ENABLE_REGISTRATION ON +#define ENABLE_DIF_REGISTRATIONS ON #include "dmod_test.h" #include "dmspi.h" +#include "dmdrvi.h" +#include "dmini.h" +#include #include static dmspi_config_t g_config; @@ -45,3 +49,71 @@ DMOD_TEST_STEP(dmspi_validate_config_rejects_null) { DMOD_TEST_EXPECT_FALSE(dmspi_validate_config(NULL)); } + +/* ---- ioctl numbering, through the dmdrvi DIF the way dmdevfs calls it ---- */ + +typedef struct +{ + dmod_dmdrvi_create_t create; + dmod_dmdrvi_free_t free; + dmod_dmdrvi_open_t open; + dmod_dmdrvi_close_t close; + dmod_dmdrvi_ioctl_t ioctl; +} driver_t; + +static bool get_driver(driver_t* drv) +{ + Dmod_Context_t* module = Dmod_GetModuleContext("dmspi"); + if (module == NULL) + { + return false; + } + drv->create = Dmod_GetDifFunction(module, dmod_dmdrvi_create_sig); + drv->free = Dmod_GetDifFunction(module, dmod_dmdrvi_free_sig); + drv->open = Dmod_GetDifFunction(module, dmod_dmdrvi_open_sig); + drv->close = Dmod_GetDifFunction(module, dmod_dmdrvi_close_sig); + drv->ioctl = Dmod_GetDifFunction(module, dmod_dmdrvi_ioctl_sig); + return drv->create != NULL && drv->free != NULL && drv->open != NULL && + drv->close != NULL && drv->ioctl != NULL; +} + +DMOD_TEST_STEP(dmspi_ioctl_answers_only_its_own_command_range) +{ + driver_t drv = { 0 }; + DMOD_TEST_EXPECT_TRUE(get_driver(&drv)); + if (drv.ioctl == NULL) + { + return; + } + dmini_context_t ini = dmini_create(); + dmini_parse_string(ini, "[dmspi]\ninstance=1\nrole=master\nbaudrate=1000000\nmode=0\n"); + dmdrvi_dev_num_t dev_num = { 0 }; + dmdrvi_context_t ctx = drv.create(ini, &dev_num); + DMOD_TEST_EXPECT_NOT_NULL(ctx); + void* handle = (ctx != NULL) ? drv.open(ctx, DMDRVI_O_RDWR, &dev_num) : NULL; + DMOD_TEST_EXPECT_NOT_NULL(handle); + if (handle != NULL) + { + dmspi_role_t role = dmspi_role_slave; + uint32_t probe[16] = { 0 }; + + /* Standard dmdrvi commands - dmdevfs sends the first two to every node. */ + DMOD_TEST_EXPECT_EQ(drv.ioctl(ctx, handle, DMDRVI_IOCTL_BLOCK_GET_INFO, probe), -ENOTTY); + DMOD_TEST_EXPECT_EQ(drv.ioctl(ctx, handle, DMDRVI_IOCTL_MONITOR_GET_POLICY, probe), -ENOTTY); + DMOD_TEST_EXPECT_EQ(drv.ioctl(ctx, handle, DMDRVI_IOCTL_NET_GET_MAC_ADDR, probe), -ENOTTY); + DMOD_TEST_EXPECT_EQ(drv.ioctl(ctx, handle, 0, probe), -ENOTTY); + DMOD_TEST_EXPECT_EQ(drv.ioctl(ctx, handle, -1, probe), -ENOTTY); + DMOD_TEST_EXPECT_EQ(drv.ioctl(ctx, handle, dmspi_ioctl_cmd_max, probe), -ENOTTY); + + /* dmspi's own commands, from DMDRVI_IOCTL_CUSTOM_BASE. */ + DMOD_TEST_EXPECT_EQ((int)dmspi_ioctl_cmd_get_role, DMDRVI_IOCTL_CUSTOM_BASE); + DMOD_TEST_EXPECT_EQ(drv.ioctl(ctx, handle, dmspi_ioctl_cmd_get_role, &role), 0); + DMOD_TEST_EXPECT_EQ(role, dmspi_role_master); + drv.close(ctx, handle); + } + if (ctx != NULL) + { + drv.free(ctx); + } + dmini_destroy(ini); +} diff --git a/tools/spitest/CMakeLists.txt b/tools/spitest/CMakeLists.txt index baa6a5f..ac86316 100644 --- a/tools/spitest/CMakeLists.txt +++ b/tools/spitest/CMakeLists.txt @@ -25,9 +25,11 @@ dmod_add_executable(spitest ${DMOD_MODULE_VERSION} # dmosi is a separate module - fetch its headers for the worker thread # used by the loopback test. The real implementation is resolved at # runtime against the system's own instance, same as dmtty/tools/tty's -# dmtty_attach() - no static linkage. +# dmtty_attach() - no static linkage. dmdrvi: headers only, for +# DMDRVI_IOCTL_CUSTOM_BASE (the base of dmspi_ioctl_cmd_t). dmod_link_modules(spitest dmosi + dmdrvi@>=2.0 ) # dmspi_types.h lives in this same repo (no dmf-get fetch needed) - it only