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
7 changes: 4 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,16 @@ dmod_add_library(${DMOD_MODULE_NAME} ${DMOD_MODULE_VERSION}
)

# Link to other DMOD modules (will be downloaded on build time)
# dmdrvi 2.4: DMDRVI_IOCTL_MONITOR_* / _GFX_* / _INPUT_* (node reporting) and
# dmdrvi 2.6: DMDRVI_IOCTL_MONITOR_* / _GFX_* / _INPUT_* / _AUDIO_* (node
# reporting; _AUDIO_* since 2.6) and
# DMDRVI_IOCTL_DEVFS_GET_FRIEND (answered by dmdevfs itself), see
# src/dmdevfs.c.
# libsystemd: nodes are reported as "monitor"/"block"/"display"/"input"
# libsystemd: nodes are reported as "monitor"/"block"/"display"/"input"/"audio"
# devices so device rules can start services (dmdevmon, automount, dmview)
# for them.
dmod_link_modules(${DMOD_MODULE_NAME}
dmfsi
dmdrvi@>=2.4
dmdrvi@>=2.6
dmini
dmlist
dmosi
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ DMOD Driver File System - A driver-based file system module for embedded systems
- **DMFSI Compatible**: Implements the standard DMOD file system interface
- **DMVFS Integration**: Can be mounted as a file system in DMVFS
- **Modular Design**: Built on DMOD framework for easy integration
- **libsystemd Device Reporting**: Reports monitored, block, display and input device nodes to libsystemd, so device rules can start services for them (see [libsystemd Device Reporting](#libsystemd-device-reporting))
- **libsystemd Device Reporting**: Reports monitored, block, display, input and audio device nodes to libsystemd, so device rules can start services for them (see [libsystemd Device Reporting](#libsystemd-device-reporting))
- **Partition Nodes**: Exposes the MBR (incl. extended/logical) and GPT partitions of block devices as their own nodes (`<node>p<N>`), with offsets translated by DMDEVFS (see [Partition Nodes](#partition-nodes))
- **dmdevmon Service**: Generic monitor service that drives every node implementing the dmdrvi monitor contract - presence detection, hot-plug and polling without driver threads (see [services/dmdevmon](services/dmdevmon/README.md))

Expand Down Expand Up @@ -151,7 +151,7 @@ parameter2 = value2
- **`driver_order`**: An integer that controls when the driver is configured relative to other drivers. All drivers with `driver_order = 0` (the default when the field is omitted) are configured before any driver with `driver_order = 1`, which in turn is configured before `driver_order = 2`, and so on. Use this to force a driver to be configured after another one when there is no dmod dependency between them to infer the order from automatically. Negative values are allowed and are configured before the default group: `driver_order = -1` runs before `0`, and `-2` before `-1`. Use a negative value to put a driver ahead of everything left at the default, without having to raise `driver_order` in every other configuration file.
- **`friends_group`**: Groups configurations that form one logical peripheral. Once device paths are available, DMDEVFS reports every other member of the group through the optional driver's `dmdrvi_friend_changed()` callback.
- **`friend_role`**: Optional application-defined role of this member inside its friends group, such as `chip_select`. It lets consumers distinguish several devices of the same type without relying on section names or generated paths.
- **`report`**: What this device's nodes may be reported to libsystemd as: `all` (default), `monitor`, `block`, `display`, `input` or `none`. Hot-plugged nodes inherit it. See [libsystemd Device Reporting](#libsystemd-device-reporting).
- **`report`**: What this device's nodes may be reported to libsystemd as: `all` (default), `monitor`, `block`, `display`, `input`, `audio` or `none`. Hot-plugged nodes inherit it. See [libsystemd Device Reporting](#libsystemd-device-reporting).

Any additional parameters in the configuration file are passed to the driver's initialization function. The interpretation of these parameters depends on the specific driver implementation.

Expand Down Expand Up @@ -215,6 +215,7 @@ driver and asks what it is:
| `DMDRVI_IOCTL_BLOCK_GET_INFO` | `block` | `[class=block] start=automount@%name` - a block device |
| `DMDRVI_IOCTL_GFX_GET_INFO` | `display` | `[class=display] start=dmview@%name` - a display (any driver implementing the standard graphics ioctls) |
| `DMDRVI_IOCTL_INPUT_GET_INFO` | `input` | `[class=input] start=...@%name` - an input device (touch panel, mouse, buttons) |
| `DMDRVI_IOCTL_AUDIO_GET_INFO` | `audio` | `[class=audio] start=...@%name` - an audio device (any driver implementing the standard audio ioctls, e.g. a codec such as dmwm8994) |

A node can be reported under several classes, or under none (then nothing
happens). The class names are defined in `dmdevfs.h` (`DMDEVFS_CLASS_*`). Each report is `libsystemd_notify_device_added(class, name, path)`:
Expand Down
5 changes: 4 additions & 1 deletion include/dmdevfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ extern "C" {
* automount;
* - "display": DMDRVI_IOCTL_GFX_GET_INFO - a display, e.g. for dmview;
* - "input": DMDRVI_IOCTL_INPUT_GET_INFO - an input device (touch panel,
* mouse, buttons).
* mouse, buttons);
* - "audio": DMDRVI_IOCTL_AUDIO_GET_INFO - an audio codec or other audio
* device, e.g. for a player or mixer service.
*
* The device name is the node path relative to the mount with '/' replaced
* by '_' (unit names cannot contain '/'): "/dmsdio0/0" -> "dmsdio0_0". The
Expand All @@ -43,6 +45,7 @@ extern "C" {
#define DMDEVFS_CLASS_BLOCK "block"
#define DMDEVFS_CLASS_DISPLAY "display"
#define DMDEVFS_CLASS_INPUT "input"
#define DMDEVFS_CLASS_AUDIO "audio"

#ifdef __cplusplus
}
Expand Down
27 changes: 24 additions & 3 deletions src/dmdevfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,15 @@
#define DMDEVFS_REPORT_BLOCK 0x02u
#define DMDEVFS_REPORT_DISPLAY 0x04u
#define DMDEVFS_REPORT_INPUT 0x08u
#define DMDEVFS_REPORT_AUDIO 0x10u
#define DMDEVFS_REPORT_ALL (DMDEVFS_REPORT_MONITOR | DMDEVFS_REPORT_BLOCK | \
DMDEVFS_REPORT_DISPLAY | DMDEVFS_REPORT_INPUT)
DMDEVFS_REPORT_DISPLAY | DMDEVFS_REPORT_INPUT | \
DMDEVFS_REPORT_AUDIO)

/**
* @brief Configuration key limiting what a device's nodes are reported as
*
* "all" (default), "monitor", "block", "display", "input" or "none" - e.g.
* "all" (default), "monitor", "block", "display", "input", "audio" or "none" - e.g.
* "none" for raw flash that must never be picked up by automount. Read from
* the same place as friends_group (see configure_driver()); hot-plugged nodes
* inherit it.
Expand Down Expand Up @@ -2935,6 +2937,10 @@ static uint8_t read_report_mask( dmini_context_t config_ctx, const char* section
{
return DMDEVFS_REPORT_INPUT;
}
if (strcmp(value, DMDEVFS_CLASS_AUDIO) == 0)
{
return DMDEVFS_REPORT_AUDIO;
}

DMOD_LOG_WARN("Unknown %s=%s for driver %s, reporting all classes\n", DMDEVFS_REPORT_KEY, value, driver_name);
return DMDEVFS_REPORT_ALL;
Expand Down Expand Up @@ -2962,7 +2968,8 @@ static void build_report_name( const char* node_path, char* out, size_t out_size
*
* Opens the node read-only through its driver, issues
* DMDRVI_IOCTL_MONITOR_GET_POLICY, DMDRVI_IOCTL_BLOCK_GET_INFO,
* DMDRVI_IOCTL_GFX_GET_INFO and/or DMDRVI_IOCTL_INPUT_GET_INFO (only those
* DMDRVI_IOCTL_GFX_GET_INFO, DMDRVI_IOCTL_INPUT_GET_INFO and/or
* DMDRVI_IOCTL_AUDIO_GET_INFO (only those
* allowed by @p wanted) and closes it again. A driver that cannot be opened,
* or answers none of them, yields 0 - nothing is reported for it.
*
Expand Down Expand Up @@ -3009,6 +3016,12 @@ static uint8_t probe_node_classes( driver_node_t* node, uint8_t wanted )
{
classes |= DMDEVFS_REPORT_INPUT;
}
dmdrvi_audio_info_t audio;
if ((wanted & DMDEVFS_REPORT_AUDIO) != 0 &&
dmdrvi_ioctl(node->driver_context, handle, DMDRVI_IOCTL_AUDIO_GET_INFO, &audio) == 0)
{
classes |= DMDEVFS_REPORT_AUDIO;
}

dmdrvi_close(node->driver_context, handle);
return classes;
Expand Down Expand Up @@ -3085,6 +3098,10 @@ static void report_node_added( const char* abs_path, driver_node_t* node )
{
report_node_class(DMDEVFS_CLASS_INPUT, name, abs_path, true);
}
if ((classes & DMDEVFS_REPORT_AUDIO) != 0)
{
report_node_class(DMDEVFS_CLASS_AUDIO, name, abs_path, true);
}
}

/**
Expand All @@ -3106,6 +3123,10 @@ static void report_node_removed( driver_node_t* node )

path_t name;
build_report_name(node->path, name, sizeof(name));
if ((classes & DMDEVFS_REPORT_AUDIO) != 0)
{
report_node_class(DMDEVFS_CLASS_AUDIO, name, NULL, false);
}
if ((classes & DMDEVFS_REPORT_INPUT) != 0)
{
report_node_class(DMDEVFS_CLASS_INPUT, name, NULL, false);
Expand Down
10 changes: 5 additions & 5 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ target_include_directories(${DMOD_MODULE_NAME} PRIVATE
target_compile_definitions(${DMOD_MODULE_NAME} PRIVATE
DMDEVFS_TEST_FIXTURES_DIR="fixtures"
)
# dmdrvi: DMDRVI_IOCTL_DEVFS_GET_FRIEND (2.4)
# dmdrvi: DMDRVI_IOCTL_DEVFS_GET_FRIEND (2.4), DMDRVI_IOCTL_AUDIO_* (2.6)
dmod_link_modules(${DMOD_MODULE_NAME}
dmdrvi@>=2.4
dmdrvi@>=2.6
dmfsi
dmhaman
dmosi
Expand All @@ -124,7 +124,7 @@ target_compile_definitions(${DMOD_MODULE_NAME} PRIVATE
)
dmod_link_modules(${DMOD_MODULE_NAME}
dmfsi
dmdrvi@>=2.3
dmdrvi@>=2.6
dmhaman
dmosi
)
Expand All @@ -143,7 +143,7 @@ target_compile_definitions(${DMOD_MODULE_NAME} PRIVATE
)
dmod_link_modules(${DMOD_MODULE_NAME}
dmfsi
dmdrvi@>=2.3
dmdrvi@>=2.6
dmhaman
dmosi
libsystemd
Expand All @@ -163,7 +163,7 @@ target_compile_definitions(${DMOD_MODULE_NAME} PRIVATE
)
dmod_link_modules(${DMOD_MODULE_NAME}
dmfsi
dmdrvi@>=2.3
dmdrvi@>=2.6
dmhaman
dmosi
libsystemd
Expand Down
13 changes: 13 additions & 0 deletions tests/fixtures/config/mock.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
; /dev/dmdevfs_mockdrv3 display -> reported as "display"
; /dev/dmdevfs_mockdrv4 input device -> reported as "input"
; /dev/dmdevfs_mockdrv5 display, report=input -> not reported
; /dev/dmdevfs_mockdrv6 audio codec -> reported as "audio"
; /dev/dmdevfs_mockdrv7 audio, report=block -> not reported
; mock3 and mock4 are one panel (friends_group=panel): a display and its
; touch input (friend_role=touch).
; Each can hot-plug a block child /dev/dmdevfs_mockdrv<major>/0.
Expand Down Expand Up @@ -41,3 +43,14 @@ driver_name=dmdevfs_mockdrv
major=5
display=true
report=input

[mock6]
driver_name=dmdevfs_mockdrv
major=6
audio=true

[mock7]
driver_name=dmdevfs_mockdrv
major=7
audio=true
report=block
3 changes: 3 additions & 0 deletions tests/fixtures/rules/devices.ini
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ start=dsp@%name

[class=input]
start=inp@%name

[class=audio]
start=aud@%name
3 changes: 3 additions & 0 deletions tests/fixtures/units/aud@.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
; Started for every node dmdevfs reports as "audio" (rules/devices.ini)
exec=dmdevfs_testsvc
args=%v
2 changes: 1 addition & 1 deletion tests/mockdrv/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ target_include_directories(${DMOD_MODULE_NAME}_if INTERFACE
)

dmod_link_modules(${DMOD_MODULE_NAME}
dmdrvi@>=2.3
dmdrvi@>=2.6
dmini
dmlist
)
19 changes: 16 additions & 3 deletions tests/mockdrv/mockdrv.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
* Test driver for the libsystemd node reporting of dmdevfs.
*
* Configuration keys: `major` (device number), `monitor` ("true": the host
* node implements the monitor contract), `display` / `input` ("true": the
* host node answers DMDRVI_IOCTL_GFX_GET_INFO / DMDRVI_IOCTL_INPUT_GET_INFO,
* as a display or an input device would). Its policy comes from
* node implements the monitor contract), `display` / `input` / `audio`
* ("true": the host node answers DMDRVI_IOCTL_GFX_GET_INFO /
* DMDRVI_IOCTL_INPUT_GET_INFO / DMDRVI_IOCTL_AUDIO_GET_INFO, as a display,
* an input device or an audio codec would). Its policy comes from
* `event_handler`, `settle_ms` and `poll_interval_ms`; MONITOR_EVENT and
* MONITOR_REFRESH calls are only counted (DMDEVFS_MOCKDRV_IOCTL_GET_STATS),
* as are the opens of the host node and of its child.
Expand All @@ -37,6 +38,7 @@ struct dmdrvi_context
bool monitor;
bool display;
bool input;
bool audio;
bool plugged;
dmdrvi_monitor_policy_t policy;
volatile uint32_t events; /* MONITOR_EVENT calls */
Expand Down Expand Up @@ -215,6 +217,7 @@ dmod_dmdrvi_dif_api_declaration(2.0, dmdevfs_mockdrv, dmdrvi_context_t, _create,
ctx->monitor = (strcmp(monitor, "true") == 0);
ctx->display = (strcmp(dmini_get_string(config, NULL, "display", "false"), "true") == 0);
ctx->input = (strcmp(dmini_get_string(config, NULL, "input", "false"), "true") == 0);
ctx->audio = (strcmp(dmini_get_string(config, NULL, "audio", "false"), "true") == 0);
ctx->plugged = false;
ctx->events = 0;
ctx->refreshes = 0;
Expand Down Expand Up @@ -370,6 +373,16 @@ static int host_ioctl(dmdrvi_context_t context, int command, void* arg)
((dmdrvi_input_info_t*)arg)->width = 480;
((dmdrvi_input_info_t*)arg)->height = 272;
return 0;
case DMDRVI_IOCTL_AUDIO_GET_INFO:
if (!context->audio || arg == NULL)
{
return -ENOTTY;
}
memset(arg, 0, sizeof(dmdrvi_audio_info_t));
((dmdrvi_audio_info_t*)arg)->config.sample_rate_hz = 48000;
((dmdrvi_audio_info_t*)arg)->config.channels = 2;
((dmdrvi_audio_info_t*)arg)->config.sample_bits = 16;
return 0;
case DMDEVFS_MOCKDRV_IOCTL_SET_MEDIA:
if (context->plugged || arg == NULL)
{
Expand Down
25 changes: 21 additions & 4 deletions tests/test_dmdevfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
* dmdevfs_mockdrv (mockdrv/). libsystemd has the units from fixtures/units
* and the rules from fixtures/rules loaded, so every report dmdevfs makes
* starts a real process (dmdevfs_testsvc) as mon@<name> ("monitor"),
* blk@<name> ("block"), dsp@<name> ("display") or inp@<name> ("input"), and
* blk@<name> ("block"), dsp@<name> ("display"), inp@<name> ("input") or
* aud@<name> ("audio"), and
* every removal stops it again - both observable
* through libsystemd_status(). A unit that was never reported does not exist
* at all (-ENOENT).
Expand Down Expand Up @@ -77,12 +78,13 @@ void dmod_test_setup(void)
if (g_mount != NULL)
{
g_fs.mounted(g_mount, "/dev");
/* Every mount starts mon@dmdevfs_mockdrv0, dsp@dmdevfs_mockdrv3 and
* inp@dmdevfs_mockdrv4 - let them come up before any step (or the
* teardown) may stop them. */
/* Every mount starts mon@dmdevfs_mockdrv0, dsp@dmdevfs_mockdrv3,
* inp@dmdevfs_mockdrv4 and aud@dmdevfs_mockdrv6 - let them come up
* before any step (or the teardown) may stop them. */
wait_ready("mon@dmdevfs_mockdrv0", "/dev/dmdevfs_mockdrv0");
wait_ready("dsp@dmdevfs_mockdrv3", "/dev/dmdevfs_mockdrv3");
wait_ready("inp@dmdevfs_mockdrv4", "/dev/dmdevfs_mockdrv4");
wait_ready("aud@dmdevfs_mockdrv6", "/dev/dmdevfs_mockdrv6");
}
}

Expand Down Expand Up @@ -199,6 +201,21 @@ DMOD_TEST_STEP(dmdevfs_reports_display_and_input_nodes)
DMOD_TEST_EXPECT_TRUE(wait_state("inp@dmdevfs_mockdrv4", DMOSI_PROCESS_STATE_TERMINATED));
}

DMOD_TEST_STEP(dmdevfs_reports_audio_nodes)
{
DMOD_TEST_EXPECT_NOT_NULL(g_mount);
DMOD_TEST_EXPECT_TRUE(wait_ready("aud@dmdevfs_mockdrv6", "/dev/dmdevfs_mockdrv6"));
DMOD_TEST_EXPECT_EQ(unit_state("dsp@dmdevfs_mockdrv6"), -1); /* audio only */
DMOD_TEST_EXPECT_EQ(unit_state("mon@dmdevfs_mockdrv6"), -1);
DMOD_TEST_EXPECT_EQ(unit_state("aud@dmdevfs_mockdrv7"), -1); /* report=block */
DMOD_TEST_EXPECT_EQ(unit_state("aud@dmdevfs_mockdrv3"), -1); /* display, no audio */
DMOD_TEST_EXPECT_EQ(unit_state("aud@dmdevfs_mockdrv2"), -1); /* plain node */

g_fs.deinit(g_mount);
g_mount = NULL;
DMOD_TEST_EXPECT_TRUE(wait_state("aud@dmdevfs_mockdrv6", DMOSI_PROCESS_STATE_TERMINATED));
}

/** DMDRVI_IOCTL_DEVFS_GET_FRIEND on @p node through dmdevfs. */
static int get_friend(const char* node, dmdrvi_devfs_friend_t* f)
{
Expand Down
Loading