Christian Werner e0a78871ca feat(kernel): Add GPIO descriptor-based initialization and self-test script
Replace legacy gpio_to_desc() with GPIO descriptor resolution by chip label
and hardware number, ensuring compatibility with kernels using non-zero
gpiochip bases. Added `selftest.sh` for non-persistent module testing, which
performs a hardware self-test and verifies wiring before connecting the real
bus. Included a detailed README.md documenting the self-test process.
2026-06-19 00:23:45 +02:00

79 lines
3.6 KiB
Markdown

# `iec_listener` kernel module
Commodore IEC **listener** (printer, default address 4) for the Raspberry Pi
Zero 2 W. The timing-critical IEC handshake runs in this kernel module; received
bytes and bus events are pushed to userspace through `/dev/iec0`.
Build/deploy, pinning, GPIO-descriptor and timing details live in
[`../docs/kernel-notes.md`](../docs/kernel-notes.md). This README documents the
**self-test**, which is the first thing to run after loading the module on real
hardware.
## Self-test
The module exposes a hardware self-test via the `IEC_IOC_SELFTEST` ioctl on
`/dev/iec0`. It drives the **DATA** line (the only line the Pi asserts) low and
reads it back, releases it, then samples **ATN / CLK / RESET**, returning a
bitmask of line states. Run it **with the C64 disconnected** to check the wiring
before connecting the real bus (see PLAN.md §10).
### Running it
`selftest.sh` is non-persistent (nothing is installed into `/lib/modules`, no
autoload): it checks vermagic, loads the module, runs the self-test, and always
unloads it again.
```bash
sudo ./selftest.sh # auto-finds ./ or ~/iec_listener.ko, address 4
sudo ./selftest.sh ~/iec_listener.ko --address 5 # runs with ~/iec_listener.ko, address 5
```
It exits non-zero unless the result is a full pass (`0x1F`), so it is usable in
scripts/CI.
### Result bitmask
The ioctl returns a `u32`; a full pass is **`0x1F`** (all five bits set).
| Bit | Value | Meaning | Source |
|-----|-------|---------|--------|
| `DATA_ASSERT_OK` | `0x01` | DATA read **low** while the module drives it low | Pi drive + sense path |
| `DATA_FLOAT_OK` | `0x02` | DATA read **high** after release (Hi-Z) | external pull-up on DATA |
| `ATN_RELEASED` | `0x04` | ATN high (idle) | line state |
| `CLK_RELEASED` | `0x08` | CLK high (idle) | line state |
| `RESET_RELEASED` | `0x10` | RESET high (idle) | line state |
Constants are defined in [`iec_listener.h`](iec_listener.h); the ioctl number is
`_IOR('I', 3, __u32)` = `0x80044903`.
### Interpreting failures
- **`DATA_ASSERT_OK` missing** → driving DATA low doesn't read back low: level
shifter wired backwards/inverting, wrong pin, or the sense path is broken.
This is the only bit that is fully internal to the Pi — if it fails, suspect
the module/build or the DATA wiring, not pull-ups.
- **`DATA_FLOAT_OK` missing** → DATA stays low after release: missing/weak
pull-up on DATA, or the pin didn't return to input.
- **`CLK_RELEASED` missing** → CLK reads low at idle: no pull-up on CLK, short,
or a swapped signal.
- **`ATN_RELEASED` / `RESET_RELEASED` missing** → that line reads low at idle:
short, missing pull-up, or swapped wiring.
### ⚠️ Bare-board caveat
With **nothing connected**, the expected result is **`0x15`**, *not* a fault:
- `DATA_ASSERT_OK` (`0x01`) passes — it's internal to the Pi.
- `ATN_RELEASED` (`0x04`) and `RESET_RELEASED` (`0x10`) pass **only because
GPIO2/GPIO3 have fixed ~1.8 kΩ pull-ups built into the BCM2710 SoC** (they are
the I²C0 pins; the pull-ups can't be disabled). They read high even with
nothing wired, so on a bare board these two bits prove **nothing** about your
wiring.
- `DATA_FLOAT_OK` (`0x02`) and `CLK_RELEASED` (`0x08`) read low because GPIO17/18
have no such built-in pull-up and nothing is attached.
So on a bare board the only meaningful signal is `DATA_ASSERT_OK`. A full `0x1F`
is only reachable once the level shifter (with its CLK/DATA pull-ups) is wired
and powered. To make ATN/RESET meaningful, briefly ground each at the connector
and confirm the corresponding bit *drops*.