ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
fins.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 fins.h
6 * @brief Omron FINS frame codec (PROTOCORE_ENABLE_FINS) - zero-heap command/response builder +
7 * parser for the Factory Interface Network Service (FINS/UDP), so a device can talk
8 * to an Omron PLC over the shipped UDP transport.
9 *
10 * A FINS message is a 10-octet header then the command code and data:
11 * @code
12 * ICF RSV GCT DNA DA1 DA2 SNA SA1 SA2 SID MRC SRC [params / data...]
13 * @endcode
14 * - ICF: bit 6 = command(0)/response(1), bit 0 = response required(0)/not(1), bit 7 = use
15 * gateway. RSV = 0, GCT = 0x02. DNA/DA1/DA2 = destination net/node/unit; SNA/SA1/SA2 =
16 * source; SID = service id (echoed in the response).
17 * - MRC/SRC are the main/sub command code. A response inserts a 2-octet end code
18 * (MRES/SRES) before its data; MRES = SRES = 0 means normal completion.
19 * - Multi-octet command parameters (addresses, counts) are big-endian.
20 *
21 * FINS/UDP carries this frame directly (UDP provides integrity, so there is no checksum);
22 * FINS/TCP would prepend its own header. This is the message codec; the send is the app's.
23 *
24 * @author Douglas Quigg (dstroy0)
25 * @date 2026
26 */
27
28#ifndef PROTOCORE_FINS_H
29#define PROTOCORE_FINS_H
30
31#include "protocore_config.h" // the entry point: protocore_types.h for the widths
32
33#if PROTOCORE_ENABLE_FINS
34
36
37// This module holds nothing between calls, so it carves no borrow and states none. An entry
38// takes one all the same, and never reads it, so every namespace in the tree is invoked the
39// same way.
40
41#define FINS_HEADER_SIZE 10
42
43#define FINS_ICF_COMMAND 0x80 ///< command, response required, gateway
44#define FINS_ICF_RESPONSE 0xC0 ///< response
45#define FINS_ICF_NO_RESPONSE 0x01 ///< OR into ICF: response not required
46
47// Common command codes (MRC, SRC).
48#define FINS_MRC_MEMORY_AREA 0x01
49#define FINS_SRC_MEMORY_AREA_READ 0x01
50#define FINS_SRC_MEMORY_AREA_WRITE 0x02
51#define FINS_MRC_OPERATING_MODE 0x04
52#define FINS_SRC_RUN 0x01
53#define FINS_SRC_STOP 0x02
54
55/** @brief The operating mode requested by a RUN (0401) command. */
56typedef enum PROTO_ENUM_PACKED
57{
58 FINS_RUN_MODE_MONITOR = 0x02, ///< MONITOR mode (program runs, online edits allowed)
59 FINS_RUN_MODE_RUN = 0x04, ///< RUN mode (program runs, no online edits)
60} FinsRunMode;
61
62/** @brief The 10-octet FINS routing header. */
63typedef struct
64{
65 uint8_t icf;
66 uint8_t rsv;
67 uint8_t gct;
68 uint8_t dna;
69 uint8_t da1;
70 uint8_t da2; ///< destination network / node / unit
71 uint8_t sna;
72 uint8_t sa1;
73 uint8_t sa2; ///< source network / node / unit
74 uint8_t sid; ///< service id
75} FinsHeader;
76
77/** @brief A parsed command (request side). @ref params points INTO the source buffer. */
78typedef struct
79{
80 FinsHeader header;
81 uint8_t mrc;
82 uint8_t src;
83 const uint8_t *params;
84 size_t params_len;
85} FinsCommand;
86
87/** @brief A parsed response. @ref data points INTO the source buffer. */
88typedef struct
89{
90 FinsHeader header;
91 uint8_t mrc;
92 uint8_t src; ///< echoed command code
93 uint8_t mres;
94 uint8_t sres; ///< end code (0/0 = normal completion)
95 const uint8_t *data;
96 size_t data_len;
97} FinsResponse;
98
99/** @brief What build_command takes: buf, cap, h, mrc, src, params, ... */
100typedef struct
101{
102 uint8_t *buf;
103 size_t cap;
104 const FinsHeader *h;
105 uint8_t mrc;
106 uint8_t src;
107 const uint8_t *params;
108 size_t params_len;
109} FinsBuildCommandArgs;
110
111/** @brief What build_memory_area_read takes: buf, cap, h, area, ... */
112typedef struct
113{
114 uint8_t *buf;
115 size_t cap;
116 const FinsHeader *h;
117 uint8_t area;
118 uint16_t address;
119 uint8_t bit;
120 uint16_t count;
121} FinsBuildMemoryAreaReadArgs;
122
123/** @brief What build_memory_area_write takes: buf, cap, h, area, ... */
124typedef struct
125{
126 uint8_t *buf;
127 size_t cap;
128 const FinsHeader *h;
129 uint8_t area;
130 uint16_t address;
131 uint8_t bit;
132 uint16_t count;
133 const uint8_t *data;
134 size_t data_len;
135} FinsBuildMemoryAreaWriteArgs;
136
137/** @brief What build_run takes: buf, cap, h, mode. */
138typedef struct
139{
140 uint8_t *buf;
141 size_t cap;
142 const FinsHeader *h;
143 FinsRunMode mode;
144} FinsBuildRunArgs;
145
146/** @brief What build_stop takes: buf, cap, h. */
147typedef struct
148{
149 uint8_t *buf;
150 size_t cap;
151 const FinsHeader *h;
152} FinsBuildStopArgs;
153
154/** @brief What parse_command takes: buf, len, out. */
155typedef struct
156{
157 const uint8_t *buf;
158 size_t len;
159 FinsCommand *out;
160} FinsParseCommandArgs;
161
162/** @brief What parse_response takes: buf, len, out. */
163typedef struct
164{
165 const uint8_t *buf;
166 size_t len;
167 FinsResponse *out;
168} FinsParseResponseArgs;
169
170/**
171 * @brief Omron FINS frame codec (PROTOCORE_ENABLE_FINS) - zero-heap command/response builder + parser for the Factory
172 * Interface Network Service (FINS/UDP), so a device can talk to an Omron PLC over the shipped UDP transport.
173 *
174 * A caller sets the members a call takes, invokes it through ::Fins with the bytes it runs
175 * out of, and reads the outcome off the same handle.
176 *
177 * Fins.build_command_args.buf = ...;
178 * Fins.build_command_args.cap = ...;
179 * Fins.build_command_args.h = ...;
180 * Fins.build_command_args.mrc = ...;
181 * Fins.build_command_args.src = ...;
182 * Fins.build_command_args.params = ...;
183 * Fins.build_command_args.params_len = ...;
184 * Fins.build_command(work);
185 * // Fins.n is what the call reports
186 *
187 * @var FinsNs::build_command_args what build_command takes: buf, cap, h, mrc, src, params,
188 * @var FinsNs::build_memory_area_read_args what build_memory_area_read takes: buf, cap, h, area,
189 * @var FinsNs::build_memory_area_write_args what build_memory_area_write takes: buf, cap, h, area,
190 * @var FinsNs::build_run_args what build_run takes: buf, cap, h, mode
191 * @var FinsNs::build_stop_args what build_stop takes: buf, cap, h
192 * @var FinsNs::parse_command_args what parse_command takes: buf, len, out
193 * @var FinsNs::parse_response_args what parse_response takes: buf, len, out
194 * @var FinsNs::ok a call's true/false outcome
195 * @var FinsNs::n total octets written, or 0 on a null data pointer with a nonzero ...
196 * @var FinsNs::build_command build a command frame: header + MRC + SRC + params. Returns total ...
197 * @var FinsNs::build_memory_area_read build a Memory Area Read command (0101): area code, 2-octet word ...
198 * @var FinsNs::build_memory_area_write build a Memory Area Write command (0102): the same area / word ...
199 * @var FinsNs::build_run build a RUN command (0401): switches the PLC to mode. Parameters ...
200 * @var FinsNs::build_stop build a STOP command (0402): switches the PLC to PROGRAM mode ...
201 * @var FinsNs::parse_command parse a command frame (header + MRC + SRC + params)
202 * @var FinsNs::parse_response parse a response frame (header + MRC + SRC + MRES + SRES + data)
203 *
204 * @c work is bytes the CALLER holds. This module reads none of them: it carries nothing
205 * between calls, so there is no state to keep and nothing to wipe. The parameter is there so
206 * a caller drives every namespace the same way.
207 */
208typedef struct
209{
210 FinsBuildCommandArgs build_command_args;
211 FinsBuildMemoryAreaReadArgs build_memory_area_read_args;
212 FinsBuildMemoryAreaWriteArgs build_memory_area_write_args;
213 FinsBuildRunArgs build_run_args;
214 FinsBuildStopArgs build_stop_args;
215 FinsParseCommandArgs parse_command_args;
216 FinsParseResponseArgs parse_response_args;
217 proto_bool ok;
218 size_t n;
219} FinsVars;
220
221/** @brief The operands and the outcome. */
222extern FinsVars FinsV;
223
224/** @brief The entries. */
225typedef struct
226{
227 void (*const build_command)(uint8_t *work);
228 void (*const build_memory_area_read)(uint8_t *work);
229 void (*const build_memory_area_write)(uint8_t *work);
230 void (*const build_run)(uint8_t *work);
231 void (*const build_stop)(uint8_t *work);
232 void (*const parse_command)(uint8_t *work);
233 void (*const parse_response)(uint8_t *work);
234} FinsNs;
235
236// What the table binds, defined once in the .c and taking one parameter each: everything
237// else an entry needs is an operand in FinsV or a region of the borrow at a fixed offset.
238void protocore_fins_build_command(uint8_t *work);
239void protocore_fins_build_memory_area_read(uint8_t *work);
240void protocore_fins_build_memory_area_write(uint8_t *work);
241void protocore_fins_build_run(uint8_t *work);
242void protocore_fins_build_stop(uint8_t *work);
243void protocore_fins_parse_command(uint8_t *work);
244void protocore_fins_parse_response(uint8_t *work);
245
246// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
247// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
248// `Fins.build_command(work)` resolves to a named function and becomes a DIRECT call. An extern table
249// leaves the call indirect and the symbol live at every level, -O2 -flto included.
250static const FinsNs Fins __attribute__((unused)) = {
251 .build_command = protocore_fins_build_command,
252 .build_memory_area_read = protocore_fins_build_memory_area_read,
253 .build_memory_area_write = protocore_fins_build_memory_area_write,
254 .build_run = protocore_fins_build_run,
255 .build_stop = protocore_fins_build_stop,
256 .parse_command = protocore_fins_parse_command,
257 .parse_response = protocore_fins_parse_response,
258};
259
261
262#endif // PROTOCORE_ENABLE_FINS
263
264#endif // PROTOCORE_FINS_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