drivers/relays: hardened + 100% MC/DC (Tier-1, first driver)
Bring relays.c into warden-sdk/drivers/ behind a relay_io seam (ADR-0005): the sysfs plumbing is an injectable backend, the gpio root is $WARDEN_GPIO_ROOT- overridable. Host harness (drivers/relays/test/) reaches 100% MC/DC (40/40 conditions, 100% lines) by combining fake-io unit tests (logic branches incl. export->node-appears) with real-sysfs integration tests over a scratch tree (backend fopen/stat branches). CI `mcdc` job enforces 100% across drivers/*/test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
d4acc9f3b3
commit
1752dea7b6
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* The two onboard relays (86-Panel bottom board).
|
||||
*
|
||||
* RELAY1 <- GPIO1_A0 (sysfs gpio 32)
|
||||
* RELAY2 <- GPIO1_A1 (sysfs gpio 33)
|
||||
*
|
||||
* gpiochip1 base is 32; A0..A7 are offsets 0..7. Nothing in the DT claims these
|
||||
* (there is no relay driver), so we drive them through /sys/class/gpio directly.
|
||||
* An off-by-one here would toggle the neighbouring RS485 pair (GPIO1_B0/B1)
|
||||
* under a running Modbus master, so the numbers come from the schematic, not
|
||||
* inference. State is never assumed at startup: a relay may be holding a
|
||||
* contactor closed, and deciding it should be off because we just booted is not
|
||||
* this module's call.
|
||||
*
|
||||
* Hardened for warden-sdk: the sysfs plumbing sits behind a `relay_io` seam so
|
||||
* the decision logic runs and is measured to 100% MC/DC on the host, and the
|
||||
* gpio root is `$WARDEN_GPIO_ROOT`-overridable so the real backend can run
|
||||
* against a scratch tree in an integration test.
|
||||
*/
|
||||
#ifndef WARDEN_RELAYS_H
|
||||
#define WARDEN_RELAYS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define WARDEN_RELAY_COUNT 2
|
||||
|
||||
/** Display name for @p idx, e.g. "Relay 1"; "-" if out of range. */
|
||||
const char * warden_relay_name(uint32_t idx);
|
||||
|
||||
/** Current output state, false if it cannot be read. */
|
||||
bool warden_relay_get(uint32_t idx);
|
||||
|
||||
/** Drive the output. Exports and sets the direction on first use. */
|
||||
void warden_relay_set(uint32_t idx, bool on);
|
||||
|
||||
/** True if the GPIO is exported and usable — the page says so if it is not. */
|
||||
bool warden_relay_available(uint32_t idx);
|
||||
|
||||
/* --- Hardware-abstraction seam ---------------------------------------------
|
||||
* The four filesystem primitives the relay logic needs. Production binds the
|
||||
* sysfs backend (the default); a unit test binds an in-memory fake that can
|
||||
* model export -> node-appears, which a passive scratch tree cannot. */
|
||||
struct relay_io {
|
||||
bool (*exists)(const char *path); /* stat(path)==0 */
|
||||
void (*write)(const char *path, const char *value); /* best-effort */
|
||||
bool (*read_line)(const char *path, char *buf, size_t n); /* a line was read */
|
||||
bool (*read_int)(const char *path, int *out); /* one int parsed */
|
||||
};
|
||||
|
||||
/** Swap the io backend (test hook). Pass NULL to restore the sysfs default. */
|
||||
void warden_relay__set_io(const struct relay_io *io);
|
||||
|
||||
#endif /* WARDEN_RELAYS_H */
|
||||
Reference in New Issue
Block a user