Skip to content

Number ioctl commands from DMDRVI_IOCTL_CUSTOM_BASE, answer the rest with -ENOTTY - #37

Merged
JohnAmadis merged 2 commits into
masterfrom
claude/dmgpio-ioctl-enotty
Sep 29, 2026
Merged

JohnAmadis merged 2 commits into
masterfrom
claude/dmgpio-ioctl-enotty

Conversation

@JohnAmadis

@JohnAmadis JohnAmadis commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Part of choco-technologies/dmod-ecosystem#17. Fixes the misclassified GPIO nodes reported on STM32F746G-DISCO in choco-technologies/dmsdio#2.

Breaking: release as dmgpio 2.0, together with dmspi 2.0 (choco-technologies/dmspi#6) and dmsdio (choco-technologies/dmsdio#2).

The ioctl command values change. Consumers send them through Dmod_Ioctl() on the GPIO node, so dmgpio never appears in their .dmd. dmgpio@>=2.0 is only a build-time requirement, and nothing prevents mixing versions on the board:

  • dmgpio 2.0 with dmspi 1.x breaks software chip select. dmspi sends 1, which dmgpio now rejects with -ENOTTY.
  • dmspi 2.0 with dmgpio 1.0 works only by accident: 1.0 truncates 0x1001 to the byte 1.

The board image (dmod-boot flash.dmd) has to take all three together.

Problem

With dmdevfs ≥ 1.1, every GPIO node was reported as both a monitor and a block device. That meant one dmdevmon instance per pin (~40 on STM32F746G-DISCO, each logging refresh failed (-22)) plus a partition scan. On top of that, every probe toggled the pins.

  1. Truncated command. dmgpio_dmdrvi_ioctl() switched on (dmgpio_ioctl_cmd_t)command. arm-none-eabi uses short enums (sizeof == 1), so GCC truncates the command to a byte (uxtb r3, r2 before tbb). DMDRVI_IOCTL_BLOCK_GET_INFO (0x100) and DMDRVI_IOCTL_MONITOR_GET_POLICY (0x200) both became 0 = toggle_pins and returned 0. A host build (4-byte enums) never shows this.
  2. Command range. dmgpio numbered its commands from 0. dmdrvi reserves everything below DMDRVI_IOCTL_CUSTOM_BASE (0x1000) for standard commands, which generic code may send to any node. As a result, 1–4 were also DMDRVI_IOCTL_NET_*.

Fix

  • dmgpio_ioctl_cmd_t now starts at DMDRVI_IOCTL_CUSTOM_BASE.
    • dmgpio_types.h includes dmdrvi_ioctl.h.
    • dmgpio and dmgpio_port (headers only, via dmgpio_port.h) link dmdrvi@>=2.0.
  • switch (command) on the int.
  • Every other command returns -ENOTTY without a log entry, as dmdrvi requires. dmdevfs asks every node.
  • README and docs/api-reference.md show the real commands (they listed ones dmgpio does not have), the numbering and the return values.

stm32f7 disassembly after the change: only 0x1000..0x1004 reach the jump table. Everything else, including 0x100/0x200, returns -25 = -ENOTTY:

33c: f5a2 5280 sub.w r2, r2, #4096
340: 2a04      cmp   r2, #4
342: d832      bhi.n 3aa            ; -> mvn.w r0, #24  (-ENOTTY)
344: e8df f002 tbb   [pc, r2]

Consumers

Across the choco-technologies driver repositories, only two modules send dmgpio ioctls:

dmuart, dmspi, dmfmc and dmeth use dmgpio only for pin muxing in .ini board files, and dmeth mentions it only in a comment. None of them needs a rebuild.

Test plan

  • stm32f4/stm32f7 builds (the CI matrix) with no warnings. The disassembly shows the new range and the -ENOTTY path.
  • Root cause reproduced: sizeof(enum) == 1 with arm-none-eabi-gcc -mcpu=cortex-m7, and uxtb in the released dmdrvi_dmgpio_ioctl.
  • dmspi and dmsdio build against a local dmgpio 2.0 package from this branch. test_dmspi passes 6/6 and test_dmsdio 41/41.
  • On target, with default report=all:
    • GPIO nodes are no longer reported as monitor/block;
    • pins do not toggle at dmdevfs mount;
    • no "Unknown ioctl command" errors;
    • dmsdio card detect and dmspi software CS still work.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TgkwHQKqf6c4DzypSKzZT7

dmgpio_dmdrvi_ioctl() switched on (dmgpio_ioctl_cmd_t)command. On
arm-none-eabi enums are short (sizeof == 1 here), so the cast kept only
the low byte: GCC emits `uxtb` before the jump table. dmdevfs probes
every node with DMDRVI_IOCTL_BLOCK_GET_INFO (0x100) and
DMDRVI_IOCTL_MONITOR_GET_POLICY (0x200); both became 0 -
dmgpio_ioctl_cmd_toggle_pins - so each probe toggled the pins and
returned 0, and every GPIO node was reported as a block and a monitor
device (a dmdevmon instance and a partition scan per pin). The host
build (4-byte enums) never showed it.

Switch on the int command instead, and answer anything that is not a
dmgpio command with -ENOTTY, as dmdrvi requires, without logging it -
dmdevfs asks every node.

docs/api-reference.md listed commands dmgpio does not have; it now
shows the real dmgpio_ioctl_cmd_t and the return values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TgkwHQKqf6c4DzypSKzZT7
dmdrvi reserves everything below DMDRVI_IOCTL_CUSTOM_BASE (0x1000) for
standard commands - network 0x01.., block 0x100.., monitor 0x200.. -
that generic code such as dmdevfs may send to any node. dmgpio numbered
its own commands from 0, so 1-4 were also DMDRVI_IOCTL_NET_*: a network
probe sent to a GPIO node would have set pins or installed an interrupt
handler.

dmgpio_ioctl_cmd_t now starts at DMDRVI_IOCTL_CUSTOM_BASE, and every
standard command gets -ENOTTY. dmgpio_types.h includes dmdrvi_ioctl.h
for the base, so dmgpio_port (whose header includes dmgpio_types.h)
links dmdrvi >= 2.0 for its headers too; both pin dmdrvi >= 2.0, the
first version with the base.

This changes the command values: modules that send dmgpio ioctls
(dmsdio, dmspi) must be rebuilt, and the release has to be dmgpio 2.0
so that binaries built against 1.x (which require dmgpio < 2.0) are
never paired with it.

README and docs/api-reference.md show the real commands and the new
numbering.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TgkwHQKqf6c4DzypSKzZT7
@JohnAmadis JohnAmadis changed the title Answer unknown ioctls with -ENOTTY; stop truncating the command Number ioctl commands from DMDRVI_IOCTL_CUSTOM_BASE, answer the rest with -ENOTTY Sep 29, 2026
@JohnAmadis
JohnAmadis merged commit 7244161 into master Sep 29, 2026
3 checks passed
JohnAmadis pushed a commit to choco-technologies/dmsdio that referenced this pull request Sep 29, 2026
dmgpio 2.0 numbers its ioctls from DMDRVI_IOCTL_CUSTOM_BASE
(choco-technologies/dmgpio#37), so the card detect read
(dmgpio_ioctl_cmd_get_high/low_pins_state) has new values: link
dmgpio >= 2.0. This is a build-time requirement only - dmsdio talks to
the GPIO through Dmod_Ioctl() on its node, so dmsdio.dmd does not list
dmgpio and the board image has to carry dmgpio 2.0 together with this
build.

DMSDIO_IOCTL_BASE was a literal 0x1000; it is now dmdrvi's
DMDRVI_IOCTL_CUSTOM_BASE itself. dmsdio_types.h includes dmdrvi_ioctl.h
for it, so dmsdio_port fetches the dmdrvi headers too - in the same,
single dmod_link_modules() call as its other modules, because every call
rewrites the target's version requirements file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TgkwHQKqf6c4DzypSKzZT7
JohnAmadis pushed a commit to choco-technologies/dmspi that referenced this pull request Sep 29, 2026
choco-technologies/dmgpio#37 was released as dmgpio 1.1, not 2.0, so
dmgpio@>=2.0 could not be resolved and CI had no dmgpio headers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TgkwHQKqf6c4DzypSKzZT7
JohnAmadis pushed a commit to choco-technologies/dmsdio that referenced this pull request Sep 29, 2026
choco-technologies/dmgpio#37 was released as dmgpio 1.1, not 2.0, so
dmgpio@>=2.0 could not be resolved and CI had no dmgpio headers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TgkwHQKqf6c4DzypSKzZT7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants