ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
gpio_map.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file gpio_map.h
6 * @brief Browser GPIO pin-mapper / diagnostics (PROTOCORE_ENABLE_GPIO_MAP).
7 *
8 * Exposes a compile-time table of GPIO pins (number, label, configured direction,
9 * live level) as JSON so a browser diag panel can show the pin map and toggle
10 * outputs. The live read and write go through protocore_platform_gpio_* where a pin seam
11 * exists; the JSON serializer and the control-POST parser are pure and host-tested.
12 * No allocation: the pin table is caller-owned and the JSON is written into a
13 * caller buffer.
14 *
15 * @author Douglas Quigg (dstroy0)
16 * @date 2026
17 */
18
19#ifndef PROTOCORE_GPIO_MAP_H
20#define PROTOCORE_GPIO_MAP_H
21
22#include "protocore_config.h" // the entry point: protocore_types.h for the widths
23
24#if PROTOCORE_ENABLE_GPIO_MAP
25
27
28/**
29 * @brief Configured direction of a mapped pin (how the panel renders / drives it).
30 *
31 * The members carry the type's own name, `PROTOCORE_GPIO_DIR_`, and not a bare `PROTOCORE_GPIO_`. The board
32 * profiles spell their pin-mode argument `PROTOCORE_GPIO_IN` / `PROTOCORE_GPIO_OUT` as `#define`s, and a macro
33 * rewrites a token before the compiler sees a declaration at all (docs/SYMBOLS.md section 2), so a
34 * member sharing one of those names is replaced by its number wherever both headers reach one
35 * translation unit. The two encodings are also different numbers, which is what made the collision
36 * silent: the profile numbers a direction 0/1/2/3 as IN/OUT/PULLUP/PULLDOWN, this enum in
37 * declaration order.
38 */
39typedef enum PROTO_ENUM_PACKED
40{
41 PROTOCORE_GPIO_DIR_IN = 0, ///< read-only input.
42 PROTOCORE_GPIO_DIR_IN_PULLUP, ///< input with internal pull-up.
43 PROTOCORE_GPIO_DIR_IN_PULLDOWN, ///< input with internal pull-down.
44 PROTOCORE_GPIO_DIR_OUT, ///< output (drivable from the panel).
45} protocore_gpio_dir;
46
47/** @brief One mapped GPIO pin. */
48typedef struct
49{
50 uint8_t pin; ///< GPIO number.
51 const char *label; ///< human label (null-terminated, caller-owned).
52 protocore_gpio_dir dir; ///< pin direction.
53 uint8_t level; ///< live level (0 / 1); filled by protocore_gpio_read.
54} protocore_gpio_pin;
55
56// ---------------------------------------------------------------------------
57// Host-testable core
58// ---------------------------------------------------------------------------
59
60/** @brief The pin table a call walks, and the pin it names. */
61typedef struct
62{
63 const protocore_gpio_pin *pins; ///< the table a call reads
64 protocore_gpio_pin *pins_rw; ///< the same table, where a sample writes levels back into it
65 uint8_t count; ///< how many pins it holds
66 uint8_t pin; ///< the pin a write or a lookup names
67 uint8_t level; ///< the level a write drives
68 protocore_gpio_dir dir; ///< the direction a name lookup names
69 const char *path; ///< the route the map is served on
70} GpioArgs;
71
72/** @brief The request body a set parses, and where its two fields land. */
73typedef struct
74{
75 const char *body; ///< the submitted body
76 size_t len; ///< its length
77 uint8_t *pin_out; ///< where the parsed pin lands
78 uint8_t *level_out; ///< where the parsed level lands
79} GpioParseArgs;
80
81/** @brief Where a report is written. */
82typedef struct
83{
84 char *out; ///< where the JSON lands
85 uint32_t cap; ///< how much room it has
86} GpioOutArgs;
87
88/**
89 * @brief The pin map and its HTTP surface.
90 *
91 * A caller sets the members a call takes, invokes it through ::GpioMap, and reads the outcome off
92 * the same handle. The pin table is the caller's.
93 *
94 * @var GpioMapNs::args the pin table a call walks, and the pin it names
95 * @var GpioMapNs::parse_args the request body a set parses, and where its fields land
96 * @var GpioMapNs::out_args where a report is written
97 * @var GpioMapNs::ok a call's true/false outcome
98 * @var GpioMapNs::text the direction name a lookup reports
99 * @var GpioMapNs::n bytes a report wrote, or < 0 when it did not fit
100 * @var GpioMapNs::dir_name the wire name for a direction
101 * @var GpioMapNs::json serialize the pin table
102 * @var GpioMapNs::parse_set parse a set request into its pin and level
103 * @var GpioMapNs::is_output whether the named pin is configured as an output
104 * @var GpioMapNs::begin_pins drive every pin in the table to its configured direction
105 * @var GpioMapNs::sample read every input pin's level back into the table
106 * @var GpioMapNs::write drive one output pin
107 * @var GpioMapNs::begin install the map's route and arm its pins
108 *
109 * No storage member: the pin table is the caller's and the pins themselves live in the part.
110 */
111typedef struct
112{
113 GpioArgs args;
114 GpioParseArgs parse_args;
115 GpioOutArgs out_args;
116 proto_bool ok;
117 const char *text;
118 int32_t n;
119} GpioMapVars;
120
121/** @brief The operands and the outcome. */
122extern GpioMapVars GpioMapV;
123
124/** @brief The entries. */
125typedef struct
126{
127 void (*const dir_name)(uint8_t *work);
128 void (*const json)(uint8_t *work);
129 void (*const parse_set)(uint8_t *work);
130 void (*const is_output)(uint8_t *work);
131 void (*const begin_pins)(uint8_t *work);
132 void (*const sample)(uint8_t *work);
133 void (*const write)(uint8_t *work);
134 void (*const begin)(uint8_t *work);
135} GpioMapNs;
136
137// What the table binds, defined once in the .c and taking one parameter each: everything
138// else an entry needs is an operand in GpioMapV or a region of the borrow at a fixed offset.
139void protocore_gpio_map_dir_name(uint8_t *work);
140void protocore_gpio_map_json(uint8_t *work);
141void protocore_gpio_map_parse_set(uint8_t *work);
142void protocore_gpio_map_is_output(uint8_t *work);
143void protocore_gpio_map_begin_pins(uint8_t *work);
144void protocore_gpio_map_sample(uint8_t *work);
145void protocore_gpio_map_write(uint8_t *work);
146void protocore_gpio_map_begin(uint8_t *work);
147
148// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
149// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
150// `GpioMap.dir_name(work)` resolves to a named function and becomes a DIRECT call. An extern table
151// leaves the call indirect and the symbol live at every level, -O2 -flto included.
152static const GpioMapNs GpioMap __attribute__((unused)) = {
153 .dir_name = protocore_gpio_map_dir_name,
154 .json = protocore_gpio_map_json,
155 .parse_set = protocore_gpio_map_parse_set,
156 .is_output = protocore_gpio_map_is_output,
157 .begin_pins = protocore_gpio_map_begin_pins,
158 .sample = protocore_gpio_map_sample,
159 .write = protocore_gpio_map_write,
160 .begin = protocore_gpio_map_begin,
161};
162
164
165#endif // PROTOCORE_ENABLE_GPIO_MAP
166
167#endif // PROTOCORE_GPIO_MAP_H
PROTO_ENUM_PACKED
Application protocol spoken on a listener port or connection slot.
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97