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
36 changes: 29 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,48 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Build dmsdio core module
- name: Build dmsdio core module and tests
run: |
mkdir -p build
cd build
# dmsdio links against dmsdio_port_if (headers only),
# not dmsdio_port itself, so building only these two targets
# never touches src/port/*/port.c - safe to build natively here
# regardless of which CPU family the port defaults to.
cmake .. -DDMOD_MODE=DMOD_MODULE -DDMOD_TOOLS_NAME=arch/x86_64
# The core module and its test run natively here, but DMOD_CPU_FAMILY
# has to stay at its default (stm32f7): dmsdio needs dmgpio's
# headers (card-detect friend) and dmgpio only publishes stm32
# packages, so a DMOD_CPU_FAMILY=x86_64 header fetch would 404.
# That is the only reason DMOD_TOOLS_NAME is overridden by hand -
# everywhere else the tools follow from DMOD_CPU_FAMILY.
cmake .. -DDMOD_TOOLS_NAME=arch/x86_64
cmake --build . --target dmsdio test_dmsdio

- name: Build simulated-card dmsdio_port (x86_64)
run: |
# src/port/x86_64 is not a hardware port: it simulates an SD card
# at the command level so the whole protocol in dmsdio can run on
# this runner. DMOD_CPU_FAMILY=x86_64 alone selects the host tools
# (src/port/x86_64/config.cmake).
mkdir -p build_port_x86_64
cd build_port_x86_64
cmake .. -DDMOD_CPU_FAMILY=x86_64
cmake --build . --target dmsdio_port
cp dmf/dmsdio_port.dmf dmf/dmsdio_port.dmd ../build/dmf/

- name: Verify module files
run: |
echo "Checking for module files..."
ls -lh build/dmf/
test -f build/dmf/dmsdio.dmf
test -f build/dmf/dmsdio_version.txt
test -f build/dmf/test_dmsdio.dmf
test -f build/dmf/dmsdio_port.dmf
echo "Module files present"

- name: Run tests with dmod_loader
run: |
export DMOD_DMF_DIR=$(pwd)/build/dmf
# Runtime dependencies of dmsdio/test_dmsdio (see build/dmf/*.dmd).
# dmsdio_port is the simulated card built above - it is never
# published for x86_64, so it is not fetched here.
dmf-get install dmini -y
dmod_loader build/dmf/test_dmsdio.dmf

discover-cpu-families:
Expand All @@ -58,9 +77,12 @@ jobs:
- name: List available CPU families
id: list-families
run: |
# Find all CPU families by looking for directories containing port.c
# Find all CPU families by looking for directories containing port.c.
# x86_64 is the simulated card used by the test job above, not a
# hardware family.
CPU_FAMILIES=$(find src/port -mindepth 2 -maxdepth 2 -name "port.c" -type f -printf '%h\n' | \
xargs -n1 basename | \
grep -vx x86_64 | \
sort | \
jq -R -s -c 'split("\n") | map(select(length > 0))')

Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ jobs:
- name: List available CPU families
id: list-families
run: |
# x86_64 is the simulated SD card used by the tests - never released.
CPU_FAMILIES=$(find src/port -mindepth 2 -maxdepth 2 -name "port.c" -type f -printf '%h\n' | \
xargs -n1 basename | \
grep -vx x86_64 | \
sort | \
jq -R -s -c 'split("\n") | map(select(length > 0))')

Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ build/
*.tmp
*.swp
*~

# Local CI builds
build_*/
26 changes: 20 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ set(DMOD_BUILD_TEMPLATES OFF CACHE BOOL "Build templates")
FetchContent_MakeAvailable(dmod)

project(dmsdio VERSION ${DMOD_MODULE_VERSION}
DESCRIPTION "dmsdio DMOD library module"
DESCRIPTION "SD memory card driver (SDSC/SDHC/SDXC) for DMOD"
LANGUAGES C CXX)

set(DMOD_DIR ${dmod_SOURCE_DIR} CACHE PATH "DMOD source directory")
Expand Down Expand Up @@ -65,6 +65,25 @@ set(DMOD_DIF_IMPLS )
dmod_add_library(${DMOD_MODULE_NAME} ${DMOD_MODULE_VERSION}
# List of source files - can include C and C++ files
src/dmsdio.c
src/dmsdio_card.c
src/dmsdio_cmd.c
src/dmsdio_config.c
src/dmsdio_decode.c
src/dmsdio_detect.c
src/dmsdio_ident.c
src/dmsdio_io.c
src/dmsdio_xfer.c
)

# dmdrvi 2.1: the host node implements the monitor ioctls (presence is
# monitored by dmdevfs' dmdevmon service, see docs/api-reference.md).
# dmgpio 1.1: its ioctls start at DMDRVI_IOCTL_CUSTOM_BASE - the card detect
# read (dmgpio_ioctl_cmd_get_high/low_pins_state) has other values in 1.0.
dmod_link_modules(${DMOD_MODULE_NAME}
dmdrvi@>=2.1
dmini
dmosi
dmgpio@>=1.1
)

target_include_directories(${DMOD_MODULE_NAME} PRIVATE
Expand All @@ -75,11 +94,6 @@ target_include_directories(${DMOD_MODULE_NAME}_if INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/include
)

# To depend on another module's headers, use:
# dmod_link_modules(${DMOD_MODULE_NAME}
# other_module@1.0
# )

add_subdirectory(src/port)
target_link_libraries(${DMOD_MODULE_NAME} ${DMOD_MODULE_NAME}_port_if)
enable_testing()
Expand Down
17 changes: 15 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# #############################################################################
#
# This is an example of a simple library module.
# dmsdio - SD memory card driver (SDSC/SDHC/SDXC).
#
# The CMake build is the reference build: it also fetches the headers of
# dmdrvi, dmini, dmosi and dmgpio (dmod_link_modules) and builds
# dmsdio_port and the tests. Pass their include directories through
# DMOD_INC_DIRS when building with make.
#
# #############################################################################
DMOD_DIR=@DMOD_DIR@
Expand All @@ -24,7 +29,15 @@ DMOD_MODULE_VERSION=0.1
DMOD_AUTHOR_NAME=Patryk Kubiak

# The list of C sources
DMOD_CSOURCES=src/dmsdio.c
DMOD_CSOURCES=src/dmsdio.c \
src/dmsdio_card.c \
src/dmsdio_cmd.c \
src/dmsdio_config.c \
src/dmsdio_decode.c \
src/dmsdio_detect.c \
src/dmsdio_ident.c \
src/dmsdio_io.c \
src/dmsdio_xfer.c

# The list of C++ sources
DMOD_CXXSOURCES=
Expand Down
132 changes: 101 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,30 @@
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![CI](https://github.com/choco-technologies/dmsdio/actions/workflows/ci.yml/badge.svg)](https://github.com/choco-technologies/dmsdio/actions/workflows/ci.yml)

dmsdio DMOD library module.
SD memory card driver for DMOD: SDSC v1/v2, SDHC and SDXC cards exposed as
a 64-bit block device through dmdrvi 2.0.

## Description

TODO: describe what this module does.
- Complete, hardware-independent SD protocol: identification (CMD0, CMD8,
ACMD41, CMD2, CMD3, CMD9, CMD7), OCR/CID/CSD/SCR/SD Status decoding,
400 kHz identification, default speed and CMD6 High Speed, 1-bit/4-bit
bus negotiation.
- SDSC byte addressing and SDHC/SDXC block addressing; full 64-bit
capacity and offsets.
- Single/multi-block read and write with CMD12, CMD13 ready polling, erase
and discard, retry with recovery, precise errors.
- Arbitrary byte offsets: direct full-sector transfers, read-modify-write
for partial sectors.
- dmdrvi device model: persistent host node `/dev/dmsdio0` plus the
hot-plugged card node `/dev/dmsdio0/0`; handles are invalidated with a
card generation identifier.
- No threads in the driver: the host node implements the dmdrvi 2.1
monitor contract (`DMDRVI_IOCTL_MONITOR_GET_POLICY`/`_EVENT`/`_REFRESH`).
dmdevfs reports it as a `monitor` device and the generic
[dmdevmon](https://github.com/choco-technologies/dmdevfs/blob/main/services/dmdevmon/README.md) service drives it - card detect
edge interrupt, settling, optional polling. The card node is reported as a
`block` device (e.g. for automount).

## Building

Expand All @@ -31,59 +50,86 @@ make DMOD_MODE=DMOD_MODULE DMOD_DIR=/path/to/dmod

## Testing

Tests are built automatically alongside the module (see `tests/`). Once built,
run them with `ctest`:
`tests/dmsdio_test.c` runs the complete protocol against the simulated SD
card of the x86_64 port (`src/port/x86_64`): SDSC v1/v2, SDHC and SDXC
identification, 64-bit capacity and offsets, sector and unaligned I/O,
erase, write protection, CRC/timeout/malformed-response errors, retries,
removal during a transfer, stale handles and the monitor commands. The test
uses the driver the way dmdevfs does - through the dmdrvi DIF - implements
the dmdrvi MAL to observe hot-plug notifications and calls the monitor
ioctls the way dmdevmon does.

```bash
cd build
ctest --output-on-failure
```

`ctest` installs the test module's dependencies with `dmf-get` and then runs
it through `dmod_loader`. To run it manually instead:
# core module and test: built for the host, but DMOD_CPU_FAMILY stays at its
# default (stm32f7) because dmgpio only publishes stm32 headers
mkdir -p build && cd build
cmake .. -DDMOD_TOOLS_NAME=arch/x86_64
cmake --build . --target dmsdio test_dmsdio
cd ..

# simulated card port
mkdir -p build_port_x86_64 && cd build_port_x86_64
cmake .. -DDMOD_CPU_FAMILY=x86_64
cmake --build . --target dmsdio_port
cp dmf/dmsdio_port.dmf dmf/dmsdio_port.dmd ../build/dmf/
cd ..

```bash
export DMOD_DMF_DIR=$(pwd)/build/dmf
dmf-get install -d ${DMOD_DMF_DIR}/test_dmsdio-local.dmd -y
dmf-get install dmini -y
dmod_loader build/dmf/test_dmsdio.dmf
```

## Usage

<TBD>

This library module provides functions that can be used by other modules:
dmsdio is a dmdrvi driver loaded by dmdevfs from an ini configuration (see
[docs/configuration.md](docs/configuration.md)):

```c
#include "dmsdio.h"
```ini
[dmsdio]
instance=1
bus_width=4
```

Card presence is monitored by dmdevfs' dmdevmon service: install its
`dmdevmon.rules` and `dmdevmon@.ini` into libsystemd's rules and units
directories, and libsystemd starts `dmdevmon@dmsdio0` as soon as dmdevfs
reports `/dev/dmsdio0`. Its first `REFRESH` identifies a card already in the
slot; `/dev/dmsdio0/0` then appears and can be used with the regular file
API using 64-bit offsets, `DMDRVI_IOCTL_BLOCK_GET_INFO` reports the
geometry. Without a monitor no card is ever identified.

## API

| Function | Description |
|----------|-------------|
| `dmsdio_create()` | Create a new `dmsdio_t` instance. |
| `dmsdio_destroy()` | Destroy an instance created by `_create()`. |
| `dmsdio_is_valid()` | Check whether a handle is a valid instance. |
| Item | Description |
|------|-------------|
| dmdrvi DIF (`dmdrvi_create`, `_open`, `_read`, `_write`, `_ioctl`, `_flush`, `_stat`, `_friend_changed`, ...) | Device model, see [docs/api-reference.md](docs/api-reference.md) |
| `DMDRVI_IOCTL_MONITOR_GET_POLICY` / `_EVENT` / `_REFRESH` | dmdrvi monitor contract on the host node |
| `dmsdio_ioctl_cmd_get_host_info` / `_get_card_info` | Driver specific ioctls |
| `dmsdio_decode_csd()` / `_cid()` / `_scr()` / `_ssr()` | Register decoders |

See [include/dmsdio.h](include/dmsdio.h) for the full
declarations and [docs/api-reference.md](docs/api-reference.md) for the
complete reference.
See [include/dmsdio.h](include/dmsdio.h) and
[include/dmsdio_types.h](include/dmsdio_types.h) for the declarations and
[docs/api-reference.md](docs/api-reference.md) for the complete reference.

## Documentation

See the `docs/` directory:

- **[api-reference.md](docs/api-reference.md)** - Complete API documentation
- **[configuration.md](docs/configuration.md)** - Configuration keys and card detect
- **[port-implementation.md](docs/port-implementation.md)** - Port API contract

View documentation using `dmf-man dmsdio`.

## Hardware Port

This module ships two DMOD modules: the architecture-independent
`dmsdio` and `dmsdio_port`, which contains the
architecture-specific implementation. The active architecture is selected via
`DMOD_CPU_FAMILY` (default: `stm32f7`):
`dmsdio` (all SD protocol logic) and `dmsdio_port`, which contains the
protocol-free host controller primitives. The active architecture is
selected via `DMOD_CPU_FAMILY` (default: `stm32f7`): `stm32f4` (SDIO) and
`stm32f7` (SDMMC1) share their implementation in `src/port/stm32_common`;
`x86_64` is the simulated card used by the tests. Board configurations for
STM32F746G-DISCO and STM32F407G-DISC1 are in [configs/](configs/README.md).

```bash
cmake .. -DDMOD_CPU_FAMILY=stm32f7
Expand All @@ -96,8 +142,21 @@ another architecture. Port-specific files:
├── include/dmsdio_port.h
├── src/port/
│ ├── CMakeLists.txt
│ └── stm32f7/
│ ├── stm32_common/ # SDIO/SDMMC host shared by F4 and F7
│ │ ├── stm32_common.c # lifecycle, clock, commands, IRQ
│ │ ├── stm32_common.h
│ │ └── stm32_data.c # data path: DMA2 lease (dmdma), FIFO for small reads
│ ├── stm32f4/
│ │ ├── config.cmake
│ │ └── port.c
│ ├── stm32f7/
│ │ ├── config.cmake
│ │ └── port.c
│ └── x86_64/ # simulated SD card (tests only)
│ ├── config.cmake
│ ├── dmsdio_mock.h
│ ├── mock_card.c/.h
│ ├── mock_registers.c
│ └── port.c
└── dmsdio_port.dmr
```
Expand All @@ -107,9 +166,20 @@ another architecture. Port-specific files:
dmsdio/
├── docs/ # Documentation (markdown format)
├── include/ # Public headers
│ └── dmsdio.h
│ ├── dmsdio.h
│ ├── dmsdio_port.h
│ └── dmsdio_types.h
├── src/
│ └── dmsdio.c
│ ├── dmsdio.c # dmdrvi device model
│ ├── dmsdio_card.c # attach/detach, card generation
│ ├── dmsdio_cmd.c # command layer, errno mapping
│ ├── dmsdio_config.c # ini configuration
│ ├── dmsdio_decode.c # CID/CSD/SCR/SD Status decoders
│ ├── dmsdio_detect.c # card detect pin, lock-free MONITOR_EVENT
│ ├── dmsdio_ident.c # identification and bus negotiation
│ ├── dmsdio_io.c # byte-offset I/O, read-modify-write
│ ├── dmsdio_xfer.c # block transfers, erase, retry/recovery
│ └── port/
├── tests/
│ ├── CMakeLists.txt
│ └── dmsdio_test.c
Expand Down
19 changes: 19 additions & 0 deletions configs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# dmsdio Configuration Files

Board configurations loaded by dmdevfs to bring the SD card slot up. Each
file configures the SD bus pins through dmgpio (AF12, pull-ups on CMD and
D0-D3), the optional card-detect GPIO (`friend_role=card_detect`) and the
dmsdio device itself, all in one `friends_group`. The pin sources are cited
in each file - cross-check them against your own board revision before
flashing.

```
configs/
└── board/
├── stm32f407g-disc1/
│ └── sdio.ini # external microSD breakout on SDIO
└── stm32f746g-disco/
└── sdmmc1.ini # on-board microSD slot on SDMMC1
```

See [../docs/configuration.md](../docs/configuration.md) for every dmsdio key.
Loading
Loading