# `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 detects the running kernel, picks the matching module, loads it, runs the self-test, and always unloads it again. A released package ships several kernel builds side by side: ``` comodore-iec-emu/ ├── selftest.sh └── modules/ ├── iec_listener_1-6.12.93-1+rpt1.ko # built for 6.12.x (bookworm) └── iec_listener_1-6.18.34-1+rpt1.ko # built for 6.18.x (trixie) ``` You don't pick the file yourself: the script reads `uname -r`, then scans `modules/` (and a few fallback locations) and selects the `.ko` whose **vermagic** matches the running kernel. Matching is by vermagic rather than filename because the packaged name carries a Debian epoch/revision (`1:6.12.93-1+rpt1`) that `uname -r` (`6.12.93+rpt-rpi-v8`) does not. ```bash sudo ./selftest.sh # auto-selects the matching module, address 4 sudo ./selftest.sh --address 5 # same, address 5 sudo ./selftest.sh ~/iec_listener.ko --address 5 # force a specific module file ``` If no module matches the running kernel, the script lists the modules it found (with the kernel each was built for) and exits — rebuild for the current kernel, or pass a path explicitly. 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*.