ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
snp.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 snp.h
6 * @brief GE Fanuc SNP (Series Ninety Protocol) serial frame codec (PROTOCORE_ENABLE_SNP).
7 *
8 * SNP is the GE Fanuc Series 90 (90-30 / 90-70) master-slave serial protocol over RS-485. A message is
9 * a BCC-checked frame delimited by control characters:
10 *
11 * [SOH-or-other-control][data...][checksum]
12 *
13 * SNP frames the payload with an ASCII/binary control byte, a length, the data, and the Block Check
14 * Code of GE Fanuc GFK-0582D p. 7-62 (seed zero; per byte, XOR then rotate the accumulator left one
15 * bit). This builds/validates that frame so a device can
16 * read/write registers on a Series 90 PLC; the RS-485 UART transport (and the SNP-X session setup) is
17 * the remaining device step. Pure, zero heap, no stdlib, host-testable.
18 */
19
20#ifndef PROTOCORE_SNP_H
21#define PROTOCORE_SNP_H
22
23#include "protocore_config.h" // the entry point: protocore_types.h for the widths
24
25#if PROTOCORE_ENABLE_SNP
26
28
29// This module holds nothing between calls, so it carves no borrow and states none. An entry
30// takes one all the same, and never reads it, so every namespace in the tree is invoked the
31// same way.
32
33/** @brief SNP control bytes (subset). */
34// SNP control bytes: wire values compared/emitted, so integer constants in a namespacing struct.
35#define SNP_ENQ 0x05 ///< enquiry / attach.
36#define SNP_ACK 0x06 ///< acknowledge.
37#define SNP_NAK 0x15 ///< negative acknowledge.
38#define SNP_SOH 0x01 ///< start of header (a request/response frame).
39#define SNP_EOT 0x04 ///< end of transmission.
40
41/** @brief A parsed SNP frame (data points into the input). */
42typedef struct
43{
44 uint8_t control;
45 const uint8_t *data;
46 size_t data_len;
47} SnpFrame;
48
49/** @brief What bcc takes: bytes, len. */
50typedef struct
51{
52 const uint8_t *bytes;
53 size_t len;
54} SnpBccArgs;
55
56/** @brief What build takes: control, data, data_len, out, cap. */
57typedef struct
58{
59 uint8_t control;
60 const uint8_t *data;
61 size_t data_len;
62 uint8_t *out;
63 size_t cap;
64} SnpBuildArgs;
65
66/** @brief What parse takes: frame, len, out. */
67typedef struct
68{
69 const uint8_t *frame;
70 size_t len;
71 SnpFrame *out;
72} SnpParseArgs;
73
74/**
75 * @brief GE Fanuc SNP (Series Ninety Protocol) serial frame codec (PROTOCORE_ENABLE_SNP).
76 *
77 * A caller sets the members a call takes, invokes it through ::Snp with the bytes it runs
78 * out of, and reads the outcome off the same handle.
79 *
80 * Snp.bcc_args.bytes = ...;
81 * Snp.bcc_args.len = ...;
82 * Snp.bcc(work);
83 * // Snp.value is what the call reports
84 *
85 * @var SnpNs::bcc_args what bcc takes: bytes, len
86 * @var SnpNs::build_args what build takes: control, data, data_len, out, cap
87 * @var SnpNs::parse_args what parse takes: frame, len, out
88 * @var SnpNs::ok a call's true/false outcome
89 * @var SnpNs::value the value a call reports
90 * @var SnpNs::n the frame length (2 + data_len + 1), or 0 on overflow / bad args ...
91 * @var SnpNs::bcc block Check Code over len bytes (GFK-0582D p. 7-62). Seeded at ...
92 * @var SnpNs::build build an SNP frame: [control][length][data...][BCC]. length is the ...
93 * @var SnpNs::parse validate the BCC and parse an SNP frame. true if the BCC matches ...
94 *
95 * @c work is bytes the CALLER holds. This module reads none of them: it carries nothing
96 * between calls, so there is no state to keep and nothing to wipe. The parameter is there so
97 * a caller drives every namespace the same way.
98 */
99typedef struct
100{
101 SnpBccArgs bcc_args;
102 SnpBuildArgs build_args;
103 SnpParseArgs parse_args;
104 proto_bool ok;
105 uint8_t value;
106 size_t n;
107} SnpVars;
108
109/** @brief The operands and the outcome. */
110extern SnpVars SnpV;
111
112/** @brief The entries. */
113typedef struct
114{
115 void (*const bcc)(uint8_t *work);
116 void (*const build)(uint8_t *work);
117 void (*const parse)(uint8_t *work);
118} SnpNs;
119
120// What the table binds, defined once in the .c and taking one parameter each: everything
121// else an entry needs is an operand in SnpV or a region of the borrow at a fixed offset.
122void protocore_snp_bcc(uint8_t *work);
123void protocore_snp_build(uint8_t *work);
124void protocore_snp_parse(uint8_t *work);
125
126// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
127// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
128// `Snp.bcc(work)` resolves to a named function and becomes a DIRECT call. An extern table
129// leaves the call indirect and the symbol live at every level, -O2 -flto included.
130static const SnpNs Snp __attribute__((unused)) = {
131 .bcc = protocore_snp_bcc,
132 .build = protocore_snp_build,
133 .parse = protocore_snp_parse,
134};
135
137
138#endif // PROTOCORE_ENABLE_SNP
139
140#endif // PROTOCORE_SNP_H
#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