16static size_t put16(uint8_t *p, uint16_t v)
18 p[0] = (uint8_t)(v & 0xFF);
19 p[1] = (uint8_t)(v >> 8);
23static size_t put32(uint8_t *p, uint32_t v)
25 p[0] = (uint8_t)(v & 0xFF);
26 p[1] = (uint8_t)(v >> 8);
27 p[2] = (uint8_t)(v >> 16);
28 p[3] = (uint8_t)(v >> 24);
32static uint16_t get16(
const uint8_t *p)
34 return (uint16_t)(p[0] | ((uint16_t)p[1] << 8));
37static uint32_t get32(
const uint8_t *p)
39 return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
42size_t pc_eip_build(uint8_t *buf,
size_t cap,
const EipHeader *h,
const uint8_t *data,
size_t data_len)
44 if (!buf || !h || (data_len && !data) || data_len > 0xFFFF)
48 size_t total = EIP_HEADER_SIZE + data_len;
54 p += put16(buf + p, h->command);
55 p += put16(buf + p, (uint16_t)data_len);
56 p += put32(buf + p, h->session_handle);
57 p += put32(buf + p, h->status);
58 memcpy(buf + p, h->sender_context, 8);
60 p += put32(buf + p, h->options);
63 memcpy(buf + p, data, data_len);
69bool pc_eip_parse(
const uint8_t *buf,
size_t len, EipHeader *out,
const uint8_t **data,
size_t *data_len)
71 if (!buf || !out || len < EIP_HEADER_SIZE)
75 out->command = get16(buf);
76 out->length = get16(buf + 2);
77 out->session_handle = get32(buf + 4);
78 out->status = get32(buf + 8);
79 memcpy(out->sender_context, buf + 12, 8);
80 out->options = get32(buf + 20);
81 if ((
size_t)EIP_HEADER_SIZE + out->length > len)
87 *data = buf + EIP_HEADER_SIZE;
91 *data_len = out->length;
96size_t pc_eip_build_register_session(uint8_t *buf,
size_t cap,
const uint8_t sender_context[8])
99 memset(&h, 0,
sizeof(h));
100 h.command = EIP_CMD_REGISTER_SESSION;
103 memcpy(h.sender_context, sender_context, 8);
108 return pc_eip_build(buf, cap, &h, data,
sizeof(data));
111size_t pc_eip_build_unregister_session(uint8_t *buf,
size_t cap, uint32_t session_handle,
112 const uint8_t sender_context[8])
115 memset(&h, 0,
sizeof(h));
116 h.command = EIP_CMD_UNREGISTER_SESSION;
117 h.session_handle = session_handle;
120 memcpy(h.sender_context, sender_context, 8);
122 return pc_eip_build(buf, cap, &h,
nullptr, 0);
125size_t pc_eip_build_send_rr_data(uint8_t *buf,
size_t cap, uint32_t session_handle,
const uint8_t sender_context[8],
126 uint16_t timeout,
const uint8_t *cip,
size_t pc_cip_len)
128 if (!buf || (pc_cip_len && !cip) || pc_cip_len > 0xFFFF)
133 size_t data_len = 4 + 2 + 2 + 4 + 4 + pc_cip_len;
134 size_t total = EIP_HEADER_SIZE + data_len;
135 if (total > cap || data_len > 0xFFFF)
143 memset(&h, 0,
sizeof(h));
144 h.command = EIP_CMD_SEND_RR_DATA;
145 h.session_handle = session_handle;
148 memcpy(h.sender_context, sender_context, 8);
152 if (pc_eip_build(buf, cap, &h,
nullptr, 0) == 0)
158 put16(buf + 2, (uint16_t)data_len);
160 size_t p = EIP_HEADER_SIZE;
161 p += put32(buf + p, 0);
162 p += put16(buf + p, timeout);
163 p += put16(buf + p, 2);
164 p += put16(buf + p, EIP_CPF_NULL);
165 p += put16(buf + p, 0);
166 p += put16(buf + p, EIP_CPF_UNCONNECTED_DATA);
167 p += put16(buf + p, (uint16_t)pc_cip_len);
170 memcpy(buf + p, cip, pc_cip_len);
176size_t pc_eip_build_list_identity(uint8_t *buf,
size_t cap,
const uint8_t sender_context[8])
179 memset(&h, 0,
sizeof(h));
180 h.command = EIP_CMD_LIST_IDENTITY;
183 memcpy(h.sender_context, sender_context, 8);
185 return pc_eip_build(buf, cap, &h,
nullptr, 0);
188bool pc_eip_parse_list_identity(
const uint8_t *data,
size_t data_len, EipIdentity *out)
190 if (!data || !out || data_len < 2)
194 uint16_t item_count = get16(data);
196 for (uint16_t i = 0; i < item_count; i++)
198 if (pos + 4 > data_len)
202 uint16_t type = get16(data + pos);
203 uint16_t ilen = get16(data + pos + 2);
205 if (pos + ilen > data_len)
209 if (type == EIP_CPF_LIST_IDENTITY)
211 const uint8_t *it = data + pos;
217 uint8_t name_len = it[32];
218 if ((
size_t)ilen < (
size_t)34 + name_len)
222 out->protocol_version = get16(it);
224 out->vendor_id = get16(it + 18);
225 out->device_type = get16(it + 20);
226 out->product_code = get16(it + 22);
227 out->revision_major = it[24];
228 out->revision_minor = it[25];
229 out->status = get16(it + 26);
230 out->serial_number = get32(it + 28);
231 out->product_name_len = name_len;
232 out->product_name = (
const char *)(it + 33);
233 out->state = it[33 + name_len];
241bool pc_eip_parse_send_rr_data(
const uint8_t *data,
size_t data_len,
const uint8_t **cip,
size_t *pc_cip_len)
243 if (!data || data_len < 8)
248 uint16_t item_count = get16(data + pos);
250 for (uint16_t i = 0; i < item_count; i++)
252 if (pos + 4 > data_len)
256 uint16_t type = get16(data + pos);
257 uint16_t ilen = get16(data + pos + 2);
259 if (pos + ilen > data_len)
263 if (type == EIP_CPF_UNCONNECTED_DATA)
EtherNet/IP encapsulation codec (PC_ENABLE_ENIP) - zero-heap builder + parser for the ODVA EtherNet/I...