Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |

Expand Down
6 changes: 5 additions & 1 deletion include/dmspi_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "dmdrvi_ioctl.h"

/**
* @brief SPI instance type (1-based, e.g. 1 = SPI1)
Expand Down Expand Up @@ -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 */
Expand Down
7 changes: 4 additions & 3 deletions src/dmspi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions src/port/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
72 changes: 72 additions & 0 deletions tests/dmspi_test.c
Original file line number Diff line number Diff line change
@@ -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 <errno.h>
#include <string.h>

static dmspi_config_t g_config;
Expand Down Expand Up @@ -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);
}
4 changes: 3 additions & 1 deletion tools/spitest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading