Add BACnet Zigbee VMAC table and unit test. (#1054)

This commit is contained in:
Steve Karg
2025-07-31 08:57:53 -05:00
committed by GitHub
parent 262ee3fb40
commit f31193998c
13 changed files with 1003 additions and 3 deletions
+14
View File
@@ -74,6 +74,11 @@ option(
"compile with ipv6 datalink support" "compile with ipv6 datalink support"
ON) ON)
option(
BACDL_ZIGBEE
"compile with zigbee datalink support"
ON)
option( option(
BACDL_BSC BACDL_BSC
"compile with secure-connect support" "compile with secure-connect support"
@@ -84,6 +89,7 @@ if(NOT (BACDL_ETHERNET OR
BACDL_ARCNET OR BACDL_ARCNET OR
BACDL_BIP OR BACDL_BIP OR
BACDL_BIP6 OR BACDL_BIP6 OR
BACDL_ZIGBEE OR
BACDL_BSC OR BACDL_BSC OR
BACDL_CUSTOM)) BACDL_CUSTOM))
add_definitions(-DBACDL_NONE) add_definitions(-DBACDL_NONE)
@@ -295,6 +301,8 @@ add_library(${PROJECT_NAME}
$<$<BOOL:${BACDL_BIP6}>:src/bacnet/basic/bbmd6/h_bbmd6.h> $<$<BOOL:${BACDL_BIP6}>:src/bacnet/basic/bbmd6/h_bbmd6.h>
$<$<BOOL:${BACDL_BIP6}>:src/bacnet/basic/bbmd6/vmac.c> $<$<BOOL:${BACDL_BIP6}>:src/bacnet/basic/bbmd6/vmac.c>
$<$<BOOL:${BACDL_BIP6}>:src/bacnet/basic/bbmd6/vmac.h> $<$<BOOL:${BACDL_BIP6}>:src/bacnet/basic/bbmd6/vmac.h>
$<$<BOOL:${BACDL_ZIGBEE}>:src/bacnet/basic/bzll/bzllvmac.c>
$<$<BOOL:${BACDL_ZIGBEE}>:src/bacnet/basic/bzll/bzllvmac.h>
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bvlc-sc.c> $<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bvlc-sc.c>
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bvlc-sc.h> $<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bvlc-sc.h>
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bsc-socket.c> $<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bsc-socket.c>
@@ -693,6 +701,7 @@ target_compile_definitions(
$<$<BOOL:${BACDL_BIP}>:BACDL_BIP> $<$<BOOL:${BACDL_BIP}>:BACDL_BIP>
$<$<BOOL:${BACDL_BSC}>:BACDL_BSC> $<$<BOOL:${BACDL_BSC}>:BACDL_BSC>
$<$<BOOL:${BACDL_BIP6}>:BACDL_BIP6> $<$<BOOL:${BACDL_BIP6}>:BACDL_BIP6>
$<$<BOOL:${BACDL_ZIGBEE}>:BACDL_ZIGBEE>
$<$<BOOL:${BACDL_ARCNET}>:BACDL_ARCNET> $<$<BOOL:${BACDL_ARCNET}>:BACDL_ARCNET>
$<$<BOOL:${BACDL_MSTP}>:BACDL_MSTP> $<$<BOOL:${BACDL_MSTP}>:BACDL_MSTP>
$<$<BOOL:${BACDL_ETHERNET}>:BACDL_ETHERNET> $<$<BOOL:${BACDL_ETHERNET}>:BACDL_ETHERNET>
@@ -733,6 +742,7 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
ports/linux/datetime-init.c ports/linux/datetime-init.c
$<$<BOOL:${BACDL_BIP}>:ports/linux/bip-init.c> $<$<BOOL:${BACDL_BIP}>:ports/linux/bip-init.c>
$<$<BOOL:${BACDL_BIP6}>:ports/linux/bip6.c> $<$<BOOL:${BACDL_BIP6}>:ports/linux/bip6.c>
$<$<BOOL:${BACDL_ZIGBEE}>:ports/linux/bzll-init.c>
$<$<BOOL:${BACDL_ARCNET}>:ports/linux/arcnet.c> $<$<BOOL:${BACDL_ARCNET}>:ports/linux/arcnet.c>
$<$<BOOL:${BACDL_MSTP}>:ports/linux/rs485.c> $<$<BOOL:${BACDL_MSTP}>:ports/linux/rs485.c>
$<$<BOOL:${BACDL_MSTP}>:ports/linux/rs485.h> $<$<BOOL:${BACDL_MSTP}>:ports/linux/rs485.h>
@@ -761,6 +771,7 @@ elseif(WIN32)
ports/win32/bacport.h ports/win32/bacport.h
$<$<BOOL:${BACDL_BIP6}>:ports/win32/bip6.c> $<$<BOOL:${BACDL_BIP6}>:ports/win32/bip6.c>
$<$<BOOL:${BACDL_BIP}>:ports/win32/bip-init.c> $<$<BOOL:${BACDL_BIP}>:ports/win32/bip-init.c>
$<$<BOOL:${BACDL_ZIGBEE}>:ports/win32/bzll-init.c>
ports/win32/datetime-init.c ports/win32/datetime-init.c
$<$<BOOL:${BACDL_MSTP}>:ports/win32/dlmstp.c> $<$<BOOL:${BACDL_MSTP}>:ports/win32/dlmstp.c>
# ports/win32/dlmstp-mm.c # ports/win32/dlmstp-mm.c
@@ -808,6 +819,7 @@ elseif(APPLE)
target_sources(${PROJECT_NAME} PRIVATE target_sources(${PROJECT_NAME} PRIVATE
ports/bsd/bacport.h ports/bsd/bacport.h
$<$<BOOL:${BACDL_BIP}>:ports/bsd/bip-init.c> $<$<BOOL:${BACDL_BIP}>:ports/bsd/bip-init.c>
$<$<BOOL:${BACDL_ZIGBEE}>:ports/bsd/bzll-init.c>
$<$<BOOL:${BACDL_BIP6}>:ports/bsd/bip6.c> $<$<BOOL:${BACDL_BIP6}>:ports/bsd/bip6.c>
$<$<BOOL:${BACDL_MSTP}>:ports/bsd/rs485.c> $<$<BOOL:${BACDL_MSTP}>:ports/bsd/rs485.c>
$<$<BOOL:${BACDL_MSTP}>:ports/bsd/rs485.h> $<$<BOOL:${BACDL_MSTP}>:ports/bsd/rs485.h>
@@ -848,6 +860,7 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
target_sources(${PROJECT_NAME} PRIVATE target_sources(${PROJECT_NAME} PRIVATE
ports/bsd/bacport.h ports/bsd/bacport.h
$<$<BOOL:${BACDL_BIP}>:ports/bsd/bip-init.c> $<$<BOOL:${BACDL_BIP}>:ports/bsd/bip-init.c>
$<$<BOOL:${BACDL_ZIGBEE}>:ports/bsd/bzll-init.c>
$<$<BOOL:${BACDL_BIP6}>:ports/bsd/bip6.c> $<$<BOOL:${BACDL_BIP6}>:ports/bsd/bip6.c>
$<$<BOOL:${BACDL_MSTP}>:ports/bsd/rs485.c> $<$<BOOL:${BACDL_MSTP}>:ports/bsd/rs485.c>
$<$<BOOL:${BACDL_MSTP}>:ports/bsd/rs485.h> $<$<BOOL:${BACDL_MSTP}>:ports/bsd/rs485.h>
@@ -1231,4 +1244,5 @@ message(STATUS "BACNET: BACDL_BIP:......................\"${BACDL_BIP}\"")
message(STATUS "BACNET: BACDL_BSC:......................\"${BACDL_BSC}\"") message(STATUS "BACNET: BACDL_BSC:......................\"${BACDL_BSC}\"")
message(STATUS "BACNET: BACDL_ARCNET:...................\"${BACDL_ARCNET}\"") message(STATUS "BACNET: BACDL_ARCNET:...................\"${BACDL_ARCNET}\"")
message(STATUS "BACNET: BACDL_MSTP:.....................\"${BACDL_MSTP}\"") message(STATUS "BACNET: BACDL_MSTP:.....................\"${BACDL_MSTP}\"")
message(STATUS "BACNET: BACDL_ZIGBEE:...................\"${BACDL_ZIGBEE}\"")
message(STATUS "BACNET: BACDL_ETHERNET:.................\"${BACDL_ETHERNET}\"") message(STATUS "BACNET: BACDL_ETHERNET:.................\"${BACDL_ETHERNET}\"")
+8
View File
@@ -47,6 +47,10 @@ PORT_MSTP_SRC = \
PORT_ETHERNET_SRC = \ PORT_ETHERNET_SRC = \
$(BACNET_PORT_DIR)/ethernet.c $(BACNET_PORT_DIR)/ethernet.c
PORT_ZIGBEE_SRC = \
$(BACNET_PORT_DIR)/bzll-init.c \
$(BACNET_SRC_DIR)/bacnet/basic/bzll/bzllvmac.c
PORT_BIP_SRC = \ PORT_BIP_SRC = \
$(BACNET_PORT_DIR)/bip-init.c \ $(BACNET_PORT_DIR)/bip-init.c \
$(BACNET_SRC_DIR)/bacnet/basic/bbmd/h_bbmd.c $(BACNET_SRC_DIR)/bacnet/basic/bbmd/h_bbmd.c
@@ -81,6 +85,7 @@ PORT_ALL_SRC = \
$(PORT_ARCNET_SRC) \ $(PORT_ARCNET_SRC) \
$(PORT_MSTP_SRC) \ $(PORT_MSTP_SRC) \
$(PORT_ETHERNET_SRC) \ $(PORT_ETHERNET_SRC) \
$(PORT_ZIGBEE_SRC) \
$(PORT_BIP_SRC) \ $(PORT_BIP_SRC) \
$(PORT_BIP6_SRC) \ $(PORT_BIP6_SRC) \
$(PORT_BSC_SRC) $(PORT_BSC_SRC)
@@ -103,6 +108,9 @@ endif
ifeq (${BACDL_DEFINE},-DBACDL_ETHERNET=1) ifeq (${BACDL_DEFINE},-DBACDL_ETHERNET=1)
BACNET_PORT_SRC = ${PORT_ETHERNET_SRC} ${APPS_ENVIRONMENT_SRC} BACNET_PORT_SRC = ${PORT_ETHERNET_SRC} ${APPS_ENVIRONMENT_SRC}
endif endif
ifeq (${BACDL_DEFINE},-DBACDL_ZIGBEE=1)
BACNET_PORT_SRC = ${PORT_ZIGBEE_SRC} ${APPS_ENVIRONMENT_SRC}
endif
ifeq (${BACDL_DEFINE},-DBACDL_NONE=1) ifeq (${BACDL_DEFINE},-DBACDL_NONE=1)
BACNET_PORT_SRC = ${PORT_NONE_SRC} BACNET_PORT_SRC = ${PORT_NONE_SRC}
endif endif
+118
View File
@@ -0,0 +1,118 @@
/**
* @file
* @brief Initializes BACnet Zigbee Link Layer interface (BSD).
* @author Steve Karg <skarg@users.sourceforge.net>
* @date July 2025
* @copyright SPDX-License-Identifier: MIT
*/
/* BACnet specific */
#include "bacnet/bacdef.h"
#include "bacnet/bacaddr.h"
#include "bacnet/bacint.h"
#include "bacnet/datalink/bzll.h"
#include "bacnet/basic/sys/debug.h"
#include "bacnet/basic/object/device.h"
/**
* @brief Initialize the datalink
* @param ifname - the name of the datalink
*/
bool bzll_init(char *ifname)
{
(void)ifname;
return true;
}
/**
* @brief Send a protocol data unit (PDU) to the network
* @param dest - destination address
* @param npdu_data - network routing data
* @param pdu - protocol data unit (PDU) to send
* @param pdu_len - size of the protocol data unit (PDU)
*/
int bzll_send_pdu(
BACNET_ADDRESS *dest,
BACNET_NPDU_DATA *npdu_data,
uint8_t *pdu,
unsigned pdu_len)
{
(void)dest;
(void)npdu_data;
(void)pdu;
(void)pdu_len;
return 0;
}
/**
* @brief Poll the datalink queue to see if a packet has arrived
* @param src - origin address of the packet
* @param pdu - protocol data unit (PDU) buffer to store received packet
* @param pdu_size - size of the protocol data unit (PDU) buffer
* @param timeout - number of milliseconds to wait for a packet
*/
uint16_t bzll_receive(
BACNET_ADDRESS *src, uint8_t *pdu, uint16_t pdu_size, unsigned timeout)
{
(void)src;
(void)pdu;
(void)pdu_size;
(void)timeout;
return 0;
}
/**
* @brief cleanup the datalink data or connections
*/
void bzll_cleanup(void)
{
/* nothing to do */
}
/**
* @brief Initialize the a data link broadcast address
* @param dest - address to be filled with broadcast designator
*/
void bzll_get_broadcast_address(BACNET_ADDRESS *dest)
{
int i = 0;
if (dest) {
dest->mac_len = 3;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->mac[i] = 0xFF;
}
dest->net = BACNET_BROADCAST_NETWORK;
/* always zero when DNET is broadcast */
dest->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = 0;
}
}
return;
}
/**
* @brief Set the BACnet address for my interface
* @param my_address - address to be filled with my interface address
*/
void bzll_get_my_address(BACNET_ADDRESS *my_address)
{
uint32_t instance;
instance = Device_Object_Instance_Number();
bacnet_vmac_address_set(my_address, instance);
return;
}
/**
* @brief Set the maintenance timer for the datalink
* @param seconds - number of seconds to set the timer
*/
void bzll_maintenance_timer(uint16_t seconds)
{
(void)seconds;
}
+119
View File
@@ -0,0 +1,119 @@
/**
* @file
* @brief Initializes BACnet Zigbee Link Layer interface (Linux).
* @author Steve Karg <skarg@users.sourceforge.net>
* @date July 2025
* @copyright SPDX-License-Identifier: MIT
*/
/* BACnet specific */
#include "bacnet/bacdef.h"
#include "bacnet/bacaddr.h"
#include "bacnet/bacint.h"
#include "bacnet/datalink/bzll.h"
#include "bacnet/basic/sys/debug.h"
#include "bacnet/basic/object/device.h"
/**
* @brief Initialize the datalink
* @param ifname - the name of the datalink
*/
bool bzll_init(char *ifname)
{
(void)ifname;
return true;
}
/**
* @brief Send a protocol data unit (PDU) to the network
* @param dest - destination address
* @param npdu_data - network routing data
* @param pdu - protocol data unit (PDU) to send
* @param pdu_len - size of the protocol data unit (PDU)
*/
int bzll_send_pdu(
BACNET_ADDRESS *dest,
BACNET_NPDU_DATA *npdu_data,
uint8_t *pdu,
unsigned pdu_len)
{
(void)dest;
(void)npdu_data;
(void)pdu;
(void)pdu_len;
return 0;
}
/**
* @brief Poll the datalink queue to see if a packet has arrived
* @param src - origin address of the packet
* @param pdu - protocol data unit (PDU) buffer to store received packet
* @param pdu_size - size of the protocol data unit (PDU) buffer
* @param timeout - number of milliseconds to wait for a packet
*/
uint16_t bzll_receive(
BACNET_ADDRESS *src, uint8_t *pdu, uint16_t pdu_size, unsigned timeout)
{
(void)src;
(void)pdu;
(void)pdu_size;
(void)timeout;
return 0;
}
/**
* @brief cleanup the datalink data or connections
*/
void bzll_cleanup(void)
{
/* nothing to do */
}
/**
* @brief Initialize the a data link broadcast address
* @param dest - address to be filled with broadcast designator
*/
void bzll_get_broadcast_address(BACNET_ADDRESS *dest)
{
int i = 0;
if (dest) {
dest->mac_len = 3;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->mac[i] = 0xFF;
}
dest->net = BACNET_BROADCAST_NETWORK;
/* always zero when DNET is broadcast */
dest->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = 0;
}
}
return;
}
/**
* @brief Set the BACnet address for my interface
* @param my_address - address to be filled with my interface address
*/
void bzll_get_my_address(BACNET_ADDRESS *my_address)
{
uint32_t instance;
instance = Device_Object_Instance_Number();
bacnet_vmac_address_set(my_address, instance);
return;
}
/**
* @brief Set the maintenance timer for the datalink
* @param seconds - number of seconds to set the timer
*/
void bzll_maintenance_timer(uint16_t seconds)
{
(void)seconds;
}
+119
View File
@@ -0,0 +1,119 @@
/**
* @file
* @brief Initializes BACnet Zigbee Link Layer interface (Windows).
* @author Steve Karg <skarg@users.sourceforge.net>
* @date July 2025
* @copyright SPDX-License-Identifier: MIT
*/
/* BACnet specific */
#include "bacnet/bacdef.h"
#include "bacnet/bacaddr.h"
#include "bacnet/bacint.h"
#include "bacnet/datalink/bzll.h"
#include "bacnet/basic/sys/debug.h"
#include "bacnet/basic/object/device.h"
/**
* @brief Initialize the datalink
* @param ifname - the name of the datalink
*/
bool bzll_init(char *ifname)
{
(void)ifname;
return true;
}
/**
* @brief Send a protocol data unit (PDU) to the network
* @param dest - destination address
* @param npdu_data - network routing data
* @param pdu - protocol data unit (PDU) to send
* @param pdu_len - size of the protocol data unit (PDU)
*/
int bzll_send_pdu(
BACNET_ADDRESS *dest,
BACNET_NPDU_DATA *npdu_data,
uint8_t *pdu,
unsigned pdu_len)
{
(void)dest;
(void)npdu_data;
(void)pdu;
(void)pdu_len;
return 0;
}
/**
* @brief Poll the datalink queue to see if a packet has arrived
* @param src - origin address of the packet
* @param pdu - protocol data unit (PDU) buffer to store received packet
* @param pdu_size - size of the protocol data unit (PDU) buffer
* @param timeout - number of milliseconds to wait for a packet
*/
uint16_t bzll_receive(
BACNET_ADDRESS *src, uint8_t *pdu, uint16_t pdu_size, unsigned timeout)
{
(void)src;
(void)pdu;
(void)pdu_size;
(void)timeout;
return 0;
}
/**
* @brief cleanup the datalink data or connections
*/
void bzll_cleanup(void)
{
/* nothing to do */
}
/**
* @brief Initialize the a data link broadcast address
* @param dest - address to be filled with broadcast designator
*/
void bzll_get_broadcast_address(BACNET_ADDRESS *dest)
{
int i = 0;
if (dest) {
dest->mac_len = 3;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->mac[i] = 0xFF;
}
dest->net = BACNET_BROADCAST_NETWORK;
/* always zero when DNET is broadcast */
dest->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = 0;
}
}
return;
}
/**
* @brief Set the BACnet address for my interface
* @param my_address - address to be filled with my interface address
*/
void bzll_get_my_address(BACNET_ADDRESS *my_address)
{
uint32_t instance;
instance = Device_Object_Instance_Number();
bacnet_vmac_address_set(my_address, instance);
return;
}
/**
* @brief Set the maintenance timer for the datalink
* @param seconds - number of seconds to set the timer
*/
void bzll_maintenance_timer(uint16_t seconds)
{
(void)seconds;
}
+26
View File
@@ -539,3 +539,29 @@ int bacnet_vmac_entry_decode(
return apdu_len; return apdu_len;
} }
/** Set a BACnet VMAC Address from a Device ID
*
* @param addr - BACnet address that be set
* @param device_id - 22-bit device ID
*
* @return true if the address is set
*/
bool bacnet_vmac_address_set(BACNET_ADDRESS *addr, uint32_t device_id)
{
bool status = false;
size_t i;
if (addr) {
encode_unsigned24(&addr->mac[0], device_id);
addr->mac_len = 3;
addr->net = 0;
addr->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
addr->adr[i] = 0;
}
status = true;
}
return status;
}
+2
View File
@@ -87,6 +87,8 @@ int bacnet_vmac_entry_encode(
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
int bacnet_vmac_entry_decode( int bacnet_vmac_entry_decode(
const uint8_t *apdu, uint32_t apdu_size, BACNET_VMAC_ENTRY *value); const uint8_t *apdu, uint32_t apdu_size, BACNET_VMAC_ENTRY *value);
BACNET_STACK_EXPORT
bool bacnet_vmac_address_set(BACNET_ADDRESS *addr, uint32_t device_id);
#ifdef __cplusplus #ifdef __cplusplus
} }
+305
View File
@@ -0,0 +1,305 @@
/**
* @file
* @brief Virtual MAC (VMAC) for BACnet ZigBee Link Layer
* @author Steve Karg <skarg@users.sourceforge.net>
* @date July 2025
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
*/
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "bacnet/bacdef.h"
#include "bacnet/basic/sys/debug.h"
#include "bacnet/basic/sys/keylist.h"
/* me! */
#include "bacnet/basic/bzll/bzllvmac.h"
/* enable debugging */
static bool VMAC_Debug = false;
/**
* @brief Enable debugging if print is enabled
*/
void BZLL_VMAC_Debug_Enable(void)
{
VMAC_Debug = true;
}
/** @file
Handle VMAC address binding */
/* This module is used to handle the virtual MAC address binding that */
/* occurs in BACnet for ZigBee or IPv6. */
/* Key List for storing the object data sorted by instance number */
static OS_Keylist VMAC_List;
/**
* Returns the number of VMAC in the list
*/
unsigned int BZLL_VMAC_Count(void)
{
return (unsigned int)Keylist_Count(VMAC_List);
}
/**
* Adds a VMAC to the list
*
* @param device_id - BACnet device object instance number
* @param vmac - BACnet ZigBee Link Layer address
*
* @return true if the device ID and MAC are added
*/
bool BZLL_VMAC_Add(uint32_t device_id, const struct bzll_vmac_data *vmac)
{
bool status = false;
struct bzll_vmac_data *list_vmac = NULL;
uint32_t list_device_id = 0;
int index = 0;
size_t i = 0;
bool found = false;
if (BZLL_VMAC_Entry_To_Device_ID(vmac, &list_device_id)) {
if (list_device_id == device_id) {
/* valid VMAC entry exists. */
found = true;
status = true;
} else {
/* VMAC exists, but device ID changed */
BZLL_VMAC_Delete(list_device_id);
}
}
if (!found) {
list_vmac = Keylist_Data(VMAC_List, device_id);
if (list_vmac) {
/* device ID already exists. Update MAC. */
memmove(list_vmac, vmac, sizeof(struct bzll_vmac_data));
found = true;
status = true;
}
}
if (!found) {
/* new entry - add it! */
list_vmac = calloc(1, sizeof(struct bzll_vmac_data));
if (list_vmac) {
/* copy the MAC into the data store */
for (i = 0; i < sizeof(list_vmac->mac); i++) {
list_vmac->mac[i] = vmac->mac[i];
}
list_vmac->endpoint = vmac->endpoint;
index = Keylist_Data_Add(VMAC_List, device_id, list_vmac);
if (index >= 0) {
status = true;
if (VMAC_Debug) {
debug_fprintf(
stderr, "BZLL VMAC %u added.\n",
(unsigned int)device_id);
}
}
}
}
return status;
}
/**
* Finds a VMAC in the list by seeking the Device ID, and deletes it.
*
* @param device_id - BACnet device object instance number
*
* @return pointer to the VMAC data from the list - be sure to free() it!
*/
bool BZLL_VMAC_Delete(uint32_t device_id)
{
bool status = false;
struct bzll_vmac_data *pVMAC;
pVMAC = Keylist_Data_Delete(VMAC_List, device_id);
if (pVMAC) {
free(pVMAC);
status = true;
}
return status;
}
/**
* Finds a VMAC in the list by seeking the Device ID.
*
* @param device_id - BACnet device object instance number
*
* @return pointer to the VMAC data from the list
*/
bool BZLL_VMAC_Entry_By_Device_ID(
uint32_t device_id, struct bzll_vmac_data *vmac)
{
struct bzll_vmac_data *data = Keylist_Data(VMAC_List, device_id);
if (data && vmac) {
memcpy(vmac, data, sizeof(struct bzll_vmac_data));
return true;
}
return false;
}
/**
* Finds a VMAC in the list by seeking the list index
*
* @param index - Index that shall be returned
* @param device_id - BACnet device object instance number
*
* @return true if the device_id and vmac are found
*/
bool BZLL_VMAC_Entry_By_Index(
int index, uint32_t *device_id, struct bzll_vmac_data *vmac)
{
bool status = false;
struct bzll_vmac_data *data;
KEY key = 0;
data = Keylist_Data_Index(VMAC_List, index);
if (data) {
/* virtual MAC is the Device ID */
status = Keylist_Index_Key(VMAC_List, index, &key);
if (status) {
if (device_id) {
*device_id = key;
}
if (vmac) {
memcpy(vmac, data, sizeof(struct bzll_vmac_data));
}
}
}
return status;
}
/** Compare the VMAC address
*
* @param vmac1 - VMAC address that will be compared to vmac2
* @param vmac2 - VMAC address that will be compared to vmac1
*
* @return true if the addresses are the same
*/
bool BZLL_VMAC_Same(
const struct bzll_vmac_data *vmac1, const struct bzll_vmac_data *vmac2)
{
bool status = false;
if (vmac1 && vmac2) {
if (memcmp(vmac1->mac, vmac2->mac, BZLL_VMAC_EUI64) == 0 &&
vmac1->endpoint == vmac2->endpoint) {
status = true;
}
}
return status;
}
/**
* Finds a VMAC in the list by seeking a matching VMAC address
*
* @param vmac - VMAC address that will be sought
* @param device_id - BACnet device object instance number
*
* @return true if the VMAC address was found
*/
bool BZLL_VMAC_Entry_To_Device_ID(
const struct bzll_vmac_data *vmac, uint32_t *device_id)
{
bool status = false;
struct bzll_vmac_data *list_vmac;
int count = 0;
int index = 0;
if (!vmac) {
return false; /* invalid parameter */
}
count = Keylist_Count(VMAC_List);
while (count) {
index = count - 1;
list_vmac = Keylist_Data_Index(VMAC_List, index);
if (list_vmac) {
if (BZLL_VMAC_Same(vmac, list_vmac)) {
status = Keylist_Index_Key(VMAC_List, index, device_id);
break;
}
}
count--;
}
return status;
}
/**
* Copies the VMAC address into the provided MAC and endpoint
*
* @param vmac - VMAC address that will be copied
* @param mac - pointer to the MAC array to copy into
* @param endpoint - pointer to the endpoint to copy into
*
* @return true if the VMAC address was copied
*/
bool BZLL_VMAC_Entry_Set(
struct bzll_vmac_data *vmac, const uint8_t *mac, uint8_t endpoint)
{
bool status = false;
unsigned int i;
if (vmac && mac) {
for (i = 0; i < BZLL_VMAC_EUI64; i++) {
vmac->mac[i] = mac[i]; /* copy the MAC */
}
vmac->endpoint = endpoint;
}
return status;
}
/**
* Cleans up the memory used by the VMAC list data
*/
void BZLL_VMAC_Cleanup(void)
{
struct bzll_vmac_data *pVMAC;
const int index = 0;
unsigned i = 0;
if (VMAC_List) {
do {
uint32_t device_id;
if (VMAC_Debug) {
Keylist_Index_Key(VMAC_List, index, &device_id);
}
pVMAC = Keylist_Data_Delete_By_Index(VMAC_List, index);
if (pVMAC) {
if (VMAC_Debug) {
debug_fprintf(
stderr, "BZLL VMAC List: %lu [",
(unsigned long)device_id);
/* print the MAC */
for (i = 0; i < BZLL_VMAC_EUI64; i++) {
debug_fprintf(stderr, "%02X", pVMAC->mac[i]);
}
debug_fprintf(stderr, "]\n");
}
free(pVMAC);
}
} while (pVMAC);
Keylist_Delete(VMAC_List);
VMAC_List = NULL;
}
}
/**
* Initializes the VMAC list data
*/
void BZLL_VMAC_Init(void)
{
VMAC_List = Keylist_Create();
if (VMAC_List) {
atexit(BZLL_VMAC_Cleanup);
debug_fprintf(stderr, "BZLL VMAC List initialized.\n");
}
}
+66
View File
@@ -0,0 +1,66 @@
/**
* @file
* @brief API for Virtual MAC (VMAC) of BACnet Zigbee Link Layer (BZLL)
* @author Steve Karg <skarg@users.sourceforge.net>
* @date July 2025
* @copyright SPDX-License-Identifier: MIT
*/
#ifndef BACNET_BASIC_BZLL_VMAC_H
#define BACNET_BASIC_BZLL_VMAC_H
#include <stdint.h>
#include <stdbool.h>
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
/* define the max MAC as big as EUI64 */
#define BZLL_VMAC_EUI64 8
/**
* VMAC data structure
*
* @{
*/
struct bzll_vmac_data {
uint8_t mac[BZLL_VMAC_EUI64];
uint8_t endpoint;
};
/** @} */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
BACNET_STACK_EXPORT
bool BZLL_VMAC_Entry_By_Device_ID(
uint32_t device_id, struct bzll_vmac_data *vmac);
BACNET_STACK_EXPORT
bool BZLL_VMAC_Entry_By_Index(
int index, uint32_t *device_id, struct bzll_vmac_data *vmac);
BACNET_STACK_EXPORT
bool BZLL_VMAC_Entry_To_Device_ID(
const struct bzll_vmac_data *vmac, uint32_t *device_id);
BACNET_STACK_EXPORT
bool BZLL_VMAC_Entry_Set(
struct bzll_vmac_data *vmac, const uint8_t *mac, uint8_t endpoint);
BACNET_STACK_EXPORT
bool BZLL_VMAC_Add(uint32_t device_id, const struct bzll_vmac_data *vmac);
BACNET_STACK_EXPORT
bool BZLL_VMAC_Delete(uint32_t device_id);
BACNET_STACK_EXPORT
bool BZLL_VMAC_Same(
const struct bzll_vmac_data *vmac1, const struct bzll_vmac_data *vmac2);
BACNET_STACK_EXPORT
unsigned int BZLL_VMAC_Count(void);
BACNET_STACK_EXPORT
void BZLL_VMAC_Cleanup(void);
BACNET_STACK_EXPORT
void BZLL_VMAC_Init(void);
BACNET_STACK_EXPORT
void BZLL_VMAC_Debug_Enable(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+3 -3
View File
@@ -276,7 +276,7 @@ void datalink_cleanup(void)
#endif #endif
#if defined(BACDL_ZIGBEE) #if defined(BACDL_ZIGBEE)
case DATALINK_ZIGBEE: case DATALINK_ZIGBEE:
bytes = bzll_cleanup(); bzll_cleanup();
break; break;
#endif #endif
#if defined(BACDL_BSC) #if defined(BACDL_BSC)
@@ -321,7 +321,7 @@ void datalink_get_broadcast_address(BACNET_ADDRESS *dest)
#endif #endif
#if defined(BACDL_ZIGBEE) #if defined(BACDL_ZIGBEE)
case DATALINK_ZIGBEE: case DATALINK_ZIGBEE:
bytes = bzll_get_broadcast_address(dest); bzll_get_broadcast_address(dest);
break; break;
#endif #endif
#if defined(BACDL_BSC) #if defined(BACDL_BSC)
@@ -366,7 +366,7 @@ void datalink_get_my_address(BACNET_ADDRESS *my_address)
#endif #endif
#if defined(BACDL_ZIGBEE) #if defined(BACDL_ZIGBEE)
case DATALINK_ZIGBEE: case DATALINK_ZIGBEE:
bytes = bzll_get_my_address(my_address); bzll_get_my_address(my_address);
break; break;
#endif #endif
#if defined(BACDL_BSC) #if defined(BACDL_BSC)
+1
View File
@@ -133,6 +133,7 @@ list(APPEND testdirs
bacnet/basic/binding/address bacnet/basic/binding/address
bacnet/basic/bbmd bacnet/basic/bbmd
bacnet/basic/bbmd6 bacnet/basic/bbmd6
bacnet/basic/bzll
# basic/object # basic/object
bacnet/basic/object/acc bacnet/basic/object/acc
bacnet/basic/object/access_credential bacnet/basic/object/access_credential
+46
View File
@@ -0,0 +1,46 @@
# SPDX-License-Identifier: MIT
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME)
project(test_${basename}
VERSION 1.0.0
LANGUAGES C)
string(REGEX REPLACE
"/test/bacnet/[a-zA-Z0-9_/-]*$"
"/src"
SRC_DIR
${CMAKE_CURRENT_SOURCE_DIR})
string(REGEX REPLACE
"/test/bacnet/[a-zA-Z0-9_/-]*$"
"/test"
TST_DIR
${CMAKE_CURRENT_SOURCE_DIR})
add_compile_definitions(
BIG_ENDIAN=0
)
include_directories(
${SRC_DIR}
)
add_executable(${PROJECT_NAME}
# File(s) under test
${SRC_DIR}/bacnet/basic/bzll/bzllvmac.c
# Support files and stubs (pathname alphabetical)
${SRC_DIR}/bacnet/bacaddr.c
${SRC_DIR}/bacnet/bacdcode.c
${SRC_DIR}/bacnet/bacint.c
${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bacreal.c
${SRC_DIR}/bacnet/iam.c
${SRC_DIR}/bacnet/npdu.c
${SRC_DIR}/bacnet/basic/sys/bigend.c
${SRC_DIR}/bacnet/basic/sys/debug.c
${SRC_DIR}/bacnet/basic/sys/keylist.c
${SRC_DIR}/bacnet/hostnport.c
# Test and test library files
./src/main.c
)
+176
View File
@@ -0,0 +1,176 @@
/**
* @file
* @brief Test file for a basic BACnet Zigbee Link Layer (BZLL)
* @author Steve Karg
* @date July 2025
* @copyright SPDX-License-Identifier: MIT
*/
#include <stdio.h> /* for standard i/o, like printing */
#include <stdint.h> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include <string.h> /* for memcpy */
#include <assert.h>
#include <string.h>
#include "bacnet/bacaddr.h"
#include "bacnet/bacdcode.h"
#include "bacnet/iam.h"
#include "bacnet/npdu.h"
#include "bacnet/datalink/bzll.h"
#include "bacnet/basic/sys/debug.h"
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/bzll/bzllvmac.h"
struct device_info_t {
uint32_t Device_ID;
/* MAC Address shall be a ZigBee EUI64 and BACnet endpoint */
struct bzll_vmac_data VMAC_Data;
BACNET_ADDRESS BACnet_Address;
};
static struct device_info_t TD;
static struct device_info_t IUT;
/* network stub functions */
/**
* BACnet/IP Datalink Receive handler.
*
* @param src - returns the source address
* @param npdu - returns the NPDU buffer
* @param max_npdu -maximum size of the NPDU buffer
* @param timeout - number of milliseconds to wait for a packet
*
* @return Number of bytes received, or 0 if none or timeout.
*/
uint16_t bzll_receive(
BACNET_ADDRESS *src, uint8_t *npdu, uint16_t max_npdu, unsigned timeout)
{
(void)src;
(void)npdu;
(void)max_npdu;
(void)timeout;
return 0;
}
/**
* The send function for BACnet/IPv6 driver layer
*
* @param dest - Points to a BACNET_IP6_ADDRESS structure containing the
* destination address.
* @param mtu - the bytes of data to send
* @param mtu_len - the number of bytes of data to send
*
* @return Upon successful completion, returns the number of bytes sent.
* Otherwise, -1 shall be returned to indicate the error.
*/
int bzll_send_mpdu(
const BACNET_IP6_ADDRESS *dest, const uint8_t *mtu, uint16_t mtu_len)
{
(void)dest;
(void)mtu;
(void)mtu_len;
return 0;
}
/** Return the Object Instance number for our (single) Device Object.
* This is a key function, widely invoked by the handler code, since
* it provides "our" (ie, local) address.
*
* @return The Instance number used in the BACNET_OBJECT_ID for the Device.
*/
uint32_t Device_Object_Instance_Number(void)
{
return IUT.Device_ID;
}
/**
* Test setup function
*/
static void test_setup(void)
{
uint8_t td_mac[BZLL_VMAC_EUI64] = { 0x00, 0x12, 0x34, 0x56,
0x78, 0x9A, 0xBC, 0xDE };
uint8_t td_endpoint = 0x01;
uint8_t iut_mac[BZLL_VMAC_EUI64] = { 0x00, 0x12, 0x34, 0x56,
0x78, 0x9A, 0xBC, 0xDF };
uint8_t iut_endpoint = 0x02;
BZLL_VMAC_Init();
TD.Device_ID = 12345;
bacnet_vmac_address_set(&TD.BACnet_Address, TD.Device_ID);
BZLL_VMAC_Entry_Set(&TD.VMAC_Data, td_mac, td_endpoint);
IUT.Device_ID = 67890;
bacnet_vmac_address_set(&IUT.BACnet_Address, IUT.Device_ID);
BZLL_VMAC_Entry_Set(&IUT.VMAC_Data, iut_mac, iut_endpoint);
}
/**
* Cleanup function to free resources
*/
static void test_cleanup(void)
{
BZLL_VMAC_Cleanup();
}
/**
* Test function to execute the virtual address resolution
* and verify the functionality of the BZLL VMAC handling.
*
* This function will test adding, retrieving, and comparing VMAC entries.
* It will also check the behavior when changing device IDs.
*/
static void test_Execute_Virtual_Address_Resolution(void)
{
uint32_t test_vmac_src = 0;
uint32_t test_device_id = 0;
uint32_t old_device_id = 0;
struct bzll_vmac_data test_vmac_data = { 0 };
unsigned int count = 0;
int index = 0;
bool status = false;
test_setup();
status = BZLL_VMAC_Add(TD.Device_ID, &TD.VMAC_Data);
assert(status == true);
status = BZLL_VMAC_Entry_By_Device_ID(TD.Device_ID, &test_vmac_data);
assert(status == true);
status = BZLL_VMAC_Same(&TD.VMAC_Data, &test_vmac_data);
assert(status == true);
/* change Device ID */
old_device_id = TD.Device_ID;
TD.Device_ID += 42;
status = BZLL_VMAC_Add(TD.Device_ID, &TD.VMAC_Data);
assert(status == true);
count = BZLL_VMAC_Count();
assert(count == 1);
status = BZLL_VMAC_Entry_By_Device_ID(TD.Device_ID, &test_vmac_data);
assert(status == true);
status = BZLL_VMAC_Entry_By_Device_ID(old_device_id, &test_vmac_data);
assert(status == false);
status = BZLL_VMAC_Entry_By_Index(0, &test_device_id, &test_vmac_data);
assert(status == true);
assert(test_device_id == TD.Device_ID);
status = BZLL_VMAC_Same(&TD.VMAC_Data, &test_vmac_data);
assert(status == true);
status = BZLL_VMAC_Add(IUT.Device_ID, &IUT.VMAC_Data);
assert(status == true);
count = BZLL_VMAC_Count();
for (index = 0; index < count; index++) {
status = BZLL_VMAC_Entry_By_Index(index, &test_vmac_src, NULL);
assert(status == true);
status = BZLL_VMAC_Entry_By_Device_ID(test_vmac_src, &test_vmac_data);
assert(status == true);
}
test_cleanup();
}
/**
* Main function to execute the test
*
* @return 0 on success, non-zero on failure
*/
int main(void)
{
test_Execute_Virtual_Address_Resolution();
return 0;
}