DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
devicenet.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file devicenet.cpp
6 * @brief DeviceNet link-adaptation codec (pure, host-tested).
7 */
8
10
11#if DETWS_ENABLE_DEVICENET
12
13#include <string.h>
14
15bool devicenet_encode_id(uint32_t *id, DeviceNetGroup group, uint8_t msg_id, uint8_t mac_id)
16{
17 if (!id || mac_id > DEVICENET_MAC_MASK)
18 return false;
19 switch (group)
20 {
21 case DeviceNetGroup::DEVICENET_GROUP_1:
22 if (msg_id > 0x0Fu)
23 return false;
24 *id = DEVICENET_G1_BASE | ((uint32_t)msg_id << 6) | mac_id;
25 return true;
26 case DeviceNetGroup::DEVICENET_GROUP_2:
27 if (msg_id > 0x07u)
28 return false;
29 *id = DEVICENET_G2_BASE | ((uint32_t)mac_id << 3) | msg_id;
30 return true;
31 case DeviceNetGroup::DEVICENET_GROUP_3:
32 if (msg_id > 0x07u)
33 return false;
34 *id = DEVICENET_G3_BASE | ((uint32_t)msg_id << 6) | mac_id;
35 return true;
36 case DeviceNetGroup::DEVICENET_GROUP_4:
37 if (msg_id > 0x2Fu) // Group 4 has no MAC id; message ids 0x00..0x2F
38 return false;
39 *id = DEVICENET_G4_BASE | msg_id;
40 return true;
41 default:
42 return false;
43 }
44}
45
46bool devicenet_decode_id(uint32_t can_id, DeviceNetId *out)
47{
48 if (!out)
49 return false;
50 uint32_t id = can_id & DET_CAN_STD_ID_MASK;
51 if (id < DEVICENET_G2_BASE) // Group 1: 0 MsgID(4) MAC(6)
52 {
53 out->group = DeviceNetGroup::DEVICENET_GROUP_1;
54 out->msg_id = (uint8_t)((id >> 6) & 0x0Fu);
55 out->mac_id = (uint8_t)(id & DEVICENET_MAC_MASK);
56 return true;
57 }
58 if (id < DEVICENET_G3_BASE) // Group 2: 10 MAC(6) MsgID(3)
59 {
60 out->group = DeviceNetGroup::DEVICENET_GROUP_2;
61 out->mac_id = (uint8_t)((id >> 3) & DEVICENET_MAC_MASK);
62 out->msg_id = (uint8_t)(id & 0x07u);
63 return true;
64 }
65 if (id < DEVICENET_G4_BASE) // Group 3: 11 MsgID(3) MAC(6)
66 {
67 out->group = DeviceNetGroup::DEVICENET_GROUP_3;
68 out->msg_id = (uint8_t)((id >> 6) & 0x07u);
69 out->mac_id = (uint8_t)(id & DEVICENET_MAC_MASK);
70 return true;
71 }
72 if (id <= 0x7EFu) // Group 4: 11111 MsgID(6)
73 {
74 out->group = DeviceNetGroup::DEVICENET_GROUP_4;
75 out->msg_id = (uint8_t)(id & 0x3Fu);
76 out->mac_id = 0;
77 return true;
78 }
79 return false; // 0x7F0..0x7FF are invalid identifiers
80}
81
82uint8_t devicenet_msg_header(bool frag, bool xid, uint8_t mac_id)
83{
84 return (uint8_t)((frag ? DEVICENET_HDR_FRAG : 0u) | (xid ? DEVICENET_HDR_XID : 0u) | (mac_id & DEVICENET_MAC_MASK));
85}
86
87uint8_t devicenet_frag_octet(uint8_t type, uint8_t count)
88{
89 return (uint8_t)((type & DEVICENET_FRAG_TYPE_MASK) | (count & DEVICENET_FRAG_COUNT_MASK));
90}
91
92bool devicenet_build_explicit(CanFrame *out, DeviceNetGroup group, uint8_t msg_id, uint8_t mac_id, const uint8_t *body,
93 uint8_t body_len)
94{
95 if (!out || body_len > 7 || (body_len && !body)) // 1 header octet + up to 7 body octets
96 return false;
97 uint32_t id;
98 if (!devicenet_encode_id(&id, group, msg_id, mac_id))
99 return false;
100 out->id = id;
101 out->extended = false;
102 out->rtr = false;
103 out->dlc = (uint8_t)(1 + body_len);
104 memset(out->data, 0, sizeof(out->data));
105 out->data[0] = devicenet_msg_header(false, false, mac_id); // not fragmented
106 if (body_len)
107 memcpy(out->data + 1, body, body_len);
108 return true;
109}
110
111void devicenet_frag_reset(DeviceNetFragRx *rx)
112{
113 if (rx)
114 memset(rx, 0, sizeof(*rx));
115}
116
117// Append @p n octets to the reassembly buffer; false if it would overflow.
118static bool frag_append(DeviceNetFragRx *rx, const uint8_t *p, uint8_t n)
119{
120 if ((uint32_t)rx->len + n > DETWS_DEVICENET_MSG_MAX)
121 return false;
122 memcpy(rx->buf + rx->len, p, n);
123 rx->len = (uint16_t)(rx->len + n);
124 return true;
125}
126
127DeviceNetFragResult devicenet_frag_feed(DeviceNetFragRx *rx, const uint8_t *body, uint8_t body_len)
128{
129 if (!rx || !body || body_len < 1)
130 return DeviceNetFragResult::DEVICENET_FRAG_IGNORED;
131
132 if (!(body[0] & DEVICENET_HDR_FRAG)) // a complete, non-fragmented message in one frame
133 {
134 devicenet_frag_reset(rx);
135 if (body_len > 1 && !frag_append(rx, body + 1, (uint8_t)(body_len - 1)))
136 return DeviceNetFragResult::DEVICENET_FRAG_ERR; // GCOVR_EXCL_LINE unreachable: reset()->len 0, then a
137 // single append of <=254 (uint8 body_len-1) < 256=MSG_MAX
138 return DeviceNetFragResult::DEVICENET_FRAG_COMPLETE;
139 }
140 if (body_len < 2)
141 return DeviceNetFragResult::DEVICENET_FRAG_ERR; // FRAG set but no fragmentation octet
142 uint8_t type = body[1] & DEVICENET_FRAG_TYPE_MASK;
143 uint8_t count = body[1] & DEVICENET_FRAG_COUNT_MASK;
144 const uint8_t *data = body + 2;
145 uint8_t data_len = (uint8_t)(body_len - 2);
146
147 switch (type)
148 {
149 case DEVICENET_FRAG_FIRST:
150 devicenet_frag_reset(rx);
151 rx->active = true;
152 rx->next_count = (uint8_t)((count + 1u) & DEVICENET_FRAG_COUNT_MASK);
153 if (data_len && !frag_append(rx, data, data_len))
154 return DeviceNetFragResult::DEVICENET_FRAG_ERR; // GCOVR_EXCL_LINE unreachable: FIRST reset()->len 0, then
155 // a single append of
156 // <=253 (uint8 body_len-2) < 256=MSG_MAX
157 return DeviceNetFragResult::DEVICENET_FRAG_STARTED;
158 case DEVICENET_FRAG_MIDDLE:
159 if (!rx->active || count != rx->next_count)
160 {
161 devicenet_frag_reset(rx);
162 return DeviceNetFragResult::DEVICENET_FRAG_ERR;
163 }
164 if (data_len && !frag_append(rx, data, data_len))
165 {
166 devicenet_frag_reset(rx);
167 return DeviceNetFragResult::DEVICENET_FRAG_ERR;
168 }
169 rx->next_count = (uint8_t)((count + 1u) & DEVICENET_FRAG_COUNT_MASK);
170 return DeviceNetFragResult::DEVICENET_FRAG_PROGRESS;
171 case DEVICENET_FRAG_LAST:
172 if (!rx->active || count != rx->next_count)
173 {
174 devicenet_frag_reset(rx);
175 return DeviceNetFragResult::DEVICENET_FRAG_ERR;
176 }
177 if (data_len && !frag_append(rx, data, data_len))
178 {
179 devicenet_frag_reset(rx);
180 return DeviceNetFragResult::DEVICENET_FRAG_ERR;
181 }
182 rx->active = false;
183 return DeviceNetFragResult::DEVICENET_FRAG_COMPLETE;
184 default: // DEVICENET_FRAG_ACK is flow control, not data
185 return DeviceNetFragResult::DEVICENET_FRAG_IGNORED;
186 }
187}
188
189#endif // DETWS_ENABLE_DEVICENET
#define DET_CAN_STD_ID_MASK
11-bit standard identifier.
Definition can.h:31
DeviceNet link-adaptation codec (DETWS_ENABLE_DEVICENET) - the CAN-specific layer of "CIP over CAN".
Definition can.h:44
uint8_t data[DET_CAN_MAX_DLC]
Definition can.h:49
bool rtr
Definition can.h:47
uint32_t id
Definition can.h:45
uint8_t dlc
Definition can.h:48
bool extended
Definition can.h:46