ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
df1.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 df1.h
6 * @brief Allen-Bradley DF1 full-duplex frame codec (PROTOCORE_ENABLE_DF1) - zero-heap framing +
7 * DLE byte-stuffing + BCC/CRC for the Rockwell serial PLC link layer.
8 *
9 * A DF1 full-duplex message frame (AB pub. 1770-6.5.16):
10 * @code
11 * DLE STX <application data, DLE bytes doubled> DLE ETX BCC | CRC
12 * @endcode
13 * - A data byte equal to DLE (0x10) is transmitted twice (DLE DLE); the doubled DLE is
14 * counted only once in the BCC/CRC.
15 * - BCC: the 2's complement of the modulo-256 sum of the application data bytes (the ETX
16 * is NOT included). One octet.
17 * - CRC: CRC-16 (poly X16+X15+X2+X0 = 0x8005, init 0x0000, reflected) over the application
18 * data bytes AND the ETX byte; two octets, transmitted low byte first.
19 *
20 * This is the data-link framing layer; the DST/SRC/CMD/STS/TNS application header lives
21 * inside the application data. Field definitions verified against the 1770-6.5.16 manual.
22 *
23 * @author Douglas Quigg (dstroy0)
24 * @date 2026
25 */
26
27#ifndef PROTOCORE_DF1_H
28#define PROTOCORE_DF1_H
29
30#include "protocore_config.h" // the entry point: protocore_types.h for the widths
31
32#if PROTOCORE_ENABLE_DF1
33
35
36// This module holds nothing between calls, so it carves no borrow and states none. An entry
37// takes one all the same, and never reads it, so every namespace in the tree is invoked the
38// same way.
39
40#define DF1_DLE 0x10
41#define DF1_STX 0x02
42#define DF1_ETX 0x03
43
44/** @brief Which error check the frame carries. */
45typedef enum PROTO_ENUM_PACKED
46{
47 DF1_CHECK_BCC, ///< 1-octet block check character
48 DF1_CHECK_CRC ///< 2-octet CRC-16
49} Df1Check;
50
51/** @brief What bcc takes: data, len. */
52typedef struct
53{
54 const uint8_t *data;
55 size_t len;
56} Df1BccArgs;
57
58/** @brief What crc takes: data, len. */
59typedef struct
60{
61 const uint8_t *data;
62 size_t len;
63} Df1CrcArgs;
64
65/** @brief What build_frame takes: buf, cap, data, data_len, check. */
66typedef struct
67{
68 uint8_t *buf;
69 size_t cap;
70 const uint8_t *data;
71 size_t data_len;
72 Df1Check check; ///< DF1_CHECK_BCC (1 octet) or DF1_CHECK_CRC (2 octets, low byte first; CRC over the data + ETX)
73} Df1BuildFrameArgs;
74
75/** @brief What parse_frame takes: buf, len, check, out, out_cap, ... */
76typedef struct
77{
78 const uint8_t *buf;
79 size_t len;
80 Df1Check check;
81 uint8_t *out; ///< receives the de-stuffed application data
82 size_t out_cap; ///< capacity of out
83 size_t *out_len; ///< receives the application-data length
84} Df1ParseFrameArgs;
85
86/**
87 * @brief Allen-Bradley DF1 full-duplex frame codec (PROTOCORE_ENABLE_DF1) - zero-heap framing + DLE byte-stuffing +
88 * BCC/CRC for the Rockwell serial PLC link layer.
89 *
90 * A caller sets the members a call takes, invokes it through ::Df1 with the bytes it runs
91 * out of, and reads the outcome off the same handle.
92 *
93 * Df1.bcc_args.data = ...;
94 * Df1.bcc_args.len = ...;
95 * Df1.bcc(work);
96 * // Df1.value is what the call reports
97 *
98 * @var Df1Ns::bcc_args what bcc takes: data, len
99 * @var Df1Ns::crc_args what crc takes: data, len
100 * @var Df1Ns::build_frame_args what build_frame takes: buf, cap, data, data_len, check
101 * @var Df1Ns::parse_frame_args what parse_frame takes: buf, len, check, out, out_cap,
102 * @var Df1Ns::ok true on a complete, check-valid frame; false on bad framing, ...
103 * @var Df1Ns::value the value a call reports
104 * @var Df1Ns::u16 what a call reports
105 * @var Df1Ns::n total octets written, or 0 on overflow / bad input
106 * @var Df1Ns::bcc BCC: 2's complement of the modulo-256 sum of [data, data+len)
107 * @var Df1Ns::crc CRC-16/ARC (poly 0x8005 / 0xA001 reflected, init 0) over [data, ...
108 * @var Df1Ns::build_frame build a full-duplex frame around data: DLE STX + stuffed data + DLE ...
109 * @var Df1Ns::parse_frame parse + validate a full-duplex frame, un-stuffing the application ...
110 *
111 * @c work is bytes the CALLER holds. This module reads none of them: it carries nothing
112 * between calls, so there is no state to keep and nothing to wipe. The parameter is there so
113 * a caller drives every namespace the same way.
114 */
115typedef struct
116{
117 Df1BccArgs bcc_args;
118 Df1CrcArgs crc_args;
119 Df1BuildFrameArgs build_frame_args;
120 Df1ParseFrameArgs parse_frame_args;
121 proto_bool ok;
122 uint8_t value;
123 uint16_t u16;
124 size_t n;
125} Df1Vars;
126
127/** @brief The operands and the outcome. */
128extern Df1Vars Df1V;
129
130/** @brief The entries. */
131typedef struct
132{
133 void (*const bcc)(uint8_t *work);
134 void (*const crc)(uint8_t *work);
135 void (*const build_frame)(uint8_t *work);
136 void (*const parse_frame)(uint8_t *work);
137} Df1Ns;
138
139// What the table binds, defined once in the .c and taking one parameter each: everything
140// else an entry needs is an operand in Df1V or a region of the borrow at a fixed offset.
141void protocore_df1_bcc(uint8_t *work);
142void protocore_df1_crc(uint8_t *work);
143void protocore_df1_build_frame(uint8_t *work);
144void protocore_df1_parse_frame(uint8_t *work);
145
146// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
147// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
148// `Df1.bcc(work)` resolves to a named function and becomes a DIRECT call. An extern table
149// leaves the call indirect and the symbol live at every level, -O2 -flto included.
150static const Df1Ns Df1 __attribute__((unused)) = {
151 .bcc = protocore_df1_bcc,
152 .crc = protocore_df1_crc,
153 .build_frame = protocore_df1_build_frame,
154 .parse_frame = protocore_df1_parse_frame,
155};
156
158
159#endif // PROTOCORE_ENABLE_DF1
160
161#endif // PROTOCORE_DF1_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