/** * @file * @brief BACnet Virtual Link Control implementation for the PlatformIO ESP32 * port * @author Kato Gangstad */ #include #include #include #include "bip.h" #include "bvlc.h" #include "bacnet/bacint.h" #define BVLC_RESULT 0 #define BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE 1 #define BVLC_READ_BROADCAST_DIST_TABLE 2 #define BVLC_READ_BROADCAST_DIST_TABLE_ACK 3 #define BVLC_FORWARDED_NPDU 4 #define BVLC_REGISTER_FOREIGN_DEVICE 5 #define BVLC_READ_FOREIGN_DEVICE_TABLE 6 #define BVLC_READ_FOREIGN_DEVICE_TABLE_ACK 7 #define BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY 8 #define BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK 9 #define BVLC_ORIGINAL_UNICAST_NPDU 10 #define BVLC_ORIGINAL_BROADCAST_NPDU 11 #define BVLC_SECURE_BVLL 12 #define BVLC_RESULT_WRITE_BROADCAST_DISTRIBUTION_TABLE_NAK 0x0010 #define BVLC_RESULT_READ_BROADCAST_DISTRIBUTION_TABLE_NAK 0x0020 #define BVLC_RESULT_REGISTER_FOREIGN_DEVICE_NAK 0X0030 #define BVLC_RESULT_READ_FOREIGN_DEVICE_TABLE_NAK 0x0040 #define BVLC_RESULT_DELETE_FOREIGN_DEVICE_TABLE_ENTRY_NAK 0x0050 #define BVLC_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK 0x0060 static BACNET_BVLC_RESULT BVLC_Result_Code = BVLC_RESULT_SUCCESSFUL_COMPLETION; static BACNET_BVLC_FUNCTION BVLC_Function_Code = BVLC_RESULT; /** * @brief Encode a BVLC result message * @param pdu destination buffer, or NULL for length-only use * @param result_code BVLC result code to encode * @return encoded BVLC message length */ static int bvlc_encode_bvlc_result(uint8_t *pdu, BACNET_BVLC_RESULT result_code) { if (pdu) { pdu[0] = BVLL_TYPE_BACNET_IP; pdu[1] = BVLC_RESULT; encode_unsigned16(&pdu[2], 6); encode_unsigned16(&pdu[4], (uint16_t)result_code); } return 6; } /** * @brief Send a raw BVLC frame to the destination host * @param dest_addr destination IPv4 address * @param dest_port destination UDP port * @param mtu raw BVLC frame buffer * @param mtu_len frame length in bytes * @return number of bytes sent, or -1 on error */ static int bvlc_send_mpdu( const uint8_t *dest_addr, const uint16_t *dest_port, uint8_t *mtu, uint16_t mtu_len) { int bytes_sent = 0; if (!bip_valid()) { return -1; } bytes_sent = bip_socket_send(dest_addr, *dest_port, mtu, mtu_len); return bytes_sent; } /** * @brief Send a BVLC result response * @param dest_addr destination IPv4 address * @param dest_port destination UDP port * @param result_code BVLC result code to report */ static void bvlc_send_result( const uint8_t *dest_addr, const uint16_t *dest_port, BACNET_BVLC_RESULT result_code) { uint8_t mtu[BIP_MPDU_MAX] = { 0 }; uint16_t mtu_len = 0; mtu_len = (uint16_t)bvlc_encode_bvlc_result(&mtu[0], result_code); bvlc_send_mpdu(dest_addr, dest_port, mtu, mtu_len); } /** * @brief Handle non-BBMD BVLC functions for this port * @param addr source IPv4 address * @param port source UDP port * @param npdu received BVLC frame buffer * @param received_bytes frame length in bytes * @return non-zero when the frame was consumed by BVLC handling */ uint16_t bvlc_for_non_bbmd( uint8_t *addr, uint16_t *port, uint8_t *npdu, uint16_t received_bytes) { uint16_t result_code = 0; if (received_bytes >= 1) { BVLC_Function_Code = npdu[1]; switch (BVLC_Function_Code) { case BVLC_RESULT: if (received_bytes >= 6) { (void)decode_unsigned16(&npdu[4], &result_code); BVLC_Result_Code = (BACNET_BVLC_RESULT)result_code; fprintf(stderr, "BVLC: Result Code=%d\n", BVLC_Result_Code); result_code = 0; } break; case BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE: result_code = BVLC_RESULT_WRITE_BROADCAST_DISTRIBUTION_TABLE_NAK; break; case BVLC_READ_BROADCAST_DIST_TABLE: result_code = BVLC_RESULT_READ_BROADCAST_DISTRIBUTION_TABLE_NAK; break; case BVLC_REGISTER_FOREIGN_DEVICE: result_code = BVLC_RESULT_REGISTER_FOREIGN_DEVICE_NAK; break; case BVLC_READ_FOREIGN_DEVICE_TABLE: result_code = BVLC_RESULT_READ_FOREIGN_DEVICE_TABLE_NAK; break; case BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY: result_code = BVLC_RESULT_DELETE_FOREIGN_DEVICE_TABLE_ENTRY_NAK; break; case BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK: result_code = BVLC_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK; break; default: break; } } if (result_code > 0) { bvlc_send_result(addr, port, result_code); fprintf(stderr, "BVLC: NAK code=%d\n", result_code); } return result_code; } /** * @brief Get the last decoded BVLC function code * @return BVLC function code */ BACNET_BVLC_FUNCTION pico_bvlc_get_function_code(void) { return BVLC_Function_Code; }