15size_t pc_bvlc_build(uint8_t *buf,
size_t cap, uint8_t function,
const uint8_t *npdu,
size_t pc_npdu_len)
17 if (!buf || (pc_npdu_len && !npdu))
21 size_t total = BVLC_HEADER_SIZE + pc_npdu_len;
22 if (total > 0xFFFF || total > cap)
26 buf[0] = BVLC_TYPE_BIP;
28 buf[2] = (uint8_t)(total >> 8);
29 buf[3] = (uint8_t)(total & 0xFF);
32 memcpy(buf + BVLC_HEADER_SIZE, npdu, pc_npdu_len);
37bool pc_bvlc_parse(
const uint8_t *buf,
size_t len, uint8_t *function,
const uint8_t **npdu,
size_t *pc_npdu_len)
39 if (!buf || len < BVLC_HEADER_SIZE || buf[0] != BVLC_TYPE_BIP)
43 size_t total = ((size_t)buf[2] << 8) | buf[3];
44 if (total < BVLC_HEADER_SIZE || total > len)
54 *npdu = buf + BVLC_HEADER_SIZE;
58 *pc_npdu_len = total - BVLC_HEADER_SIZE;
63size_t pc_npdu_build(uint8_t *buf,
size_t cap,
bool expecting_reply, uint8_t priority,
bool has_dest, uint16_t dnet,
64 const uint8_t *dadr, uint8_t dadr_len, uint8_t hop_count,
const uint8_t *apdu,
size_t apdu_len)
66 if (!buf || (apdu_len && !apdu) || (dadr_len && !dadr))
70 size_t need = 2 + apdu_len;
73 need += 2 + 1 + dadr_len + 1;
81 buf[p++] = NPDU_VERSION;
82 uint8_t control = (uint8_t)(priority & NPCI_PRIORITY_MASK);
85 control |= NPCI_EXPECTING_REPLY;
89 control |= NPCI_DEST_PRESENT;
94 buf[p++] = (uint8_t)(dnet >> 8);
95 buf[p++] = (uint8_t)(dnet & 0xFF);
99 memcpy(buf + p, dadr, dadr_len);
102 buf[p++] = hop_count;
106 memcpy(buf + p, apdu, apdu_len);
112bool pc_npdu_parse(
const uint8_t *buf,
size_t len, NpduInfo *out)
114 if (!buf || !out || len < 2 || buf[0] != NPDU_VERSION)
118 uint8_t control = buf[1];
121 out->control = control;
122 out->network_message = (control & NPCI_NETWORK_MSG) != 0;
123 out->dest_present = (control & NPCI_DEST_PRESENT) != 0;
124 out->src_present = (control & NPCI_SRC_PRESENT) != 0;
129 if (out->dest_present)
135 out->dnet = (uint16_t)((buf[p] << 8) | buf[p + 1]);
136 uint8_t dlen = buf[p + 2];
143 if (out->src_present)
149 out->snet = (uint16_t)((buf[p] << 8) | buf[p + 1]);
150 uint8_t slen = buf[p + 2];
157 if (out->dest_present)
163 out->hop_count = buf[p++];
166 out->apdu_len = len - p;
173static size_t bacnet_put_tagged_uint(uint8_t *buf, uint8_t tag_number, uint32_t value,
bool context)
183 for (
int shift = 24; shift >= 0; shift -= 8)
185 uint8_t b = (uint8_t)(value >> shift);
186 if (vlen == 0 && b == 0)
194 buf[p++] = (uint8_t)(((uint32_t)tag_number << 4) | (context ? 0x08u : 0x00u) | (uint8_t)vlen);
195 for (
size_t i = 0; i < vlen; i++)
202size_t pc_apdu_build_who_is(uint8_t *buf,
size_t cap, uint32_t low_limit, uint32_t high_limit,
bool has_limits)
210 tmp[p++] = (uint8_t)(BACNET_PDU_UNCONFIRMED_REQUEST << 4);
211 tmp[p++] = BACNET_SVC_UN_WHO_IS;
214 if (low_limit > BACNET_MAX_INSTANCE || high_limit > BACNET_MAX_INSTANCE || low_limit > high_limit)
218 p += bacnet_put_tagged_uint(tmp + p, 0, low_limit,
true);
219 p += bacnet_put_tagged_uint(tmp + p, 1, high_limit,
true);
229size_t pc_apdu_build_i_am(uint8_t *buf,
size_t cap, uint32_t device_instance, uint32_t max_apdu, uint8_t segmentation,
232 if (!buf || device_instance > BACNET_MAX_INSTANCE || segmentation > 3)
238 tmp[p++] = (uint8_t)(BACNET_PDU_UNCONFIRMED_REQUEST << 4);
239 tmp[p++] = BACNET_SVC_UN_I_AM;
242 uint32_t oid = ((uint32_t)BACNET_OBJ_DEVICE << 22) | device_instance;
243 tmp[p++] = (uint8_t)(oid >> 24);
244 tmp[p++] = (uint8_t)(oid >> 16);
245 tmp[p++] = (uint8_t)(oid >> 8);
246 tmp[p++] = (uint8_t)oid;
247 p += bacnet_put_tagged_uint(tmp + p, 2, max_apdu,
false);
249 tmp[p++] = segmentation;
250 p += bacnet_put_tagged_uint(tmp + p, 2, vendor_id,
false);
259size_t pc_apdu_build_read_property(uint8_t *buf,
size_t cap, uint8_t invoke_id, uint8_t max_resp, uint16_t object_type,
260 uint32_t object_instance, uint32_t property_id)
262 if (!buf || object_instance > BACNET_MAX_INSTANCE || object_type > 0x3FFu)
268 tmp[p++] = (uint8_t)(BACNET_PDU_CONFIRMED_REQUEST << 4);
270 tmp[p++] = invoke_id;
271 tmp[p++] = BACNET_SVC_CONF_READ_PROPERTY;
274 uint32_t oid = ((uint32_t)object_type << 22) | object_instance;
275 tmp[p++] = (uint8_t)(oid >> 24);
276 tmp[p++] = (uint8_t)(oid >> 16);
277 tmp[p++] = (uint8_t)(oid >> 8);
278 tmp[p++] = (uint8_t)oid;
280 p += bacnet_put_tagged_uint(tmp + p, 1, property_id,
true);
291static bool apdu_parse_confirmed_request(
const uint8_t *apdu,
size_t len, BacnetApdu *out,
size_t *p)
293 out->segmented = (apdu[0] & BACNET_APDU_SEG) != 0;
294 out->more_follows = (apdu[0] & BACNET_APDU_MOR) != 0;
295 out->sa = (apdu[0] & BACNET_APDU_SA) != 0;
300 out->invoke_id = apdu[*p + 1];
314 out->service_choice = apdu[(*p)++];
320static bool apdu_parse_complex_ack(
const uint8_t *apdu,
size_t len, BacnetApdu *out,
size_t *p)
322 out->segmented = (apdu[0] & BACNET_APDU_SEG) != 0;
323 out->more_follows = (apdu[0] & BACNET_APDU_MOR) != 0;
328 out->invoke_id = apdu[(*p)++];
341 out->service_choice = apdu[(*p)++];
345bool pc_apdu_parse(
const uint8_t *apdu,
size_t len, BacnetApdu *out)
347 if (!apdu || !out || len < 1)
351 memset(out, 0,
sizeof(*out));
352 out->pdu_type = (uint8_t)(apdu[0] >> 4);
354 switch (out->pdu_type)
356 case BACNET_PDU_CONFIRMED_REQUEST:
357 if (!apdu_parse_confirmed_request(apdu, len, out, &p))
362 case BACNET_PDU_UNCONFIRMED_REQUEST:
367 out->service_choice = apdu[p++];
369 case BACNET_PDU_SIMPLE_ACK:
374 out->invoke_id = apdu[p++];
375 out->service_choice = apdu[p++];
377 case BACNET_PDU_COMPLEX_ACK:
378 if (!apdu_parse_complex_ack(apdu, len, out, &p))
386 out->service_data = (p < len) ? apdu + p : nullptr;
387 out->service_data_len = len - p;
BACnet/IP BVLC + NPDU codec (PC_ENABLE_BACNET) - zero-heap framing for the ASHRAE 135 building-automa...