diff --git a/src/profeth/.gitignore b/src/profeth/.gitignore
new file mode 100644
index 00000000..5a3278d2
--- /dev/null
+++ b/src/profeth/.gitignore
@@ -0,0 +1,32 @@
+*~
+*.o
+*.la
+*.lo
+*.so
+.deps
+.libs
+
+/aclocal.m4
+/autom4te.cache/
+/aux
+/compile
+/config.h
+/config.h.in
+/config.guess
+/config.log
+/config.status
+/config.sub
+/configure
+/depcomp
+/install-sh
+/libtool
+/ltmain.sh
+/m4
+/missing
+
+GPATH
+GRTAGS
+GTAGS
+Makefile
+Makefile.in
+
diff --git a/src/profeth/GSDML-V2.43-Kernelkit-Infix-Switch-20230227.xml b/src/profeth/GSDML-V2.43-Kernelkit-Infix-Switch-20230227.xml
new file mode 100644
index 00000000..6e9c9199
--- /dev/null
+++ b/src/profeth/GSDML-V2.43-Kernelkit-Infix-Switch-20230227.xml
@@ -0,0 +1,180 @@
+
+
+
+
+ PROFINET Device Profile
+ 1.00
+ Device Profile for PROFINET Devices
+ PROFIBUS Nutzerorganisation e. V. (PNO)
+ Device
+
+ 4
+ 1
+ GSDML
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/profeth/Makefile.am b/src/profeth/Makefile.am
new file mode 100644
index 00000000..66442e23
--- /dev/null
+++ b/src/profeth/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = src
+DISTCLEANFILES = *~ *.d
+ACLOCAL_AMFLAGS = -I m4
diff --git a/src/profeth/configure.ac b/src/profeth/configure.ac
new file mode 100644
index 00000000..d6c3fc9d
--- /dev/null
+++ b/src/profeth/configure.ac
@@ -0,0 +1,42 @@
+AC_PREREQ(2.61)
+AC_INIT([profeth], [1.0.0],
+ [https://github.com/kernelkit/infix/issues])
+AM_INIT_AUTOMAKE(1.11 foreign subdir-objects)
+AM_SILENT_RULES(yes)
+
+LT_INIT
+
+AC_CONFIG_FILES([
+ Makefile
+ src/Makefile
+])
+
+AC_PROG_CC
+AC_PROG_INSTALL
+
+AC_CHECK_LIB([profinet], [pnet_init],, AC_MSG_ERROR([Profinet not found]))
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DATAROOTDIR=`eval echo $datarootdir`
+DATAROOTDIR=`eval echo $DATAROOTDIR`
+AC_SUBST(DATAROOTDIR)
+
+LIBDIR=`eval echo $libdir`
+LIBDIR=`eval echo $LIBDIR`
+AC_SUBST(LIBDIR)
+
+LOCALSTATEDIR=`eval echo $localstatedir`
+LOCALSTATEDIR=`eval echo $LOCALSTATEDIR`
+AC_SUBST(LOCALSTATEDIR)
+
+RUNSTATEDIR=`eval echo $runstatedir`
+RUNSTATEDIR=`eval echo $RUNSTATEDIR`
+AC_SUBST(RUNSTATEDIR)
+
+SYSCONFDIR=`eval echo $sysconfdir`
+SYSCONFDIR=`eval echo $SYSCONFDIR`
+AC_SUBST(SYSCONFDIR)
+
+AC_OUTPUT
diff --git a/src/profeth/src/Makefile.am b/src/profeth/src/Makefile.am
new file mode 100644
index 00000000..c54cd64f
--- /dev/null
+++ b/src/profeth/src/Makefile.am
@@ -0,0 +1,15 @@
+sbin_PROGRAMS = profeth
+
+profeth_SOURCES = profeth.c \
+ app_data.c app_data.h \
+ app_gsdml.c app_gsdml.h \
+ app_log.c app_log.h \
+ app_utils.c app_utils.h \
+ pnal_filetools.c pnal_filetools.h \
+ pnal.h \
+ pnal_sys.h \
+ sampleapp_common.c sampleapp_common.h
+
+profeth_CFLAGS = -Wall -Wextra -Werror -Wno-unused-parameter
+profeth_LDADD = -losal -lprofinet
+
diff --git a/src/profeth/src/app_data.c b/src/profeth/src/app_data.c
new file mode 100644
index 00000000..0e30ee65
--- /dev/null
+++ b/src/profeth/src/app_data.c
@@ -0,0 +1,315 @@
+/*********************************************************************
+ * _ _ _
+ * _ __ | |_ _ | | __ _ | |__ ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | | | |_ _ | || (_| || |_) |\__ \
+ * |_| \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2021 rt-labs AB, Sweden.
+ *
+ * This software is dual-licensed under GPLv3 and a commercial
+ * license. See the file LICENSE.md distributed with this software for
+ * full license information.
+ ********************************************************************/
+
+#include "app_data.h"
+#include "app_utils.h"
+#include "app_gsdml.h"
+#include "app_log.h"
+#include "sampleapp_common.h"
+#include "osal.h"
+#include "pnal.h"
+#include
+
+#include
+#include
+#include
+
+#define APP_DATA_DEFAULT_OUTPUT_DATA 0
+
+/* Parameter data for digital submodules
+ * The stored value is shared between all digital submodules in this example.
+ *
+ * Todo: Data is always in pnio data format. Add conversion to uint32_t.
+ */
+static uint32_t app_param_1 = 0; /* Network endianness */
+static uint32_t app_param_2 = 0; /* Network endianness */
+
+/* Parameter data for echo submodules
+ * The stored value is shared between all echo submodules in this example.
+ *
+ * Todo: Data is always in pnio data format. Add conversion to uint32_t.
+ */
+static uint32_t app_param_echo_gain = 1; /* Network endianness */
+
+/* Digital submodule process data
+ * The stored value is shared between all digital submodules in this example. */
+static uint8_t inputdata[APP_GSDML_INPUT_DATA_DIGITAL_SIZE] = {0};
+static uint8_t outputdata[APP_GSDML_OUTPUT_DATA_DIGITAL_SIZE] = {0};
+static uint8_t counter = 0;
+
+/* Network endianness */
+static uint8_t echo_inputdata[APP_GSDML_INPUT_DATA_ECHO_SIZE] = {0};
+static uint8_t echo_outputdata[APP_GSDML_OUTPUT_DATA_ECHO_SIZE] = {0};
+
+CC_PACKED_BEGIN
+typedef struct CC_PACKED app_echo_data
+{
+ /* Network endianness.
+ Used as a float, but we model it as a 4-byte integer to easily
+ do endianness conversion */
+ uint32_t echo_float_bytes;
+
+ /* Network endianness */
+ uint32_t echo_int;
+} app_echo_data_t;
+CC_PACKED_END
+CC_STATIC_ASSERT (sizeof (app_echo_data_t) == APP_GSDML_INPUT_DATA_ECHO_SIZE);
+CC_STATIC_ASSERT (sizeof (app_echo_data_t) == APP_GSDML_OUTPUT_DATA_ECHO_SIZE);
+
+/**
+ * Set LED state.
+ *
+ * Compares new state with previous state, to minimize system calls.
+ *
+ * Uses the hardware specific app_set_led() function.
+ *
+ * @param led_state In: New LED state
+ */
+static void app_handle_data_led_state (bool led_state)
+{
+ static bool previous_led_state = false;
+
+ if (led_state != previous_led_state)
+ {
+ app_set_led (APP_DATA_LED_ID, led_state);
+ }
+ previous_led_state = led_state;
+}
+
+uint8_t * app_data_get_input_data (
+ uint16_t slot_nbr,
+ uint16_t subslot_nbr,
+ uint32_t submodule_id,
+ bool button_pressed,
+ uint16_t * size,
+ uint8_t * iops)
+{
+ float inputfloat;
+ float outputfloat;
+ uint32_t hostorder_inputfloat_bytes;
+ uint32_t hostorder_outputfloat_bytes;
+ app_echo_data_t * p_echo_inputdata = (app_echo_data_t *)&echo_inputdata;
+ app_echo_data_t * p_echo_outputdata = (app_echo_data_t *)&echo_outputdata;
+
+ if (size == NULL || iops == NULL)
+ {
+ return NULL;
+ }
+
+ if (
+ submodule_id == APP_GSDML_SUBMOD_ID_DIGITAL_IN ||
+ submodule_id == APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT)
+ {
+ /* Prepare digital input data
+ * Lowest 7 bits: Counter Most significant bit: Button
+ */
+ inputdata[0] = counter++;
+ if (button_pressed)
+ {
+ inputdata[0] |= 0x80;
+ }
+ else
+ {
+ inputdata[0] &= 0x7F;
+ }
+
+ *size = APP_GSDML_INPUT_DATA_DIGITAL_SIZE;
+ *iops = PNET_IOXS_GOOD;
+ return inputdata;
+ }
+
+ if (submodule_id == APP_GSDML_SUBMOD_ID_ECHO)
+ {
+ /* Calculate echodata input (to the PLC)
+ * by multiplying the output (from the PLC) with a gain factor
+ */
+
+ /* Integer */
+ p_echo_inputdata->echo_int = CC_TO_BE32 (
+ CC_FROM_BE32 (p_echo_outputdata->echo_int) *
+ CC_FROM_BE32 (app_param_echo_gain));
+
+ /* Float */
+ /* Use memcopy to avoid strict-aliasing rule warnings */
+ hostorder_outputfloat_bytes =
+ CC_FROM_BE32 (p_echo_outputdata->echo_float_bytes);
+ memcpy (&outputfloat, &hostorder_outputfloat_bytes, sizeof (outputfloat));
+ inputfloat = outputfloat * CC_FROM_BE32 (app_param_echo_gain);
+ memcpy (&hostorder_inputfloat_bytes, &inputfloat, sizeof (outputfloat));
+ p_echo_inputdata->echo_float_bytes =
+ CC_TO_BE32 (hostorder_inputfloat_bytes);
+
+ *size = APP_GSDML_INPUT_DATA_ECHO_SIZE;
+ *iops = PNET_IOXS_GOOD;
+ return echo_inputdata;
+ }
+
+ /* Automated RT Tester scenario 2 - unsupported (sub)module */
+ return NULL;
+}
+
+int app_data_set_output_data (
+ uint16_t slot_nbr,
+ uint16_t subslot_nbr,
+ uint32_t submodule_id,
+ uint8_t * data,
+ uint16_t size)
+{
+ bool led_state;
+
+ if (data == NULL)
+ {
+ return -1;
+ }
+
+ if (
+ submodule_id == APP_GSDML_SUBMOD_ID_DIGITAL_OUT ||
+ submodule_id == APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT)
+ {
+ if (size == APP_GSDML_OUTPUT_DATA_DIGITAL_SIZE)
+ {
+ memcpy (outputdata, data, size);
+
+ /* Most significant bit: LED */
+ led_state = (outputdata[0] & 0x80) > 0;
+ app_handle_data_led_state (led_state);
+
+ return 0;
+ }
+ }
+ else if (submodule_id == APP_GSDML_SUBMOD_ID_ECHO)
+ {
+ if (size == APP_GSDML_OUTPUT_DATA_ECHO_SIZE)
+ {
+ memcpy (echo_outputdata, data, size);
+
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+int app_data_set_default_outputs (void)
+{
+ outputdata[0] = APP_DATA_DEFAULT_OUTPUT_DATA;
+ app_handle_data_led_state (false);
+ return 0;
+}
+
+int app_data_write_parameter (
+ uint16_t slot_nbr,
+ uint16_t subslot_nbr,
+ uint32_t submodule_id,
+ uint32_t index,
+ const uint8_t * data,
+ uint16_t length)
+{
+ const app_gsdml_param_t * par_cfg;
+
+ par_cfg = app_gsdml_get_parameter_cfg (submodule_id, index);
+ if (par_cfg == NULL)
+ {
+ APP_LOG_WARNING (
+ "PLC write request unsupported submodule/parameter. "
+ "Submodule id: %u Index: %u\n",
+ (unsigned)submodule_id,
+ (unsigned)index);
+ return -1;
+ }
+
+ if (length != par_cfg->length)
+ {
+ APP_LOG_WARNING (
+ "PLC write request unsupported length. "
+ "Index: %u Length: %u Expected length: %u\n",
+ (unsigned)index,
+ (unsigned)length,
+ par_cfg->length);
+ return -1;
+ }
+
+ if (index == APP_GSDML_PARAMETER_1_IDX)
+ {
+ memcpy (&app_param_1, data, length);
+ }
+ else if (index == APP_GSDML_PARAMETER_2_IDX)
+ {
+ memcpy (&app_param_2, data, length);
+ }
+ else if (index == APP_GSDML_PARAMETER_ECHO_IDX)
+ {
+ memcpy (&app_param_echo_gain, data, length);
+ }
+
+ APP_LOG_DEBUG (" Writing parameter \"%s\"\n", par_cfg->name);
+ app_log_print_bytes (APP_LOG_LEVEL_DEBUG, data, length);
+
+ return 0;
+}
+
+int app_data_read_parameter (
+ uint16_t slot_nbr,
+ uint16_t subslot_nbr,
+ uint32_t submodule_id,
+ uint32_t index,
+ uint8_t ** data,
+ uint16_t * length)
+{
+ const app_gsdml_param_t * par_cfg;
+
+ par_cfg = app_gsdml_get_parameter_cfg (submodule_id, index);
+ if (par_cfg == NULL)
+ {
+ APP_LOG_WARNING (
+ "PLC read request unsupported submodule/parameter. "
+ "Submodule id: %u Index: %u\n",
+ (unsigned)submodule_id,
+ (unsigned)index);
+ return -1;
+ }
+
+ if (*length < par_cfg->length)
+ {
+ APP_LOG_WARNING (
+ "PLC read request unsupported length. "
+ "Index: %u Max length: %u Data length for our parameter: %u\n",
+ (unsigned)index,
+ (unsigned)*length,
+ par_cfg->length);
+ return -1;
+ }
+
+ APP_LOG_DEBUG (" Reading \"%s\"\n", par_cfg->name);
+ if (index == APP_GSDML_PARAMETER_1_IDX)
+ {
+ *data = (uint8_t *)&app_param_1;
+ *length = sizeof (app_param_1);
+ }
+ else if (index == APP_GSDML_PARAMETER_2_IDX)
+ {
+ *data = (uint8_t *)&app_param_2;
+ *length = sizeof (app_param_2);
+ }
+ else if (index == APP_GSDML_PARAMETER_ECHO_IDX)
+ {
+ *data = (uint8_t *)&app_param_echo_gain;
+ *length = sizeof (app_param_echo_gain);
+ }
+
+ app_log_print_bytes (APP_LOG_LEVEL_DEBUG, *data, *length);
+
+ return 0;
+}
diff --git a/src/profeth/src/app_data.h b/src/profeth/src/app_data.h
new file mode 100644
index 00000000..83e7c4bb
--- /dev/null
+++ b/src/profeth/src/app_data.h
@@ -0,0 +1,140 @@
+/*********************************************************************
+ * _ _ _
+ * _ __ | |_ _ | | __ _ | |__ ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | | | |_ _ | || (_| || |_) |\__ \
+ * |_| \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2018 rt-labs AB, Sweden.
+ *
+ * This software is dual-licensed under GPLv3 and a commercial
+ * license. See the file LICENSE.md distributed with this software for
+ * full license information.
+ ********************************************************************/
+
+#ifndef APP_DATA_H
+#define APP_DATA_H
+
+/**
+ * @file
+ * @brief Sample application data interface
+ *
+ * Functions for:
+ * - Getting input data (Button 1 and counter value)
+ * - Setting output data (LED 1)
+ * - Setting default output state. This should be
+ * part of all device implementations for setting
+ * defined state when device is not connected to PLC
+ * - Reading and writing parameters
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+
+/**
+ * Get application specific PNIO input data (for sending to PLC)
+ *
+ * The main sample application keeps track
+ * of button so it is a parameter to this function.
+ *
+ * This function is not called for the DAP submodules (slot_nbr==0).
+ *
+ * @param slot_nbr In: Slot number
+ * @param subslot_nbr In: Subslot number
+ * @param submodule_id In: Submodule id
+ * @param button_state In: State of button 1
+ * @param size Out: Size of pnio data.
+ * Not modified on error.
+ * @param iops Out: Provider status. If for example
+ * a sensor is failing or a short
+ * circuit is detected on digital
+ * input this shall be set to BAD.
+ * Not modified on error.
+ * @return Reference to PNIO data, NULL on error
+ */
+uint8_t * app_data_get_input_data (
+ uint16_t slot_nbr,
+ uint16_t subslot_nbr,
+ uint32_t submodule_id,
+ bool button_state,
+ uint16_t * size,
+ uint8_t * iops);
+
+/**
+ * Set application specific PNIO output data (received from PLC)
+ *
+ * This function is not called for the DAP submodules (slot_nbr==0).
+ *
+ * @param slot_nbr In: Slot number
+ * @param subslot_nbr In: Subslot number
+ * @param submodule_id In: Submodule id
+ * @param data In: Reference to output data
+ * @param size In: Length of output data
+ * @return 0 on success, -1 on error
+ */
+int app_data_set_output_data (
+ uint16_t slot_nbr,
+ uint16_t subslot_nbr,
+ uint32_t submodule_id,
+ uint8_t * data,
+ uint16_t size);
+
+/**
+ * Set default outputs for all subslots.
+ *
+ * For the sample application this means that
+ * LED 1 is turned off.
+ *
+ * @return 0 on success, -1 on error
+ */
+int app_data_set_default_outputs (void);
+
+/**
+ * Write parameter index for a subslot
+ *
+ * @param slot_nbr In: Slot number
+ * @param subslot_nbr In: Subslot number
+ * @param submodule_id In: Submodule id
+ * @param index In: Parameter index
+ * @param data In: New parameter value
+ * @param write_length In: Length of parameter data
+ * @return 0 on success, -1 on error
+ */
+int app_data_write_parameter (
+ uint16_t slot_nbr,
+ uint16_t subslot_nbr,
+ uint32_t submodule_id,
+ uint32_t index,
+ const uint8_t * data,
+ uint16_t write_length);
+
+/**
+ * Read parameter index from a subslot
+ *
+ * @param slot_nbr In: Slot number
+ * @param subslot_nbr In: Subslot number
+ * @param submodule_id In: Submodule id
+ * @param index In: Parameter index
+ * @param data In: Reference to parameter data
+ * @param length InOut: The maximum (in) and actual (out) length in
+ * bytes of the data.
+ * @return 0 on success, -1 on error
+ */
+int app_data_read_parameter (
+ uint16_t slot_nbr,
+ uint16_t subslot_nbr,
+ uint32_t submodule_id,
+ uint32_t index,
+ uint8_t ** data,
+ uint16_t * length);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APP_DATA_H */
diff --git a/src/profeth/src/app_gsdml.c b/src/profeth/src/app_gsdml.c
new file mode 100644
index 00000000..e0ac13e1
--- /dev/null
+++ b/src/profeth/src/app_gsdml.c
@@ -0,0 +1,260 @@
+/*********************************************************************
+ * _ _ _
+ * _ __ | |_ _ | | __ _ | |__ ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | | | |_ _ | || (_| || |_) |\__ \
+ * |_| \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2021 rt-labs AB, Sweden.
+ *
+ * This software is dual-licensed under GPLv3 and a commercial
+ * license. See the file LICENSE.md distributed with this software for
+ * full license information.
+ ********************************************************************/
+
+#include "sampleapp_common.h"
+#include "app_utils.h"
+#include "app_gsdml.h"
+#include "app_log.h"
+#include "osal.h"
+#include "pnal.h"
+#include
+
+#include
+#include
+#include
+
+/******************* Supported modules ***************************/
+
+static const app_gsdml_module_t dap_1 = {
+ .id = PNET_MOD_DAP_IDENT,
+ .name = "DAP 1",
+ .submodules = {
+ PNET_SUBMOD_DAP_IDENT,
+ PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT,
+ PNET_SUBMOD_DAP_INTERFACE_1_PORT_2_IDENT,
+ PNET_SUBMOD_DAP_INTERFACE_1_PORT_3_IDENT,
+ PNET_SUBMOD_DAP_INTERFACE_1_PORT_4_IDENT,
+ 0}};
+
+static const app_gsdml_module_t module_digital_in = {
+ .id = APP_GSDML_MOD_ID_8_0_DIGITAL_IN,
+ .name = "DI 8xLogicLevel",
+ .submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_IN, 0},
+};
+
+static const app_gsdml_module_t module_digital_out = {
+ .id = APP_GSDML_MOD_ID_0_8_DIGITAL_OUT,
+ .name = "DO 8xLogicLevel",
+ .submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_OUT, 0}};
+
+static const app_gsdml_module_t module_digital_in_out = {
+ .id = APP_GSDML_MOD_ID_8_8_DIGITAL_IN_OUT,
+ .name = "DIO 8xLogicLevel",
+ .submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT, 0}};
+
+static const app_gsdml_module_t module_echo = {
+ .id = APP_GSDML_MOD_ID_ECHO,
+ .name = "Echo module",
+ .submodules = {APP_GSDML_SUBMOD_ID_ECHO, 0}};
+
+/******************* Supported submodules ************************/
+
+static const app_gsdml_submodule_t dap_indentity_1 = {
+ .name = "DAP Identity 1",
+ .api = APP_GSDML_API,
+ .id = PNET_SUBMOD_DAP_IDENT,
+ .data_dir = PNET_DIR_NO_IO,
+ .insize = 0,
+ .outsize = 0,
+ .parameters = {0}};
+
+static const app_gsdml_submodule_t dap_interface_1 = {
+ .name = "DAP Interface 1",
+ .api = APP_GSDML_API,
+ .id = PNET_SUBMOD_DAP_INTERFACE_1_IDENT,
+ .data_dir = PNET_DIR_NO_IO,
+ .insize = 0,
+ .outsize = 0,
+ .parameters = {0}};
+
+static const app_gsdml_submodule_t dap_port_1 = {
+ .name = "DAP Port 1",
+ .api = APP_GSDML_API,
+ .id = PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT,
+ .data_dir = PNET_DIR_NO_IO,
+ .insize = 0,
+ .outsize = 0,
+ .parameters = {0}};
+
+static const app_gsdml_submodule_t dap_port_2 = {
+ .name = "DAP Port 2",
+ .api = APP_GSDML_API,
+ .id = PNET_SUBMOD_DAP_INTERFACE_1_PORT_2_IDENT,
+ .data_dir = PNET_DIR_NO_IO,
+ .insize = 0,
+ .outsize = 0,
+ .parameters = {0}};
+
+static const app_gsdml_submodule_t dap_port_3 = {
+ .name = "DAP Port 3",
+ .api = APP_GSDML_API,
+ .id = PNET_SUBMOD_DAP_INTERFACE_1_PORT_3_IDENT,
+ .data_dir = PNET_DIR_NO_IO,
+ .insize = 0,
+ .outsize = 0,
+ .parameters = {0}};
+
+static const app_gsdml_submodule_t dap_port_4 = {
+ .name = "DAP Port 4",
+ .api = APP_GSDML_API,
+ .id = PNET_SUBMOD_DAP_INTERFACE_1_PORT_4_IDENT,
+ .data_dir = PNET_DIR_NO_IO,
+ .insize = 0,
+ .outsize = 0,
+ .parameters = {0}};
+
+static const app_gsdml_submodule_t submod_digital_in = {
+ .id = APP_GSDML_SUBMOD_ID_DIGITAL_IN,
+ .name = "Digital Input",
+ .api = APP_GSDML_API,
+ .data_dir = PNET_DIR_INPUT,
+ .insize = APP_GSDML_INPUT_DATA_DIGITAL_SIZE,
+ .outsize = 0,
+ .parameters = {0}};
+
+static const app_gsdml_submodule_t submod_digital_out = {
+ .id = APP_GSDML_SUBMOD_ID_DIGITAL_OUT,
+ .name = "Digital Output",
+ .api = APP_GSDML_API,
+ .data_dir = PNET_DIR_OUTPUT,
+ .insize = 0,
+ .outsize = APP_GSDML_OUTPUT_DATA_DIGITAL_SIZE,
+ .parameters = {0}};
+
+static const app_gsdml_submodule_t submod_digital_inout = {
+ .id = APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT,
+ .name = "Digital Input/Output",
+ .api = APP_GSDML_API,
+ .data_dir = PNET_DIR_IO,
+ .insize = APP_GSDML_INPUT_DATA_DIGITAL_SIZE,
+ .outsize = APP_GSDML_OUTPUT_DATA_DIGITAL_SIZE,
+ .parameters = {APP_GSDML_PARAMETER_1_IDX, APP_GSDML_PARAMETER_2_IDX, 0}};
+
+static const app_gsdml_submodule_t submod_echo = {
+ .id = APP_GSDML_SUBMOD_ID_ECHO,
+ .name = "Echo submodule",
+ .api = APP_GSDML_API,
+ .data_dir = PNET_DIR_IO,
+ .insize = APP_GSDML_INPUT_DATA_ECHO_SIZE,
+ .outsize = APP_GSDML_OUTPUT_DATA_ECHO_SIZE,
+ .parameters = {APP_GSDML_PARAMETER_ECHO_IDX, 0}};
+
+/** List of supported modules */
+static const app_gsdml_module_t * app_gsdml_modules[] = {
+ &dap_1,
+ &module_digital_in,
+ &module_digital_out,
+ &module_digital_in_out,
+ &module_echo};
+
+/** List of supported submodules */
+static const app_gsdml_submodule_t * app_gsdml_submodules[] = {
+ &dap_indentity_1,
+ &dap_interface_1,
+ &dap_port_1,
+ &dap_port_2,
+ &dap_port_3,
+ &dap_port_4,
+
+ &submod_digital_in,
+ &submod_digital_out,
+ &submod_digital_inout,
+
+ &submod_echo,
+};
+
+/* List of supported parameters.
+ * Note that parameters are submodule attributes.
+ * This list contain all parameters while each
+ * submodule list its supported parameters using
+ * their indexes.
+ */
+static app_gsdml_param_t app_gsdml_parameters[] = {
+ {
+ .index = APP_GSDML_PARAMETER_1_IDX,
+ .name = "Demo 1",
+ .length = APP_GSDML_PARAMETER_LENGTH,
+ },
+ {
+ .index = APP_GSDML_PARAMETER_2_IDX,
+ .name = "Demo 2",
+ .length = APP_GSDML_PARAMETER_LENGTH,
+ },
+ {
+ .index = APP_GSDML_PARAMETER_ECHO_IDX,
+ .name = "Echo gain setting",
+ .length = APP_GSDML_PARAMETER_LENGTH,
+ }};
+
+const app_gsdml_module_t * app_gsdml_get_module_cfg (uint32_t id)
+{
+ uint32_t i;
+ for (i = 0; i < NELEMENTS (app_gsdml_modules); i++)
+ {
+ if (app_gsdml_modules[i]->id == id)
+ {
+ return app_gsdml_modules[i];
+ }
+ }
+ return NULL;
+}
+
+const app_gsdml_submodule_t * app_gsdml_get_submodule_cfg (uint32_t id)
+{
+ uint32_t i;
+ for (i = 0; i < NELEMENTS (app_gsdml_submodules); i++)
+ {
+ if (app_gsdml_submodules[i]->id == id)
+ {
+ return app_gsdml_submodules[i];
+ }
+ }
+ return NULL;
+}
+
+const app_gsdml_param_t * app_gsdml_get_parameter_cfg (
+ uint32_t submodule_id,
+ uint32_t index)
+{
+ uint16_t i;
+ uint16_t j;
+
+ const app_gsdml_submodule_t * submodule_cfg =
+ app_gsdml_get_submodule_cfg (submodule_id);
+
+ if (submodule_cfg == NULL)
+ {
+ /* Unsupported submodule id */
+ return NULL;
+ }
+
+ /* Search for parameter index in submodule configuration */
+ for (i = 0; submodule_cfg->parameters[i] != 0; i++)
+ {
+ if (submodule_cfg->parameters[i] == index)
+ {
+ /* Find parameter configuration */
+ for (j = 0; j < NELEMENTS (app_gsdml_parameters); j++)
+ {
+ if (app_gsdml_parameters[j].index == index)
+ {
+ return &app_gsdml_parameters[j];
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/profeth/src/app_gsdml.h b/src/profeth/src/app_gsdml.h
new file mode 100644
index 00000000..648bf883
--- /dev/null
+++ b/src/profeth/src/app_gsdml.h
@@ -0,0 +1,184 @@
+/*********************************************************************
+ * _ _ _
+ * _ __ | |_ _ | | __ _ | |__ ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | | | |_ _ | || (_| || |_) |\__ \
+ * |_| \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2018 rt-labs AB, Sweden.
+ *
+ * This software is dual-licensed under GPLv3 and a commercial
+ * license. See the file LICENSE.md distributed with this software for
+ * full license information.
+ ********************************************************************/
+
+#ifndef APP_GSDML_H
+#define APP_GSDML_H
+
+/**
+ * @file
+ * @brief Device properties defined by the GSDML device definition
+ *
+ * Functions for getting module, submodule and parameter
+ * configurations using their ids.
+ *
+ * Important:
+ * Any change in this file may require an update of the GSDML file.
+ * Note that when the GSDML file is updated it has to be reloaded
+ * in your Profinet engineering tool. PLC applications may be affected.
+ *
+ * Design requires unique submodule IDs and unique parameter indexes.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#define APP_GSDML_API 0
+
+#define APP_GSDML_DEFAULT_STATION_NAME "rt-labs-dev"
+
+/* GSDML tag: VendorID */
+#define APP_GSDML_VENDOR_ID 0x0493
+
+/* GSDML tag: DeviceID */
+#define APP_GSDML_DEVICE_ID 0x0002
+
+/* Used in DCP communication */
+#define APP_GSDML_OEM_VENDOR_ID 0xcafe
+#define APP_GSDML_OEM_DEVICE_ID 0xee02
+
+/* Used in I&M0 */
+#define APP_GSDML_IM_HARDWARE_REVISION 3
+#define APP_GSDML_IM_VERSION_MAJOR 1
+#define APP_GSDML_IM_VERSION_MINOR 2
+
+/* Allowed: 'V', 'R', 'P', 'U', 'T' */
+#define APP_GSDML_SW_REV_PREFIX 'V'
+#define APP_GSDML_PROFILE_ID 0x1234
+#define APP_GSDML_PROFILE_SPEC_TYPE 0x5678
+#define APP_GSDML_IM_REVISION_COUNTER 0 /* Typically 0 */
+
+/* Note: You need to read out the actual hardware serial number instead */
+#define APP_GSDML_EXAMPLE_SERIAL_NUMBER "007"
+
+/* Initial values. Can be overwritten by PLC */
+#define APP_GSDML_TAG_FUNCTION "my function"
+#define APP_GSDML_TAG_LOCATION "my location"
+#define APP_GSDML_IM_DATE "2022-03-01 10:03"
+#define APP_GSDML_DESCRIPTOR "my descriptor"
+#define APP_GSDML_SIGNATURE ""
+
+/* GSDML tag: Writeable_IM_Records */
+#define APP_GSDML_IM_SUPPORTED \
+ (PNET_SUPPORTED_IM1 | PNET_SUPPORTED_IM2 | PNET_SUPPORTED_IM3)
+
+/* GSDML tag: OrderNumber */
+#define APP_GSDML_ORDER_ID "12345 Abcdefghijk"
+
+/* GSDML tag: ModuleInfo / Name */
+#define APP_GSDML_PRODUCT_NAME "P-Net Sample Application"
+
+/* GSDML tag: MinDeviceInterval */
+#define APP_GSDML_MIN_DEVICE_INTERVAL 32 /* 1 ms */
+
+#define APP_GSDML_DIAG_CUSTOM_USI 0x1234
+
+/* See "Specification for GSDML" 8.26 LogBookEntryItem for allowed values */
+#define APP_GSDML_LOGBOOK_ERROR_CODE 0x20 /* Manufacturer specific */
+#define APP_GSDML_LOGBOOK_ERROR_DECODE 0x82 /* Manufacturer specific */
+#define APP_GSDML_LOGBOOK_ERROR_CODE_1 PNET_ERROR_CODE_1_FSPM
+#define APP_GSDML_LOGBOOK_ERROR_CODE_2 0x00 /* Manufacturer specific */
+#define APP_GSDML_LOGBOOK_ENTRY_DETAIL 0xFEE1DEAD /* Manufacturer specific */
+
+#define APP_GSDML_PARAMETER_1_IDX 123
+#define APP_GSDML_PARAMETER_2_IDX 124
+#define APP_GSDML_PARAMETER_ECHO_IDX 125
+
+/* Use same size for all parameters in example */
+#define APP_GSDML_PARAMETER_LENGTH 4
+
+#define APP_GSDML_DEFAULT_MAUTYPE 0x10 /* Copper 100 Mbit/s Full duplex */
+
+typedef struct app_gsdml_module
+{
+ uint32_t id;
+
+ /** Module name */
+ const char * name;
+
+ /** Submodule IDs. Variable length, ends with 0. */
+ uint32_t submodules[];
+} app_gsdml_module_t;
+
+typedef struct app_gsdml_submodule
+{
+ uint32_t id;
+
+ /** Submodule name */
+ const char * name;
+
+ uint32_t api;
+ pnet_submodule_dir_t data_dir;
+ uint16_t insize;
+ uint16_t outsize;
+
+ /** Parameter indexes. See app_gsdml_parameters.
+ * Variable length, ends with 0. */
+ uint16_t parameters[];
+} app_gsdml_submodule_t;
+
+typedef struct
+{
+ uint32_t index;
+ const char * name;
+ uint16_t length;
+} app_gsdml_param_t;
+
+#define APP_GSDML_MOD_ID_8_0_DIGITAL_IN 0x00000030
+#define APP_GSDML_MOD_ID_0_8_DIGITAL_OUT 0x00000031
+#define APP_GSDML_MOD_ID_8_8_DIGITAL_IN_OUT 0x00000032
+#define APP_GSDML_MOD_ID_ECHO 0x00000040
+#define APP_GSDML_SUBMOD_ID_DIGITAL_IN 0x00000130
+#define APP_GSDML_SUBMOD_ID_DIGITAL_OUT 0x00000131
+#define APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT 0x00000132
+#define APP_GSDML_SUBMOD_ID_ECHO 0x00000140
+#define APP_GSDML_INPUT_DATA_DIGITAL_SIZE 1 /* bytes */
+#define APP_GSDML_OUTPUT_DATA_DIGITAL_SIZE 1 /* bytes */
+#define APP_GSDML_INPUT_DATA_ECHO_SIZE 8 /* bytes */
+#define APP_GSDML_OUTPUT_DATA_ECHO_SIZE APP_GSDML_INPUT_DATA_ECHO_SIZE
+#define APP_GSDML_ALARM_PAYLOAD_SIZE 1 /* bytes */
+
+/**
+ * Get module configuration from module ID
+ * @param module_id In: Module ID
+ * @return Module configuration, NULL if not found
+ */
+const app_gsdml_module_t * app_gsdml_get_module_cfg (uint32_t module_id);
+
+/**
+ * Get submodule module configuration from submodule ID
+ * @param submodule_id In: Submodule ID
+ * @return Submodule configuration, NULL if not found
+ */
+const app_gsdml_submodule_t * app_gsdml_get_submodule_cfg (
+ uint32_t submodule_id);
+
+/**
+ * Get parameter configuration from parameter index
+ * @param submodule_id In: Submodule ID
+ * @param index In: Parameters index
+ * @return Parameter configuration, NULL if not found
+ */
+const app_gsdml_param_t * app_gsdml_get_parameter_cfg (
+ uint32_t submodule_id,
+ uint32_t index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APP_GSDML_H */
diff --git a/src/profeth/src/app_log.c b/src/profeth/src/app_log.c
new file mode 100644
index 00000000..9b9d782b
--- /dev/null
+++ b/src/profeth/src/app_log.c
@@ -0,0 +1,52 @@
+/*********************************************************************
+ * _ _ _
+ * _ __ | |_ _ | | __ _ | |__ ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | | | |_ _ | || (_| || |_) |\__ \
+ * |_| \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2021 rt-labs AB, Sweden.
+ *
+ * This software is dual-licensed under GPLv3 and a commercial
+ * license. See the file LICENSE.md distributed with this software for
+ * full license information.
+ ********************************************************************/
+
+#include "app_log.h"
+
+#include
+#include
+
+static int32_t log_level = APP_DEFAULT_LOG_LEVEL;
+
+void app_log_set_log_level (int32_t level)
+{
+ log_level = level;
+}
+
+void app_log (int32_t level, const char * fmt, ...)
+{
+ va_list list;
+
+ if (level >= log_level)
+ {
+ va_start (list, fmt);
+ vprintf (fmt, list);
+ va_end (list);
+ fflush (stdout);
+ }
+}
+
+void app_log_print_bytes (int32_t level, const uint8_t * bytes, uint32_t len)
+{
+ if (level >= log_level)
+ {
+ printf (" Bytes: ");
+ for (uint32_t i = 0; i < len; i++)
+ {
+ printf ("%02X ", bytes[i]);
+ }
+ printf ("\n");
+ }
+}
diff --git a/src/profeth/src/app_log.h b/src/profeth/src/app_log.h
new file mode 100644
index 00000000..259b0704
--- /dev/null
+++ b/src/profeth/src/app_log.h
@@ -0,0 +1,75 @@
+/*********************************************************************
+ * _ _ _
+ * _ __ | |_ _ | | __ _ | |__ ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | | | |_ _ | || (_| || |_) |\__ \
+ * |_| \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2018 rt-labs AB, Sweden.
+ *
+ * This software is dual-licensed under GPLv3 and a commercial
+ * license. See the file LICENSE.md distributed with this software for
+ * full license information.
+ ********************************************************************/
+
+#ifndef APP_LOG_H
+#define APP_LOG_H
+
+/**
+ * @file
+ * @brief Application debug log utility
+ *
+ * Runtime configurable debug log using printf()
+ * Levels matches levels used in P-Net.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#define APP_LOG_LEVEL_DEBUG 0x00
+#define APP_LOG_LEVEL_INFO 0x01
+#define APP_LOG_LEVEL_WARNING 0x02
+#define APP_LOG_LEVEL_ERROR 0x03
+#define APP_LOG_LEVEL_FATAL 0x04
+
+#define APP_DEFAULT_LOG_LEVEL APP_LOG_LEVEL_FATAL
+
+#define APP_LOG(level, ...) app_log (level, __VA_ARGS__)
+
+#define APP_LOG_DEBUG(...) APP_LOG (APP_LOG_LEVEL_DEBUG, __VA_ARGS__)
+#define APP_LOG_INFO(...) APP_LOG (APP_LOG_LEVEL_INFO, __VA_ARGS__)
+#define APP_LOG_WARNING(...) APP_LOG (APP_LOG_LEVEL_WARNING, __VA_ARGS__)
+#define APP_LOG_ERROR(...) APP_LOG (APP_LOG_LEVEL_ERROR, __VA_ARGS__)
+#define APP_LOG_FATAL(...) APP_LOG (APP_LOG_LEVEL_FATAL, __VA_ARGS__)
+
+/**
+ * Print log message depending on level
+ * Use the APP_LOG_xxxxx macros instead of this function.
+ * @param level In: Message log level
+ * @param fmt In: Log message format string
+ */
+void app_log (int32_t level, const char * fmt, ...);
+
+/**
+ * Log an array of bytes
+ * @param level In: Log level
+ * @param bytes In: Array of bytes
+ * @param length In: Length of array
+ */
+void app_log_print_bytes (int32_t level, const uint8_t * bytes, uint32_t length);
+
+/**
+ * Set log level
+ * @param level In: Log level
+ */
+void app_log_set_log_level (int32_t level);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APP_LOG_H */
diff --git a/src/profeth/src/app_utils.c b/src/profeth/src/app_utils.c
new file mode 100644
index 00000000..99381f56
--- /dev/null
+++ b/src/profeth/src/app_utils.c
@@ -0,0 +1,865 @@
+/*********************************************************************
+ * _ _ _
+ * _ __ | |_ _ | | __ _ | |__ ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | | | |_ _ | || (_| || |_) |\__ \
+ * |_| \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2021 rt-labs AB, Sweden.
+ *
+ * This software is dual-licensed under GPLv3 and a commercial
+ * license. See the file LICENSE.md distributed with this software for
+ * full license information.
+ ********************************************************************/
+
+#define _GNU_SOURCE /* For asprintf() */
+
+#include "app_utils.h"
+#include "app_log.h"
+#include "app_gsdml.h"
+#include "sampleapp_common.h"
+#include "osal.h"
+#include "osal_log.h" /* For LOG_LEVEL */
+#include "pnal.h"
+#include
+
+#include
+#include
+#include
+#include
+
+#define GET_HIGH_BYTE(id) ((id >> 8) & 0xFF)
+#define GET_LOW_BYTE(id) (id & 0xFF)
+
+int app_ar_add_arep (app_api_t * api, uint32_t arep, app_ar_t ** ar)
+{
+ uint16_t i;
+
+ CC_ASSERT (api != NULL);
+ CC_ASSERT (ar != NULL);
+ for (i = 0; i < PNET_MAX_AR; ++i)
+ {
+ if (api->ar[i].arep == UINT32_MAX)
+ {
+ api->ar[i].arep = arep;
+ api->ar[i].events = 0;
+ *ar = api->ar + i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+uint32_t app_ar_arep (app_ar_t * ar)
+{
+ CC_ASSERT (ar != NULL);
+ return ar->arep;
+}
+
+void app_ar_event_set (app_ar_t * ar, uint32_t event)
+{
+ CC_ASSERT (ar != NULL);
+ ar->events |= event;
+}
+
+int app_ar_event_clr (app_ar_t * ar, uint32_t event)
+{
+ CC_ASSERT (ar != NULL);
+ if (ar->events & event)
+ {
+ ar->events &= ~event;
+ return 1;
+ }
+ return 0;
+}
+
+void app_ar_iterator_init (
+ app_ar_iterator_t * iterator,
+ app_api_t * api)
+{
+ CC_ASSERT (iterator != NULL);
+ CC_ASSERT (api != NULL);
+ iterator->ar = api->ar;
+ iterator->index = -1;
+ iterator->modified = false;
+}
+
+int app_ar_iterator_next (app_ar_iterator_t * iterator, app_ar_t ** ar)
+{
+#if PNET_MAX_AR > 1
+ uint16_t i;
+ uint16_t j;
+#endif
+
+ CC_ASSERT (iterator != NULL);
+ CC_ASSERT (ar != NULL);
+ ++iterator->index;
+ if (
+ (iterator->index < PNET_MAX_AR) &&
+ (iterator->ar[iterator->index].arep != UINT32_MAX))
+ {
+ *ar = iterator->ar + iterator->index;
+ return 1;
+ }
+#if PNET_MAX_AR > 1
+ else if (iterator->modified)
+ {
+ for (i = 0; i < PNET_MAX_AR; ++i)
+ {
+ if (iterator->ar[i].arep == UINT32_MAX)
+ {
+ for (j = i + 1; j < PNET_MAX_AR; ++j)
+ {
+ if (iterator->ar[j].arep != UINT32_MAX)
+ {
+ iterator->ar[i] = iterator->ar[j];
+ iterator->ar[j].arep = UINT32_MAX;
+ break;
+ }
+ }
+ if (j >= PNET_MAX_AR - 1)
+ {
+ break;
+ }
+ }
+ }
+ }
+#endif
+ return 0;
+}
+
+int app_ar_iterator_done (app_ar_iterator_t * iterator)
+{
+ CC_ASSERT (iterator != NULL);
+ if (
+ (iterator->index < 0) ||
+ ((iterator->index < PNET_MAX_AR) &&
+ (iterator->ar[iterator->index].arep != UINT32_MAX)))
+ {
+ return 0;
+ }
+ return 1;
+}
+
+void app_ar_iterator_delete_current (app_ar_iterator_t * iterator)
+{
+ CC_ASSERT (iterator != NULL);
+ CC_ASSERT (iterator->index >= 0);
+ if (iterator->index < PNET_MAX_AR)
+ {
+ iterator->ar[iterator->index].arep = UINT32_MAX;
+ iterator->modified = true;
+ }
+}
+
+void app_utils_ip_to_string (pnal_ipaddr_t ip, char * outputstring)
+{
+ snprintf (
+ outputstring,
+ PNAL_INET_ADDRSTR_SIZE,
+ "%u.%u.%u.%u",
+ (uint8_t)((ip >> 24) & 0xFF),
+ (uint8_t)((ip >> 16) & 0xFF),
+ (uint8_t)((ip >> 8) & 0xFF),
+ (uint8_t)(ip & 0xFF));
+}
+
+void app_utils_mac_to_string (pnet_ethaddr_t mac, char * outputstring)
+{
+ snprintf (
+ outputstring,
+ PNAL_ETH_ADDRSTR_SIZE,
+ "%02X:%02X:%02X:%02X:%02X:%02X",
+ mac.addr[0],
+ mac.addr[1],
+ mac.addr[2],
+ mac.addr[3],
+ mac.addr[4],
+ mac.addr[5]);
+}
+
+const char * app_utils_submod_dir_to_string (pnet_submodule_dir_t direction)
+{
+ const char * s = "";
+
+ switch (direction)
+ {
+ case PNET_DIR_NO_IO:
+ s = "NO_IO";
+ break;
+ case PNET_DIR_INPUT:
+ s = "INPUT";
+ break;
+ case PNET_DIR_OUTPUT:
+ s = "OUTPUT";
+ break;
+ case PNET_DIR_IO:
+ s = "INPUT_OUTPUT";
+ break;
+ }
+
+ return s;
+}
+
+const char * app_utils_ioxs_to_string (pnet_ioxs_values_t ioxs)
+{
+ const char * s = "";
+ switch (ioxs)
+ {
+ case PNET_IOXS_BAD:
+ s = "IOXS_BAD";
+ break;
+ case PNET_IOXS_GOOD:
+ s = "IOXS_GOOD";
+ break;
+ }
+
+ return s;
+}
+
+void app_utils_get_error_code_strings (
+ uint16_t err_cls,
+ uint16_t err_code,
+ const char ** err_cls_str,
+ const char ** err_code_str)
+{
+ if (err_cls_str == NULL || err_cls_str == NULL)
+ {
+ return;
+ }
+ *err_cls_str = "Not decoded";
+ *err_code_str = "Not decoded";
+
+ switch (err_cls)
+ {
+ case PNET_ERROR_CODE_1_RTA_ERR_CLS_PROTOCOL:
+ *err_cls_str = "Real-Time Acyclic Protocol";
+ switch (err_code)
+ {
+ case PNET_ERROR_CODE_2_ABORT_AR_CONSUMER_DHT_EXPIRED:
+ *err_code_str = "Device missed cyclic data "
+ "deadline, device terminated AR";
+ break;
+ case PNET_ERROR_CODE_2_ABORT_AR_CMI_TIMEOUT:
+ *err_code_str = "Communication initialization "
+ "timeout, device terminated AR";
+ break;
+ case PNET_ERROR_CODE_2_ABORT_AR_RELEASE_IND_RECEIVED:
+ *err_code_str = "AR release indication received";
+ break;
+ case PNET_ERROR_CODE_2_ABORT_AR_RPC_READ_ERROR:
+ *err_code_str = "AR RPC-Read error";
+ break;
+ case PNET_ERROR_CODE_2_ABORT_DCP_STATION_NAME_CHANGED:
+ *err_code_str = "DCP station name changed, "
+ "device terminated AR";
+ break;
+ case PNET_ERROR_CODE_2_ABORT_DCP_RESET_TO_FACTORY:
+ *err_code_str = "DCP reset to factory or factory "
+ "reset, device terminated AR";
+ break;
+ }
+ break;
+
+ case PNET_ERROR_CODE_1_CTLDINA:
+ *err_cls_str = "CTLDINA = Name and IP assignment from controller";
+ switch (err_code)
+ {
+ case PNET_ERROR_CODE_2_CTLDINA_ARP_MULTIPLE_IP_ADDRESSES:
+ *err_code_str = "Multiple users of same IP address";
+ break;
+ }
+ break;
+ }
+}
+
+void app_utils_copy_ip_to_struct (
+ pnet_cfg_ip_addr_t * destination_struct,
+ pnal_ipaddr_t ip)
+{
+ destination_struct->a = ((ip >> 24) & 0xFF);
+ destination_struct->b = ((ip >> 16) & 0xFF);
+ destination_struct->c = ((ip >> 8) & 0xFF);
+ destination_struct->d = (ip & 0xFF);
+}
+
+const char * app_utils_dcontrol_cmd_to_string (
+ pnet_control_command_t control_command)
+{
+ const char * s = NULL;
+
+ switch (control_command)
+ {
+ case PNET_CONTROL_COMMAND_PRM_BEGIN:
+ s = "PRM_BEGIN";
+ break;
+ case PNET_CONTROL_COMMAND_PRM_END:
+ s = "PRM_END";
+ break;
+ case PNET_CONTROL_COMMAND_APP_RDY:
+ s = "APP_RDY";
+ break;
+ case PNET_CONTROL_COMMAND_RELEASE:
+ s = "RELEASE";
+ break;
+ default:
+ s = "";
+ break;
+ }
+
+ return s;
+}
+
+const char * app_utils_event_to_string (pnet_event_values_t event)
+{
+ const char * s = "";
+
+ switch (event)
+ {
+ case PNET_EVENT_ABORT:
+ s = "PNET_EVENT_ABORT";
+ break;
+ case PNET_EVENT_STARTUP:
+ s = "PNET_EVENT_STARTUP";
+ break;
+ case PNET_EVENT_PRMEND:
+ s = "PNET_EVENT_PRMEND";
+ break;
+ case PNET_EVENT_APPLRDY:
+ s = "PNET_EVENT_APPLRDY";
+ break;
+ case PNET_EVENT_DATA:
+ s = "PNET_EVENT_DATA";
+ break;
+ }
+
+ return s;
+}
+
+int app_utils_pnet_cfg_init_default (pnet_cfg_t * cfg)
+{
+ memset (cfg, 0, sizeof (pnet_cfg_t));
+
+ cfg->tick_us = APP_TICK_INTERVAL_US;
+
+ /* Identification & Maintenance */
+
+ cfg->im_0_data.im_vendor_id_hi = GET_HIGH_BYTE (APP_GSDML_VENDOR_ID);
+ cfg->im_0_data.im_vendor_id_lo = GET_LOW_BYTE (APP_GSDML_VENDOR_ID);
+
+ cfg->im_0_data.im_hardware_revision = APP_GSDML_IM_HARDWARE_REVISION;
+ cfg->im_0_data.im_sw_revision_prefix = APP_GSDML_SW_REV_PREFIX;
+ cfg->im_0_data.im_sw_revision_functional_enhancement = PNET_VERSION_MAJOR;
+ cfg->im_0_data.im_sw_revision_bug_fix = PNET_VERSION_MINOR;
+ cfg->im_0_data.im_sw_revision_internal_change = PNET_VERSION_PATCH;
+ cfg->im_0_data.im_revision_counter = APP_GSDML_IM_REVISION_COUNTER;
+ cfg->im_0_data.im_profile_id = APP_GSDML_PROFILE_ID;
+ cfg->im_0_data.im_profile_specific_type = APP_GSDML_PROFILE_SPEC_TYPE;
+ cfg->im_0_data.im_version_major = 1; /** Always 1 */
+ cfg->im_0_data.im_version_minor = 1; /** Always 1 */
+ cfg->im_0_data.im_supported = APP_GSDML_IM_SUPPORTED;
+
+ snprintf (
+ cfg->im_0_data.im_order_id,
+ sizeof (cfg->im_0_data.im_order_id),
+ "%s",
+ APP_GSDML_ORDER_ID);
+ snprintf (
+ cfg->im_0_data.im_serial_number,
+ sizeof (cfg->im_0_data.im_serial_number),
+ "%s",
+ APP_GSDML_EXAMPLE_SERIAL_NUMBER);
+ snprintf (
+ cfg->im_1_data.im_tag_function,
+ sizeof (cfg->im_1_data.im_tag_function),
+ "%s",
+ APP_GSDML_TAG_FUNCTION);
+ snprintf (
+ cfg->im_1_data.im_tag_location,
+ sizeof (cfg->im_1_data.im_tag_location),
+ "%s",
+ APP_GSDML_TAG_LOCATION);
+ snprintf (
+ cfg->im_2_data.im_date,
+ sizeof (cfg->im_2_data.im_date),
+ "%s",
+ APP_GSDML_IM_DATE);
+ snprintf (
+ cfg->im_3_data.im_descriptor,
+ sizeof (cfg->im_3_data.im_descriptor),
+ "%s",
+ APP_GSDML_DESCRIPTOR);
+ snprintf (
+ cfg->im_4_data.im_signature,
+ sizeof (cfg->im_4_data.im_signature),
+ "%s",
+ APP_GSDML_SIGNATURE);
+
+ /* Device configuration */
+ cfg->device_id.vendor_id_hi = GET_HIGH_BYTE (APP_GSDML_VENDOR_ID);
+ cfg->device_id.vendor_id_lo = GET_LOW_BYTE (APP_GSDML_VENDOR_ID);
+ cfg->device_id.device_id_hi = GET_HIGH_BYTE (APP_GSDML_DEVICE_ID);
+ cfg->device_id.device_id_lo = GET_LOW_BYTE (APP_GSDML_DEVICE_ID);
+ cfg->oem_device_id.vendor_id_hi = GET_HIGH_BYTE (APP_GSDML_OEM_VENDOR_ID);
+ cfg->oem_device_id.vendor_id_lo = GET_LOW_BYTE (APP_GSDML_OEM_VENDOR_ID);
+ cfg->oem_device_id.device_id_hi = GET_HIGH_BYTE (APP_GSDML_OEM_DEVICE_ID);
+ cfg->oem_device_id.device_id_lo = GET_LOW_BYTE (APP_GSDML_OEM_DEVICE_ID);
+
+ snprintf (
+ cfg->product_name,
+ sizeof (cfg->product_name),
+ "%s",
+ APP_GSDML_PRODUCT_NAME);
+
+ cfg->send_hello = true;
+
+ /* Timing */
+ cfg->min_device_interval = APP_GSDML_MIN_DEVICE_INTERVAL;
+
+ /* Should be set by application as part of network configuration. */
+ cfg->num_physical_ports = 1;
+
+ snprintf (
+ cfg->station_name,
+ sizeof (cfg->station_name),
+ "%s",
+ APP_GSDML_DEFAULT_STATION_NAME);
+
+ /* Diagnosis mechanism */
+ /* We prefer using "Extended channel diagnosis" instead of
+ * "Qualified channel diagnosis" format on the wire,
+ * as this is better supported by Wireshark.
+ */
+ cfg->use_qualified_diagnosis = false;
+
+ return 0;
+}
+
+int app_utils_get_netif_namelist (
+ const char * arg_str,
+ uint16_t max_port,
+ app_utils_netif_namelist_t * p_if_list,
+ uint16_t * p_num_ports)
+{
+ int ret = 0;
+ uint16_t i = 0;
+ uint16_t j = 0;
+ uint16_t if_index = 0;
+ uint16_t number_of_given_names = 1;
+ uint16_t if_list_size = max_port + 1;
+ char c;
+
+ if (max_port == 0)
+ {
+ printf ("Error: max_port is 0.\n");
+ return -1;
+ }
+
+ memset (p_if_list, 0, sizeof (*p_if_list));
+ c = arg_str[i++];
+ while (c != '\0')
+ {
+ if (c != ',')
+ {
+ if (if_index < if_list_size)
+ {
+ p_if_list->netif[if_index].name[j++] = c;
+ }
+ }
+ else
+ {
+ if (if_index < if_list_size)
+ {
+ p_if_list->netif[if_index].name[j++] = '\0';
+ j = 0;
+ if_index++;
+ }
+ number_of_given_names++;
+ }
+
+ c = arg_str[i++];
+ }
+
+ if (max_port == 1 && number_of_given_names > 1)
+ {
+ printf ("Error: Only 1 network interface expected as max_port is 1.\n");
+ return -1;
+ }
+ if (number_of_given_names == 2)
+ {
+ printf ("Error: It is illegal to give 2 interface names. Use 1, or one "
+ "more than the number of physical interfaces.\n");
+ return -1;
+ }
+ if (number_of_given_names > max_port + 1)
+ {
+ printf (
+ "Error: You have given %u interface names, but max is %u as "
+ "PNET_MAX_PHYSICAL_PORTS is %u.\n",
+ number_of_given_names,
+ max_port + 1,
+ max_port);
+ return -1;
+ }
+
+ if (number_of_given_names == 1)
+ {
+ if (strlen (p_if_list->netif[0].name) == 0)
+ {
+ printf ("Error: Zero length network interface name.\n");
+ return -1;
+ }
+ else
+ {
+ p_if_list->netif[1] = p_if_list->netif[0];
+ *p_num_ports = 1;
+ }
+ }
+ else
+ {
+ for (i = 0; i < number_of_given_names; i++)
+ {
+ if (strlen (p_if_list->netif[i].name) == 0)
+ {
+ printf ("Error: Zero length network interface name (%d).\n", i);
+ return -1;
+ }
+ }
+
+ *p_num_ports = number_of_given_names - 1;
+ }
+
+ return ret;
+}
+
+int app_utils_pnet_cfg_init_netifs (
+ const char * netif_list_str,
+ app_utils_netif_namelist_t * if_list,
+ uint16_t * number_of_ports,
+ pnet_if_cfg_t * if_cfg)
+{
+ int ret = 0;
+ int i = 0;
+ pnal_ipaddr_t ip;
+ pnal_ipaddr_t netmask;
+ pnal_ipaddr_t gateway;
+
+ ret = app_utils_get_netif_namelist (
+ netif_list_str,
+ PNET_MAX_PHYSICAL_PORTS,
+ if_list,
+ number_of_ports);
+ if (ret != 0)
+ {
+ return ret;
+ }
+ if_cfg->main_netif_name = if_list->netif[0].name;
+
+ for (i = 1; i <= *number_of_ports; i++)
+ {
+ if_cfg->physical_ports[i - 1].netif_name = if_list->netif[i].name;
+ if_cfg->physical_ports[i - 1].default_mau_type =
+ APP_GSDML_DEFAULT_MAUTYPE;
+ }
+
+ /* Read IP, netmask, gateway from operating system */
+ ip = pnal_get_ip_address (if_cfg->main_netif_name);
+ netmask = pnal_get_netmask (if_cfg->main_netif_name);
+ gateway = pnal_get_gateway (if_cfg->main_netif_name);
+
+ app_utils_copy_ip_to_struct (&if_cfg->ip_cfg.ip_addr, ip);
+ app_utils_copy_ip_to_struct (&if_cfg->ip_cfg.ip_gateway, gateway);
+ app_utils_copy_ip_to_struct (&if_cfg->ip_cfg.ip_mask, netmask);
+
+ return ret;
+}
+
+static void app_utils_print_mac_address (const char * netif_name)
+{
+ pnal_ethaddr_t pnal_mac_addr;
+ if (pnal_get_macaddress (netif_name, &pnal_mac_addr) == 0)
+ {
+ APP_LOG_INFO (
+ "%02X:%02X:%02X:%02X:%02X:%02X\n",
+ pnal_mac_addr.addr[0],
+ pnal_mac_addr.addr[1],
+ pnal_mac_addr.addr[2],
+ pnal_mac_addr.addr[3],
+ pnal_mac_addr.addr[4],
+ pnal_mac_addr.addr[5]);
+ }
+ else
+ {
+ APP_LOG_ERROR ("Failed read mac address\n");
+ }
+}
+
+void app_utils_print_network_config (
+ pnet_if_cfg_t * if_cfg,
+ uint16_t number_of_ports)
+{
+ uint16_t i;
+ char hostname_string[PNAL_HOSTNAME_MAX_SIZE]; /* Terminated string */
+
+ APP_LOG_INFO ("Management port: %s ", if_cfg->main_netif_name);
+ app_utils_print_mac_address (if_cfg->main_netif_name);
+ for (i = 1; i <= number_of_ports; i++)
+ {
+ APP_LOG_INFO (
+ "Physical port [%u]: %s ",
+ i,
+ if_cfg->physical_ports[i - 1].netif_name);
+
+ app_utils_print_mac_address (if_cfg->physical_ports[i - 1].netif_name);
+ }
+
+ if (pnal_get_hostname (hostname_string) != 0)
+ {
+ hostname_string[0] = '\0';
+ }
+
+ APP_LOG_INFO ("Hostname: %s\n", hostname_string);
+ APP_LOG_INFO (
+ "IP address: %u.%u.%u.%u\n",
+ if_cfg->ip_cfg.ip_addr.a,
+ if_cfg->ip_cfg.ip_addr.b,
+ if_cfg->ip_cfg.ip_addr.c,
+ if_cfg->ip_cfg.ip_addr.d);
+ APP_LOG_INFO (
+ "Netmask: %u.%u.%u.%u\n",
+ if_cfg->ip_cfg.ip_mask.a,
+ if_cfg->ip_cfg.ip_mask.b,
+ if_cfg->ip_cfg.ip_mask.c,
+ if_cfg->ip_cfg.ip_mask.d);
+ APP_LOG_INFO (
+ "Gateway: %u.%u.%u.%u\n",
+ if_cfg->ip_cfg.ip_gateway.a,
+ if_cfg->ip_cfg.ip_gateway.b,
+ if_cfg->ip_cfg.ip_gateway.c,
+ if_cfg->ip_cfg.ip_gateway.d);
+}
+
+void app_utils_print_ioxs_change (
+ const app_subslot_t * subslot,
+ const char * ioxs_str,
+ uint8_t ioxs_current,
+ uint8_t ioxs_new)
+{
+ if (ioxs_current != ioxs_new)
+ {
+ if (ioxs_new == PNET_IOXS_BAD)
+ {
+ APP_LOG_DEBUG (
+ "PLC reports %s BAD for slot %u subslot %u \"%s\"\n",
+ ioxs_str,
+ subslot->slot_nbr,
+ subslot->subslot_nbr,
+ subslot->submodule_name);
+ }
+ else if (ioxs_new == PNET_IOXS_GOOD)
+ {
+ APP_LOG_DEBUG (
+ "PLC reports %s GOOD for slot %u subslot %u \"%s\".\n",
+ ioxs_str,
+ subslot->slot_nbr,
+ subslot->subslot_nbr,
+ subslot->submodule_name);
+ }
+ else if (ioxs_new != PNET_IOXS_GOOD)
+ {
+ APP_LOG_DEBUG (
+ "PLC reports %s %u for slot %u subslot %u \"%s\".\n"
+ " Is the PLC in STOP mode?\n",
+ ioxs_str,
+ ioxs_new,
+ subslot->slot_nbr,
+ subslot->subslot_nbr,
+ subslot->submodule_name);
+ }
+ }
+}
+
+int app_utils_plug_module (
+ app_api_t * p_api,
+ uint16_t slot_nbr,
+ uint32_t id,
+ const char * name)
+{
+ if (slot_nbr >= PNET_MAX_SLOTS)
+ {
+ return -1;
+ }
+
+ p_api->slots[slot_nbr].module_id = id;
+ p_api->slots[slot_nbr].plugged = true;
+ p_api->slots[slot_nbr].name = name;
+
+ return 0;
+}
+
+int app_utils_pull_module (app_api_t * p_api, uint16_t slot_nbr)
+{
+ if (slot_nbr >= PNET_MAX_SLOTS)
+ {
+ return -1;
+ }
+
+ p_api->slots[slot_nbr].plugged = false;
+
+ return 0;
+}
+
+app_subslot_t * app_utils_plug_submodule (
+ app_api_t * p_api,
+ uint16_t slot_nbr,
+ uint16_t subslot_nbr,
+ uint32_t submodule_ident,
+ const pnet_data_cfg_t * p_data_cfg,
+ const char * submodule_name,
+ app_utils_cyclic_callback cyclic_callback,
+ void * tag)
+{
+ uint16_t subslot_ix;
+
+ if (slot_nbr >= PNET_MAX_SLOTS || p_api == NULL || p_data_cfg == NULL)
+ {
+ return NULL;
+ }
+
+ /** Find a free subslot */
+ for (subslot_ix = 0; subslot_ix < PNET_MAX_SUBSLOTS; subslot_ix++)
+ {
+ if (p_api->slots[slot_nbr].subslots[subslot_ix].used == false)
+ {
+ app_subslot_t * p_subslot =
+ &p_api->slots[slot_nbr].subslots[subslot_ix];
+
+ p_subslot->used = true;
+ p_subslot->plugged = true;
+ p_subslot->slot_nbr = slot_nbr;
+ p_subslot->subslot_nbr = subslot_nbr;
+ p_subslot->submodule_name = submodule_name;
+ p_subslot->submodule_id = submodule_ident;
+ p_subslot->data_cfg = *p_data_cfg;
+ p_subslot->cyclic_callback = cyclic_callback;
+ p_subslot->tag = tag;
+ p_subslot->indata_iocs = PNET_IOXS_BAD;
+ p_subslot->outdata_iops = PNET_IOXS_BAD;
+ return p_subslot;
+ }
+ }
+
+ return NULL;
+}
+
+int app_utils_pull_submodule (
+ app_api_t * p_api,
+ uint16_t slot_nbr,
+ uint16_t subslot_nbr)
+{
+ app_subslot_t * p_subslot = NULL;
+
+ if (slot_nbr >= PNET_MAX_SUBSLOTS || p_api == NULL)
+ {
+ return -1;
+ }
+
+ p_subslot = app_utils_subslot_get (p_api, slot_nbr, subslot_nbr);
+ if (p_subslot == NULL)
+ {
+ return -1;
+ }
+
+ memset (p_subslot, 0, sizeof (app_subslot_t));
+ p_subslot->used = false;
+
+ return 0;
+}
+
+app_subslot_t * app_utils_subslot_get (
+ app_api_t * p_api,
+ uint16_t slot_nbr,
+ uint16_t subslot_nbr)
+{
+ uint16_t subslot_ix;
+
+ if (slot_nbr >= PNET_MAX_SLOTS || p_api == NULL)
+ {
+ return NULL;
+ }
+
+ for (subslot_ix = 0; subslot_ix < PNET_MAX_SUBSLOTS; subslot_ix++)
+ {
+ if (p_api->slots[slot_nbr].subslots[subslot_ix].subslot_nbr == subslot_nbr)
+ {
+ return &p_api->slots[slot_nbr].subslots[subslot_ix];
+ }
+ }
+
+ return NULL;
+}
+
+bool app_utils_subslot_is_input (const app_subslot_t * p_subslot)
+{
+ if (p_subslot == NULL || p_subslot->used == false)
+ {
+ return false;
+ }
+
+ if (
+ p_subslot->data_cfg.data_dir == PNET_DIR_INPUT ||
+ p_subslot->data_cfg.data_dir == PNET_DIR_IO)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool app_utils_subslot_is_no_io (const app_subslot_t * p_subslot)
+{
+ if (p_subslot == NULL || p_subslot->used == false)
+ {
+ return false;
+ }
+
+ return p_subslot->data_cfg.data_dir == PNET_DIR_NO_IO;
+}
+
+bool app_utils_subslot_is_output (const app_subslot_t * p_subslot)
+{
+ if (p_subslot == NULL || p_subslot->used == false)
+ {
+ return false;
+ }
+
+ if (
+ p_subslot->data_cfg.data_dir == PNET_DIR_OUTPUT ||
+ p_subslot->data_cfg.data_dir == PNET_DIR_IO)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+void app_utils_cyclic_data_poll (app_api_t * p_api)
+{
+ uint16_t slot_nbr;
+ uint16_t subslot_index;
+ app_subslot_t * p_subslot;
+
+ for (slot_nbr = 0; slot_nbr < PNET_MAX_SLOTS; slot_nbr++)
+ {
+ for (subslot_index = 0; subslot_index < PNET_MAX_SUBSLOTS;
+ subslot_index++)
+ {
+ p_subslot = &p_api->slots[slot_nbr].subslots[subslot_index];
+ if (p_subslot->plugged && p_subslot->cyclic_callback != NULL)
+ {
+ p_subslot->cyclic_callback (p_subslot, p_subslot->tag);
+ }
+ }
+ }
+}
diff --git a/src/profeth/src/app_utils.h b/src/profeth/src/app_utils.h
new file mode 100644
index 00000000..37c4f65f
--- /dev/null
+++ b/src/profeth/src/app_utils.h
@@ -0,0 +1,519 @@
+/*********************************************************************
+ * _ _ _
+ * _ __ | |_ _ | | __ _ | |__ ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | | | |_ _ | || (_| || |_) |\__ \
+ * |_| \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2018 rt-labs AB, Sweden.
+ *
+ * This software is dual-licensed under GPLv3 and a commercial
+ * license. See the file LICENSE.md distributed with this software for
+ * full license information.
+ ********************************************************************/
+
+#ifndef APP_UTILS_H
+#define APP_UTILS_H
+
+/**
+ * @file
+ * @brief Application utilities and helper functions
+ *
+ * Functions for getting string representation of
+ * P-Net events, error codes and more.
+ *
+ * API, slot and subslot administration.
+ *
+ * Initialization of P-Net configuration from app_gsdml.h.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "osal.h"
+#include "pnal.h"
+#include
+
+typedef struct app_utils_netif_name
+{
+ char name[PNET_INTERFACE_NAME_MAX_SIZE];
+} app_utils_netif_name_t;
+
+typedef struct app_utils_netif_namelist
+{
+ app_utils_netif_name_t netif[PNET_MAX_PHYSICAL_PORTS + 1];
+} app_utils_netif_namelist_t;
+
+/* Forward declaration */
+typedef struct app_subslot app_subslot_t;
+
+/**
+ * Callback for updated cyclic data
+ *
+ * @param subslot InOut: Subslot structure
+ * @param tag InOut: Typically a handle to a submodule
+ */
+typedef void (*app_utils_cyclic_callback) (app_subslot_t * subslot, void * tag);
+
+/**
+ * Information of submodule plugged into a subslot.
+ *
+ * Note that submodule data is not stored here but must
+ * be handled by the submodule implementation.
+ *
+ * All parameters are initialized by the app_utils_plug_submodule()
+ * function.
+ *
+ * The cyclic_callback is used when app_utils_cyclic_data_poll()
+ * is called. Typically on the tick event in the main task.
+ * The \a tag parameter is passed with the cyclic_callback and
+ * is typically a handle to a submodule on application.
+ */
+typedef struct app_subslot
+{
+ /** True when the position in the subslot array is occupied */
+ bool used;
+
+ /** True when the subslot is plugged */
+ bool plugged;
+
+ uint16_t slot_nbr;
+ uint16_t subslot_nbr;
+ uint32_t submodule_id;
+ const char * submodule_name;
+ pnet_data_cfg_t data_cfg;
+
+ /** Status indicator from PLC */
+ uint8_t indata_iocs;
+
+ /** Status indicator from PLC */
+ uint8_t outdata_iops;
+
+ /** Callback for cyclic input- or output data, or NULL if not implemented */
+ app_utils_cyclic_callback cyclic_callback;
+ void * tag;
+} app_subslot_t;
+
+/**
+ * Information of module plugged into a slot,
+ * and array of subslots for admin of submodules.
+ */
+typedef struct app_slot
+{
+ bool plugged;
+ uint32_t module_id;
+ const char * name; /** Module name */
+
+ /** Subslots. Use a separate index, as the subslot number might be large.
+ * For example the subslot for DAP port 1 has number 0x8001 */
+ app_subslot_t subslots[PNET_MAX_SUBSLOTS];
+} app_slot_t;
+
+/**
+ * Information relating to an application relation.
+ */
+typedef struct app_ar
+{
+ uint32_t arep;
+ uint32_t events;
+} app_ar_t;
+
+/**
+ * AR list iterator state.
+ */
+typedef struct app_ar_iterator
+{
+ app_ar_t *ar;
+ int16_t index;
+ bool modified;
+} app_ar_iterator_t;
+
+/**
+ * Profinet API state for application
+ *
+ * Used to manage plugged modules into slots (and submodules into subslots).
+ */
+typedef struct app_api_t
+{
+ uint32_t api_id;
+
+ /**
+ * Active AR:s.
+ * A list which is terminated by an entry with arep == UINT32_MAX.
+ */
+ app_ar_t ar[PNET_MAX_AR];
+
+ /** Slots. Use slot number as index */
+ app_slot_t slots[PNET_MAX_SLOTS];
+} app_api_t;
+
+/**
+ * Add an arep to the AR list of an API.
+ * @param api InOut: The \a app_api_t instance.
+ * @param arep In: The arep to add.
+ * @param ar Out: The AR entry, if successful.
+ *
+ * @return 0 if the arep could not be added.
+ * 1 if the arep was added.
+ */
+int app_ar_add_arep (app_api_t * api, uint32_t arep, app_ar_t ** ar);
+
+/**
+ * Get the arep of an AR.
+ * @param ar In: The AR.
+ * @return the arep of the AR.
+ */
+uint32_t app_ar_arep (app_ar_t * ar);
+
+/**
+ * Clear an event from the events value of the AR.
+ * @param ar InOut: The AR to modify.
+ * @param event In: The event(s) (bitmask) that should be cleared.
+ * @return 0 if none of the set bits in \a event were set for the AR.
+ * 1 if at least one of the set bits in \a event were set for the AR.
+ */
+int app_ar_event_clr (app_ar_t * ar, uint32_t event);
+
+/**
+ * Set an event from the events value of the AR.
+ * @param ar InOut: The AR to modify.
+ * @param event In: The event(s) (bitmask) that should be set.
+ */
+void app_ar_event_set (app_ar_t * ar, uint32_t event);
+
+/**
+ * Initialize a list iterator for the AR list of an API.
+ * @param iterator InOut: The iterator to be initialized.
+ * @param api In: The \a app_api_t instance.
+ */
+void app_ar_iterator_init (
+ app_ar_iterator_t * iterator,
+ app_api_t * api);
+
+/**
+ * Get the next AR from a list iterator.
+ * @param iterator InOut: The iterator to use.
+ * @param ar Out: If the return value is 1, the next AR from
+ * the list.
+ * @return 0 if there were no more items in the list.
+ * 1 if there was another item in the list.
+ */
+int app_ar_iterator_next (app_ar_iterator_t * iterator, app_ar_t ** ar);
+
+/**
+ * Check whether the iterator has run to the end of entries.
+ * @param iterator InOut: The iterator.
+ * @return 0 if the iterator is finished.
+ * 1 if the iterator is finished.
+ */
+int app_ar_iterator_done (app_ar_iterator_t * iterator);
+
+/**
+ * Delete the current AR entry of the list iterator.
+ * @param ar InOut: The AR.
+ */
+void app_ar_iterator_delete_current (app_ar_iterator_t * iterator);
+
+/**
+ * Convert IP address to string
+ * @param ip In: IP address
+ * @param outputstring Out: Resulting string buffer. Should have size
+ * PNAL_INET_ADDRSTR_SIZE.
+ */
+void app_utils_ip_to_string (pnal_ipaddr_t ip, char * outputstring);
+
+/**
+ * Get string description of data direction
+ * @param direction In: Submodule data direction
+ * @return String representation of data direction
+ */
+const char * app_utils_submod_dir_to_string (pnet_submodule_dir_t direction);
+
+/**
+ * Get string description of PNIO producer or consumer status
+ * @param ioxs In: Producer or consumer status (IOPS/IOCS)
+ * @return String representation of ioxs (IOPS/IOCS)
+ */
+const char * app_utils_ioxs_to_string (pnet_ioxs_values_t ioxs);
+
+/**
+ * Convert MAC address to string
+ * @param mac In: MAC address
+ * @param outputstring Out: Resulting string buffer. Should have size
+ * PNAL_ETH_ADDRSTR_SIZE.
+ */
+void app_utils_mac_to_string (pnet_ethaddr_t mac, char * outputstring);
+
+/**
+ * Convert error code to string format
+ * Only common error codes supported.
+ * Todo: Add rest of error codes.
+ *
+ * @param err_cls In: The error class. See PNET_ERROR_CODE_1_*
+ * @param err_code In: The error code. See PNET_ERROR_CODE_2_*
+ * @param err_cls_str Out: The error class string
+ * @param err_code_str Out: The error code string
+ */
+void app_utils_get_error_code_strings (
+ uint16_t err_cls,
+ uint16_t err_code,
+ const char ** err_cls_str,
+ const char ** err_code_str);
+
+/**
+ * Copy an IP address (as an integer) to a struct
+ * @param destination_struct Out: Destination
+ * @param ip In: IP address
+ */
+void app_utils_copy_ip_to_struct (
+ pnet_cfg_ip_addr_t * destination_struct,
+ pnal_ipaddr_t ip);
+
+/**
+ * Return a string representation of
+ * the given dcontrol command.
+ * @param event In: control_command
+ * @return A string representing the command
+ */
+const char * app_utils_dcontrol_cmd_to_string (
+ pnet_control_command_t control_command);
+
+/**
+ * Return a string representation of the given event.
+ * @param event In: event
+ * @return A string representing the event
+ */
+const char * app_utils_event_to_string (pnet_event_values_t event);
+
+/**
+ * Update network configuration from a string
+ * defining a list of network interfaces examples:
+ * "eth0" or "br0,eth0,eth1"
+ *
+ * Read IP, netmask etc from operating system.
+ *
+ * @param netif_list_str In: Comma separated string of network ifs
+ * @param if_list Out: Array of network ifs
+ * @param number_of_ports Out: Number of ports
+ * @param if_cfg Out: P-Net network configuration to be updated
+ * @return 0 on success, -1 on error
+ */
+int app_utils_pnet_cfg_init_netifs (
+ const char * netif_list_str,
+ app_utils_netif_namelist_t * if_list,
+ uint16_t * number_of_ports,
+ pnet_if_cfg_t * if_cfg);
+
+/**
+ * Parse a comma separated list of network interfaces and check
+ * that the number of interfaces match the PNET_MAX_PHYSICAL_PORTS
+ * configuration.
+ *
+ * For a single Ethernet interface, the \a arg_str should consist of
+ * one name. For two Ethernet interfaces, the \a arg_str should consist of
+ * three names, as we also need a bridge interface.
+ *
+ * Does only consider the number of comma separated names. No check of the
+ * names themselves are done.
+ *
+ * Examples:
+ * arg_str num_ports
+ * "eth0" 1
+ * "eth0,eth1" error (We need a bridge as well)
+ * "br0,eth0,eth1" 2
+ *
+ * @param arg_str In: Network interface list as comma separated,
+ * terminated string. For example "eth0" or
+ * "br0,eth0,eth1".
+ * @param max_port In: PNET_MAX_PHYSICAL_PORTS, passed as argument to
+ * allow test.
+ * @param p_if_list Out: List of network interfaces
+ * @param p_num_ports Out: Resulting number of physical ports
+ * @return 0 on success
+ * -1 on error
+ */
+int app_utils_get_netif_namelist (
+ const char * arg_str,
+ uint16_t max_port,
+ app_utils_netif_namelist_t * p_if_list,
+ uint16_t * p_num_ports);
+
+/**
+ * Print network configuration using APP_LOG_INFO().
+ *
+ * @param if_cfg In: Network configuration
+ * @param number_of_ports In: Number of used ports
+ */
+void app_utils_print_network_config (
+ pnet_if_cfg_t * if_cfg,
+ uint16_t number_of_ports);
+
+/**
+ * Print message if IOXS has changed.
+ *
+ * Uses APP_LOG_INFO()
+ *
+ * @param subslot In: Subslot
+ * @param ioxs_str In: String description Producer or Consumer
+ * @param ioxs_current In: Current status
+ * @param ioxs_new In: New status
+ */
+void app_utils_print_ioxs_change (
+ const app_subslot_t * subslot,
+ const char * ioxs_str,
+ uint8_t ioxs_current,
+ uint8_t ioxs_new);
+
+/**
+ * Init the p-net configuration to default values.
+ *
+ * Most values are picked from app_gsdml.h
+ *
+ * Network configuration not initialized.
+ * This means that \a '.if_cfg' must be set by application.
+ *
+ * Use this function to init P-Net configuration before
+ * before passing config to app_init().
+ *
+ * @param pnet_cfg Out: Configuration for use by p-net
+ * @return 0 if the operation succeeded.
+ * -1 if an error occurred.
+ */
+int app_utils_pnet_cfg_init_default (pnet_cfg_t * pnet_cfg);
+
+/**
+ * Plug application module
+ *
+ * This is for the application to remember which slots are
+ * populated in the p-net stack.
+ *
+ * @param p_api InOut: API
+ * @param slot_nbr In: Slot number
+ * @param id In: Module identity
+ * @param name In: Module name
+ * @return 0 on success, -1 on error
+ */
+int app_utils_plug_module (
+ app_api_t * p_api,
+ uint16_t slot_nbr,
+ uint32_t id,
+ const char * name);
+
+/**
+ * Pull any application module in given slot.
+ *
+ * This is for the application to remember which slots are
+ * populated in the p-net stack.
+ *
+ * @param p_api InOut: API
+ * @param slot_nbr In: Slot number
+ * @return 0 on success, -1 on error
+ */
+int app_utils_pull_module (app_api_t * p_api, uint16_t slot_nbr);
+
+/**
+ * Plug application submodule.
+ *
+ * This is for the application to remember which subslots are
+ * populated in the p-net stack.
+ *
+ * @param p_api InOut: API
+ * @param slot_nbr In: Slot number
+ * @param subslot_nbr In: Subslot number
+ * @param submodule_id In: Submodule identity
+ * @param p_data_cfg In: Data configuration,
+ * direction, in and out sizes
+ * @param submodule_name In: Submodule name
+ * @param cyclic_callback In: Submodule data callback
+ * @param tag In: Tag passed in cyclic callback
+ * Typically application or
+ * submodule handle
+ * @return Reference to allocated subslot,
+ * NULL if no free subslot is available. This should
+ * never happen if application is aligned with p-net state.
+ */
+app_subslot_t * app_utils_plug_submodule (
+ app_api_t * p_api,
+ uint16_t slot_nbr,
+ uint16_t subslot_nbr,
+ uint32_t submodule_id,
+ const pnet_data_cfg_t * p_data_cfg,
+ const char * submodule_name,
+ app_utils_cyclic_callback cyclic_callback,
+ void * tag);
+
+/**
+ * Unplug any application submodule from given subslot.
+ *
+ * This is for the application to remember which subslots are
+ * populated in the p-net stack.
+ *
+ * @param p_api InOut: API
+ * @param slot_nbr In: Slot number
+ * @param subslot_nbr In: Subslot number
+ * @return 0 on success, -1 on error.
+ */
+int app_utils_pull_submodule (
+ app_api_t * p_api,
+ uint16_t slot_nbr,
+ uint16_t subslot_nbr);
+
+/**
+ * Trigger data callback for all plugged submodules in all slots.
+ *
+ * The callbacks given in \a app_utils_plug_submodule() are used.
+ *
+ * @param p_api In: API
+ */
+void app_utils_cyclic_data_poll (app_api_t * p_api);
+
+/**
+ * Get subslot application information.
+ *
+ * @param p_appdata InOut: Application state.
+ * @param slot_nbr In: Slot number.
+ * @param subslot_nbr In: Subslot number. Range 0 - 0x9FFF.
+ * @return Reference to application subslot,
+ * NULL if subslot is not found/plugged.
+ */
+app_subslot_t * app_utils_subslot_get (
+ app_api_t * p_api,
+ uint16_t slot_nbr,
+ uint16_t subslot_nbr);
+
+/**
+ * Return true if subslot is input.
+ *
+ * @param p_subslot In: Reference to subslot.
+ * @return true if subslot is input or input/output.
+ * false if not.
+ */
+bool app_utils_subslot_is_input (const app_subslot_t * p_subslot);
+
+/**
+ * Return true if subslot is neither input or output.
+ *
+ * This is applies for DAP submodules/slots
+ *
+ * @param p_subslot In: Reference to subslot.
+ * @return true if subslot is input or input/output.
+ * false if not.
+ */
+bool app_utils_subslot_is_no_io (const app_subslot_t * p_subslot);
+
+/**
+ * Return true if subslot is output.
+ *
+ * @param p_subslot In: Reference to subslot.
+ * @return true if subslot is output or input/output,
+ * false if not.
+ */
+bool app_utils_subslot_is_output (const app_subslot_t * p_subslot);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APP_UTILS_H */
diff --git a/src/profeth/src/pnal.h b/src/profeth/src/pnal.h
new file mode 100644
index 00000000..f6371cbf
--- /dev/null
+++ b/src/profeth/src/pnal.h
@@ -0,0 +1,554 @@
+/*********************************************************************
+ * _ _ _
+ * _ __ | |_ _ | | __ _ | |__ ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | | | |_ _ | || (_| || |_) |\__ \
+ * |_| \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2018 rt-labs AB, Sweden.
+ *
+ * This software is dual-licensed under GPLv3 and a commercial
+ * license. See the file LICENSE.md distributed with this software for
+ * full license information.
+ ********************************************************************/
+
+/**
+ * @file
+ * @brief Definitions for profinet abstraction layer.
+ *
+ * The Ethernet-related functions should not use \a loc_port_num, but
+ * the interface name instead. The lookup from \a loc_port_num to
+ * interface name must happen somewhere else in the stack.
+ */
+
+#ifndef PNAL_H
+#define PNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+
+#include "options.h"
+#include "pnet_api.h"
+#include "pnal_sys.h"
+
+#define PNAL_MAKEU32(a, b, c, d) \
+ (((uint32_t)((a)&0xff) << 24) | ((uint32_t)((b)&0xff) << 16) | \
+ ((uint32_t)((c)&0xff) << 8) | (uint32_t)((d)&0xff))
+
+#define PNAL_MAKEU16(a, b) (((uint16_t)((a)&0xff) << 8) | (uint16_t)((b)&0xff))
+
+#define PNAL_INET_ADDRSTR_SIZE 16 /** Incl termination */
+#define PNAL_ETH_ADDRSTR_SIZE 18 /** Incl termination */
+#ifndef PNAL_HOSTNAME_MAX_SIZE
+#define PNAL_HOSTNAME_MAX_SIZE 64 /** Incl termination. Value from Linux. */
+#endif
+
+/**
+ * Ethernet autonegotiation capabilities (not exhaustive)
+ *
+ * See IEEE 802.3 (Ethernet) ch. 30.6 "Management for link Auto-Negotiation".
+ *
+ * See IETF RFC 4836 "Definitions of Managed Objects for
+ * IEEE 802.3 Medium Attachment Units (MAUs)", object
+ * "IANAifMauAutoNegCapBits"
+ */
+#define PNAL_ETH_AUTONEG_CAP_1000BaseT_FULL_DUPLEX BIT (0)
+#define PNAL_ETH_AUTONEG_CAP_1000BaseT_HALF_DUPLEX BIT (1)
+#define PNAL_ETH_AUTONEG_CAP_1000BaseX_FULL_DUPLEX BIT (2)
+#define PNAL_ETH_AUTONEG_CAP_1000BaseX_HALF_DUPLEX BIT (3)
+#define PNAL_ETH_AUTONEG_CAP_100BaseTX_FULL_DUPLEX BIT (10)
+#define PNAL_ETH_AUTONEG_CAP_100BaseTX_HALF_DUPLEX BIT (11)
+#define PNAL_ETH_AUTONEG_CAP_10BaseT_FULL_DUPLEX BIT (13)
+#define PNAL_ETH_AUTONEG_CAP_10BaseT_HALF_DUPLEX BIT (14)
+#define PNAL_ETH_AUTONEG_CAP_UNKNOWN BIT (15)
+
+/**
+ * Ethernet MAU type (not exhaustive).
+ *
+ * See IEEE 802.3 (Ethernet) ch. 30.5 "Layer management for medium
+ * attachment units (MAUs)".
+ *
+ * See IETF RFC 4836 "Definitions of Managed Objects for
+ * IEEE 802.3 Medium Attachment Units (MAUs)", objects
+ * "IANAifMauTypeListBits" and "dot3MauType".
+ *
+ * See Profinet "5.2.12.3.12 Coding of the field MAUType"
+ */
+typedef enum pnal_eth_mau
+{
+ PNAL_ETH_MAU_UNKNOWN = 0x00, /* When link is down */
+ PNAL_ETH_MAU_RADIO = 0x00, /* When link is up */
+ PNAL_ETH_MAU_COPPER_10BaseT = 0x05,
+ PNAL_ETH_MAU_COPPER_100BaseTX_HALF_DUPLEX = 0x0F,
+ PNAL_ETH_MAU_COPPER_100BaseTX_FULL_DUPLEX = 0x10,
+ PNAL_ETH_MAU_COPPER_1000BaseT_HALF_DUPLEX = 0x1D,
+ PNAL_ETH_MAU_COPPER_1000BaseT_FULL_DUPLEX = 0x1E,
+ PNAL_ETH_MAU_FIBER_100BaseFX_HALF_DUPLEX = 0x11,
+ PNAL_ETH_MAU_FIBER_100BaseFX_FULL_DUPLEX = 0x12,
+ PNAL_ETH_MAU_FIBER_1000BaseX_HALF_DUPLEX = 0x15,
+ PNAL_ETH_MAU_FIBER_1000BaseX_FULL_DUPLEX = 0x16,
+} pnal_eth_mau_t;
+
+/* See Profinet 2.4 section 5.2.28 and annex W */
+typedef struct pnal_port_stats
+{
+ uint32_t if_in_octets;
+ uint32_t if_out_octets;
+ uint32_t if_in_discards;
+ uint32_t if_out_discards;
+ uint32_t if_in_errors;
+ uint32_t if_out_errors;
+} pnal_port_stats_t;
+
+/** Set an IP address given by the four byte-parts */
+#define PNAL_IP4_ADDR_TO_U32(ipaddr, a, b, c, d) \
+ ipaddr = PNAL_MAKEU32 (a, b, c, d)
+
+typedef enum pnal_ethertype
+{
+ PNAL_ETHTYPE_IP = 0x0800U,
+ PNAL_ETHTYPE_ARP = 0x0806U,
+ PNAL_ETHTYPE_VLAN = 0x8100U,
+ PNAL_ETHTYPE_PROFINET = 0x8892U,
+ PNAL_ETHTYPE_ETHERCAT = 0x88A4U,
+ PNAL_ETHTYPE_LLDP = 0x88CCU,
+ PNAL_ETHTYPE_ALL = 0xFFFFU,
+} pnal_ethertype_t;
+
+/* 255.255.255.255 */
+#ifndef PNAL_IPADDR_NONE
+#define PNAL_IPADDR_NONE ((uint32_t)0xffffffffUL)
+#endif
+/* 127.0.0.1 */
+#ifndef PNAL_IPADDR_LOOPBACK
+#define PNAL_IPADDR_LOOPBACK ((uint32_t)0x7f000001UL)
+#endif
+/* 0.0.0.0 */
+#ifndef PNAL_IPADDR_ANY
+#define PNAL_IPADDR_ANY ((uint32_t)0x00000000UL)
+#endif
+#define PNAL_IPADDR_INVALID PNAL_IPADDR_ANY
+/* 255.255.255.255 */
+#ifndef PNAL_IPADDR_BROADCAST
+#define PNAL_IPADDR_BROADCAST ((uint32_t)0xffffffffUL)
+#endif
+
+typedef uint32_t pnal_ipaddr_t;
+typedef uint16_t pnal_ipport_t;
+
+/**
+ * The Ethernet MAC address.
+ *
+ * TODO: Remove this and use pnet_ethaddr_t instead
+ */
+typedef struct pnal_ethaddr
+{
+ uint8_t addr[6];
+} pnal_ethaddr_t;
+
+/**
+ * Ethernet link status.
+ *
+ * See also type pf_lldp_link_status_t.
+ */
+typedef struct pnal_eth_status_t
+{
+ /* Is autonegotiation supported on this port? */
+ bool is_autonegotiation_supported;
+
+ /* Is autonegotiation supported on this port? */
+ bool is_autonegotiation_enabled;
+
+ /* Capabilities advertised to link partner during autonegotiation.
+ *
+ * See macros PNAL_ETH_AUTONEG_CAP_xxx.
+ */
+ uint16_t autonegotiation_advertised_capabilities;
+
+ /* Operational MAU type.
+ *
+ * This specifies both the physical medium as well as
+ * speed and duplex setting used for the link.
+ */
+ pnal_eth_mau_t operational_mau_type;
+
+ /* Corresponds to linux "RUNNING" flag.
+ * Up and cable connected
+ */
+ bool running;
+} pnal_eth_status_t;
+
+/**
+ * Get system uptime from the SNMP implementation.
+ *
+ * This is the sysUpTime, as used by SNMP:
+ * "The time (in hundredths of a second) since the network
+ * management portion of the system was last re-initialized."
+ * - IETF RFC 3418 (SNMP MIB-2).
+ *
+ * Typically used to store the SNMP-timestamp for incoming LLDP-packets.
+ * This timestamp is later reported back to the SNMP implementation, and
+ * remote SNMP managers might use it for filtering.
+ *
+ * The returned value should be the one used by the SNMP implementation.
+ * Ask the SNMP implementation for the value, or make sure that this
+ * function and the SNMP implementation use the same mechanism to get the
+ * value.
+ *
+ * Starts at 0, with wrap-around after ~497 days.
+ *
+ * @return System uptime, in units of 10 milliseconds.
+ */
+uint32_t pnal_get_system_uptime_10ms (void);
+
+/**
+ * Load a binary file.
+ *
+ * Can load the data into two buffers.
+ *
+ * @param fullpath In: Full path to the file
+ * @param object_1 Out: Data to load, or NULL. Mandatory if size_1 > 0
+ * @param size_1 In: Size of object_1.
+ * @param object_2 Out: Data to load, or NULL. Mandatory if size_2 > 0
+ * @param size_2 In: Size of object_2.
+ * @return 0 if the operation succeeded.
+ * -1 if not found or an error occurred.
+ */
+int pnal_load_file (
+ const char * fullpath,
+ void * object_1,
+ size_t size_1,
+ void * object_2,
+ size_t size_2);
+
+/**
+ * Save a binary file.
+ *
+ * Can handle two output buffers.
+ *
+ * @param fullpath In: Full path to the file
+ * @param object_1 In: Data to save, or NULL. Mandatory if size_1 > 0
+ * @param size_1 In: Size of object_1.
+ * @param object_2 In: Data to save, or NULL. Mandatory if size_2 > 0
+ * @param size_2 In: Size of object_2.
+ * @return 0 if the operation succeeded.
+ * -1 if an error occurred.
+ */
+int pnal_save_file (
+ const char * fullpath,
+ const void * object_1,
+ size_t size_1,
+ const void * object_2,
+ size_t size_2);
+
+/**
+ * Clear a binary file.
+ *
+ * @param fullpath In: Full path to the file
+ */
+void pnal_clear_file (const char * fullpath);
+
+/*
+ */
+
+/**
+ * Allocate a buffer
+ *
+ * The resulting \a pnal_buf_t buffer is defined for
+ * each operating system, and should at least contain:
+ * void * payload;
+ * uint16_t len;
+ *
+ * @param length In: Length, in bytes
+ * @return a pnal_buf_t, or NULL at failure
+ */
+pnal_buf_t * pnal_buf_alloc (uint16_t length);
+
+/**
+ * Free a buffer
+ *
+ * @param p In: Buffer to free
+ */
+void pnal_buf_free (pnal_buf_t * p);
+
+/** Not yet used */
+uint8_t pnal_buf_header (pnal_buf_t * p, int16_t header_size_increment);
+
+/**
+ * Network interface handle, forward declaration.
+ */
+typedef struct pnal_eth_handle pnal_eth_handle_t;
+
+/**
+ * The prototype of raw Ethernet reception call-back functions.
+ *
+ * @param eth_handle InOut: Network interface handle
+ * @param arg InOut: User-defined (may be NULL).
+ * @param p_buf InOut: The incoming Ethernet frame
+ *
+ * @return 0 If the frame was NOT handled by this function.
+ * 1 If the frame was handled and the buffer freed.
+ */
+typedef int (pnal_eth_callback_t) (
+ pnal_eth_handle_t * eth_handle,
+ void * arg,
+ pnal_buf_t * p_buf);
+
+/**
+ * Get status of Ethernet link on specified port
+ *
+ * @param interface_name In: Ethernet interface name, for example eth0
+ * @param status Out: Returned link status (autoneg etc)
+ * @return 0 if the operation succeeded.
+ * -1 if an error occurred.
+ */
+int pnal_eth_get_status (
+ const char * interface_name,
+ pnal_eth_status_t * status);
+
+/**
+ * Get network interface index
+ *
+ * The interface index (ifIndex) is the (row) index for the network interface
+ * in the table ifTable, which is part of the SNMP MIB-II data structure.
+ * See RFC 2863 "The Interfaces Group MIB".
+ *
+ * @param interface_name In: Ethernet interface name, for example eth0
+ *
+ * @return The interface index, or 0 if not available.
+ */
+int pnal_get_interface_index (const char * interface_name);
+
+/**
+ * Get network interface (port) statistics
+ *
+ * In Profinet naming a physical Ethernet interface is a "port".
+ * This corresponds to for example a Linux Ethernet interface.
+ *
+ * @param interface_name In: Ethernet interface name for example eth0
+ * @param port_stats Out: Returned statistics
+ *
+ * @return 0 if the operation succeeded.
+ * -1 if an error occurred.
+ */
+int pnal_get_port_statistics (
+ const char * interface_name,
+ pnal_port_stats_t * port_stats);
+
+/**
+ * Send raw Ethernet data
+ *
+ * @param handle In: Ethernet handle
+ * @param buf In: Buffer with data to be sent
+ * @return The number of bytes sent, or -1 if an error occurred.
+ */
+int pnal_eth_send (pnal_eth_handle_t * handle, pnal_buf_t * buf);
+
+/**
+ * Initialize receiving of raw Ethernet frames on one interface (in separate
+ * thread)
+ *
+ * @param if_name In: Ethernet interface name
+ * @param receive_type In: Ethernet frame types that shall be received
+ * by the network interface / port.
+ * @param pnal_cfg In: Operating system dependent configuration
+ * @param callback In: Callback for received raw Ethernet frames
+ * @param arg InOut: User argument passed to the callback
+ *
+ * @return the Ethernet handle, or NULL if an error occurred.
+ */
+pnal_eth_handle_t * pnal_eth_init (
+ const char * if_name,
+ pnal_ethertype_t receive_type,
+ const pnal_cfg_t * pnal_cfg,
+ pnal_eth_callback_t * callback,
+ void * arg);
+
+/**
+ * Open an UDP socket
+ *
+ * @param addr In: IP address to listen to. Typically used with
+ * PNAL_IPADDR_ANY.
+ * @param port In: UDP port to listen to.
+ * @return Socket ID, or -1 if an error occurred.
+ */
+int pnal_udp_open (pnal_ipaddr_t addr, pnal_ipport_t port);
+
+/**
+ * Send UDP data
+ *
+ * @param id In: Socket ID
+ * @param dst_addr In: Destination IP address
+ * @param dst_port In: Destination UDP port
+ * @param data In: Data to be sent
+ * @param size In: Size of data
+ * @return The number of bytes sent, or -1 if an error occurred.
+ */
+int pnal_udp_sendto (
+ uint32_t id,
+ pnal_ipaddr_t dst_addr,
+ pnal_ipport_t dst_port,
+ const uint8_t * data,
+ int size);
+
+/**
+ * Receive UDP data.
+ *
+ * This is a nonblocking function, and it
+ * returns 0 immediately if no data is available.
+ *
+ * @param id In: Socket ID
+ * @param src_addr Out: Source IP address
+ * @param src_port Out: Source UDP port
+ * @param data Out: Received data
+ * @param size In: Size of buffer for received data
+ * @return The number of bytes received, or -1 if an error occurred.
+ */
+int pnal_udp_recvfrom (
+ uint32_t id,
+ pnal_ipaddr_t * src_addr,
+ pnal_ipport_t * src_port,
+ uint8_t * data,
+ int size);
+
+/**
+ * Close an UDP socket
+ *
+ * @param id In: Socket ID
+ */
+void pnal_udp_close (uint32_t id);
+
+/**
+ * Configure SNMP server.
+ *
+ * This function configures a platform-specific SNMP server as to
+ * enable a connected SNMP client to read variables from the p-net stack,
+ * as well as to write some variables to it.
+ *
+ * @param net InOut: The p-net stack instance
+ * @param pnal_cfg In: Operating system dependent configuration
+ * @return 0 if the operation succeeded.
+ * -1 if an error occurred.
+ */
+int pnal_snmp_init (pnet_t * net, const pnal_cfg_t * pnal_cfg);
+
+/**
+ * Get network parameters (IP address, netmask etc)
+ *
+ * For example:
+ *
+ * IP address Represented by
+ * 1.0.0.0 0x01000000 = 16777216
+ * 0.0.0.1 0x00000001 = 1
+ *
+ * @param interface_name In: Ethernet interface name, for example eth0
+ * @param p_ipaddr Out: IPv4 address
+ * @param p_netmask Out: Netmask
+ * @param p_gw Out: Default gateway
+ * @param hostname Out: Host name, for example my_laptop_4. Existing
+ * buffer should have size
+ * PNAL_HOSTNAME_MAX_SIZE.
+ * @return 0 if the operation succeeded.
+ * -1 if an error occurred.
+ */
+int pnal_get_ip_suite (
+ const char * interface_name,
+ pnal_ipaddr_t * p_ipaddr,
+ pnal_ipaddr_t * p_netmask,
+ pnal_ipaddr_t * p_gw,
+ char * hostname);
+
+/**
+ * Read the IP address as an integer. For IPv4.
+ *
+ * For example:
+ *
+ * IP address Represented by
+ * 1.0.0.0 0x01000000 = 16777216
+ * 0.0.0.1 0x00000001 = 1
+ *
+ * @param interface_name In: Name of network interface
+ * @return IP address on success and
+ * 0 if an error occurred
+ */
+pnal_ipaddr_t pnal_get_ip_address (const char * interface_name);
+
+/**
+ * Read the netmask as an integer. For IPv4.
+ *
+ * @param interface_name In: Name of network interface
+ * @return netmask
+ */
+pnal_ipaddr_t pnal_get_netmask (const char * interface_name);
+
+/**
+ * Read the default gateway address as an integer. For IPv4.
+ *
+ * Assumes the default gateway is found on .1 on same subnet as the IP address.
+ *
+ * @param interface_name In: Name of network interface
+ * @return netmask
+ */
+pnal_ipaddr_t pnal_get_gateway (const char * interface_name);
+
+/**
+ * Read the MAC address.
+ *
+ * @param interface_name In: Name of network interface
+ * @param p_mac Out: MAC address
+ *
+ * @return 0 on success and
+ * -1 if no such interface is available
+ */
+int pnal_get_macaddress (const char * interface_name, pnal_ethaddr_t * p_mac);
+
+/**
+ * Read the current host name
+ *
+ * The resulting string is null terminated.
+ *
+ * @param hostname Out: Host name, for example my_laptop_4. Existing
+ * buffer should have size
+ * PNAL_HOSTNAME_MAX_SIZE.
+ * @return 0 on success and
+ * -1 if an error occurred
+ */
+int pnal_get_hostname (char * hostname);
+
+/**
+ * Set network parameters (IP address, netmask etc)
+ *
+ * For example:
+ *
+ * IP address Represented by
+ * 1.0.0.0 0x01000000 = 16777216
+ * 0.0.0.1 0x00000001 = 1
+ *
+ * @param interface_name In: Ethernet interface name, for example eth0
+ * @param p_ipaddr In: IPv4 address
+ * @param p_netmask In: Netmask
+ * @param p_gw In: Default gateway
+ * @param hostname In: Host name, for example my_laptop_4
+ * @param permanent In: 1 if changes are permanent, or 0 if temporary
+ * @return 0 if the operation succeeded.
+ * -1 if an error occurred.
+ */
+int pnal_set_ip_suite (
+ const char * interface_name,
+ const pnal_ipaddr_t * p_ipaddr,
+ const pnal_ipaddr_t * p_netmask,
+ const pnal_ipaddr_t * p_gw,
+ const char * hostname,
+ bool permanent);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PNAL_H */
diff --git a/src/profeth/src/pnal_filetools.c b/src/profeth/src/pnal_filetools.c
new file mode 100644
index 00000000..f19e9958
--- /dev/null
+++ b/src/profeth/src/pnal_filetools.c
@@ -0,0 +1,225 @@
+
+/*********************************************************************
+ * _ _ _
+ * _ __ | |_ _ | | __ _ | |__ ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | | | |_ _ | || (_| || |_) |\__ \
+ * |_| \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2020 rt-labs AB, Sweden.
+ *
+ * This software is dual-licensed under GPLv3 and a commercial
+ * license. See the file LICENSE.md distributed with this software for
+ * full license information.
+ ********************************************************************/
+
+#define _GNU_SOURCE /* Enable execvpe () */
+
+#include "pnal_filetools.h"
+
+#include "pnet_options.h"
+#include "options.h"
+#include "osal_log.h"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+bool pnal_does_file_exist (const char * filepath)
+{
+ struct stat statbuffer;
+
+ return (stat (filepath, &statbuffer) == 0);
+}
+
+/**
+ * @internal
+ * Get the path to the directory where the main binary is located.
+ *
+ * @param tempstring Temp: Temporary buffer.
+ * @param dirpath Out: Path to directory. Terminated string.
+ * @param size In: Size of the stringbuffers. Should be large
+ * enough for the full path to the binary.
+ *
+ * @return 0 on success.
+ * -1 if an error occurred.
+ */
+static int pnal_get_directory_of_binary (
+ char * tempstring,
+ char * dirpath,
+ size_t size)
+{
+ ssize_t num_bytes = 0;
+ memset (tempstring, 0, size); /* Path to executable */
+ memset (dirpath, 0, size);
+ char * resulting_directory = NULL;
+
+ num_bytes = readlink ("/proc/self/exe", tempstring, size);
+ if (num_bytes == -1)
+ {
+ return -1;
+ }
+
+ /* Verify that we have the full path (size is big enough) */
+ if (!pnal_does_file_exist (tempstring))
+ {
+ return -1;
+ }
+
+ resulting_directory = dirname (tempstring);
+ snprintf (dirpath, size, "%s", resulting_directory);
+
+ return 0;
+}
+
+/**
+ * @internal
+ * Create a PATH string used when searching for scripts
+ *
+ * The PATH string is colon separated, and includes the directory where
+ * the main binary is located.
+ *
+ * @param path Out: Resulting path. Terminated string.
+ * Might be modified also on failure.
+ * @param size In: Size of the outputbuffer. Should be
+ * sizeof(PNAL_DEFAULT_SEARCHPATH) +
+ * PNET_MAX_DIRECTORYPATH_SIZE
+ * @return 0 on success.
+ * -1 if an error occurred.
+ */
+int pnal_create_searchpath (char * path, size_t size)
+{
+ int written = 0;
+
+ /** Should temporarily hold full path. Resulting size is
+ * PNET_MAX_DIRECTORYPATH_SIZE */
+ char
+ directory_of_binary[PNET_MAX_DIRECTORYPATH_SIZE + PNET_MAX_FILENAME_SIZE] =
+ {0};
+ char tempstring[PNET_MAX_DIRECTORYPATH_SIZE + PNET_MAX_FILENAME_SIZE] = {0};
+
+ if (
+ pnal_get_directory_of_binary (
+ tempstring,
+ directory_of_binary,
+ sizeof (directory_of_binary)) != 0)
+ {
+ return -1;
+ }
+
+ written = snprintf (
+ path,
+ size,
+ "%s:%s",
+ PNAL_DEFAULT_SEARCHPATH,
+ directory_of_binary);
+ if (written < 0 || (unsigned)written >= size)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+int pnal_execute_script (const char * argv[])
+{
+ /** Terminated string, see pnal_create_searchpath() */
+ char child_searchpath
+ [sizeof (PNAL_DEFAULT_SEARCHPATH) + PNET_MAX_DIRECTORYPATH_SIZE] = {0};
+ int childstatus = 0;
+ pid_t childpid;
+ char * scriptenviron[] = {NULL};
+ int script_returnvalue = 0;
+#if LOG_DEBUG_ENABLED(PF_PNAL_LOG)
+ uint16_t ix;
+#endif
+
+ if (argv == NULL)
+ {
+ LOG_ERROR (
+ PF_PNAL_LOG,
+ "PNAL(%d): No argument vector given for running script.\n",
+ __LINE__);
+ return -1;
+ }
+
+ if (argv[0] == NULL)
+ {
+ LOG_ERROR (PF_PNAL_LOG, "PNAL(%d): No script name given.\n", __LINE__);
+ return -1;
+ }
+
+ if (pnal_create_searchpath (child_searchpath, sizeof (child_searchpath)) != 0)
+ {
+ LOG_ERROR (
+ PF_PNAL_LOG,
+ "PNAL(%d): Could not build PATH to run script.\n",
+ __LINE__);
+ return -1;
+ }
+
+#if LOG_DEBUG_ENABLED(PF_PNAL_LOG)
+ printf ("PNAL(%d): Command for script:", __LINE__);
+ for (ix = 0; argv[ix] != NULL; ix++)
+ {
+ if (strlen (argv[ix]) > 0)
+ {
+ printf (" %s", argv[ix]);
+ }
+ else
+ {
+ printf (" ''");
+ }
+ }
+ printf ("\n");
+#endif
+
+ /* Fork and exec */
+ childpid = fork();
+ if (childpid < 0)
+ {
+ LOG_ERROR (
+ PF_PNAL_LOG,
+ "PNAL(%d): Failed to fork the process to run script.\n",
+ __LINE__);
+ return -1;
+ }
+ else if (childpid == 0)
+ {
+ /* We are in the child process */
+
+ /* setenv() will malloc memory. We will not free() it, but it does not
+ matter since we do it in the short-lived child process */
+ if (setenv ("PATH", child_searchpath, 1) != 0)
+ {
+ printf (
+ "PNAL(%d): Failed to set PATH in child process to run script.\n",
+ __LINE__);
+ exit (EXIT_FAILURE);
+ }
+
+ execvpe (argv[0], (char * const *)argv, scriptenviron);
+
+ printf (
+ "PNAL(%d): Failed to execute in child process. Is the script file "
+ "missing or lacks execution permission? %s Search path %s\n",
+ __LINE__,
+ argv[0],
+ child_searchpath);
+ exit (EXIT_FAILURE);
+ }
+ else
+ {
+ /* We are in the parent (original) process */
+ wait (&childstatus);
+ script_returnvalue = WEXITSTATUS (childstatus);
+ }
+
+ return (script_returnvalue == 0) ? 0 : -1;
+}
diff --git a/src/profeth/src/pnal_filetools.h b/src/profeth/src/pnal_filetools.h
new file mode 100644
index 00000000..a9882210
--- /dev/null
+++ b/src/profeth/src/pnal_filetools.h
@@ -0,0 +1,57 @@
+/*********************************************************************
+ * _ _ _
+ * _ __ | |_ _ | | __ _ | |__ ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | | | |_ _ | || (_| || |_) |\__ \
+ * |_| \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2020 rt-labs AB, Sweden.
+ *
+ * This software is dual-licensed under GPLv3 and a commercial
+ * license. See the file LICENSE.md distributed with this software for
+ * full license information.
+ ********************************************************************/
+
+#ifndef PNAL_FILETOOLS_H
+#define PNAL_FILETOOLS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+/* Colon separated paths to search for scripts. No colon at end. */
+#define PNAL_DEFAULT_SEARCHPATH "/bin:/usr/bin"
+
+/**
+ * Check if a file or directory exists
+ *
+ * @param filepath In: Path to file or directory. Null terminated.
+ * Trailing slash is optional for directories.
+ * @return true if file exists
+ */
+bool pnal_does_file_exist (const char * filepath);
+
+/**
+ * Execute a script.
+ *
+ * The script will be searched for in:
+ * - Directories given in PNAL_DEFAULT_SEARCHPATH
+ * - Same directory as the main binary is located in.
+ *
+ * @param argv In: Arguments (null terminated strings).
+ * The first argument should be the name of
+ * the script.
+ * The last element should be NULL.
+ * @return 0 on success
+ * -1 if an error occurred
+ */
+int pnal_execute_script (const char * argv[]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PNAL_FILETOOLS_H */
diff --git a/src/profeth/src/pnal_sys.h b/src/profeth/src/pnal_sys.h
new file mode 100644
index 00000000..e38d1a51
--- /dev/null
+++ b/src/profeth/src/pnal_sys.h
@@ -0,0 +1,39 @@
+/*********************************************************************
+ * _ _ _
+ * _ __ | |_ _ | | __ _ | |__ ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | | | |_ _ | || (_| || |_) |\__ \
+ * |_| \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2018 rt-labs AB, Sweden.
+ *
+ * This software is dual-licensed under GPLv3 and a commercial
+ * license. See the file LICENSE.md distributed with this software for
+ * full license information.
+ ********************************************************************/
+
+#ifndef PNAL_SYS_H
+#define PNAL_SYS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "osal.h"
+
+#include
+
+#define PNAL_BUF_MAX_SIZE 1522
+
+typedef struct os_buf
+{
+ void * payload;
+ uint16_t len;
+} pnal_buf_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PNAL_SYS_H */
diff --git a/src/profeth/src/profeth.c b/src/profeth/src/profeth.c
new file mode 100644
index 00000000..2f560364
--- /dev/null
+++ b/src/profeth/src/profeth.c
@@ -0,0 +1,516 @@
+/*********************************************************************
+ * _ _ _
+ * _ __ | |_ _ | | __ _ | |__ ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | | | |_ _ | || (_| || |_) |\__ \
+ * |_| \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2018 rt-labs AB, Sweden.
+ *
+ * This software is dual-licensed under GPLv3 and a commercial
+ * license. See the file LICENSE.md distributed with this software for
+ * full license information.
+ ********************************************************************/
+
+#define _GNU_SOURCE /* For asprintf() */
+
+#include "sampleapp_common.h"
+#include "app_gsdml.h"
+#include "app_log.h"
+#include "app_utils.h"
+
+#include "osal.h"
+#include "osal_log.h" /* For LOG_LEVEL */
+#include "pnal.h"
+#include "pnal_filetools.h"
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#if PNET_MAX_PHYSICAL_PORTS == 1
+#define APP_DEFAULT_ETHERNET_INTERFACE "eth0"
+#else
+#define APP_DEFAULT_ETHERNET_INTERFACE "br0,eth0,eth1"
+#endif
+
+#define APP_MAIN_SLEEPTIME_US 5000 * 1000
+#define APP_SNMP_THREAD_PRIORITY 1
+#define APP_SNMP_THREAD_STACKSIZE 256 * 1024 /* bytes */
+#define APP_ETH_THREAD_PRIORITY 10
+#define APP_ETH_THREAD_STACKSIZE 4096 /* bytes */
+#define APP_BG_WORKER_THREAD_PRIORITY 5
+#define APP_BG_WORKER_THREAD_STACKSIZE 4096 /* bytes */
+
+/* Note that this sample application uses os_timer_create() for the timer
+ that controls the ticks. It is implemented in OSAL, and the Linux
+ implementation uses a thread internally. To modify the timer thread priority,
+ modify OSAL or use some other timer */
+
+app_args_t app_args = {0};
+
+/************************* Utilities ******************************************/
+
+void show_usage()
+{
+ printf ("\nSample application for p-net Profinet device stack.\n");
+ printf ("\n");
+ printf ("Wait for connection from IO-controller.\n");
+ printf ("Then read buttons (input) and send to controller.\n");
+ printf ("Listen for application LED output (from controller) and set "
+ "application LED state.\n");
+ printf ("It will also send a counter value (useful also without buttons and "
+ "LED).\n");
+ printf ("Button1 value is sent in the periodic data.\n");
+ printf ("Button2 cycles through triggering an alarm, setting diagnosis and "
+ "creating logbook entries.\n");
+ printf ("\n");
+ printf ("Also the mandatory Profinet signal LED is controlled by this "
+ "application.\n");
+ printf ("\n");
+ printf ("The LEDs are controlled by the script set_profinet_leds\n");
+ printf ("located in the same directory as the application binary.\n");
+ printf ("A version for Raspberry Pi is available, and also a version "
+ "writing\n");
+ printf ("to plain text files (useful for demo if no LEDs are available).\n");
+ printf ("\n");
+ printf ("Assumes the default gateway is found on .1 on same subnet as the "
+ "IP address.\n");
+ printf ("\n");
+ printf ("Optional arguments:\n");
+ printf (" --help Show this help text and exit\n");
+ printf (" -h Show this help text and exit\n");
+ printf (" -v Incresase verbosity. Can be repeated.\n");
+ printf (" -f Reset to factory settings, and store to file. "
+ "Exit.\n");
+ printf (" Remember to give the -p flag if necessary.\n");
+ printf (" -r Remove stored files and exit.\n");
+ printf (" Remember to give the -p flag if necessary.\n");
+ printf (" -g Show stack details and exit. Repeat for more "
+ "details.\n");
+ printf (
+ " -i INTERF Name of Ethernet interface to use. Defaults to %s\n",
+ APP_DEFAULT_ETHERNET_INTERFACE);
+ printf (" Comma separated list if more than one interface "
+ "given.\n");
+ printf (
+ " -s NAME Set station name. Defaults to \"%s\". Only used\n",
+ APP_GSDML_DEFAULT_STATION_NAME);
+ printf (" if not already available in storage file.\n");
+ printf (" -b FILE Path (absolute or relative) to read Button1. "
+ "Defaults to not read Button1.\n");
+ printf (" -d FILE Path (absolute or relative) to read Button2. "
+ "Defaults to not read Button2.\n");
+ printf (" -p PATH Absolute path to storage directory. Defaults to "
+ "use current directory.\n");
+#if PNET_OPTION_DRIVER_ENABLE
+ printf (" -m MODE Application offload mode. Only used if P-Net is\n");
+ printf (" built with hw offload enabled "
+ " (PNET_OPTION_DRIVER_ENABLE). \n");
+ printf (" Supported modes: none, cpu, full\n");
+ printf (" Defaults to none\n");
+#endif
+ printf ("\n");
+ printf ("p-net revision: " PNET_VERSION "\n");
+}
+
+/**
+ * Parse command line arguments
+ *
+ * @param argc In: Number of arguments
+ * @param argv In: Arguments
+ * @return Parsed arguments
+ */
+app_args_t parse_commandline_arguments (int argc, char * argv[])
+{
+ app_args_t output_arguments = {0};
+ int option;
+
+ /* Special handling of long argument */
+ if (argc > 1)
+ {
+ if (strcmp (argv[1], "--help") == 0)
+ {
+ show_usage();
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ /* Default values */
+ strcpy (output_arguments.path_button1, "");
+ strcpy (output_arguments.path_button2, "");
+ strcpy (output_arguments.path_storage_directory, "");
+ strcpy (output_arguments.station_name, APP_GSDML_DEFAULT_STATION_NAME);
+ strcpy (output_arguments.eth_interfaces, APP_DEFAULT_ETHERNET_INTERFACE);
+ output_arguments.verbosity = 0;
+ output_arguments.show = 0;
+ output_arguments.factory_reset = false;
+ output_arguments.remove_files = false;
+ output_arguments.mode = MODE_HW_OFFLOAD_NONE;
+
+ while ((option = getopt (argc, argv, "hvgfri:s:b:d:p:m:")) != -1)
+ {
+ switch (option)
+ {
+ case 'v':
+ output_arguments.verbosity++;
+ break;
+ case 'g':
+ output_arguments.show++;
+ break;
+ case 'f':
+ output_arguments.factory_reset = true;
+ break;
+ case 'r':
+ output_arguments.remove_files = true;
+ break;
+ case 'i':
+ if ((strlen (optarg) + 1) > sizeof (output_arguments.eth_interfaces))
+ {
+ printf ("Error: The argument to -i is too long.\n");
+ exit (EXIT_FAILURE);
+ }
+ strcpy (output_arguments.eth_interfaces, optarg);
+ break;
+ case 's':
+ strcpy (output_arguments.station_name, optarg);
+ break;
+ case 'b':
+ if (strlen (optarg) + 1 > PNET_MAX_FILE_FULLPATH_SIZE)
+ {
+ printf ("Error: The argument to -b is too long.\n");
+ exit (EXIT_FAILURE);
+ }
+ strcpy (output_arguments.path_button1, optarg);
+ break;
+ case 'd':
+ if (strlen (optarg) + 1 > PNET_MAX_FILE_FULLPATH_SIZE)
+ {
+ printf ("Error: The argument to -d is too long.\n");
+ exit (EXIT_FAILURE);
+ }
+ strcpy (output_arguments.path_button2, optarg);
+ break;
+ case 'p':
+ if (strlen (optarg) + 1 > PNET_MAX_FILE_FULLPATH_SIZE)
+ {
+ printf ("Error: The argument to -p is too long.\n");
+ exit (EXIT_FAILURE);
+ }
+ strcpy (output_arguments.path_storage_directory, optarg);
+ break;
+#if PNET_OPTION_DRIVER_ENABLE
+ case 'm':
+ if (strcmp ("none", optarg) == 0)
+ {
+ output_arguments.mode = MODE_HW_OFFLOAD_NONE;
+ }
+ else if (strcmp ("cpu", optarg) == 0)
+ {
+ output_arguments.mode = MODE_HW_OFFLOAD_CPU;
+ }
+ else if (strcmp ("full", optarg) == 0)
+ {
+ output_arguments.mode = MODE_HW_OFFLOAD_FULL;
+ }
+ else
+ {
+ printf ("Error: mode (-m) not supported.\n");
+ exit (EXIT_FAILURE);
+ }
+ break;
+#endif
+ case 'h':
+ /* fallthrough */
+ case '?':
+ /* fallthrough */
+ default:
+ show_usage();
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ /* Use current directory for storage, if not given */
+ if (strlen (output_arguments.path_storage_directory) == 0)
+ {
+ if (
+ getcwd (
+ output_arguments.path_storage_directory,
+ sizeof (output_arguments.path_storage_directory)) == NULL)
+ {
+ printf ("Error: Could not read current working directory. Is "
+ "PNET_MAX_DIRECTORYPATH_SIZE too small?\n");
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ return output_arguments;
+}
+
+/**
+ * Read a bool from a file
+ *
+ * @param filepath In: Path to file
+ * @return true if file exists and the first character is '1'
+ */
+bool read_bool_from_file (const char * filepath)
+{
+ FILE * fp;
+ char ch;
+ int eof_indicator;
+
+ fp = fopen (filepath, "r");
+ if (fp == NULL)
+ {
+ return false;
+ }
+
+ ch = fgetc (fp);
+ eof_indicator = feof (fp);
+ fclose (fp);
+
+ if (eof_indicator)
+ {
+ return false;
+ }
+ return ch == '1';
+}
+
+bool app_get_button (uint16_t id)
+{
+ if (id == 0)
+ {
+ if (app_args.path_button1[0] != '\0')
+ {
+ return read_bool_from_file (app_args.path_button1);
+ }
+ }
+ else if (id == 1)
+ {
+ if (app_args.path_button2[0] != '\0')
+ {
+ return read_bool_from_file (app_args.path_button2);
+ }
+ }
+ return false;
+}
+
+void app_set_led (uint16_t id, bool led_state)
+{
+ /* Important:
+ * The Linux sample application uses a script to set the LED state,
+ * for easy adaption to different development boards.
+ *
+ * The script typically writes to files in the /sys directory to set LED
+ * state via GPIO operations. If you do not have any physical LEDs you can
+ * use a script that writes to regular files instead.
+ *
+ * However, file operations shall be avoided within the main task
+ * in a real application. File operations may affect the timing of the
+ * Profinet communication depending on file system implementation.
+ */
+
+ char id_str[7] = {0}; /** Terminated string */
+ const char * argv[4];
+
+ sprintf (id_str, "%u", id);
+ id_str[sizeof (id_str) - 1] = '\0';
+
+ argv[0] = "set_profinet_leds";
+ argv[1] = (char *)&id_str;
+ argv[2] = (led_state == 1) ? "1" : "0";
+ argv[3] = NULL;
+
+ if (pnal_execute_script (argv) != 0)
+ {
+ printf ("Failed to set LED state\n");
+ }
+}
+
+/** Update configuration with file storage path.
+ * Validate this path, and Linux button file paths
+ *
+ * @param p_cfg InOut: Configuration to be updated
+ * @param p_args In: Command line arguments
+ * @return 0 on success, -1 on error.
+ */
+static int app_pnet_cfg_init_storage (
+ pnet_cfg_t * p_cfg,
+ const app_args_t * p_args)
+{
+ strcpy (p_cfg->file_directory, p_args->path_storage_directory);
+
+ if (p_args->verbosity > 0)
+ {
+ printf ("Storage directory: %s\n\n", p_cfg->file_directory);
+ }
+
+ /* Validate paths */
+ if (!pnal_does_file_exist (p_cfg->file_directory))
+ {
+ printf (
+ "Error: The given storage directory does not exist: %s\n",
+ p_cfg->file_directory);
+ return -1;
+ }
+
+ if (p_args->path_button1[0] != '\0')
+ {
+ if (!pnal_does_file_exist (p_args->path_button1))
+ {
+ printf (
+ "Error: The given input file for Button1 does not exist: %s\n",
+ p_args->path_button1);
+ return -1;
+ }
+ }
+
+ if (p_args->path_button2[0] != '\0')
+ {
+ if (!pnal_does_file_exist (p_args->path_button2))
+ {
+ printf (
+ "Error: The given input file for Button2 does not exist: %s\n",
+ p_args->path_button2);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/****************************** Main ******************************************/
+
+int main (int argc, char * argv[])
+{
+ int ret;
+ int32_t app_log_level = APP_LOG_LEVEL_FATAL;
+ pnet_cfg_t pnet_cfg = {0};
+ app_data_t * sample_app = NULL;
+ app_utils_netif_namelist_t netif_name_list;
+ pnet_if_cfg_t netif_cfg = {0};
+ uint16_t number_of_ports = 1;
+
+ /* Enable line buffering for printouts, especially when logging to
+ the journal (which is default when running as a systemd job) */
+ setvbuf (stdout, NULL, _IOLBF, 0);
+
+ /* Parse and display command line arguments */
+ app_args = parse_commandline_arguments (argc, argv);
+
+ app_log_level = (app_args.verbosity <= APP_LOG_LEVEL_FATAL)
+ ? APP_LOG_LEVEL_FATAL - app_args.verbosity
+ : APP_LOG_LEVEL_DEBUG;
+ app_log_set_log_level (app_log_level);
+ printf ("\n** Starting P-Net sample application " PNET_VERSION " **\n");
+
+ APP_LOG_INFO (
+ "Number of slots: %u (incl slot for DAP module)\n",
+ PNET_MAX_SLOTS);
+ APP_LOG_INFO ("P-net log level: %u (DEBUG=0, FATAL=4)\n", LOG_LEVEL);
+ APP_LOG_INFO ("App log level: %u (DEBUG=0, FATAL=4)\n", app_log_level);
+ APP_LOG_INFO ("Max number of ports: %u\n", PNET_MAX_PHYSICAL_PORTS);
+ APP_LOG_INFO ("Network interfaces: %s\n", app_args.eth_interfaces);
+ APP_LOG_INFO ("Button1 file: %s\n", app_args.path_button1);
+ APP_LOG_INFO ("Button2 file: %s\n", app_args.path_button2);
+ APP_LOG_INFO ("Default station name: %s\n", app_args.station_name);
+
+ /* Prepare configuration */
+ app_pnet_cfg_init_default (&pnet_cfg);
+ strcpy (pnet_cfg.station_name, app_args.station_name);
+ ret = app_utils_pnet_cfg_init_netifs (
+ app_args.eth_interfaces,
+ &netif_name_list,
+ &number_of_ports,
+ &netif_cfg);
+ if (ret != 0)
+ {
+ exit (EXIT_FAILURE);
+ }
+ pnet_cfg.if_cfg = netif_cfg;
+ pnet_cfg.num_physical_ports = number_of_ports;
+
+ app_utils_print_network_config (&netif_cfg, number_of_ports);
+
+ /* Operating system specific settings */
+ pnet_cfg.pnal_cfg.snmp_thread.prio = APP_SNMP_THREAD_PRIORITY;
+ pnet_cfg.pnal_cfg.snmp_thread.stack_size = APP_SNMP_THREAD_STACKSIZE;
+ pnet_cfg.pnal_cfg.eth_recv_thread.prio = APP_ETH_THREAD_PRIORITY;
+ pnet_cfg.pnal_cfg.eth_recv_thread.stack_size = APP_ETH_THREAD_STACKSIZE;
+ pnet_cfg.pnal_cfg.bg_worker_thread.prio = APP_BG_WORKER_THREAD_PRIORITY;
+ pnet_cfg.pnal_cfg.bg_worker_thread.stack_size =
+ APP_BG_WORKER_THREAD_STACKSIZE;
+
+ ret = app_pnet_cfg_init_storage (&pnet_cfg, &app_args);
+ if (ret != 0)
+ {
+ printf ("Failed to initialize storage.\n");
+ printf ("Aborting application\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Remove files and exit */
+ if (app_args.remove_files == true)
+ {
+ printf ("\nRemoving stored files\n");
+ printf ("Exit application\n");
+ (void)pnet_remove_data_files (pnet_cfg.file_directory);
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Initialise stack and application */
+ sample_app = app_init (&pnet_cfg, &app_args);
+ if (sample_app == NULL)
+ {
+ printf ("Failed to initialize P-Net.\n");
+ printf ("Do you have enough Ethernet interface permission?\n");
+ printf ("Aborting application\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Do factory reset and exit */
+ if (app_args.factory_reset == true)
+ {
+ printf ("\nPerforming factory reset\n");
+ printf ("Exit application\n");
+ (void)pnet_factory_reset (app_get_pnet_instance (sample_app));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Show stack info and exit */
+ if (app_args.show > 0)
+ {
+ int level = 0xFFFF;
+
+ printf ("\nShowing stack information.\n\n");
+ if (app_args.show == 1)
+ {
+ level = 0x2010; /* See documentation for pnet_show() */
+ }
+
+ pnet_show (app_get_pnet_instance (sample_app), level);
+ printf ("Exit application\n");
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Start main loop */
+ if (app_start (sample_app, RUN_IN_SEPARATE_THREAD) != 0)
+ {
+ printf ("Failed to start\n");
+ printf ("Aborting application\n");
+ exit (EXIT_FAILURE);
+ }
+
+ for (;;)
+ {
+ os_usleep (APP_MAIN_SLEEPTIME_US);
+ }
+
+ return 0;
+}
diff --git a/src/profeth/src/sampleapp_common.c b/src/profeth/src/sampleapp_common.c
new file mode 100644
index 00000000..e4bad7b0
--- /dev/null
+++ b/src/profeth/src/sampleapp_common.c
@@ -0,0 +1,1759 @@
+/*********************************************************************
+ * _ _ _
+ * _ __ | |_ _ | | __ _ | |__ ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | | | |_ _ | || (_| || |_) |\__ \
+ * |_| \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2018 rt-labs AB, Sweden.
+ *
+ * This software is dual-licensed under GPLv3 and a commercial
+ * license. See the file LICENSE.md distributed with this software for
+ * full license information.
+ ********************************************************************/
+
+#include "sampleapp_common.h"
+
+#include "app_utils.h"
+#include "app_gsdml.h"
+#include "app_data.h"
+#include "app_log.h"
+#include "osal.h"
+#include "pnal.h"
+#include
+
+#include
+#include
+#include
+
+/* Events handled by main task */
+#define APP_EVENT_READY_FOR_DATA BIT (0)
+#define APP_EVENT_TIMER BIT (1)
+#define APP_EVENT_ALARM BIT (2)
+#define APP_EVENT_SM_RELEASED BIT (3)
+#define APP_EVENT_ABORT BIT (15)
+
+/* Defines used for alarm demo functionality */
+#define CHANNEL_ERRORTYPE_SHORT_CIRCUIT 0x0001
+#define CHANNEL_ERRORTYPE_LINE_BREAK 0x0006
+#define CHANNEL_ERRORTYPE_DATA_TRANSMISSION_IMPOSSIBLE 0x8000
+#define CHANNEL_ERRORTYPE_NETWORK_COMPONENT_FUNCTION_MISMATCH 0x8008
+#define EXTENDED_CHANNEL_ERRORTYPE_FRAME_DROPPED 0x8000
+#define EXTENDED_CHANNEL_ERRORTYPE_MAUTYPE_MISMATCH 0x8001
+#define EXTENDED_CHANNEL_ERRORTYPE_LINE_DELAY_MISMATCH 0x8002
+
+#define APP_ALARM_USI 0x0010
+#define APP_DIAG_CHANNEL_NUMBER 4
+#define APP_DIAG_CHANNEL_DIRECTION PNET_DIAG_CH_PROP_DIR_INPUT
+#define APP_DIAG_CHANNEL_NUMBER_OF_BITS PNET_DIAG_CH_PROP_TYPE_1_BIT
+#define APP_DIAG_CHANNEL_SEVERITY PNET_DIAG_CH_PROP_MAINT_FAULT
+#define APP_DIAG_CHANNEL_ERRORTYPE CHANNEL_ERRORTYPE_SHORT_CIRCUIT
+#define APP_DIAG_CHANNEL_ADDVALUE_A 0
+#define APP_DIAG_CHANNEL_ADDVALUE_B 1234
+#define APP_DIAG_CHANNEL_EXTENDED_ERRORTYPE 0
+#define APP_DIAG_CHANNEL_QUAL_SEVERITY 0 /* Not used (Max one bit set) */
+
+typedef enum app_demo_state
+{
+ APP_DEMO_STATE_ALARM_SEND = 0,
+ APP_DEMO_STATE_LOGBOOK_ENTRY,
+ APP_DEMO_STATE_ABORT_AR,
+ APP_DEMO_STATE_CYCLIC_REDUNDANT,
+ APP_DEMO_STATE_CYCLIC_NORMAL,
+ APP_DEMO_STATE_DIAG_STD_ADD,
+ APP_DEMO_STATE_DIAG_STD_UPDATE,
+ APP_DEMO_STATE_DIAG_STD_REMOVE,
+ APP_DEMO_STATE_DIAG_USI_ADD,
+ APP_DEMO_STATE_DIAG_USI_UPDATE,
+ APP_DEMO_STATE_DIAG_USI_REMOVE,
+} app_demo_state_t;
+
+typedef struct app_data_t
+{
+ pnet_t * net;
+
+ /* P-Net configuration passed in app_init(). */
+ const pnet_cfg_t * pnet_cfg;
+
+ /* Application API for administration of plugged
+ * (sub)modules and connection state. */
+ app_api_t main_api;
+
+ os_timer_t * main_timer;
+ os_event_t * main_events;
+
+ bool alarm_allowed;
+ pnet_alarm_argument_t alarm_arg;
+ app_demo_state_t alarm_demo_state;
+ uint8_t alarm_payload[APP_GSDML_ALARM_PAYLOAD_SIZE];
+
+ bool button1_pressed;
+ bool button2_pressed;
+ bool button2_pressed_previous;
+
+ /* Counters used to control when buttons are checked
+ * and process data is updated */
+ uint32_t buttons_tick_counter;
+ uint32_t process_data_tick_counter;
+
+} app_data_t;
+
+/* Forward declarations */
+static void app_plug_dap (app_data_t * app, uint16_t number_of_ports);
+static int app_set_initial_data_and_ioxs (app_data_t * app);
+static void app_cyclic_data_callback (app_subslot_t * subslot, void * tag);
+
+/** Static app data */
+static app_data_t app_state;
+
+pnet_t * app_get_pnet_instance (app_data_t * app)
+{
+ if (app == NULL)
+ {
+ return NULL;
+ }
+
+ return app->net;
+}
+
+/** Check if we are connected to the controller
+ *
+ * @param app InOut: Application handle
+ * @return true if we are connected to the IO-controller
+ */
+static bool app_is_connected_to_controller (app_data_t * app)
+{
+ return app->main_api.ar[0].arep != UINT32_MAX;
+}
+
+app_data_t * app_init (const pnet_cfg_t * pnet_cfg, const app_args_t * app_args)
+{
+ app_data_t * app;
+ uint16_t i;
+
+ APP_LOG_INFO ("Init P-Net stack and sample application\n");
+
+ app = &app_state;
+
+ app->alarm_allowed = true;
+ for (i = 0; i < PNET_MAX_AR; ++i)
+ {
+ app->main_api.ar[i].arep = UINT32_MAX;
+ app->main_api.ar[i].events = 0;
+ }
+ app->pnet_cfg = pnet_cfg;
+
+ app->net = pnet_init (app->pnet_cfg);
+
+ if (app->net == NULL)
+ {
+ return NULL;
+ }
+
+ return app;
+}
+
+/**
+ * Callback for timer tick.
+ *
+ * This is a callback for the timer defined in OSAL.
+ * See \a os_timer_create() for details.
+ *
+ * @param timer InOut: Timer instance
+ * @param arg InOut: User defined argument, app_data_t pointer
+ */
+static void main_timer_tick (os_timer_t * timer, void * arg)
+{
+ app_data_t * app = (app_data_t *)arg;
+
+ os_event_set (app->main_events, APP_EVENT_TIMER);
+}
+
+int app_start (app_data_t * app, app_run_in_separate_task_t task_config)
+{
+ APP_LOG_INFO ("Start sample application main loop\n");
+ if (app == NULL)
+ {
+ return -1;
+ }
+
+ app->main_events = os_event_create();
+ if (app->main_events == NULL)
+ {
+ return -1;
+ }
+
+ app->main_timer = os_timer_create (
+ APP_TICK_INTERVAL_US,
+ main_timer_tick,
+ (void *)app,
+ false);
+
+ if (app->main_timer == NULL)
+ {
+ os_event_destroy (app->main_events);
+ return -1;
+ }
+
+ if (task_config == RUN_IN_SEPARATE_THREAD)
+ {
+ os_thread_create (
+ "p-net_sample_app",
+ APP_MAIN_THREAD_PRIORITY,
+ APP_MAIN_THREAD_STACKSIZE,
+ app_loop_forever,
+ (void *)app);
+ }
+
+ os_timer_start (app->main_timer);
+
+ return 0;
+}
+
+/**
+ * Set outputs to default value
+ */
+static void app_set_outputs_default_value (void)
+{
+ APP_LOG_DEBUG ("Setting outputs to default values.\n");
+ app_data_set_default_outputs();
+}
+
+/**
+ * Set event flag(s) for one arep.
+ */
+static void app_event_set (
+ app_data_t * app,
+ uint32_t arep,
+ uint32_t event,
+ bool add_arep)
+{
+ app_ar_iterator_t iter;
+ app_ar_t * ar;
+
+ app_ar_iterator_init (&iter, &app->main_api);
+ while (app_ar_iterator_next (&iter, &ar))
+ {
+ if (app_ar_arep (ar) == arep)
+ {
+ app_ar_event_set (ar, event);
+ break;
+ }
+ }
+ if (add_arep)
+ {
+ if (app_ar_iterator_done (&iter))
+ {
+ if (app_ar_add_arep (&app->main_api, arep, &ar))
+ {
+ app_ar_event_set (ar, event);
+ }
+ }
+ }
+ os_event_set (app->main_events, event);
+}
+
+/*********************************** Callbacks ********************************/
+
+static int app_connect_ind (
+ pnet_t * net,
+ void * arg,
+ uint32_t arep,
+ pnet_result_t * p_result)
+{
+ APP_LOG_DEBUG ("PLC connect indication. AREP: %u\n", arep);
+ /*
+ * Handle the request on an application level.
+ * This is a very simple application which does not need to handle anything.
+ * All the needed information is in the AR data structure.
+ */
+
+ return 0;
+}
+
+static int app_release_ind (
+ pnet_t * net,
+ void * arg,
+ uint32_t arep,
+ pnet_result_t * p_result)
+{
+ APP_LOG_DEBUG ("PLC release (disconnect) indication. AREP: %u\n", arep);
+
+ app_set_outputs_default_value();
+
+ return 0;
+}
+
+static int app_dcontrol_ind (
+ pnet_t * net,
+ void * arg,
+ uint32_t arep,
+ pnet_control_command_t control_command,
+ pnet_result_t * p_result)
+{
+ APP_LOG_DEBUG (
+ "PLC dcontrol message (The PLC is done with parameter writing). "
+ "AREP: %u Command: %s\n",
+ arep,
+ app_utils_dcontrol_cmd_to_string (control_command));
+
+ return 0;
+}
+
+static int app_sm_released_ind (
+ pnet_t * net,
+ void * arg,
+ uint32_t arep,
+ uint32_t api,
+ uint16_t slot_number,
+ uint16_t subslot_number,
+ pnet_result_t * p_result)
+{
+ app_data_t * app = (app_data_t *)arg;
+
+ APP_LOG_DEBUG (
+ "SM released indication.\n"
+ " AREP: %u API: %u Slot: 0x%x Subslot: 0x%x\n",
+ arep,
+ api,
+ slot_number,
+ subslot_number);
+
+ app_event_set (app, arep, APP_EVENT_SM_RELEASED, false);
+
+ return 0;
+}
+
+static int app_ccontrol_cnf (
+ pnet_t * net,
+ void * arg,
+ uint32_t arep,
+ pnet_result_t * p_result)
+{
+ APP_LOG_DEBUG (
+ "PLC ccontrol message confirmation (The PLC has received our Application "
+ "Ready message). AREP: %u Status codes: %d %d %d %d\n",
+ arep,
+ p_result->pnio_status.error_code,
+ p_result->pnio_status.error_decode,
+ p_result->pnio_status.error_code_1,
+ p_result->pnio_status.error_code_2);
+
+ return 0;
+}
+
+static int app_write_ind (
+ pnet_t * net,
+ void * arg,
+ uint32_t arep,
+ uint32_t api,
+ uint16_t slot_nbr,
+ uint16_t subslot_nbr,
+ uint16_t idx,
+ uint16_t sequence_number,
+ uint16_t write_length,
+ const uint8_t * p_write_data,
+ pnet_result_t * p_result)
+{
+ int result = 0;
+ app_data_t * app = (app_data_t *)arg;
+ app_subslot_t * subslot;
+ APP_LOG_DEBUG (
+ "PLC write record indication.\n"
+ " AREP: %u API: %u Slot: %2u Subslot: %u Index: %u Sequence: %2u "
+ "Length: %u\n",
+ arep,
+ api,
+ slot_nbr,
+ subslot_nbr,
+ (unsigned)idx,
+ sequence_number,
+ write_length);
+
+ subslot = app_utils_subslot_get (&app->main_api, slot_nbr, subslot_nbr);
+ if (subslot == NULL)
+ {
+ APP_LOG_WARNING (
+ "No submodule plugged in AREP: %u API: %u Slot: %2u Subslot: %u "
+ "Index will not be written.\n",
+ arep,
+ api,
+ slot_nbr,
+ subslot_nbr);
+ p_result->pnio_status.error_code = PNET_ERROR_CODE_WRITE;
+ p_result->pnio_status.error_decode = PNET_ERROR_DECODE_PNIORW;
+ p_result->pnio_status.error_code_1 = PNET_ERROR_CODE_1_APP_WRITE_ERROR;
+ p_result->pnio_status.error_code_2 = 0; /* User specific */
+
+ return -1;
+ }
+
+ result = app_data_write_parameter (
+ slot_nbr,
+ subslot_nbr,
+ subslot->submodule_id,
+ idx,
+ p_write_data,
+ write_length);
+ if (result != 0)
+ {
+ APP_LOG_WARNING (
+ "Failed to write index for AREP: %u API: %u Slot: %2u Subslot: %u "
+ "index %u.\n",
+ arep,
+ api,
+ slot_nbr,
+ subslot_nbr,
+ idx);
+ p_result->pnio_status.error_code = PNET_ERROR_CODE_WRITE;
+ p_result->pnio_status.error_decode = PNET_ERROR_DECODE_PNIORW;
+ p_result->pnio_status.error_code_1 = PNET_ERROR_CODE_1_APP_WRITE_ERROR;
+ p_result->pnio_status.error_code_2 = 0; /* User specific */
+ }
+
+ return result;
+}
+
+static int app_read_ind (
+ pnet_t * net,
+ void * arg,
+ uint32_t arep,
+ uint32_t api,
+ uint16_t slot_nbr,
+ uint16_t subslot_nbr,
+ uint16_t idx,
+ uint16_t sequence_number,
+ uint8_t ** pp_read_data,
+ uint16_t * p_read_length,
+ pnet_result_t * p_result)
+{
+ int result = 0;
+ app_data_t * app = (app_data_t *)arg;
+ app_subslot_t * subslot;
+
+ APP_LOG_DEBUG (
+ "PLC read record indication.\n"
+ " AREP: %u API: %u Slot: %2u Subslot: %u Index: %u Sequence: %2u Max "
+ "length: %u\n",
+ arep,
+ api,
+ slot_nbr,
+ subslot_nbr,
+ (unsigned)idx,
+ sequence_number,
+ (unsigned)*p_read_length);
+
+ subslot = app_utils_subslot_get (&app->main_api, slot_nbr, subslot_nbr);
+ if (subslot == NULL)
+ {
+ APP_LOG_WARNING (
+ "No submodule plugged in AREP: %u API: %u Slot: %2u Subslot: %u "
+ "Index will not be read.\n",
+ arep,
+ api,
+ slot_nbr,
+ subslot_nbr);
+ p_result->pnio_status.error_code = PNET_ERROR_CODE_READ;
+ p_result->pnio_status.error_decode = PNET_ERROR_DECODE_PNIORW;
+ p_result->pnio_status.error_code_1 = PNET_ERROR_CODE_1_APP_READ_ERROR;
+ p_result->pnio_status.error_code_2 = 0; /* User specific */
+ return -1;
+ }
+
+ result = app_data_read_parameter (
+ slot_nbr,
+ subslot_nbr,
+ subslot->submodule_id,
+ idx,
+ pp_read_data,
+ p_read_length);
+
+ if (result != 0)
+ {
+ APP_LOG_WARNING (
+ "Failed to read index for AREP: %u API: %u Slot: %2u Subslot: %u "
+ "index %u.\n",
+ arep,
+ api,
+ slot_nbr,
+ subslot_nbr,
+ idx);
+ p_result->pnio_status.error_code = PNET_ERROR_CODE_READ;
+ p_result->pnio_status.error_decode = PNET_ERROR_DECODE_PNIORW;
+ p_result->pnio_status.error_code_1 = PNET_ERROR_CODE_1_APP_READ_ERROR;
+ p_result->pnio_status.error_code_2 = 0; /* User specific */
+ }
+
+ return result;
+}
+
+static int app_state_ind (
+ pnet_t * net,
+ void * arg,
+ uint32_t arep,
+ pnet_event_values_t event)
+{
+ uint16_t err_cls = 0; /* Error code 1 */
+ uint16_t err_code = 0; /* Error code 2 */
+ const char * error_class_description = "";
+ const char * error_code_description = "";
+
+ app_data_t * app = (app_data_t *)arg;
+
+ APP_LOG_DEBUG (
+ "Event indication %s AREP: %u\n",
+ app_utils_event_to_string (event),
+ arep);
+
+ if (event == PNET_EVENT_ABORT)
+ {
+ if (pnet_get_ar_error_codes (net, arep, &err_cls, &err_code) == 0)
+ {
+ app_utils_get_error_code_strings (
+ err_cls,
+ err_code,
+ &error_class_description,
+ &error_code_description);
+ APP_LOG_DEBUG (
+ " Error class: 0x%02x %s \n"
+ " Error code: 0x%02x %s \n",
+ (unsigned)err_cls,
+ error_class_description,
+ (unsigned)err_code,
+ error_code_description);
+ }
+ else
+ {
+ APP_LOG_DEBUG (" No error status available\n");
+ }
+ /* Set output values */
+ app_set_outputs_default_value();
+
+ app_event_set (app, arep, APP_EVENT_ABORT, false);
+ }
+ else if (event == PNET_EVENT_PRMEND)
+ {
+ app_set_initial_data_and_ioxs (app);
+
+ (void)pnet_set_provider_state (net, true);
+
+ /* Send application ready at next tick
+ Do not call pnet_application_ready() here as it will affect
+ the internal stack states */
+ app_event_set (app, arep, APP_EVENT_READY_FOR_DATA, true);
+ }
+ else if (event == PNET_EVENT_DATA)
+ {
+ APP_LOG_DEBUG ("Cyclic data transmission started\n\n");
+ }
+
+ return 0;
+}
+
+static int app_reset_ind (
+ pnet_t * net,
+ void * arg,
+ bool should_reset_application,
+ uint16_t reset_mode)
+{
+ APP_LOG_DEBUG (
+ "PLC reset indication. Application reset mandatory: %u Reset mode: %d\n",
+ should_reset_application,
+ reset_mode);
+
+ return 0;
+}
+
+static int app_signal_led_ind (pnet_t * net, void * arg, bool led_state)
+{
+ APP_LOG_INFO ("Profinet signal LED indication. New state: %u\n", led_state);
+
+ app_set_led (APP_PROFINET_SIGNAL_LED_ID, led_state);
+ return 0;
+}
+
+static int app_exp_module_ind (
+ pnet_t * net,
+ void * arg,
+ uint32_t api,
+ uint16_t slot,
+ uint32_t module_ident)
+{
+ int ret = -1;
+ int result = 0;
+ app_data_t * app = (app_data_t *)arg;
+ const char * module_name = "unknown";
+ const app_gsdml_module_t * module_config;
+
+ APP_LOG_DEBUG ("Module plug indication\n");
+
+ if (slot >= PNET_MAX_SLOTS)
+ {
+ APP_LOG_ERROR (
+ "Wrong slot number received: %u It should be less than %u\n",
+ slot,
+ PNET_MAX_SLOTS);
+ return -1;
+ }
+
+ module_config = app_gsdml_get_module_cfg (module_ident);
+ if (module_config == NULL)
+ {
+ APP_LOG_ERROR (" Module ID %08x not found.\n", (unsigned)module_ident);
+ /*
+ * Needed to pass Behavior scenario 2
+ */
+ APP_LOG_DEBUG (" Plug expected module anyway\n");
+ }
+ else
+ {
+ module_name = module_config->name;
+ }
+
+ APP_LOG_DEBUG (" Pull old module. API: %u Slot: %2u\n", api, slot);
+ result = pnet_pull_module (net, api, slot);
+
+ if (result == 0)
+ {
+ (void)app_utils_pull_module (&app->main_api, slot);
+ }
+
+ APP_LOG_DEBUG (
+ " Plug module. API: %u Slot: %2u Module ID: 0x%x \"%s\"\n",
+ api,
+ slot,
+ (unsigned)module_ident,
+ module_name);
+
+ ret = pnet_plug_module (net, api, slot, module_ident);
+ if (ret == 0)
+ {
+ (void)app_utils_plug_module (
+ &app->main_api,
+ slot,
+ module_ident,
+ module_name);
+ }
+ else
+ {
+ APP_LOG_ERROR (
+ "Plug module failed. Ret: %u API: %u Slot: %2u Module ID: 0x%x\n",
+ ret,
+ api,
+ slot,
+ (unsigned)module_ident);
+ }
+
+ return ret;
+}
+
+static int app_exp_submodule_ind (
+ pnet_t * net,
+ void * arg,
+ uint32_t api,
+ uint16_t slot,
+ uint16_t subslot,
+ uint32_t module_id,
+ uint32_t submodule_id,
+ const pnet_data_cfg_t * p_exp_data)
+{
+ int ret = -1;
+ int result = 0;
+ pnet_data_cfg_t data_cfg = {0};
+ app_data_t * app = (app_data_t *)arg;
+ const app_gsdml_submodule_t * submod_cfg;
+ const char * name = "Unsupported";
+ app_utils_cyclic_callback cyclic_data_callback = NULL;
+
+ APP_LOG_DEBUG ("Submodule plug indication.\n");
+
+ submod_cfg = app_gsdml_get_submodule_cfg (submodule_id);
+ if (submod_cfg != NULL)
+ {
+ data_cfg.data_dir = submod_cfg->data_dir;
+ data_cfg.insize = submod_cfg->insize;
+ data_cfg.outsize = submod_cfg->outsize;
+ name = submod_cfg->name;
+
+ if (data_cfg.insize > 0 || data_cfg.outsize > 0)
+ {
+ cyclic_data_callback = app_cyclic_data_callback;
+ }
+ }
+ else
+ {
+ APP_LOG_WARNING (
+ " Submodule ID 0x%x in module ID 0x%x not found. API: %u Slot: %2u "
+ "Subslot %u \n",
+ (unsigned)submodule_id,
+ (unsigned)module_id,
+ api,
+ slot,
+ subslot);
+
+ /*
+ * Needed for behavior scenario 2 to pass.
+ * Iops will be set to bad for this subslot
+ */
+ APP_LOG_WARNING (" Plug expected submodule anyway \n");
+
+ data_cfg.data_dir = p_exp_data->data_dir;
+ data_cfg.insize = p_exp_data->insize;
+ data_cfg.outsize = p_exp_data->outsize;
+ }
+
+ APP_LOG_DEBUG (
+ " Pull old submodule. API: %u Slot: %2u Subslot: %u\n",
+ api,
+ slot,
+ subslot);
+
+ result = pnet_pull_submodule (net, api, slot, subslot);
+ if (result == 0)
+ {
+ (void)app_utils_pull_submodule (&app->main_api, slot, subslot);
+ }
+
+ APP_LOG_DEBUG (
+ " Plug submodule. API: %u Slot: %2u Module ID: 0x%-4x\n"
+ " Subslot: %u Submodule ID: 0x%x \"%s\"\n",
+ api,
+ slot,
+ (unsigned)module_id,
+ subslot,
+ (unsigned)submodule_id,
+ name);
+
+ APP_LOG_DEBUG (
+ " Data Dir: %s In: %u bytes Out: %u bytes\n",
+ app_utils_submod_dir_to_string (data_cfg.data_dir),
+ data_cfg.insize,
+ data_cfg.outsize);
+
+ if (
+ data_cfg.data_dir != p_exp_data->data_dir ||
+ data_cfg.insize != p_exp_data->insize ||
+ data_cfg.outsize != p_exp_data->outsize)
+ {
+ APP_LOG_WARNING (
+ " Warning expected Data Dir: %s In: %u bytes Out: %u bytes\n",
+ app_utils_submod_dir_to_string (p_exp_data->data_dir),
+ p_exp_data->insize,
+ p_exp_data->outsize);
+ }
+ ret = pnet_plug_submodule (
+ net,
+ api,
+ slot,
+ subslot,
+ module_id,
+ submodule_id,
+ data_cfg.data_dir,
+ data_cfg.insize,
+ data_cfg.outsize);
+
+ if (ret == 0)
+ {
+ (void)app_utils_plug_submodule (
+ &app->main_api,
+ slot,
+ subslot,
+ submodule_id,
+ &data_cfg,
+ name,
+ cyclic_data_callback,
+ app);
+ }
+ else
+ {
+ APP_LOG_ERROR (
+ " Plug submodule failed. Ret: %u API: %u Slot: %2u Subslot %u "
+ "Module ID: 0x%x Submodule ID: 0x%x \n",
+ ret,
+ api,
+ slot,
+ subslot,
+ (unsigned)module_id,
+ (unsigned)submodule_id);
+ }
+
+ return ret;
+}
+
+static int app_new_data_status_ind (
+ pnet_t * net,
+ void * arg,
+ uint32_t arep,
+ uint32_t crep,
+ uint8_t changes,
+ uint8_t data_status)
+{
+ bool is_running = data_status & BIT (PNET_DATA_STATUS_BIT_PROVIDER_STATE);
+ bool is_valid = data_status & BIT (PNET_DATA_STATUS_BIT_DATA_VALID);
+
+ APP_LOG_DEBUG (
+ "Data status indication. AREP: %u Data status changes: 0x%02x "
+ "Data status: 0x%02x\n",
+ arep,
+ changes,
+ data_status);
+ APP_LOG_DEBUG (
+ " %s, %s, %s, %s, %s\n",
+ is_running ? "Run" : "Stop",
+ is_valid ? "Valid" : "Invalid",
+ (data_status & BIT (PNET_DATA_STATUS_BIT_STATE)) ? "Primary" : "Backup",
+ (data_status & BIT (PNET_DATA_STATUS_BIT_STATION_PROBLEM_INDICATOR))
+ ? "Normal operation"
+ : "Problem",
+ (data_status & BIT (PNET_DATA_STATUS_BIT_IGNORE))
+ ? "Ignore data status"
+ : "Evaluate data status");
+
+ if (is_running == false || is_valid == false)
+ {
+ app_set_outputs_default_value();
+ }
+
+ return 0;
+}
+
+static int app_alarm_ind (
+ pnet_t * net,
+ void * arg,
+ uint32_t arep,
+ const pnet_alarm_argument_t * p_alarm_arg,
+ uint16_t data_len,
+ uint16_t data_usi,
+ const uint8_t * p_data)
+{
+ app_data_t * app = (app_data_t *)arg;
+
+ APP_LOG_DEBUG (
+ "Alarm indication. AREP: %u API: %d Slot: %d Subslot: %d "
+ "Type: %d Seq: %d Length: %d USI: %d\n",
+ arep,
+ p_alarm_arg->api_id,
+ p_alarm_arg->slot_nbr,
+ p_alarm_arg->subslot_nbr,
+ p_alarm_arg->alarm_type,
+ p_alarm_arg->sequence_number,
+ data_len,
+ data_usi);
+
+ app->alarm_arg = *p_alarm_arg;
+
+ app_event_set (app, arep, APP_EVENT_ALARM, false);
+
+ return 0;
+}
+
+static int app_alarm_cnf (
+ pnet_t * net,
+ void * arg,
+ uint32_t arep,
+ const pnet_pnio_status_t * p_pnio_status)
+{
+ app_data_t * app = (app_data_t *)arg;
+
+ APP_LOG_DEBUG (
+ "PLC alarm confirmation. AREP: %u Status code %u, "
+ "%u, %u, %u\n",
+ arep,
+ p_pnio_status->error_code,
+ p_pnio_status->error_decode,
+ p_pnio_status->error_code_1,
+ p_pnio_status->error_code_2);
+
+ app->alarm_allowed = true;
+
+ return 0;
+}
+
+static int app_alarm_ack_cnf (pnet_t * net, void * arg, uint32_t arep, int res)
+{
+ APP_LOG_DEBUG (
+ "PLC alarm ACK confirmation. AREP: %u Result: "
+ "%d\n",
+ arep,
+ res);
+
+ return 0;
+}
+
+/******************************************************************************/
+
+/**
+ * Plug all DAP (sub)modules
+ * Use existing callback functions to plug the (sub-)modules
+ * @param app InOut: Application handle
+ * @param number_of_ports In: Number of active ports
+ */
+static void app_plug_dap (app_data_t * app, uint16_t number_of_ports)
+{
+ const pnet_data_cfg_t cfg_dap_data = {
+ .data_dir = PNET_DIR_NO_IO,
+ .insize = 0,
+ .outsize = 0,
+ };
+
+ APP_LOG_DEBUG ("\nPlug DAP module and its submodules\n");
+
+ app_exp_module_ind (
+ app->net,
+ app,
+ APP_GSDML_API,
+ PNET_SLOT_DAP_IDENT,
+ PNET_MOD_DAP_IDENT);
+
+ app_exp_submodule_ind (
+ app->net,
+ app,
+ APP_GSDML_API,
+ PNET_SLOT_DAP_IDENT,
+ PNET_SUBSLOT_DAP_IDENT,
+ PNET_MOD_DAP_IDENT,
+ PNET_SUBMOD_DAP_IDENT,
+ &cfg_dap_data);
+
+ app_exp_submodule_ind (
+ app->net,
+ app,
+ APP_GSDML_API,
+ PNET_SLOT_DAP_IDENT,
+ PNET_SUBSLOT_DAP_INTERFACE_1_IDENT,
+ PNET_MOD_DAP_IDENT,
+ PNET_SUBMOD_DAP_INTERFACE_1_IDENT,
+ &cfg_dap_data);
+
+ app_exp_submodule_ind (
+ app->net,
+ app,
+ APP_GSDML_API,
+ PNET_SLOT_DAP_IDENT,
+ PNET_SUBSLOT_DAP_INTERFACE_1_PORT_1_IDENT,
+ PNET_MOD_DAP_IDENT,
+ PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT,
+ &cfg_dap_data);
+
+ if (number_of_ports >= 2)
+ {
+ app_exp_submodule_ind (
+ app->net,
+ app,
+ APP_GSDML_API,
+ PNET_SLOT_DAP_IDENT,
+ PNET_SUBSLOT_DAP_INTERFACE_1_PORT_2_IDENT,
+ PNET_MOD_DAP_IDENT,
+ PNET_SUBMOD_DAP_INTERFACE_1_PORT_2_IDENT,
+ &cfg_dap_data);
+ }
+
+ if (number_of_ports >= 3)
+ {
+ app_exp_submodule_ind (
+ app->net,
+ app,
+ APP_GSDML_API,
+ PNET_SLOT_DAP_IDENT,
+ PNET_SUBSLOT_DAP_INTERFACE_1_PORT_3_IDENT,
+ PNET_MOD_DAP_IDENT,
+ PNET_SUBMOD_DAP_INTERFACE_1_PORT_3_IDENT,
+ &cfg_dap_data);
+ }
+
+ if (number_of_ports >= 4)
+ {
+ app_exp_submodule_ind (
+ app->net,
+ app,
+ APP_GSDML_API,
+ PNET_SLOT_DAP_IDENT,
+ PNET_SUBSLOT_DAP_INTERFACE_1_PORT_4_IDENT,
+ PNET_MOD_DAP_IDENT,
+ PNET_SUBMOD_DAP_INTERFACE_1_PORT_4_IDENT,
+ &cfg_dap_data);
+ }
+
+ APP_LOG_DEBUG ("Done plugging DAP\n\n");
+}
+
+/**
+ * Handle cyclic input- and output data for a subslot.
+ *
+ * Data is read and written using functions in the .c file,
+ * which handles the data and update the physical input and outputs.
+ *
+ * @param subslot InOut: Subslot reference
+ * @param tag In: Application handle, here \a app_data_t pointer
+ */
+static void app_cyclic_data_callback (app_subslot_t * subslot, void * tag)
+{
+ app_data_t * app = (app_data_t *)tag;
+ uint8_t indata_iops = PNET_IOXS_BAD;
+ uint8_t indata_iocs = PNET_IOXS_BAD;
+ uint8_t * indata;
+ uint16_t indata_size = 0;
+ bool outdata_updated;
+ uint16_t outdata_length;
+ uint8_t outdata_iops;
+ uint8_t outdata_buf[20]; /* Todo: Remove temporary buffer */
+
+ if (app == NULL)
+ {
+ APP_LOG_ERROR ("Application tag not set in subslot?\n");
+ return;
+ }
+
+ if (subslot->slot_nbr != PNET_SLOT_DAP_IDENT && subslot->data_cfg.outsize > 0)
+ {
+ outdata_length = subslot->data_cfg.outsize;
+ CC_ASSERT (outdata_length < sizeof (outdata_buf));
+
+ /* Get output data from the PLC */
+ (void)pnet_output_get_data_and_iops (
+ app->net,
+ APP_GSDML_API,
+ subslot->slot_nbr,
+ subslot->subslot_nbr,
+ &outdata_updated,
+ outdata_buf,
+ &outdata_length,
+ &outdata_iops);
+
+ app_utils_print_ioxs_change (
+ subslot,
+ "Provider Status (IOPS)",
+ subslot->outdata_iops,
+ outdata_iops);
+ subslot->outdata_iops = outdata_iops;
+
+ if (outdata_length != subslot->data_cfg.outsize)
+ {
+ APP_LOG_ERROR ("Wrong outputdata length: %u\n", outdata_length);
+ app_set_outputs_default_value();
+ }
+ else if (outdata_iops == PNET_IOXS_GOOD)
+ {
+ /* Application specific handling of the output data to a submodule.
+ For the sample application, the data sets a LED. */
+ (void)app_data_set_output_data (
+ subslot->slot_nbr,
+ subslot->subslot_nbr,
+ subslot->submodule_id,
+ outdata_buf,
+ outdata_length);
+ }
+ else
+ {
+ app_set_outputs_default_value();
+ }
+ }
+
+ if (subslot->slot_nbr != PNET_SLOT_DAP_IDENT && subslot->data_cfg.insize > 0)
+ {
+ /* Get application specific input data from a submodule (not DAP)
+ *
+ * For the sample application, the data includes a button
+ * state and a counter value. */
+ indata = app_data_get_input_data (
+ subslot->slot_nbr,
+ subslot->subslot_nbr,
+ subslot->submodule_id,
+ app->button1_pressed,
+ &indata_size,
+ &indata_iops);
+
+ /* Send input data to the PLC */
+ (void)pnet_input_set_data_and_iops (
+ app->net,
+ APP_GSDML_API,
+ subslot->slot_nbr,
+ subslot->subslot_nbr,
+ indata,
+ indata_size,
+ indata_iops);
+
+ (void)pnet_input_get_iocs (
+ app->net,
+ APP_GSDML_API,
+ subslot->slot_nbr,
+ subslot->subslot_nbr,
+ &indata_iocs);
+
+ app_utils_print_ioxs_change (
+ subslot,
+ "Consumer Status (IOCS)",
+ subslot->indata_iocs,
+ indata_iocs);
+ subslot->indata_iocs = indata_iocs;
+ }
+}
+
+/**
+ * Set initial input data, provider and consumer status for a subslot.
+ *
+ * @param app In: Application handle
+ */
+static int app_set_initial_data_and_ioxs (app_data_t * app)
+{
+ int ret;
+ uint16_t slot;
+ uint16_t subslot_index;
+ const app_subslot_t * p_subslot;
+ uint8_t * indata;
+ uint16_t indata_size;
+ uint8_t indata_iops;
+
+ for (slot = 0; slot < PNET_MAX_SLOTS; slot++)
+ {
+ for (subslot_index = 0; subslot_index < PNET_MAX_SUBSLOTS;
+ subslot_index++)
+ {
+ p_subslot = &app->main_api.slots[slot].subslots[subslot_index];
+ if (p_subslot->plugged)
+ {
+ indata = NULL;
+ indata_size = 0;
+ indata_iops = PNET_IOXS_BAD;
+
+ if (
+ p_subslot->data_cfg.insize > 0 ||
+ p_subslot->data_cfg.data_dir == PNET_DIR_NO_IO)
+ {
+
+ /* Get input data for submodule
+ *
+ * For the sample application data includes
+ * includes button state and a counter value
+ */
+ if (
+ p_subslot->slot_nbr != PNET_SLOT_DAP_IDENT &&
+ p_subslot->data_cfg.insize > 0)
+ {
+ indata = app_data_get_input_data (
+ p_subslot->slot_nbr,
+ p_subslot->subslot_nbr,
+ p_subslot->submodule_id,
+ app->button1_pressed,
+ &indata_size,
+ &indata_iops);
+ }
+ else if (p_subslot->slot_nbr == PNET_SLOT_DAP_IDENT)
+ {
+ indata_iops = PNET_IOXS_GOOD;
+ }
+
+ ret = pnet_input_set_data_and_iops (
+ app->net,
+ app->main_api.api_id,
+ p_subslot->slot_nbr,
+ p_subslot->subslot_nbr,
+ indata,
+ indata_size,
+ indata_iops);
+
+ /*
+ * If a submodule is still plugged but not used in current AR,
+ * setting the data and IOPS will fail.
+ * This is not a problem.
+ * Log message below will only be printed for active submodules.
+ */
+ if (ret == 0)
+ {
+ APP_LOG_DEBUG (
+ " Set initial input data and IOPS for slot %2u subslot "
+ "%5u %9s size %3d \"%s\" \n",
+ p_subslot->slot_nbr,
+ p_subslot->subslot_nbr,
+ app_utils_ioxs_to_string (indata_iops),
+ p_subslot->data_cfg.insize,
+ p_subslot->submodule_name);
+ }
+ }
+
+ if (p_subslot->data_cfg.outsize > 0)
+ {
+ ret = pnet_output_set_iocs (
+ app->net,
+ app->main_api.api_id,
+ p_subslot->slot_nbr,
+ p_subslot->subslot_nbr,
+ PNET_IOXS_GOOD);
+
+ if (ret == 0)
+ {
+ APP_LOG_DEBUG (
+ " Set initial output IOCS for slot %2u subslot "
+ "%5u %9s \"%s\"\n",
+ p_subslot->slot_nbr,
+ p_subslot->subslot_nbr,
+ app_utils_ioxs_to_string (PNET_IOXS_GOOD),
+ p_subslot->submodule_name);
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * Send and receive cyclic/process data for all subslots.
+ *
+ * Updates the data only on every APP_TICKS_UPDATE_DATA invocation
+ *
+ * @param app In: Application handle
+ */
+static void app_handle_cyclic_data (app_data_t * app)
+{
+ /* For the sample application cyclic data is updated
+ * with a period defined by APP_TICKS_UPDATE_DATA
+ */
+ app->process_data_tick_counter++;
+ if (app->process_data_tick_counter < APP_TICKS_UPDATE_DATA)
+ {
+ return;
+ }
+ app->process_data_tick_counter = 0;
+
+ app_utils_cyclic_data_poll (&app->main_api);
+}
+
+/**
+ * Set alarm, diagnostics and logbook entries.
+ *
+ * Alternates between these functions each time the button2 is pressed:
+ * - pnet_alarm_send_process_alarm()
+ * - pnet_diag_std_add()
+ * - pnet_set_redundancy_state()
+ * - pnet_set_state()
+ * - pnet_diag_std_update()
+ * - pnet_diag_usi_add()
+ * - pnet_diag_usi_update()
+ * - pnet_diag_usi_remove()
+ * - pnet_diag_std_remove()
+ * - pnet_create_log_book_entry()
+ * - pnet_ar_abort()
+ *
+ * Uses first 8-bit digital input module, if available.
+ *
+ * @param app InOut: Application handle
+ */
+static void app_handle_demo_pnet_api (app_data_t * app)
+{
+ uint16_t slot = 0;
+ bool found_inputsubslot = false;
+ uint16_t subslot_ix = 0;
+ const app_subslot_t * p_subslot = NULL;
+ pnet_pnio_status_t pnio_status = {0};
+ pnet_diag_source_t diag_source = {
+ .api = APP_GSDML_API,
+ .slot = 0,
+ .subslot = 0,
+ .ch = APP_DIAG_CHANNEL_NUMBER,
+ .ch_grouping = PNET_DIAG_CH_INDIVIDUAL_CHANNEL,
+ .ch_direction = APP_DIAG_CHANNEL_DIRECTION};
+
+ /* Loop though all subslots to find first digital 8-bit input subslot */
+ while (!found_inputsubslot && (slot < PNET_MAX_SLOTS))
+ {
+ for (subslot_ix = 0;
+ !found_inputsubslot && (subslot_ix < PNET_MAX_SUBSLOTS);
+ subslot_ix++)
+ {
+ p_subslot = &app->main_api.slots[slot].subslots[subslot_ix];
+ if (
+ app_utils_subslot_is_input (p_subslot) &&
+ (p_subslot->submodule_id == APP_GSDML_SUBMOD_ID_DIGITAL_IN ||
+ p_subslot->submodule_id == APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT))
+ {
+ found_inputsubslot = true;
+ break;
+ }
+ }
+ if (!found_inputsubslot)
+ {
+ slot++;
+ }
+ }
+ if (!found_inputsubslot)
+ {
+ APP_LOG_DEBUG ("Did not find any input module in the slots. Skipping.\n");
+ return;
+ }
+
+ diag_source.slot = slot;
+ diag_source.subslot = p_subslot->subslot_nbr;
+
+ switch (app->alarm_demo_state)
+ {
+ case APP_DEMO_STATE_ALARM_SEND:
+ if (app->alarm_allowed == true && app_is_connected_to_controller (app))
+ {
+ app->alarm_payload[0]++;
+ APP_LOG_INFO (
+ "Sending process alarm from slot %u subslot %u USI %u to "
+ "PLC. Payload: 0x%x\n",
+ slot,
+ p_subslot->subslot_nbr,
+ APP_ALARM_USI,
+ app->alarm_payload[0]);
+ pnet_alarm_send_process_alarm (
+ app->net,
+ app->main_api.ar[0].arep,
+ APP_GSDML_API,
+ slot,
+ p_subslot->subslot_nbr,
+ APP_ALARM_USI,
+ APP_GSDML_ALARM_PAYLOAD_SIZE,
+ app->alarm_payload);
+ app->alarm_allowed = false; /* Not allowed until ACK received */
+
+ /* todo handle return code on pnet_alarm_send_process_alarm */
+ }
+ else
+ {
+ APP_LOG_WARNING (
+ "Could not send process alarm, as alarm_allowed == false or "
+ "no connection available\n");
+ }
+ break;
+
+ case APP_DEMO_STATE_CYCLIC_REDUNDANT:
+ APP_LOG_INFO (
+ "Setting cyclic data to backup and to redundant. See Wireshark.\n");
+ if (pnet_set_primary_state (app->net, false) != 0)
+ {
+ APP_LOG_WARNING (" Could not set cyclic data state to backup.\n");
+ }
+ if (pnet_set_redundancy_state (app->net, true) != 0)
+ {
+ APP_LOG_WARNING (" Could not set cyclic data state to redundant.\n");
+ }
+ break;
+
+ case APP_DEMO_STATE_CYCLIC_NORMAL:
+ APP_LOG_INFO (
+ "Setting cyclic data back to primary and non-redundant. See "
+ "Wireshark.\n");
+ if (pnet_set_primary_state (app->net, true) != 0)
+ {
+ APP_LOG_ERROR (" Could not set cyclic data state to primary.\n");
+ }
+ if (pnet_set_redundancy_state (app->net, false) != 0)
+ {
+ APP_LOG_ERROR (
+ " Could not set cyclic data state to non-redundant.\n");
+ }
+ break;
+
+ case APP_DEMO_STATE_DIAG_STD_ADD:
+ APP_LOG_INFO (
+ "Adding standard diagnosis. Slot %u subslot %u channel %u Errortype "
+ "%u\n",
+ diag_source.slot,
+ diag_source.subslot,
+ diag_source.ch,
+ APP_DIAG_CHANNEL_ERRORTYPE);
+ (void)pnet_diag_std_add (
+ app->net,
+ &diag_source,
+ APP_DIAG_CHANNEL_NUMBER_OF_BITS,
+ APP_DIAG_CHANNEL_SEVERITY,
+ APP_DIAG_CHANNEL_ERRORTYPE,
+ APP_DIAG_CHANNEL_EXTENDED_ERRORTYPE,
+ APP_DIAG_CHANNEL_ADDVALUE_A,
+ APP_DIAG_CHANNEL_QUAL_SEVERITY);
+ break;
+
+ case APP_DEMO_STATE_DIAG_STD_UPDATE:
+ APP_LOG_INFO (
+ "Updating standard diagnosis. Slot %u subslot %u channel %u\n",
+ diag_source.slot,
+ diag_source.subslot,
+ diag_source.ch);
+ pnet_diag_std_update (
+ app->net,
+ &diag_source,
+ APP_DIAG_CHANNEL_ERRORTYPE,
+ APP_DIAG_CHANNEL_EXTENDED_ERRORTYPE,
+ APP_DIAG_CHANNEL_ADDVALUE_B);
+ break;
+
+ case APP_DEMO_STATE_DIAG_STD_REMOVE:
+ APP_LOG_INFO (
+ "Removing standard diagnosis. Slot %u subslot %u channel %u\n",
+ diag_source.slot,
+ diag_source.subslot,
+ diag_source.ch);
+ pnet_diag_std_remove (
+ app->net,
+ &diag_source,
+ APP_DIAG_CHANNEL_ERRORTYPE,
+ APP_DIAG_CHANNEL_EXTENDED_ERRORTYPE);
+ break;
+
+ case APP_DEMO_STATE_DIAG_USI_ADD:
+ APP_LOG_INFO (
+ "Adding USI diagnosis. Slot %u subslot %u\n",
+ slot,
+ p_subslot->subslot_nbr);
+ pnet_diag_usi_add (
+ app->net,
+ APP_GSDML_API,
+ slot,
+ p_subslot->subslot_nbr,
+ APP_GSDML_DIAG_CUSTOM_USI,
+ 11,
+ (uint8_t *)"diagdata_1");
+ break;
+
+ case APP_DEMO_STATE_DIAG_USI_UPDATE:
+ APP_LOG_INFO (
+ "Updating USI diagnosis. Slot %u subslot %u\n",
+ slot,
+ p_subslot->subslot_nbr);
+ pnet_diag_usi_update (
+ app->net,
+ APP_GSDML_API,
+ slot,
+ p_subslot->subslot_nbr,
+ APP_GSDML_DIAG_CUSTOM_USI,
+ 13,
+ (uint8_t *)"diagdata_123");
+ break;
+
+ case APP_DEMO_STATE_DIAG_USI_REMOVE:
+ APP_LOG_INFO (
+ "Removing USI diagnosis. Slot %u subslot %u\n",
+ slot,
+ p_subslot->subslot_nbr);
+ pnet_diag_usi_remove (
+ app->net,
+ APP_GSDML_API,
+ slot,
+ p_subslot->subslot_nbr,
+ APP_GSDML_DIAG_CUSTOM_USI);
+ break;
+
+ case APP_DEMO_STATE_LOGBOOK_ENTRY:
+ if (app_is_connected_to_controller (app))
+ {
+ APP_LOG_INFO (
+ "Writing to logbook. Error_code1: %02X Error_code2: %02X Entry "
+ "detail: 0x%08X\n",
+ APP_GSDML_LOGBOOK_ERROR_CODE_1,
+ APP_GSDML_LOGBOOK_ERROR_CODE_2,
+ APP_GSDML_LOGBOOK_ENTRY_DETAIL);
+ pnio_status.error_code = APP_GSDML_LOGBOOK_ERROR_CODE;
+ pnio_status.error_decode = APP_GSDML_LOGBOOK_ERROR_DECODE;
+ pnio_status.error_code_1 = APP_GSDML_LOGBOOK_ERROR_CODE_1;
+ pnio_status.error_code_2 = APP_GSDML_LOGBOOK_ERROR_CODE_2;
+ pnet_create_log_book_entry (
+ app->net,
+ app->main_api.ar[0].arep,
+ &pnio_status,
+ APP_GSDML_LOGBOOK_ENTRY_DETAIL);
+ }
+ else
+ {
+ APP_LOG_WARNING (
+ "Could not add logbook entry as no connection is available\n");
+ }
+ break;
+
+ case APP_DEMO_STATE_ABORT_AR:
+ if (app_is_connected_to_controller (app))
+ {
+ APP_LOG_INFO (
+ "Sample app will disconnect and reconnect. Executing "
+ "pnet_ar_abort() AREP: %u\n",
+ app->main_api.ar[0].arep);
+ (void)pnet_ar_abort (app->net, app->main_api.ar[0].arep);
+ }
+ else
+ {
+ APP_LOG_WARNING (
+ "Could not execute pnet_ar_abort(), as no connection is "
+ "available\n");
+ }
+ break;
+ }
+
+ switch (app->alarm_demo_state)
+ {
+ case APP_DEMO_STATE_ALARM_SEND:
+ app->alarm_demo_state = APP_DEMO_STATE_CYCLIC_REDUNDANT;
+ break;
+ case APP_DEMO_STATE_CYCLIC_REDUNDANT:
+ app->alarm_demo_state = APP_DEMO_STATE_CYCLIC_NORMAL;
+ break;
+ case APP_DEMO_STATE_CYCLIC_NORMAL:
+ app->alarm_demo_state = APP_DEMO_STATE_DIAG_STD_ADD;
+ break;
+ case APP_DEMO_STATE_DIAG_STD_ADD:
+ app->alarm_demo_state = APP_DEMO_STATE_DIAG_STD_UPDATE;
+ break;
+ case APP_DEMO_STATE_DIAG_STD_UPDATE:
+ app->alarm_demo_state = APP_DEMO_STATE_DIAG_USI_ADD;
+ break;
+ case APP_DEMO_STATE_DIAG_USI_ADD:
+ app->alarm_demo_state = APP_DEMO_STATE_DIAG_USI_UPDATE;
+ break;
+ case APP_DEMO_STATE_DIAG_USI_UPDATE:
+ app->alarm_demo_state = APP_DEMO_STATE_DIAG_USI_REMOVE;
+ break;
+ case APP_DEMO_STATE_DIAG_USI_REMOVE:
+ app->alarm_demo_state = APP_DEMO_STATE_DIAG_STD_REMOVE;
+ break;
+ case APP_DEMO_STATE_DIAG_STD_REMOVE:
+ app->alarm_demo_state = APP_DEMO_STATE_LOGBOOK_ENTRY;
+ break;
+ case APP_DEMO_STATE_LOGBOOK_ENTRY:
+ app->alarm_demo_state = APP_DEMO_STATE_ABORT_AR;
+ break;
+ default:
+ case APP_DEMO_STATE_ABORT_AR:
+ app->alarm_demo_state = APP_DEMO_STATE_ALARM_SEND;
+ break;
+ }
+}
+
+void app_pnet_cfg_init_default (pnet_cfg_t * pnet_cfg)
+{
+ app_utils_pnet_cfg_init_default (pnet_cfg);
+
+ pnet_cfg->state_cb = app_state_ind;
+ pnet_cfg->connect_cb = app_connect_ind;
+ pnet_cfg->release_cb = app_release_ind;
+ pnet_cfg->dcontrol_cb = app_dcontrol_ind;
+ pnet_cfg->ccontrol_cb = app_ccontrol_cnf;
+ pnet_cfg->read_cb = app_read_ind;
+ pnet_cfg->write_cb = app_write_ind;
+ pnet_cfg->exp_module_cb = app_exp_module_ind;
+ pnet_cfg->exp_submodule_cb = app_exp_submodule_ind;
+ pnet_cfg->new_data_status_cb = app_new_data_status_ind;
+ pnet_cfg->alarm_ind_cb = app_alarm_ind;
+ pnet_cfg->alarm_cnf_cb = app_alarm_cnf;
+ pnet_cfg->alarm_ack_cnf_cb = app_alarm_ack_cnf;
+ pnet_cfg->reset_cb = app_reset_ind;
+ pnet_cfg->signal_led_cb = app_signal_led_ind;
+ pnet_cfg->sm_released_cb = app_sm_released_ind;
+
+ pnet_cfg->cb_arg = (void *)&app_state;
+}
+
+/**
+ * Read button states from operating system
+ *
+ * Actual reading is done every APP_TICKS_READ_BUTTONS invocation
+ *
+ * @param app InOut: Application handle
+ */
+static void update_button_states (app_data_t * app)
+{
+ app->buttons_tick_counter++;
+ if (app->buttons_tick_counter > APP_TICKS_READ_BUTTONS)
+ {
+ app->button1_pressed = app_get_button (0);
+ app->button2_pressed = app_get_button (1);
+ app->buttons_tick_counter = 0;
+ }
+}
+
+/* Event handlers for the main loop. */
+
+static void app_handle_event_timer (app_data_t * app)
+{
+ os_event_clr (app->main_events, APP_EVENT_TIMER);
+
+ update_button_states (app);
+ if (app_is_connected_to_controller (app))
+ {
+ app_handle_cyclic_data (app);
+ }
+
+ /* Run alarm demo function if button2 is pressed */
+ if ((app->button2_pressed == true) && (app->button2_pressed_previous == false))
+ {
+ app_handle_demo_pnet_api (app);
+ }
+ app->button2_pressed_previous = app->button2_pressed;
+
+ /* Run p-net stack */
+ pnet_handle_periodic (app->net);
+}
+
+/**
+ * Handle AR specific events.
+ *
+ * Calls the \a handler for each AR that has a pending \a event.
+ *
+ * @param app InOut: Application handle
+ * @param event In: Event
+ * @param handler In: Event handling function
+ */
+static void app_handle_event_ar (
+ app_data_t * app,
+ uint32_t event,
+ app_ar_event_handler_t handler)
+{
+ app_ar_iterator_t iter;
+ app_ar_t * ar;
+
+ os_event_clr (app->main_events, event);
+ app_ar_iterator_init (&iter, &app->main_api);
+ while (app_ar_iterator_next (&iter, &ar))
+ {
+ if (app_ar_event_clr (ar, event))
+ {
+ if (handler (app, app_ar_arep (ar)))
+ {
+ app_ar_iterator_delete_current (&iter);
+ }
+ }
+ }
+}
+
+/* AR specific event handlers */
+
+/**
+ * Handle an AR connection PrmEnd.
+ *
+ * @param app InOut: Application handle
+ * @param arep In: Arep
+ *
+ * @return 0 to indicate that the arep should not be removed.
+ */
+static int app_ar_ready_for_data_handler (app_data_t * app, uint32_t arep)
+{
+ int err;
+
+ APP_LOG_DEBUG (
+ "Application will signal that it is ready for data, for AREP %u.\n",
+ arep);
+ /* When the PLC sends a confirmation to this message, the
+ pnet_ccontrol_cnf() callback will be triggered. */
+ err = pnet_application_ready (app->net, arep);
+ if (err)
+ {
+ APP_LOG_ERROR (
+ "Error returned when application telling that it is ready for "
+ "data. Have you set IOCS or IOPS for all subslots?\n");
+ }
+ return 0;
+}
+
+/**
+ * Handle an AR alarm.
+ *
+ * @param app InOut: Application handle
+ * @param arep In: Arep
+ *
+ * @return 0 to indicate that the arep should not be removed.
+ */
+static int app_ar_alarm_handler (app_data_t * app, uint32_t arep)
+{
+ pnet_pnio_status_t pnio_status = {0, 0, 0, 0};
+ int err;
+
+ err = pnet_alarm_send_ack (app->net, arep, &app->alarm_arg, &pnio_status);
+ if (err)
+ {
+ APP_LOG_DEBUG ("Error when sending alarm ACK. Error: %d\n", err);
+ }
+ else
+ {
+ APP_LOG_DEBUG ("Alarm ACK sent\n");
+ }
+ return 0;
+}
+
+/**
+ * Handle a released submodule.
+ *
+ * @param app InOut: Application handle
+ * @param arep In: Arep
+ *
+ * @return 0 to indicate that the arep should not be removed.
+ */
+static int app_ar_sm_released_handler (app_data_t * app, uint32_t arep)
+{
+ /* Ideally, we should only set data for the indicated submodule,
+ but setting everything works. */
+ app_set_initial_data_and_ioxs (app);
+ pnet_sm_released_cnf (app->net, arep);
+ return 0;
+}
+
+/**
+ * Handle an AR abort.
+ *
+ * @param app InOut: Application handle
+ * @param arep In: Arep
+ *
+ * @return 1 to indicate that the arep should be removed.
+ */
+static int app_ar_abort_handler (app_data_t * app, uint32_t arep)
+{
+ APP_LOG_DEBUG ("Connection (AREP %u) closed\n", arep);
+ return 1;
+}
+
+/**
+ * Wait for events generated elsewhere in this program.
+ *
+ * @param app InOut: Application handle
+ * @param mask In: Bitmask of events to wait for
+ * @param flags Out: Bitmask of pending events
+ */
+static void app_event_wait (app_data_t * app, uint32_t mask, uint32_t * flags)
+{
+ os_event_wait (app->main_events, mask, flags, OS_WAIT_FOREVER);
+}
+
+void app_loop_forever (void * arg)
+{
+ app_data_t * app = (app_data_t *)arg;
+ uint32_t mask = APP_EVENT_READY_FOR_DATA | APP_EVENT_TIMER |
+ APP_EVENT_ALARM | APP_EVENT_SM_RELEASED | APP_EVENT_ABORT;
+ uint32_t flags = 0;
+
+ app_set_led (APP_DATA_LED_ID, false);
+ app_plug_dap (app, app->pnet_cfg->num_physical_ports);
+ APP_LOG_INFO ("Waiting for PLC connect request\n\n");
+
+ /* Main event loop */
+ for (;;)
+ {
+ app_event_wait (app, mask, &flags);
+ if (flags & APP_EVENT_READY_FOR_DATA)
+ {
+ app_handle_event_ar (
+ app,
+ APP_EVENT_READY_FOR_DATA,
+ app_ar_ready_for_data_handler);
+ }
+ if (flags & APP_EVENT_ALARM)
+ {
+ app_handle_event_ar (app, APP_EVENT_ALARM, app_ar_alarm_handler);
+ }
+ if (flags & APP_EVENT_TIMER)
+ {
+ app_handle_event_timer (app);
+ }
+ if (flags & APP_EVENT_SM_RELEASED)
+ {
+ app_handle_event_ar (
+ app,
+ APP_EVENT_SM_RELEASED,
+ app_ar_sm_released_handler);
+ }
+ if (flags & APP_EVENT_ABORT)
+ {
+ app_handle_event_ar (app, APP_EVENT_ABORT, app_ar_abort_handler);
+ app->alarm_allowed = true;
+ }
+ }
+}
diff --git a/src/profeth/src/sampleapp_common.h b/src/profeth/src/sampleapp_common.h
new file mode 100644
index 00000000..a34e267c
--- /dev/null
+++ b/src/profeth/src/sampleapp_common.h
@@ -0,0 +1,169 @@
+/*********************************************************************
+ * _ _ _
+ * _ __ | |_ _ | | __ _ | |__ ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | | | |_ _ | || (_| || |_) |\__ \
+ * |_| \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2018 rt-labs AB, Sweden.
+ *
+ * This software is dual-licensed under GPLv3 and a commercial
+ * license. See the file LICENSE.md distributed with this software for
+ * full license information.
+ ********************************************************************/
+
+#ifndef SAMPLEAPP_COMMON_H
+#define SAMPLEAPP_COMMON_H
+
+#include "osal.h"
+#include "pnal.h"
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define APP_TICK_INTERVAL_US 1000 /* 1 ms */
+
+/* Thread configuration for targets where sample
+ * event loop is run in a separate thread (not main).
+ * This applies for linux sample app implementation.
+ */
+#define APP_MAIN_THREAD_PRIORITY 15
+#define APP_MAIN_THREAD_STACKSIZE 4096 /* bytes */
+
+#define APP_DATA_LED_ID 1
+#define APP_PROFINET_SIGNAL_LED_ID 2
+
+#define APP_TICKS_READ_BUTTONS 10
+#define APP_TICKS_UPDATE_DATA 100
+
+/** HW Offload configuration. */
+typedef enum
+{
+ MODE_HW_OFFLOAD_NONE = 0,
+ MODE_HW_OFFLOAD_CPU,
+ MODE_HW_OFFLOAD_FULL,
+} app_mode_t;
+
+/** Command line arguments for sample application */
+typedef struct app_args
+{
+ char path_button1[PNET_MAX_FILE_FULLPATH_SIZE]; /** Terminated string */
+ char path_button2[PNET_MAX_FILE_FULLPATH_SIZE]; /** Terminated string */
+ char path_storage_directory[PNET_MAX_DIRECTORYPATH_SIZE]; /** Terminated */
+ char station_name[PNET_STATION_NAME_MAX_SIZE]; /** Terminated string */
+ char eth_interfaces
+ [PNET_INTERFACE_NAME_MAX_SIZE * (PNET_MAX_PHYSICAL_PORTS + 1) +
+ PNET_MAX_PHYSICAL_PORTS]; /** Terminated string */
+ int verbosity;
+ int show;
+ bool factory_reset;
+ bool remove_files;
+ app_mode_t mode;
+} app_args_t;
+
+typedef enum
+{
+ RUN_IN_SEPARATE_THREAD,
+ RUN_IN_MAIN_THREAD
+} app_run_in_separate_task_t;
+
+typedef struct app_data_t app_data_t;
+
+/**
+ * AR specific event handler type.
+ *
+ * Handles an AR specific event.
+ *
+ * @param app InOut: Application handle
+ * @param arep In: Arep of the AR.
+ *
+ * @return 0 to indicate that the arep should be kept
+ * 1 to indicate that the arep should be forgotten
+ */
+typedef int (*app_ar_event_handler_t) (app_data_t * app, uint32_t arep);
+
+/** Partially initialise config values, and use proper callbacks
+ *
+ * @param pnet_cfg Out: Configuration to be updated
+ */
+void app_pnet_cfg_init_default (pnet_cfg_t * pnet_cfg);
+
+/**
+ * Initialize P-Net stack and application.
+ *
+ * The \a pnet_cfg argument shall have been initialized using
+ * \a app_pnet_cfg_init_default() before this function is
+ * called.
+ *
+ * @param pnet_cfg In: P-Net configuration
+ * @param app_args In: Application arguments
+ * @return Application handle, NULL on error
+ */
+app_data_t * app_init (const pnet_cfg_t * pnet_cfg, const app_args_t * app_args);
+
+/**
+ * Start application main loop
+ *
+ * Application must have been initialized using \a app_init() before
+ * this function is called.
+ *
+ * If \a task_config parameters is set to RUN_IN_SEPARATE_THREAD a
+ * thread execution the \a app_loop_forever() function is started.
+ * If task_config is set to RUN_IN_MAIN_THREAD no such thread is
+ * started and the caller must call the \a app_loop_forever() after
+ * calling this function.
+ *
+ * RUN_IN_MAIN_THREAD is intended for rt-kernel targets.
+ * RUN_IN_SEPARATE_THREAD is intended for linux targets.
+ *
+ * @param app In: Application handle
+ * @param task_config In: Defines if stack and application
+ * is run in main or separate task.
+ * @return 0 on success, -1 on error
+ */
+int app_start (app_data_t * app, app_run_in_separate_task_t task_config);
+
+/**
+ * Application task definition. Handles events in eternal loop.
+ *
+ * @param arg In: Application handle
+ */
+void app_loop_forever (void * arg);
+
+/**
+ * Get P-Net instance from application
+ *
+ * @param app In: Application handle
+ * @return P-Net instance, NULL on failure
+ */
+pnet_t * app_get_pnet_instance (app_data_t * app);
+
+/**
+ * Set LED state
+ * Hardware specific. Implemented in sample app main file for
+ * each supported platform.
+ *
+ * @param id In: LED number, starting from 0.
+ * @param led_state In: LED state. Use true for on and false for off.
+ */
+void app_set_led (uint16_t id, bool led_state);
+
+/**
+ * Read button state
+ *
+ * Hardware specific. Implemented in sample app main file for
+ * each supported platform.
+ *
+ * @param id In: Button number, starting from 0.
+ * @return true if button is pressed, false if not
+ */
+bool app_get_button (uint16_t id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SAMPLEAPP_COMMON_H */