369da70f2a
* format: Strip trailing whitespaces
We want to get rid of trailing whitespaces completly as they make just git
noice. Much better to start using automated tools to get rid of them once and
not getting them back again. This way git history will be cleaner and review
easier.
Commit was generated with:
pre-commit run --all-files trailing-whitespace
* format: Files should have exactly one new line end of them
It is good practice that every file has one new line. It is not now days so
mandatory but it also is not nice if file has lot of newlines end of it. We will
use pre-commit which takes automatically care about this so let's fix all.
Commit was generated with:
pre-commit run --all-files end-of-file-fixer
* format: Convert tabs to spaces
Project mostly use spaces over tabs. When mixing tabs and spaces this usually
makes formatting issues and also when changing those in commits it will make lot
of git noise. We will force spaces most of the time and use pre-commit to fix.
Commit was generated with:
pre-commit run --all-files remove-tabs
---------
Co-authored-by: Kari Argillander <kari.argillander@fidelix.com>
243 lines
7.8 KiB
C
243 lines
7.8 KiB
C
/*
|
|
* Copyright (c) 2020 Legrand North America, LLC.
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include <stdalign.h> /*TODO: Not std until C11! */
|
|
#include <device.h>
|
|
#include <init.h>
|
|
#include <kernel.h>
|
|
#include <net/net_if.h>
|
|
#include <net/net_core.h>
|
|
#include <net/net_context.h>
|
|
#include <net/net_mgmt.h>
|
|
#include <net/net_ip.h>
|
|
#include <sys/printk.h>
|
|
/* some BACnet modules we use */
|
|
#include "bacnet/bacdef.h"
|
|
#include "bacnet/config.h"
|
|
#include "bacnet/bactext.h"
|
|
#include "bacnet/bacerror.h"
|
|
#include "bacnet/iam.h"
|
|
#include "bacnet/arf.h"
|
|
#include "bacnet/npdu.h"
|
|
#include "bacnet/apdu.h"
|
|
#include "bacnet/version.h"
|
|
/* some demo modules we use */
|
|
#include "bacnet/basic/sys/debug.h"
|
|
#include "bacnet/basic/tsm/tsm.h"
|
|
#include "bacnet/basic/binding/address.h"
|
|
#include "bacnet/basic/services.h"
|
|
/* our datalink layers */
|
|
#include "bacnet/datalink/bip.h"
|
|
#include "bacnet/datalink/bvlc.h"
|
|
/* include the device object */
|
|
#include "bacnet/basic/object/device.h"
|
|
|
|
/* Logging module registration is already done in ports/zephyr/main.c */
|
|
#include <logging/log.h>
|
|
LOG_MODULE_DECLARE(bacnet, CONFIG_BACNETSTACK_LOG_LEVEL);
|
|
|
|
enum bacnet_server_msg_type {
|
|
SERVER_MSG_TYPE_INVALID = 0,
|
|
SERVER_MSG_TYPE_IPV4_EVENT,
|
|
};
|
|
|
|
struct bacnet_server_msg {
|
|
uint8_t type;
|
|
uint8_t dummy[3];
|
|
uint32_t parm_u32;
|
|
void *parm_ptr;
|
|
};
|
|
|
|
K_MSGQ_DEFINE(bacnet_server_msgq, sizeof(struct bacnet_server_msg), 8,
|
|
alignof(struct bacnet_server_msg));
|
|
|
|
#define SERVER_IPV4_EVENTS_MASK \
|
|
(NET_EVENT_IPV4_ADDR_ADD | NET_EVENT_IPV4_ADDR_DEL)
|
|
|
|
static struct k_thread server_thread_data;
|
|
static K_THREAD_STACK_DEFINE(server_thread_stack,
|
|
CONFIG_BACNETSTACK_BACNET_SERVER_STACK_SIZE);
|
|
|
|
/* Keep a reference to the Ethernet interface */
|
|
static struct net_mgmt_event_callback mgmt_cb;
|
|
|
|
/* track our directly connected ports network number */
|
|
static uint16_t BIP_Net;
|
|
/* buffer for receiving packets from the directly connected ports */
|
|
static uint8_t BIP_Rx_Buffer[MAX_MPDU];
|
|
|
|
/** Initialize the handlers we will utilize.
|
|
* @see Device_Init, apdu_set_unconfirmed_handler, apdu_set_confirmed_handler
|
|
*/
|
|
static void service_handlers_init(void)
|
|
{
|
|
Device_Init(NULL);
|
|
/* we need to handle who-is to support dynamic device binding */
|
|
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,
|
|
handler_who_is);
|
|
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS,
|
|
handler_who_has);
|
|
/* set the handler for all the services we don't implement */
|
|
/* It is required to send the proper reject message... */
|
|
apdu_set_unrecognized_service_handler_handler(
|
|
handler_unrecognized_service);
|
|
/* Set the handlers for any confirmed services that we support. */
|
|
/* We must implement read property - it's required! */
|
|
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
|
|
handler_read_property);
|
|
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE,
|
|
handler_read_property_multiple);
|
|
apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
|
|
handler_write_property);
|
|
apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE,
|
|
handler_write_property_multiple);
|
|
apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE,
|
|
handler_reinitialize_device);
|
|
/* handle communication so we can shutup when asked */
|
|
apdu_set_confirmed_handler(
|
|
SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
|
|
handler_device_communication_control);
|
|
}
|
|
|
|
/* TODO: Update copyright as this code pattern copied from
|
|
* conn_mgr_ipv4_events_handler()
|
|
*/
|
|
static void ipv4_events_handler(struct net_mgmt_event_callback *cb,
|
|
u32_t mgmt_event, struct net_if *iface)
|
|
{
|
|
static int counter = 0;
|
|
printk("\n*** Handler[%d]: IPv4 event %08x received on iface %p ***\n",
|
|
++counter, mgmt_event, iface);
|
|
|
|
if ((mgmt_event & SERVER_IPV4_EVENTS_MASK) != mgmt_event) {
|
|
printk("\n*** Handler[%d]: ignoring event %08x on iface %p ***\n",
|
|
counter, mgmt_event, iface);
|
|
return;
|
|
}
|
|
|
|
struct bacnet_server_msg msg = {
|
|
.type = SERVER_MSG_TYPE_IPV4_EVENT,
|
|
.parm_u32 = mgmt_event,
|
|
.parm_ptr = iface,
|
|
};
|
|
|
|
int ret = k_msgq_put(&bacnet_server_msgq, &msg, K_NO_WAIT);
|
|
if (ret != 0) {
|
|
printk("\n*** Handler[%d]: queue full, type %u event 0x%08x on iface %p dropped! ***\n",
|
|
counter, msg.type, msg.parm_u32, msg.parm_ptr);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief BACnet Server Thread
|
|
*/
|
|
static void server_thread(void)
|
|
{
|
|
LOG_INF("Server: started");
|
|
service_handlers_init();
|
|
|
|
bip_init("Server: from init");
|
|
BIP_Net = 1;
|
|
|
|
net_mgmt_init_event_callback(&mgmt_cb, ipv4_events_handler,
|
|
SERVER_IPV4_EVENTS_MASK);
|
|
net_mgmt_add_event_callback(&mgmt_cb);
|
|
|
|
while (1) {
|
|
const s32_t sleep_ms = K_FOREVER;
|
|
|
|
struct bacnet_server_msg msg = {
|
|
.type = SERVER_MSG_TYPE_INVALID,
|
|
};
|
|
int ret = k_msgq_get(&bacnet_server_msgq, &msg, sleep_ms);
|
|
|
|
/* Waiting period timed out */
|
|
if (-EAGAIN == ret) {
|
|
BACNET_ADDRESS src = {
|
|
0
|
|
}; /* address where message came from */
|
|
/* input */
|
|
/* returns 0 bytes on timeout */
|
|
uint16_t pdu_len = bip_receive(&src, &BIP_Rx_Buffer[0],
|
|
MAX_MPDU, 5);
|
|
|
|
/* process */
|
|
if (pdu_len) {
|
|
LOG_INF("Server: BIP received %u bytes.",
|
|
(unsigned)pdu_len);
|
|
}
|
|
}
|
|
|
|
/* Message received */
|
|
else if (0 == ret) {
|
|
switch (msg.type) {
|
|
#if defined(CONFIG_NET_IPV4)
|
|
case SERVER_MSG_TYPE_IPV4_EVENT: {
|
|
LOG_INF("Server: MSG_TYPE_IPV4_EVENT u32: %08x ptr: %p",
|
|
msg.parm_u32, msg.parm_ptr);
|
|
const u32_t mgmt_event = msg.parm_u32;
|
|
//TODO: const struct net_if *iface = msg.parm_ptr;
|
|
|
|
/* Handle events */
|
|
if ((mgmt_event & SERVER_IPV4_EVENTS_MASK) !=
|
|
mgmt_event) {
|
|
LOG_INF("Server: thread ignoring event");
|
|
break;
|
|
}
|
|
|
|
switch (NET_MGMT_GET_COMMAND(mgmt_event)) {
|
|
#if 0
|
|
case NET_EVENT_IPV4_CMD_ADDR_ADD:
|
|
LOG_INF("Server: IPv4 addr activated");
|
|
bip_init("Server: from CMD_ADDR_ADD");
|
|
BIP_Net = bip_valid() ? 1 : 0;
|
|
break;
|
|
case NET_EVENT_IPV4_CMD_ADDR_DEL:
|
|
LOG_INF("Server: IPv4 addr deactivated");
|
|
bip_cleanup();
|
|
BIP_Net = 0;
|
|
break;
|
|
#endif
|
|
default:
|
|
LOG_INF("Server: Unsupported event %u",
|
|
mgmt_event);
|
|
break;
|
|
}
|
|
|
|
} break;
|
|
#endif /* defined(CONFIG_NET_IPV4) */
|
|
|
|
default:
|
|
LOG_WRN("Server: Dropping unsupported type %u",
|
|
msg.type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Returned without waiting and without message - why? */
|
|
else {
|
|
LOG_WRN("Server: Msgq returned w/o timeout or msg! req = %d",
|
|
ret);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int server_init(struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
k_thread_create(&server_thread_data, server_thread_stack,
|
|
K_THREAD_STACK_SIZEOF(server_thread_stack),
|
|
(k_thread_entry_t)server_thread, NULL, NULL, NULL,
|
|
K_PRIO_PREEMPT(CONFIG_BACNETSTACK_BACNET_SERVER_PRIO), 0,
|
|
K_NO_WAIT);
|
|
k_thread_name_set(&server_thread_data, "BACserver");
|
|
|
|
return 0;
|
|
}
|
|
|
|
SYS_INIT(server_init, APPLICATION, CONFIG_BACNETSTACK_BACNET_SERVER_APP_PRIORITY);
|