diff --git a/netutils/s2opc/.gitignore b/netutils/s2opc/.gitignore new file mode 100644 index 000000000..58c654469 --- /dev/null +++ b/netutils/s2opc/.gitignore @@ -0,0 +1,2 @@ +/s2opc +/S2OPC_Toolkit_*.tar.gz diff --git a/netutils/s2opc/CMakeLists.txt b/netutils/s2opc/CMakeLists.txt new file mode 100644 index 000000000..8f93a8d90 --- /dev/null +++ b/netutils/s2opc/CMakeLists.txt @@ -0,0 +1,254 @@ +# ############################################################################## +# apps/netutils/s2opc/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_NETUTILS_S2OPC) + set(S2OPC_DIR ${CMAKE_CURRENT_LIST_DIR}/s2opc) + set(S2OPC_TAG S2OPC_Toolkit_${CONFIG_S2OPC_VERSION}) + set(S2OPC_URL https://gitlab.com/systerel/S2OPC/-/archive/${S2OPC_TAG}) + + if(NOT EXISTS ${S2OPC_DIR}) + FetchContent_Declare( + s2opc_fetch + URL ${S2OPC_URL}/S2OPC-${S2OPC_TAG}.tar.gz SOURCE_DIR ${S2OPC_DIR} + BINARY_DIR ${NUTTX_BINARY_DIR}/apps/netutils/s2opc/s2opc + DOWNLOAD_NO_PROGRESS true + TIMEOUT 60) + FetchContent_GetProperties(s2opc_fetch) + + if(NOT s2opc_fetch_POPULATED) + FetchContent_Populate(s2opc_fetch) + endif() + endif() + + set(S2OPC_COMMON ${S2OPC_DIR}/src/Common) + set(S2OPC_CS ${S2OPC_DIR}/src/ClientServer) + set(S2OPC_PS ${S2OPC_DIR}/src/PubSub) + set(S2OPC_PLATFORM ${S2OPC_COMMON}/helpers_platform_dep) + + file( + GLOB + COMMON_SRCS + ${S2OPC_COMMON}/configuration/*.c + ${S2OPC_COMMON}/helpers/*.c + ${S2OPC_COMMON}/opcua_types/*.c + ${S2OPC_COMMON}/sks/*.c + ${S2OPC_COMMON}/crypto/*.c + ${S2OPC_COMMON}/crypto/lib_dep/mbedtls/*.c) + + list( + APPEND + COMMON_SRCS + ${S2OPC_PLATFORM}/sopc_askpass.c + ${S2OPC_PLATFORM}/sopc_time_reference.c + ${CMAKE_CURRENT_LIST_DIR}/port/nuttx/p_sopc_askpass.c + ${CMAKE_CURRENT_LIST_DIR}/port/nuttx/p_sopc_atomic.c + ${CMAKE_CURRENT_LIST_DIR}/port/nuttx/p_sopc_date_time.c + ${CMAKE_CURRENT_LIST_DIR}/port/nuttx/p_sopc_eth_sockets.c + ${CMAKE_CURRENT_LIST_DIR}/port/nuttx/p_sopc_filesystem.c + ${CMAKE_CURRENT_LIST_DIR}/port/nuttx/p_sopc_mem_alloc.c + ${CMAKE_CURRENT_LIST_DIR}/port/nuttx/p_sopc_random.c + ${CMAKE_CURRENT_LIST_DIR}/port/nuttx/p_sopc_sockets.c + ${CMAKE_CURRENT_LIST_DIR}/port/nuttx/p_sopc_threads.c + ${CMAKE_CURRENT_LIST_DIR}/port/nuttx/p_sopc_time_reference.c + ${CMAKE_CURRENT_LIST_DIR}/port/nuttx/p_sopc_udp_sockets.c + ${CMAKE_CURRENT_LIST_DIR}/port/nuttx/sopc_build_info.c) + + file( + GLOB + CLIENTSERVER_SRCS + ${S2OPC_CS}/address_space/*.c + ${S2OPC_CS}/address_space/internal/*.c + ${S2OPC_CS}/api_toolkit/*.c + ${S2OPC_CS}/configuration/*.c + ${S2OPC_CS}/sockets/*.c + ${S2OPC_CS}/secure_channels/*.c + ${S2OPC_CS}/services/*.c + ${S2OPC_CS}/services/b2c/*.c + ${S2OPC_CS}/services/bgenc/*.c + ${S2OPC_CS}/frontend/common_wrapper/*.c + ${S2OPC_CS}/frontend/common_wrapper/certificate_management/internal/*.c + ${S2OPC_CS}/frontend/helper_wrapper/*.c + ${S2OPC_CS}/frontend/client_wrapper/*.c + ${S2OPC_CS}/frontend/client_wrapper/internal/*.c + ${S2OPC_CS}/frontend/client_wrapper/alarm_conditions/*.c + ${S2OPC_CS}/frontend/server_wrapper/*.c + ${S2OPC_CS}/frontend/server_wrapper/internal/*.c + ${S2OPC_CS}/frontend/server_wrapper/file_transfer/*.c + ${S2OPC_CS}/frontend/server_wrapper/alarm_conditions/*.c + ${S2OPC_CS}/frontend/server_wrapper/alarm_conditions/internal/*.c + ${S2OPC_CS}/loaders/address_space_loaders/embedded/*.c) + + list(REMOVE_ITEM CLIENTSERVER_SRCS + ${S2OPC_CS}/frontend/client_wrapper/libs2opc_client_config_xml.c + ${S2OPC_CS}/frontend/server_wrapper/libs2opc_server_config_xml.c) + + file( + GLOB + PUBSUB_SRCS + ${S2OPC_PS}/common/*.c + ${S2OPC_PS}/dataset/*.c + ${S2OPC_PS}/network/*.c + ${S2OPC_PS}/protocol/*.c + ${S2OPC_PS}/publisher/*.c + ${S2OPC_PS}/security/*.c + ${S2OPC_PS}/subscriber/*.c) + + if(CONFIG_S2OPC_XML_LOADERS) + file( + GLOB + XML_SRCS + ${S2OPC_CS}/loaders/helpers/*.c + ${S2OPC_CS}/loaders/address_space_loaders/xml_expat/*.c + ${S2OPC_CS}/loaders/config_loaders/xml_expat/*.c + ${S2OPC_PS}/config_loaders/xml_expat/*.c) + list(APPEND XML_SRCS + ${S2OPC_CS}/frontend/client_wrapper/libs2opc_client_config_xml.c + ${S2OPC_CS}/frontend/server_wrapper/libs2opc_server_config_xml.c) + endif() + + # GCC 16 diagnoses upstream integer format strings on targets where uint32_t + # is unsigned long. Keep this suppression limited to the unmodified upstream + # sources that emit the diagnostics. + set_source_files_properties(${S2OPC_PS}/publisher/sopc_pub_scheduler.c + PROPERTIES COMPILE_FLAGS -Wno-format) + if(CONFIG_S2OPC_XML_LOADERS) + set_source_files_properties( + ${S2OPC_CS}/loaders/config_loaders/xml_expat/sopc_users_loader.c + PROPERTIES COMPILE_FLAGS -Wno-format) + endif() + + set(S2OPC_PUBLIC_INCLUDES + ${CMAKE_CURRENT_LIST_DIR}/port/nuttx + ${S2OPC_COMMON}/configuration + ${S2OPC_COMMON}/helpers + ${S2OPC_COMMON}/helpers_platform_dep + ${S2OPC_COMMON}/opcua_types + ${S2OPC_COMMON}/crypto + ${S2OPC_COMMON}/crypto/lib_itf + ${S2OPC_COMMON}/crypto/lib_dep + ${S2OPC_COMMON}/sks + ${S2OPC_CS}/address_space + ${S2OPC_CS}/api_toolkit + ${S2OPC_CS}/configuration + ${S2OPC_CS}/frontend/common_wrapper + ${S2OPC_CS}/frontend/common_wrapper/certificate_management + ${S2OPC_CS}/frontend/helper_wrapper + ${S2OPC_CS}/frontend/client_wrapper + ${S2OPC_CS}/frontend/client_wrapper/alarm_conditions + ${S2OPC_CS}/frontend/server_wrapper + ${S2OPC_CS}/frontend/server_wrapper/alarm_conditions + ${S2OPC_CS}/frontend/server_wrapper/file_transfer + ${S2OPC_CS}/loaders/address_space_loaders/embedded + ${S2OPC_PS}/common + ${S2OPC_PS}/dataset + ${S2OPC_PS}/network + ${S2OPC_PS}/protocol + ${S2OPC_PS}/publisher + ${S2OPC_PS}/security + ${S2OPC_PS}/subscriber) + + set(S2OPC_PRIVATE_INCLUDES + ${S2OPC_COMMON}/crypto/lib_dep/mbedtls + ${S2OPC_CS}/address_space/internal + ${S2OPC_CS}/sockets + ${S2OPC_CS}/secure_channels + ${S2OPC_CS}/services + ${S2OPC_CS}/services/b2c + ${S2OPC_CS}/services/bgenc + ${S2OPC_CS}/frontend/common_wrapper/internal + ${S2OPC_CS}/frontend/common_wrapper/certificate_management/internal + ${S2OPC_CS}/frontend/client_wrapper/internal + ${S2OPC_CS}/frontend/server_wrapper/internal + ${S2OPC_CS}/frontend/server_wrapper/alarm_conditions/internal) + + if(CONFIG_S2OPC_XML_LOADERS) + list( + APPEND + S2OPC_PUBLIC_INCLUDES + ${S2OPC_CS}/loaders/address_space_loaders + ${S2OPC_CS}/loaders/address_space_loaders/xml_expat + ${S2OPC_CS}/loaders/config_loaders + ${S2OPC_CS}/loaders/config_loaders/xml_expat + ${S2OPC_PS}/config_loaders/xml_expat) + list(APPEND S2OPC_PRIVATE_INCLUDES ${S2OPC_CS}/loaders/helpers) + endif() + + set(S2OPC_FLAGS + -std=gnu99 + -D_GNU_SOURCE + -DS2OPC_COMMON_STATIC_DEFINE + -DSOPC_TIME_T_SIZE=8 + -include + ${CMAKE_CURRENT_LIST_DIR}/port/nuttx/sopc_nuttx_config.h + -Wno-shadow + -Wno-cast-qual + -Wno-conversion + -Wno-missing-prototypes + -Wno-strict-prototypes + -Wno-unused-but-set-variable + -Wno-unused-function + -Wno-unused-variable) + + if(CONFIG_S2OPC_XML_LOADERS) + list(APPEND S2OPC_FLAGS -DUANODESET_LOADER_LOG) + endif() + + if(CONFIG_S2OPC_MQTT) + list(APPEND S2OPC_FLAGS -DUSE_MQTT_PAHO=1) + list(APPEND S2OPC_PRIVATE_INCLUDES + ${CMAKE_CURRENT_LIST_DIR}/../paho_mqtt/paho_mqtt/src) + else() + list(APPEND S2OPC_FLAGS -DUSE_MQTT_PAHO=0) + endif() + + if(CONFIG_ARCH_64BIT) + list(APPEND S2OPC_FLAGS -DSOPC_PTR_SIZE=8) + else() + list(APPEND S2OPC_FLAGS -DSOPC_PTR_SIZE=4) + endif() + + if(CONFIG_ENDIAN_BIG) + list(APPEND S2OPC_FLAGS -DSOPC_IS_LITTLE_ENDIAN=0) + else() + list(APPEND S2OPC_FLAGS -DSOPC_IS_LITTLE_ENDIAN=1) + endif() + + nuttx_add_library(s2opc STATIC) + target_sources(s2opc PRIVATE ${COMMON_SRCS} ${CLIENTSERVER_SRCS} + ${PUBSUB_SRCS} ${XML_SRCS}) + target_include_directories( + s2opc + PUBLIC ${S2OPC_PUBLIC_INCLUDES} + PRIVATE ${S2OPC_PRIVATE_INCLUDES}) + target_compile_options(s2opc PRIVATE ${S2OPC_FLAGS}) + nuttx_export_header(TARGET s2opc INCLUDE_DIRECTORIES ${S2OPC_PUBLIC_INCLUDES}) + nuttx_add_dependencies(TARGET s2opc DEPENDS mbedtls) + + if(CONFIG_S2OPC_XML_LOADERS) + nuttx_add_dependencies(TARGET s2opc DEPENDS expat) + endif() + + if(CONFIG_S2OPC_MQTT) + nuttx_add_dependencies(TARGET s2opc DEPENDS mqtt5) + endif() + +endif() diff --git a/netutils/s2opc/Kconfig b/netutils/s2opc/Kconfig new file mode 100644 index 000000000..135b8fb03 --- /dev/null +++ b/netutils/s2opc/Kconfig @@ -0,0 +1,109 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig NETUTILS_S2OPC + bool "S2OPC OPC UA toolkit" + default n + depends on CRYPTO_MBEDTLS + depends on LIBC_NETDB && PTHREAD_MUTEX_TYPES + depends on NET_TCP && NET_UDP + depends on NET_SOCKOPTS && NET_TCP_KEEPALIVE + depends on DEV_URANDOM + depends on RR_INTERVAL > 0 + depends on NET_BINDTODEVICE + ---help--- + Enable the S2OPC OPC UA client/server and UDP PubSub toolkit. The + selected S2OPC release is downloaded when the applications tree is + configured. A cryptographically suitable random source is required + when secure OPC UA policies are used. Certificate validation also + requires a correctly initialized system clock. A round-robin + timeslice is required for normal S2OPC worker threads. + +if NETUTILS_S2OPC + +config S2OPC_VERSION + string "S2OPC version" + default "1.7.3" + +config S2OPC_THREAD_STACKSIZE + int "S2OPC worker thread stack size" + default 32768 + range PTHREAD_STACK_MIN 1048576 + ---help--- + Stack size assigned to worker threads created by S2OPC. Secure OPC + UA processing needs considerably more stack than small NuttX tasks. + +config S2OPC_XML_LOADERS + bool "Enable XML configuration loaders" + default n + depends on ALLOW_MIT_COMPONENTS + depends on LIB_EXPAT + ---help--- + Enable the S2OPC client, server, user, address-space, and PubSub XML + loaders. This adds the Expat XML parser dependency. Leave this + disabled when configuration and address-space data are constructed + with the S2OPC C APIs or generated as C source. + +config S2OPC_NODE_MANAGEMENT + bool "Enable OPC UA Node Management services" + default n + ---help--- + Enable the AddNodes and DeleteNodes services. The configured server + address space must be dynamically allocated for these services to + modify it. + +config S2OPC_NODE_ADD_OPTIONAL + bool "Add optional child nodes with Node Management" + default n + depends on S2OPC_NODE_MANAGEMENT + ---help--- + Add optional child nodes declared by a type when AddNodes creates an + object, variable, or method node. Mandatory child nodes are added + regardless of this option. + +config S2OPC_HISTORY_READ + bool "Enable external HistoryRead Raw service" + default n + ---help--- + Enable S2OPC's HistoryRead Raw service. The application must provide + the external history-read callback and its underlying history store. + +config S2OPC_EVENT_MANAGEMENT + bool "Enable OPC UA Events" + default n + ---help--- + Enable server event creation, triggering, and event monitored items. + The configured address space must contain the required event types. + +config S2OPC_AUDITING + bool "Enable OPC UA security auditing" + default n + depends on S2OPC_EVENT_MANAGEMENT + ---help--- + Enable S2OPC's auditing support. S2OPC 1.7.3 limits auditing to + security audit event types and requires OPC UA Events. + +config S2OPC_MQTT + bool "Enable MQTT PubSub transport" + default n + depends on LIB_MQTT5 + ---help--- + Enable the S2OPC MQTT PubSub transport using the Eclipse Paho MQTT + Async library packaged by NuttX. + +config S2OPC_ETHERNET + bool "Enable raw Ethernet PubSub transport" + default n + depends on NET_PKT + depends on NET_MCASTGROUP + depends on NET_PKTPROTO_OPTIONS + ---help--- + Enable UADP PubSub over raw Ethernet packet sockets. Multicast + reception requires a network driver that implements multicast MAC + filtering. The NuttX packet-socket implementation requires an + interface name and a destination multicast MAC address. A combined + publisher/subscriber needs at least two packet-socket connections. + +endif # NETUTILS_S2OPC diff --git a/netutils/s2opc/Make.defs b/netutils/s2opc/Make.defs new file mode 100644 index 000000000..80247a16e --- /dev/null +++ b/netutils/s2opc/Make.defs @@ -0,0 +1,68 @@ +############################################################################ +# apps/netutils/s2opc/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_NETUTILS_S2OPC),) +CONFIGURED_APPS += $(APPDIR)/netutils/s2opc + +S2OPC_ROOT = $(APPDIR)/netutils/s2opc/s2opc +S2OPC_COMMON = $(S2OPC_ROOT)/src/Common +S2OPC_CLIENTSERVER = $(S2OPC_ROOT)/src/ClientServer +S2OPC_PUBSUB = $(S2OPC_ROOT)/src/PubSub +S2OPC_CS_LOADERS = $(S2OPC_CLIENTSERVER)/loaders +S2OPC_AS_LOADERS = $(S2OPC_CS_LOADERS)/address_space_loaders + +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(APPDIR)/netutils/s2opc/port/nuttx +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_COMMON)/configuration +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_COMMON)/helpers +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_COMMON)/helpers_platform_dep +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_COMMON)/opcua_types +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_COMMON)/crypto +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_COMMON)/crypto/lib_itf +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_COMMON)/crypto/lib_dep +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_COMMON)/sks +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_CLIENTSERVER)/address_space +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_CLIENTSERVER)/api_toolkit +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_CLIENTSERVER)/configuration +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_CLIENTSERVER)/frontend/common_wrapper +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_CLIENTSERVER)/frontend/helper_wrapper +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_CLIENTSERVER)/frontend/client_wrapper +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_CLIENTSERVER)/frontend/server_wrapper +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_AS_LOADERS)/embedded +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_PUBSUB)/common +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_PUBSUB)/dataset +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_PUBSUB)/network +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_PUBSUB)/protocol +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_PUBSUB)/publisher +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_PUBSUB)/security +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_PUBSUB)/subscriber + +ifeq ($(CONFIG_S2OPC_XML_LOADERS),y) +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_AS_LOADERS) +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_AS_LOADERS)/xml_expat +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_CS_LOADERS)/config_loaders +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_CS_LOADERS)/config_loaders/xml_expat +S2OPC_INCLUDES += ${INCDIR_PREFIX}$(S2OPC_PUBSUB)/config_loaders/xml_expat +endif + +CFLAGS += $(S2OPC_INCLUDES) +CXXFLAGS += $(S2OPC_INCLUDES) +endif diff --git a/netutils/s2opc/Makefile b/netutils/s2opc/Makefile new file mode 100644 index 000000000..190cf82bb --- /dev/null +++ b/netutils/s2opc/Makefile @@ -0,0 +1,195 @@ +############################################################################ +# apps/netutils/s2opc/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +S2OPC_VERSION = $(patsubst "%",%,$(CONFIG_S2OPC_VERSION)) +S2OPC_TAG = S2OPC_Toolkit_$(S2OPC_VERSION) +S2OPC_ARCHIVE = $(S2OPC_TAG).tar.gz +S2OPC_ARCHIVES = $(wildcard S2OPC_Toolkit_*.tar.gz) +S2OPC_SRCDIR = s2opc +S2OPC_URL ?= https://gitlab.com/systerel/S2OPC/-/archive +S2OPC_COMMON = $(S2OPC_SRCDIR)/src/Common +S2OPC_CS = $(S2OPC_SRCDIR)/src/ClientServer +S2OPC_PS = $(S2OPC_SRCDIR)/src/PubSub +S2OPC_PLATFORM = $(S2OPC_COMMON)/helpers_platform_dep +S2OPC_CSCERT = $(S2OPC_CS)/frontend/common_wrapper/certificate_management +S2OPC_CSALARM = $(S2OPC_CS)/frontend/server_wrapper/alarm_conditions +S2OPC_CS_XML_WRAPPERS = \ + $(S2OPC_CS)/frontend/client_wrapper/libs2opc_client_config_xml.c \ + $(S2OPC_CS)/frontend/server_wrapper/libs2opc_server_config_xml.c + +CSRCS += $(wildcard $(S2OPC_COMMON)/configuration/*.c) +CSRCS += $(wildcard $(S2OPC_COMMON)/helpers/*.c) +CSRCS += $(wildcard $(S2OPC_COMMON)/opcua_types/*.c) +CSRCS += $(wildcard $(S2OPC_COMMON)/sks/*.c) +CSRCS += $(wildcard $(S2OPC_COMMON)/crypto/*.c) +CSRCS += $(wildcard $(S2OPC_COMMON)/crypto/lib_dep/mbedtls/*.c) + +CSRCS += $(S2OPC_PLATFORM)/sopc_askpass.c +CSRCS += $(S2OPC_PLATFORM)/sopc_time_reference.c +CSRCS += port/nuttx/p_sopc_askpass.c +CSRCS += port/nuttx/p_sopc_atomic.c +CSRCS += port/nuttx/p_sopc_date_time.c +CSRCS += port/nuttx/p_sopc_eth_sockets.c +CSRCS += port/nuttx/p_sopc_filesystem.c +CSRCS += port/nuttx/p_sopc_mem_alloc.c +CSRCS += port/nuttx/p_sopc_random.c +CSRCS += port/nuttx/p_sopc_sockets.c +CSRCS += port/nuttx/p_sopc_threads.c +CSRCS += port/nuttx/p_sopc_time_reference.c +CSRCS += port/nuttx/p_sopc_udp_sockets.c +CSRCS += port/nuttx/sopc_build_info.c + +CSRCS += $(wildcard $(S2OPC_CS)/address_space/*.c) +CSRCS += $(wildcard $(S2OPC_CS)/address_space/internal/*.c) +CSRCS += $(wildcard $(S2OPC_CS)/api_toolkit/*.c) +CSRCS += $(wildcard $(S2OPC_CS)/configuration/*.c) +CSRCS += $(wildcard $(S2OPC_CS)/sockets/*.c) +CSRCS += $(wildcard $(S2OPC_CS)/secure_channels/*.c) +CSRCS += $(wildcard $(S2OPC_CS)/services/*.c) +CSRCS += $(wildcard $(S2OPC_CS)/services/b2c/*.c) +CSRCS += $(wildcard $(S2OPC_CS)/services/bgenc/*.c) +CSRCS += $(wildcard $(S2OPC_CS)/frontend/common_wrapper/*.c) +CSRCS += $(wildcard $(S2OPC_CSCERT)/internal/*.c) +CSRCS += $(wildcard $(S2OPC_CS)/frontend/helper_wrapper/*.c) +CSRCS += $(filter-out $(S2OPC_CS_XML_WRAPPERS), \ + $(wildcard $(S2OPC_CS)/frontend/client_wrapper/*.c)) +CSRCS += $(wildcard $(S2OPC_CS)/frontend/client_wrapper/internal/*.c) +CSRCS += $(wildcard $(S2OPC_CS)/frontend/client_wrapper/alarm_conditions/*.c) +CSRCS += $(filter-out $(S2OPC_CS_XML_WRAPPERS), \ + $(wildcard $(S2OPC_CS)/frontend/server_wrapper/*.c)) +CSRCS += $(wildcard $(S2OPC_CS)/frontend/server_wrapper/internal/*.c) +CSRCS += $(wildcard $(S2OPC_CS)/frontend/server_wrapper/file_transfer/*.c) +CSRCS += $(wildcard $(S2OPC_CSALARM)/*.c) +CSRCS += $(wildcard $(S2OPC_CSALARM)/internal/*.c) +CSRCS += $(wildcard $(S2OPC_CS)/loaders/address_space_loaders/embedded/*.c) + +ifeq ($(CONFIG_S2OPC_XML_LOADERS),y) +CSRCS += $(S2OPC_CS_XML_WRAPPERS) +CSRCS += $(wildcard $(S2OPC_CS)/loaders/helpers/*.c) +CSRCS += $(wildcard $(S2OPC_CS)/loaders/address_space_loaders/xml_expat/*.c) +CSRCS += $(wildcard $(S2OPC_CS)/loaders/config_loaders/xml_expat/*.c) +CSRCS += $(wildcard $(S2OPC_PS)/config_loaders/xml_expat/*.c) + +$(S2OPC_CS)/loaders/config_loaders/xml_expat/sopc_users_loader.c_CFLAGS += \ + -Wno-format +endif + +CSRCS += $(wildcard $(S2OPC_PS)/common/*.c) +CSRCS += $(wildcard $(S2OPC_PS)/dataset/*.c) +CSRCS += $(wildcard $(S2OPC_PS)/network/*.c) +CSRCS += $(wildcard $(S2OPC_PS)/protocol/*.c) +CSRCS += $(wildcard $(S2OPC_PS)/publisher/*.c) +CSRCS += $(wildcard $(S2OPC_PS)/security/*.c) +CSRCS += $(wildcard $(S2OPC_PS)/subscriber/*.c) + +# GCC 16 diagnoses upstream integer format strings on targets where uint32_t +# is unsigned long. Keep this suppression limited to the unmodified upstream +# sources that emit the diagnostics. + +$(S2OPC_PS)/publisher/sopc_pub_scheduler.c_CFLAGS += -Wno-format + +CFLAGS += ${INCDIR_PREFIX}port/nuttx +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_COMMON)/crypto/lib_dep/mbedtls +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CS)/address_space/internal +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CS)/sockets +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CS)/secure_channels +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CS)/services +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CS)/services/b2c +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CS)/services/bgenc +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CS)/frontend/common_wrapper/internal +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CSCERT) +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CSCERT)/internal +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CS)/frontend/client_wrapper/internal +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CS)/frontend/server_wrapper/internal +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CS)/frontend/server_wrapper/file_transfer +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CSALARM) +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CSALARM)/internal +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CS)/frontend/client_wrapper/alarm_conditions +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CS)/loaders/address_space_loaders/embedded + +CFLAGS += -std=gnu99 -D_GNU_SOURCE -DS2OPC_COMMON_STATIC_DEFINE +CFLAGS += -DSOPC_TIME_T_SIZE=8 +CFLAGS += -include $(CURDIR)/port/nuttx/sopc_nuttx_config.h +CFLAGS += -Wno-shadow -Wno-cast-qual -Wno-conversion +CFLAGS += -Wno-missing-prototypes -Wno-strict-prototypes +CFLAGS += -Wno-unused-but-set-variable -Wno-unused-function +CFLAGS += -Wno-unused-variable + +ifeq ($(CONFIG_S2OPC_XML_LOADERS),y) +CFLAGS += ${INCDIR_PREFIX}$(S2OPC_CS)/loaders/helpers +CFLAGS += -DUANODESET_LOADER_LOG +endif + +ifeq ($(CONFIG_S2OPC_MQTT),y) +CFLAGS += -DUSE_MQTT_PAHO=1 +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/netutils/paho_mqtt/paho_mqtt/src +else +CFLAGS += -DUSE_MQTT_PAHO=0 +endif + +ifeq ($(CONFIG_ARCH_64BIT),y) +CFLAGS += -DSOPC_PTR_SIZE=8 +else +CFLAGS += -DSOPC_PTR_SIZE=4 +endif + +ifeq ($(CONFIG_ENDIAN_BIG),y) +CFLAGS += -DSOPC_IS_LITTLE_ENDIAN=0 +else +CFLAGS += -DSOPC_IS_LITTLE_ENDIAN=1 +endif + +S2OPC_PORTDIR = port +S2OPC_PORT_CLEANFILES = $(S2OPC_PORTDIR)$(DELIM)*$(OBJEXT) +S2OPC_PORT_CLEANFILES += $(S2OPC_PORTDIR)$(DELIM)*.gcda +S2OPC_PORT_CLEANFILES += $(S2OPC_PORTDIR)$(DELIM)*.gcno +S2OPC_PORT_CLEANFILES += $(S2OPC_PORTDIR)$(DELIM)*.su +S2OPC_PORT_CLEANFILES += $(S2OPC_PORTDIR)$(DELIM)nuttx$(DELIM)*$(OBJEXT) +S2OPC_PORT_CLEANFILES += $(S2OPC_PORTDIR)$(DELIM)nuttx$(DELIM)*.gcda +S2OPC_PORT_CLEANFILES += $(S2OPC_PORTDIR)$(DELIM)nuttx$(DELIM)*.gcno +S2OPC_PORT_CLEANFILES += $(S2OPC_PORTDIR)$(DELIM)nuttx$(DELIM)*.su + +clean:: + $(call DELFILE, $(S2OPC_PORT_CLEANFILES)) + +ifeq ($(wildcard $(S2OPC_SRCDIR)/.git),) +$(S2OPC_ARCHIVE): + @echo "Downloading: $(S2OPC_ARCHIVE)" + $(Q) curl -o $(S2OPC_ARCHIVE) -L \ + $(S2OPC_URL)/$(S2OPC_TAG)/S2OPC-$(S2OPC_TAG).tar.gz + +$(S2OPC_SRCDIR): $(S2OPC_ARCHIVE) + $(Q) tar -xf $(S2OPC_ARCHIVE) + $(Q) mv S2OPC-$(S2OPC_TAG) $(S2OPC_SRCDIR) + +context:: $(S2OPC_SRCDIR) + +distclean:: + $(call DELDIR, $(S2OPC_SRCDIR)) + $(call DELFILE, $(S2OPC_ARCHIVES)) +endif + +MODULE = $(CONFIG_NETUTILS_S2OPC) + +include $(APPDIR)/Application.mk diff --git a/netutils/s2opc/port/nuttx/p_sopc_askpass.c b/netutils/s2opc/port/nuttx/p_sopc_askpass.c new file mode 100644 index 000000000..d46ba1b80 --- /dev/null +++ b/netutils/s2opc/port/nuttx/p_sopc_askpass.c @@ -0,0 +1,114 @@ +/**************************************************************************** + * apps/netutils/s2opc/port/nuttx/p_sopc_askpass.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to Systerel under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. Systerel licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the + * License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include "sopc_askpass.h" +#include "sopc_mem_alloc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define S2OPC_PASSWORD_EXTRA 3 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: SOPC_AskPass_CustomPromptFromTerminal + ****************************************************************************/ + +bool SOPC_AskPass_CustomPromptFromTerminal(FAR const char *prompt, + FAR char **password) +{ + struct termios original; + struct termios noecho; + FAR char *buffer; + FAR char *result; + size_t buffer_size; + size_t length; + int fd; + int ret; + + if (prompt == NULL || password == NULL) + { + return false; + } + + *password = NULL; + fd = fileno(stdin); + if (fd < 0) + { + return false; + } + + buffer_size = SOPC_PASSWORD_MAX_LENGTH + S2OPC_PASSWORD_EXTRA; + buffer = SOPC_Calloc(buffer_size, sizeof(char)); + if (buffer == NULL) + { + return false; + } + + ret = tcgetattr(fd, &original); + if (ret == 0) + { + noecho = original; + noecho.c_lflag &= (tcflag_t)~(ECHO | ECHOE | ECHOK | ECHONL); + ret = tcsetattr(fd, TCSAFLUSH, &noecho); + } + + result = NULL; + if (ret == 0) + { + fputs(prompt, stdout); + fflush(stdout); + result = fgets(buffer, buffer_size, stdin); + tcsetattr(fd, TCSAFLUSH, &original); + fputs("\n", stdout); + fflush(stdout); + } + + if (result != NULL) + { + length = strlen(buffer); + if (length > 0 && buffer[length - 1] == '\n') + { + buffer[length - 1] = '\0'; + *password = buffer; + return true; + } + } + + explicit_bzero(buffer, buffer_size); + SOPC_Free(buffer); + return false; +} diff --git a/netutils/s2opc/port/nuttx/p_sopc_atomic.c b/netutils/s2opc/port/nuttx/p_sopc_atomic.c new file mode 100644 index 000000000..e31836e66 --- /dev/null +++ b/netutils/s2opc/port/nuttx/p_sopc_atomic.c @@ -0,0 +1,77 @@ +/**************************************************************************** + * apps/netutils/s2opc/port/nuttx/p_sopc_atomic.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to Systerel under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. Systerel licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the + * License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "sopc_atomic.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: SOPC_Atomic_Int_Get + ****************************************************************************/ + +int32_t SOPC_Atomic_Int_Get(FAR int32_t *atomic) +{ + return __atomic_load_n(atomic, __ATOMIC_SEQ_CST); +} + +/**************************************************************************** + * Name: SOPC_Atomic_Int_Set + ****************************************************************************/ + +void SOPC_Atomic_Int_Set(FAR int32_t *atomic, int32_t value) +{ + __atomic_store_n(atomic, value, __ATOMIC_SEQ_CST); +} + +/**************************************************************************** + * Name: SOPC_Atomic_Int_Add + ****************************************************************************/ + +int32_t SOPC_Atomic_Int_Add(FAR int32_t *atomic, int32_t value) +{ + return __atomic_fetch_add(atomic, value, __ATOMIC_SEQ_CST); +} + +/**************************************************************************** + * Name: SOPC_Atomic_Ptr_Get + ****************************************************************************/ + +FAR void *SOPC_Atomic_Ptr_Get(FAR void **atomic) +{ + return __atomic_load_n(atomic, __ATOMIC_SEQ_CST); +} + +/**************************************************************************** + * Name: SOPC_Atomic_Ptr_Set + ****************************************************************************/ + +void SOPC_Atomic_Ptr_Set(FAR void **atomic, FAR void *value) +{ + __atomic_store_n(atomic, value, __ATOMIC_SEQ_CST); +} diff --git a/netutils/s2opc/port/nuttx/p_sopc_date_time.c b/netutils/s2opc/port/nuttx/p_sopc_date_time.c new file mode 100644 index 000000000..d79e3ca8c --- /dev/null +++ b/netutils/s2opc/port/nuttx/p_sopc_date_time.c @@ -0,0 +1,83 @@ +/**************************************************************************** + * apps/netutils/s2opc/port/nuttx/p_sopc_date_time.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to Systerel under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. Systerel licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the + * License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "sopc_date_time.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: SOPC_Time_GetCurrentTimeUTC + ****************************************************************************/ + +SOPC_DateTime SOPC_Time_GetCurrentTimeUTC(void) +{ + struct timespec current; + int64_t date_time; + int64_t units_100ns; + + if (clock_gettime(CLOCK_REALTIME, ¤t) < 0) + { + return 0; + } + + date_time = 0; + units_100ns = current.tv_nsec / 100; + if (SOPC_Time_FromUnixTime(current.tv_sec, &date_time) != SOPC_STATUS_OK || + INT64_MAX - date_time < units_100ns) + { + return INT64_MAX; + } + + return date_time + units_100ns; +} + +/**************************************************************************** + * Name: SOPC_Time_Breakdown_Local + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Time_Breakdown_Local(SOPC_Unix_Time time, + FAR struct tm *result) +{ + return localtime_r(&time, result) == NULL ? SOPC_STATUS_NOK : + SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: SOPC_Time_Breakdown_UTC + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Time_Breakdown_UTC(SOPC_Unix_Time time, + FAR struct tm *result) +{ + return gmtime_r(&time, result) == NULL ? SOPC_STATUS_NOK : + SOPC_STATUS_OK; +} diff --git a/netutils/s2opc/port/nuttx/p_sopc_eth_sockets.c b/netutils/s2opc/port/nuttx/p_sopc_eth_sockets.c new file mode 100644 index 000000000..0e4eb008a --- /dev/null +++ b/netutils/s2opc/port/nuttx/p_sopc_eth_sockets.c @@ -0,0 +1,678 @@ +/**************************************************************************** + * apps/netutils/s2opc/port/nuttx/p_sopc_eth_sockets.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include "sopc_eth_sockets.h" + +#ifdef CONFIG_S2OPC_ETHERNET + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "p_sopc_sockets.h" +#include "sopc_mem_alloc.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct SOPC_ETH_Socket_ReceiveAddressInfo +{ + struct sockaddr_ll address; + bool multicast; + bool filter_destination; + uint8_t destination[ETH_ALEN]; + bool filter_source; + uint8_t source[ETH_ALEN]; +}; + +struct SOPC_ETH_Socket_SendAddressInfo +{ + struct sockaddr_ll address; + uint8_t source[ETH_ALEN]; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: s2opc_eth_hex_value + ****************************************************************************/ + +static int s2opc_eth_hex_value(char value) +{ + if (value >= '0' && value <= '9') + { + return value - '0'; + } + + if (value >= 'a' && value <= 'f') + { + return value - 'a' + 10; + } + + if (value >= 'A' && value <= 'F') + { + return value - 'A' + 10; + } + + return -1; +} + +/**************************************************************************** + * Name: s2opc_eth_would_block + ****************************************************************************/ + +static bool s2opc_eth_would_block(int error) +{ +#if EWOULDBLOCK == EAGAIN + return error == EAGAIN; +#else + return error == EAGAIN || error == EWOULDBLOCK; +#endif +} + +/**************************************************************************** + * Name: s2opc_eth_parse_address + ****************************************************************************/ + +static bool s2opc_eth_parse_address(FAR uint8_t *address, + FAR const char *value) +{ + int high; + int low; + int index; + + if (address == NULL || value == NULL || strlen(value) != 17) + { + return false; + } + + for (index = 0; index < ETH_ALEN; index++) + { + high = s2opc_eth_hex_value(value[index * 3]); + low = s2opc_eth_hex_value(value[index * 3 + 1]); + if (high < 0 || low < 0 || + (index < ETH_ALEN - 1 && value[index * 3 + 2] != '-' && + value[index * 3 + 2] != ':')) + { + return false; + } + + address[index] = (uint8_t)((high << 4) | low); + } + + return true; +} + +/**************************************************************************** + * Name: s2opc_eth_get_interface + ****************************************************************************/ + +static bool s2opc_eth_get_interface(FAR const char *interface_name, + FAR int *interface_index, + FAR uint8_t *source) +{ + struct ifreq request; + unsigned int index; + int socket_fd; + int ret; + + if (interface_name == NULL || interface_index == NULL) + { + return false; + } + + index = if_nametoindex(interface_name); + if (index == 0 || index > INT_MAX) + { + return false; + } + + *interface_index = (int)index; + if (source == NULL) + { + return true; + } + + socket_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (socket_fd < 0) + { + return false; + } + + memset(&request, 0, sizeof(request)); + strlcpy(request.ifr_name, interface_name, sizeof(request.ifr_name)); + ret = ioctl(socket_fd, SIOCGIFHWADDR, + (unsigned long)((uintptr_t)&request)); + close(socket_fd); + if (ret < 0) + { + return false; + } + + memcpy(source, request.ifr_hwaddr.sa_data, ETH_ALEN); + return true; +} + +/**************************************************************************** + * Name: s2opc_eth_set_nonblocking + ****************************************************************************/ + +static bool s2opc_eth_set_nonblocking(int socket_fd) +{ + int flags; + + flags = fcntl(socket_fd, F_GETFL, 0); + if (flags < 0) + { + return false; + } + + return fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK) == 0; +} + +/**************************************************************************** + * Name: s2opc_eth_add_membership + ****************************************************************************/ + +static SOPC_ReturnStatus s2opc_eth_add_membership( + int socket_fd, + FAR const SOPC_ETH_Socket_ReceiveAddressInfo *receive_address) +{ +#if defined(CONFIG_NET_MCASTGROUP) && defined(CONFIG_NET_PKTPROTO_OPTIONS) + struct packet_mreq request; + int ret; + + if (!receive_address->filter_destination) + { + return SOPC_STATUS_NOT_SUPPORTED; + } + + memset(&request, 0, sizeof(request)); + request.mr_ifindex = receive_address->address.sll_ifindex; + request.mr_type = PACKET_MR_MULTICAST; + request.mr_alen = ETH_ALEN; + memcpy(request.mr_address, receive_address->destination, ETH_ALEN); + + ret = setsockopt(socket_fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, + &request, sizeof(request)); + return ret == 0 ? SOPC_STATUS_OK : SOPC_STATUS_NOK; +#else + (void)socket_fd; + (void)receive_address; + return SOPC_STATUS_NOT_SUPPORTED; +#endif +} + +/**************************************************************************** + * Name: s2opc_eth_create_socket + ****************************************************************************/ + +static SOPC_ReturnStatus s2opc_eth_create_socket( + FAR const struct sockaddr_ll *address, bool nonblocking, + bool bind_socket, FAR SOPC_Socket *socket_handle) +{ + FAR SOPC_Socket_Impl *socket_impl; + + socket_impl = SOPC_Calloc(1, sizeof(*socket_impl)); + if (socket_impl == NULL) + { + return SOPC_STATUS_OUT_OF_MEMORY; + } + + socket_impl->sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + socket_impl->family = AF_PACKET; + if (socket_impl->sock < 0) + { + SOPC_Free(socket_impl); + return SOPC_STATUS_NOK; + } + + if ((nonblocking && !s2opc_eth_set_nonblocking(socket_impl->sock)) || + (bind_socket && + bind(socket_impl->sock, (FAR const struct sockaddr *)address, + sizeof(*address)) < 0)) + { + SOPC_Socket_Close(&socket_impl); + return SOPC_STATUS_NOK; + } + + *socket_handle = socket_impl; + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: SOPC_ETH_Socket_CreateSendAddressInfo + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_ETH_Socket_CreateSendAddressInfo( + FAR const char *interface_name, FAR const char *destination, + FAR SOPC_ETH_Socket_SendAddressInfo **send_address) +{ + FAR SOPC_ETH_Socket_SendAddressInfo *address; + + if (interface_name == NULL || destination == NULL || + send_address == NULL) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + *send_address = NULL; + address = SOPC_Calloc(1, sizeof(*address)); + if (address == NULL) + { + return SOPC_STATUS_OUT_OF_MEMORY; + } + + address->address.sll_family = AF_PACKET; + address->address.sll_protocol = htons(ETH_P_ALL); + address->address.sll_halen = ETH_ALEN; + + if (!s2opc_eth_get_interface(interface_name, + &address->address.sll_ifindex, + address->source) || + !s2opc_eth_parse_address(address->address.sll_addr, destination)) + { + SOPC_Free(address); + return SOPC_STATUS_INVALID_PARAMETERS; + } + + *send_address = address; + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: SOPC_ETH_Socket_CreateReceiveAddressInfo + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_ETH_Socket_CreateReceiveAddressInfo( + FAR const char *interface_name, bool multicast, + FAR const char *destination, FAR const char *source, + FAR SOPC_ETH_Socket_ReceiveAddressInfo **receive_address) +{ + FAR SOPC_ETH_Socket_ReceiveAddressInfo *address; + + if (interface_name == NULL || receive_address == NULL) + { + return interface_name == NULL ? SOPC_STATUS_NOT_SUPPORTED : + SOPC_STATUS_INVALID_PARAMETERS; + } + + *receive_address = NULL; + address = SOPC_Calloc(1, sizeof(*address)); + if (address == NULL) + { + return SOPC_STATUS_OUT_OF_MEMORY; + } + + address->address.sll_family = AF_PACKET; + address->address.sll_protocol = htons(ETH_P_ALL); + address->multicast = multicast; + address->filter_destination = destination != NULL; + address->filter_source = source != NULL; + + if (!s2opc_eth_get_interface(interface_name, + &address->address.sll_ifindex, NULL) || + (destination != NULL && + !s2opc_eth_parse_address(address->destination, destination)) || + (source != NULL && + !s2opc_eth_parse_address(address->source, source)) || + (multicast && destination != NULL && + (address->destination[0] & 1) == 0)) + { + SOPC_Free(address); + return SOPC_STATUS_INVALID_PARAMETERS; + } + + *receive_address = address; + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: SOPC_ETH_Socket_CreateToReceive + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_ETH_Socket_CreateToReceive( + FAR SOPC_ETH_Socket_ReceiveAddressInfo *receive_address, + bool nonblocking, FAR SOPC_Socket *socket_handle) +{ + SOPC_ReturnStatus status; + + if (receive_address == NULL || socket_handle == NULL) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + *socket_handle = SOPC_INVALID_SOCKET; + status = s2opc_eth_create_socket(&receive_address->address, nonblocking, + true, socket_handle); + if (status == SOPC_STATUS_OK && receive_address->multicast) + { + status = s2opc_eth_add_membership((*socket_handle)->sock, + receive_address); + if (status != SOPC_STATUS_OK) + { + SOPC_ETH_Socket_Close(socket_handle); + } + } + + return status; +} + +/**************************************************************************** + * Name: SOPC_ETH_Socket_CreateToSend + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_ETH_Socket_CreateToSend( + FAR SOPC_ETH_Socket_SendAddressInfo *send_address, + bool nonblocking, FAR SOPC_Socket *socket_handle) +{ + if (send_address == NULL || socket_handle == NULL) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + *socket_handle = SOPC_INVALID_SOCKET; + return s2opc_eth_create_socket(&send_address->address, nonblocking, + true, socket_handle); +} + +/**************************************************************************** + * Name: SOPC_ETH_Socket_SendTo + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_ETH_Socket_SendTo( + SOPC_Socket socket_handle, + FAR const SOPC_ETH_Socket_SendAddressInfo *send_address, + uint16_t ethertype, FAR SOPC_Buffer *buffer) +{ + FAR SOPC_Buffer *frame; + SOPC_ReturnStatus status; + uint16_t network_ethertype; + ssize_t sent; + int error; + + if (socket_handle == SOPC_INVALID_SOCKET || send_address == NULL || + buffer == NULL || buffer->position != 0) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + frame = SOPC_Buffer_Create(ETHERNET_HEADER_SIZE + buffer->length); + if (frame == NULL) + { + return SOPC_STATUS_OUT_OF_MEMORY; + } + + status = SOPC_Buffer_Write(frame, send_address->address.sll_addr, + ETH_ALEN); + if (status == SOPC_STATUS_OK) + { + status = SOPC_Buffer_Write(frame, send_address->source, ETH_ALEN); + } + + if (status == SOPC_STATUS_OK) + { + network_ethertype = htons(ethertype); + status = SOPC_Buffer_Write(frame, + (FAR const uint8_t *)&network_ethertype, + sizeof(network_ethertype)); + } + + if (status == SOPC_STATUS_OK) + { + status = SOPC_Buffer_Write(frame, buffer->data, buffer->length); + } + + if (status == SOPC_STATUS_OK) + { + do + { + sent = send(socket_handle->sock, frame->data, frame->length, 0); + } + while (sent < 0 && errno == EINTR); + + error = errno; + if (sent < 0) + { + status = s2opc_eth_would_block(error) ? + SOPC_STATUS_WOULD_BLOCK : SOPC_STATUS_NOK; + } + else if ((uint32_t)sent != frame->length) + { + status = SOPC_STATUS_WOULD_BLOCK; + } + } + + SOPC_Buffer_Delete(frame); + return status; +} + +/**************************************************************************** + * Name: SOPC_ETH_Socket_ReceiveFrom + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_ETH_Socket_ReceiveFrom( + SOPC_Socket socket_handle, + FAR const SOPC_ETH_Socket_ReceiveAddressInfo *receive_address, + bool check_ethertype, uint16_t ethertype, FAR SOPC_Buffer *buffer) +{ + uint16_t network_ethertype; + ssize_t received; + + if (socket_handle == SOPC_INVALID_SOCKET || receive_address == NULL || + buffer == NULL || buffer->current_size < ETHERNET_HEADER_SIZE) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + for (; ; ) + { + do + { + received = recv(socket_handle->sock, buffer->data, + buffer->current_size, 0); + } + while (received < 0 && errno == EINTR); + + if (received < 0) + { + return s2opc_eth_would_block(errno) ? + SOPC_STATUS_WOULD_BLOCK : SOPC_STATUS_NOK; + } + + buffer->length = (uint32_t)received; + if (buffer->length < ETHERNET_HEADER_SIZE) + { + return SOPC_STATUS_NOK; + } + + if (receive_address->filter_destination && + memcmp(receive_address->destination, buffer->data, + ETH_ALEN) != 0) + { + continue; + } + + if (receive_address->filter_source && + memcmp(receive_address->source, buffer->data + ETH_ALEN, + ETH_ALEN) != 0) + { + continue; + } + + memcpy(&network_ethertype, buffer->data + 2 * ETH_ALEN, + sizeof(network_ethertype)); + if (check_ethertype && ntohs(network_ethertype) != ethertype) + { + continue; + } + + return buffer->length == buffer->current_size ? + SOPC_STATUS_OUT_OF_MEMORY : SOPC_STATUS_OK; + } +} + +/**************************************************************************** + * Name: SOPC_ETH_Socket_Close + ****************************************************************************/ + +void SOPC_ETH_Socket_Close(FAR SOPC_Socket *socket_handle) +{ + SOPC_Socket_Close(socket_handle); +} + +#else /* CONFIG_S2OPC_ETHERNET */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: SOPC_ETH_Socket_CreateSendAddressInfo + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_ETH_Socket_CreateSendAddressInfo( + FAR const char *interface_name, FAR const char *destination, + FAR SOPC_ETH_Socket_SendAddressInfo **send_address) +{ + (void)interface_name; + (void)destination; + (void)send_address; + return SOPC_STATUS_NOT_SUPPORTED; +} + +/**************************************************************************** + * Name: SOPC_ETH_Socket_CreateReceiveAddressInfo + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_ETH_Socket_CreateReceiveAddressInfo( + FAR const char *interface_name, bool multicast, + FAR const char *destination, FAR const char *source, + FAR SOPC_ETH_Socket_ReceiveAddressInfo **receive_address) +{ + (void)interface_name; + (void)multicast; + (void)destination; + (void)source; + (void)receive_address; + return SOPC_STATUS_NOT_SUPPORTED; +} + +/**************************************************************************** + * Name: SOPC_ETH_Socket_CreateToReceive + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_ETH_Socket_CreateToReceive( + FAR SOPC_ETH_Socket_ReceiveAddressInfo *receive_address, + bool nonblocking, FAR SOPC_Socket *socket_handle) +{ + (void)receive_address; + (void)nonblocking; + (void)socket_handle; + return SOPC_STATUS_NOT_SUPPORTED; +} + +/**************************************************************************** + * Name: SOPC_ETH_Socket_CreateToSend + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_ETH_Socket_CreateToSend( + FAR SOPC_ETH_Socket_SendAddressInfo *send_address, + bool nonblocking, FAR SOPC_Socket *socket_handle) +{ + (void)send_address; + (void)nonblocking; + (void)socket_handle; + return SOPC_STATUS_NOT_SUPPORTED; +} + +/**************************************************************************** + * Name: SOPC_ETH_Socket_SendTo + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_ETH_Socket_SendTo( + SOPC_Socket socket_handle, + FAR const SOPC_ETH_Socket_SendAddressInfo *send_address, + uint16_t ethertype, FAR SOPC_Buffer *buffer) +{ + (void)socket_handle; + (void)send_address; + (void)ethertype; + (void)buffer; + return SOPC_STATUS_NOT_SUPPORTED; +} + +/**************************************************************************** + * Name: SOPC_ETH_Socket_ReceiveFrom + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_ETH_Socket_ReceiveFrom( + SOPC_Socket socket_handle, + FAR const SOPC_ETH_Socket_ReceiveAddressInfo *receive_address, + bool check_ethertype, uint16_t ethertype, FAR SOPC_Buffer *buffer) +{ + (void)socket_handle; + (void)receive_address; + (void)check_ethertype; + (void)ethertype; + (void)buffer; + return SOPC_STATUS_NOT_SUPPORTED; +} + +/**************************************************************************** + * Name: SOPC_ETH_Socket_Close + ****************************************************************************/ + +void SOPC_ETH_Socket_Close(FAR SOPC_Socket *socket_handle) +{ + if (socket_handle != NULL) + { + *socket_handle = SOPC_INVALID_SOCKET; + } +} + +#endif /* CONFIG_S2OPC_ETHERNET */ diff --git a/netutils/s2opc/port/nuttx/p_sopc_filesystem.c b/netutils/s2opc/port/nuttx/p_sopc_filesystem.c new file mode 100644 index 000000000..41700b547 --- /dev/null +++ b/netutils/s2opc/port/nuttx/p_sopc_filesystem.c @@ -0,0 +1,225 @@ +/**************************************************************************** + * apps/netutils/s2opc/port/nuttx/p_sopc_filesystem.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to Systerel under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. Systerel licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the + * License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "sopc_filesystem.h" +#include "sopc_helper_string.h" +#include "sopc_mem_alloc.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: s2opc_file_path + ****************************************************************************/ + +static SOPC_ReturnStatus +s2opc_file_path(FAR const char *directory, FAR const char *name, + FAR char **path) +{ + FAR char *prefix; + SOPC_ReturnStatus status; + + prefix = NULL; + status = SOPC_StrConcat(directory, "/", &prefix); + if (status == SOPC_STATUS_OK) + { + status = SOPC_StrConcat(prefix, name, path); + } + + SOPC_Free(prefix); + return status; +} + +/**************************************************************************** + * Name: s2opc_file_path_free + ****************************************************************************/ + +static void s2opc_file_path_free(FAR void *data) +{ + if (data != NULL) + { + SOPC_Free(*(FAR char **)data); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: SOPC_FileSystem_mkdir + ****************************************************************************/ + +SOPC_FileSystem_CreationResult +SOPC_FileSystem_mkdir(FAR const char *directory_path) +{ + if (mkdir(directory_path, + S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0) + { + return SOPC_FileSystem_Creation_OK; + } + + switch (errno) + { + case ENOENT: + return SOPC_FileSystem_Creation_Error_PathPrefixInvalid; + + case EEXIST: + return SOPC_FileSystem_Creation_Error_PathAlreadyExists; + + case EACCES: + return SOPC_FileSystem_Creation_Error_PathPermisionDenied; + + default: + return SOPC_FileSystem_Creation_Error_PathResolutionIssue; + } +} + +/**************************************************************************** + * Name: SOPC_FileSystem_rmdir + ****************************************************************************/ + +SOPC_FileSystem_RemoveResult +SOPC_FileSystem_rmdir(FAR const char *directory_path) +{ + if (rmdir(directory_path) == 0) + { + return SOPC_FileSystem_Remove_OK; + } + + switch (errno) + { + case ENOENT: + return SOPC_FileSystem_Remove_Error_PathInvalid; + + case ENOTEMPTY: + case EEXIST: + return SOPC_FileSystem_Remove_Error_PathNotEmpty; + + case EACCES: + case EPERM: + case EBUSY: + return SOPC_FileSystem_Remove_Error_PathPermisionDenied; + + default: + return SOPC_FileSystem_Remove_Error_UnknownIssue; + } +} + +/**************************************************************************** + * Name: SOPC_FileSystem_GetDirFilePaths + ****************************************************************************/ + +SOPC_FileSystem_GetDirResult +SOPC_FileSystem_GetDirFilePaths(FAR const char *directory_path, + FAR SOPC_Array **file_paths) +{ + FAR SOPC_Array *paths; + FAR struct dirent *entry; + FAR char *path; + struct stat statbuf; + FAR DIR *directory; + SOPC_ReturnStatus status; + + if (directory_path == NULL || file_paths == NULL) + { + return SOPC_FileSystem_GetDir_Error_InvalidParameters; + } + + *file_paths = NULL; + directory = opendir(directory_path); + if (directory == NULL) + { + return SOPC_FileSystem_GetDir_Error_PathInvalid; + } + + paths = SOPC_Array_Create(sizeof(FAR char *), 0, s2opc_file_path_free); + if (paths == NULL) + { + closedir(directory); + return SOPC_FileSystem_GetDir_Error_UnknownIssue; + } + + status = SOPC_STATUS_OK; + errno = 0; + while (status == SOPC_STATUS_OK && (entry = readdir(directory)) != NULL) + { + path = NULL; + status = s2opc_file_path(directory_path, entry->d_name, &path); + if (status == SOPC_STATUS_OK && stat(path, &statbuf) < 0) + { + status = SOPC_STATUS_NOK; + } + + if (status == SOPC_STATUS_OK && S_ISREG(statbuf.st_mode)) + { + if (SOPC_Array_Append(paths, path)) + { + path = NULL; + } + else + { + status = SOPC_STATUS_OUT_OF_MEMORY; + } + } + + SOPC_Free(path); + errno = 0; + } + + if (errno != 0) + { + status = SOPC_STATUS_NOK; + } + + closedir(directory); + if (status != SOPC_STATUS_OK) + { + SOPC_Array_Delete(paths); + return SOPC_FileSystem_GetDir_Error_UnknownIssue; + } + + *file_paths = paths; + return SOPC_FileSystem_GetDir_OK; +} + +/**************************************************************************** + * Name: SOPC_FileSystem_fmemopen + ****************************************************************************/ + +FAR FILE *SOPC_FileSystem_fmemopen(FAR void *buffer, size_t size, + FAR const char *mode) +{ + return fmemopen(buffer, size, mode); +} diff --git a/netutils/s2opc/port/nuttx/p_sopc_mem_alloc.c b/netutils/s2opc/port/nuttx/p_sopc_mem_alloc.c new file mode 100644 index 000000000..6cbdc2048 --- /dev/null +++ b/netutils/s2opc/port/nuttx/p_sopc_mem_alloc.c @@ -0,0 +1,72 @@ +/**************************************************************************** + * apps/netutils/s2opc/port/nuttx/p_sopc_mem_alloc.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to Systerel under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. Systerel licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the + * License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include "sopc_macros.h" +#include "sopc_mem_alloc.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: SOPC_Malloc + ****************************************************************************/ + +FAR void *SOPC_Malloc(size_t size) +{ + return malloc(size); +} + +/**************************************************************************** + * Name: SOPC_Free + ****************************************************************************/ + +void SOPC_Free(FAR void *ptr) +{ + free(ptr); +} + +/**************************************************************************** + * Name: SOPC_Calloc + ****************************************************************************/ + +FAR void *SOPC_Calloc(size_t count, size_t size) +{ + return calloc(count, size); +} + +/**************************************************************************** + * Name: SOPC_Realloc + ****************************************************************************/ + +FAR void *SOPC_Realloc(FAR void *ptr, size_t old_size, size_t new_size) +{ + SOPC_UNUSED_ARG(old_size); + return realloc(ptr, new_size); +} diff --git a/netutils/s2opc/port/nuttx/p_sopc_random.c b/netutils/s2opc/port/nuttx/p_sopc_random.c new file mode 100644 index 000000000..e4cff0e69 --- /dev/null +++ b/netutils/s2opc/port/nuttx/p_sopc_random.c @@ -0,0 +1,87 @@ +/**************************************************************************** + * apps/netutils/s2opc/port/nuttx/p_sopc_random.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include + +#include "sopc_buffer.h" +#include "sopc_random.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: SOPC_GetRandom + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_GetRandom(FAR SOPC_Buffer *buffer, uint32_t length) +{ + uint8_t random_data[32]; + SOPC_ReturnStatus status; + uint32_t remaining; + + if (buffer == NULL || length == 0) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + remaining = length; + while (remaining > 0) + { + uint32_t chunk = remaining > sizeof(random_data) ? + sizeof(random_data) : remaining; + ssize_t nread = getrandom(random_data, chunk, 0); + + if (nread < 0) + { + if (errno == EINTR) + { + continue; + } + + return errno == EAGAIN ? SOPC_STATUS_WOULD_BLOCK : SOPC_STATUS_NOK; + } + + if (nread == 0) + { + return SOPC_STATUS_NOK; + } + + status = SOPC_Buffer_Write(buffer, random_data, nread); + if (status != SOPC_STATUS_OK) + { + return status; + } + + remaining -= nread; + } + + return SOPC_STATUS_OK; +} diff --git a/netutils/s2opc/port/nuttx/p_sopc_sockets.c b/netutils/s2opc/port/nuttx/p_sopc_sockets.c new file mode 100644 index 000000000..9c319c795 --- /dev/null +++ b/netutils/s2opc/port/nuttx/p_sopc_sockets.c @@ -0,0 +1,874 @@ +/**************************************************************************** + * apps/netutils/s2opc/port/nuttx/p_sopc_sockets.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to Systerel under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. Systerel licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the + * License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "p_sopc_sockets.h" +#include "sopc_mem_alloc.h" +#include "sopc_raw_sockets.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define S2OPC_DSEC_PER_SEC 10 + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: s2opc_socket_configure + ****************************************************************************/ + +static SOPC_ReturnStatus s2opc_socket_configure(int sock, bool nonblocking) +{ + int enabled = 1; + int flags; + + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &enabled, + sizeof(enabled)) < 0) + { + return SOPC_STATUS_NOK; + } + + if (!nonblocking) + { + return SOPC_STATUS_OK; + } + + flags = fcntl(sock, F_GETFL); + if (flags < 0 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) + { + return SOPC_STATUS_NOK; + } + + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: s2opc_socket_would_block + ****************************************************************************/ + +static bool s2opc_socket_would_block(int error) +{ +#if EWOULDBLOCK == EAGAIN + return error == EAGAIN; +#else + return error == EAGAIN || error == EWOULDBLOCK; +#endif +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: SOPC_Socket_Network_Enable_Keepalive + ****************************************************************************/ + +SOPC_ReturnStatus +SOPC_Socket_Network_Enable_Keepalive(SOPC_Socket sock, + unsigned int first_probe_delay, + unsigned int interval, + unsigned int counter) +{ + int enabled = 1; + int idle; + int keep_interval; + int keep_count; + + if (sock == SOPC_INVALID_SOCKET || + first_probe_delay > UINT16_MAX / S2OPC_DSEC_PER_SEC || + interval > UINT16_MAX / S2OPC_DSEC_PER_SEC || counter > UINT8_MAX) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + /* NuttX expresses TCP_KEEPIDLE and TCP_KEEPINTVL in deciseconds. */ + + idle = first_probe_delay * S2OPC_DSEC_PER_SEC; + keep_interval = interval * S2OPC_DSEC_PER_SEC; + keep_count = counter; + + if (setsockopt(sock->sock, SOL_SOCKET, SO_KEEPALIVE, &enabled, + sizeof(enabled)) < 0 || + setsockopt(sock->sock, IPPROTO_TCP, TCP_KEEPIDLE, &idle, + sizeof(idle)) < 0 || + setsockopt(sock->sock, IPPROTO_TCP, TCP_KEEPINTVL, &keep_interval, + sizeof(keep_interval)) < 0 || + setsockopt(sock->sock, IPPROTO_TCP, TCP_KEEPCNT, &keep_count, + sizeof(keep_count)) < 0) + { + return SOPC_STATUS_NOK; + } + + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: SOPC_Socket_Network_Disable_Keepalive + ****************************************************************************/ + +SOPC_ReturnStatus +SOPC_Socket_Network_Disable_Keepalive(SOPC_Socket sock) +{ + int enabled = 0; + + if (sock == SOPC_INVALID_SOCKET) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + return setsockopt(sock->sock, SOL_SOCKET, SO_KEEPALIVE, &enabled, + sizeof(enabled)) == 0 ? SOPC_STATUS_OK : SOPC_STATUS_NOK; +} + +/**************************************************************************** + * Name: SOPC_Socket_Network_Initialize + ****************************************************************************/ + +bool SOPC_Socket_Network_Initialize(void) +{ + return true; +} + +/**************************************************************************** + * Name: SOPC_Socket_Network_Clear + ****************************************************************************/ + +bool SOPC_Socket_Network_Clear(void) +{ + return true; +} + +/**************************************************************************** + * Name: SOPC_Socket_AddrInfo_Get + ****************************************************************************/ + +SOPC_ReturnStatus +SOPC_Socket_AddrInfo_Get(FAR const char *hostname, FAR const char *port, + FAR SOPC_Socket_AddressInfo **addrs) +{ + SOPC_Socket_AddressInfo hints; + FAR struct addrinfo *result; + int ret; + + if ((hostname == NULL && port == NULL) || addrs == NULL) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + *addrs = NULL; + memset(&hints, 0, sizeof(hints)); + + /* S2OPC opens one wildcard listener and assumes an IPv6 listener accepts + * IPv4 connections. NuttX does not implement IPV6_V6ONLY or dual-stack + * TCP listeners, so use the IPv4 wildcard when IPv4 is available. An + * explicit IPv6 address continues to resolve through AF_UNSPEC. + */ + +#ifdef CONFIG_NET_IPv4 + hints.addrinfo.ai_family = hostname == NULL ? AF_INET : AF_UNSPEC; +#else + hints.addrinfo.ai_family = AF_UNSPEC; +#endif + hints.addrinfo.ai_socktype = SOCK_STREAM; + hints.addrinfo.ai_flags = AI_PASSIVE; + + result = NULL; + ret = getaddrinfo(hostname, port, &hints.addrinfo, &result); + if (ret != 0) + { + return SOPC_STATUS_NOK; + } + + *addrs = (FAR SOPC_Socket_AddressInfo *)result; + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: SOPC_Socket_AddrInfo_IterNext + ****************************************************************************/ + +FAR SOPC_Socket_AddressInfo * +SOPC_Socket_AddrInfo_IterNext(FAR SOPC_Socket_AddressInfo *addr) +{ + if (addr == NULL) + { + return NULL; + } + + return (FAR SOPC_Socket_AddressInfo *)addr->addrinfo.ai_next; +} + +/**************************************************************************** + * Name: SOPC_Socket_AddrInfo_IsIPV6 + ****************************************************************************/ + +uint8_t +SOPC_Socket_AddrInfo_IsIPV6(FAR const SOPC_Socket_AddressInfo *addr) +{ + return addr != NULL && addr->addrinfo.ai_family == AF_INET6; +} + +/**************************************************************************** + * Name: SOPC_Socket_AddrInfoDelete + ****************************************************************************/ + +void SOPC_Socket_AddrInfoDelete(FAR SOPC_Socket_AddressInfo **addrs) +{ + if (addrs != NULL && *addrs != NULL) + { + freeaddrinfo(&(*addrs)->addrinfo); + *addrs = NULL; + } +} + +/**************************************************************************** + * Name: SOPC_SocketAddress_Delete + ****************************************************************************/ + +void SOPC_SocketAddress_Delete(FAR SOPC_Socket_Address **addr) +{ + if (addr != NULL && *addr != NULL) + { + SOPC_Free((*addr)->address.ai_addr); + SOPC_Free(*addr); + *addr = NULL; + } +} + +/**************************************************************************** + * Name: SOPC_Socket_CopyAddress + ****************************************************************************/ + +FAR SOPC_Socket_Address * +SOPC_Socket_CopyAddress(FAR SOPC_Socket_AddressInfo *addr) +{ + FAR SOPC_Socket_Address *copy; + + if (addr == NULL || addr->addrinfo.ai_addr == NULL) + { + return NULL; + } + + copy = SOPC_Calloc(1, sizeof(*copy)); + if (copy == NULL) + { + return NULL; + } + + copy->address.ai_addr = SOPC_Malloc(addr->addrinfo.ai_addrlen); + if (copy->address.ai_addr == NULL) + { + SOPC_Free(copy); + return NULL; + } + + memcpy(copy->address.ai_addr, addr->addrinfo.ai_addr, + addr->addrinfo.ai_addrlen); + copy->address.ai_addrlen = addr->addrinfo.ai_addrlen; + copy->address.ai_family = addr->addrinfo.ai_family; + return copy; +} + +/**************************************************************************** + * Name: SOPC_Socket_GetPeerAddress + ****************************************************************************/ + +FAR SOPC_Socket_Address *SOPC_Socket_GetPeerAddress(SOPC_Socket sock) +{ + FAR SOPC_Socket_Address *address; + FAR struct sockaddr_storage *storage; + socklen_t length; + + if (sock == SOPC_INVALID_SOCKET) + { + return NULL; + } + + address = SOPC_Calloc(1, sizeof(*address)); + storage = SOPC_Calloc(1, sizeof(*storage)); + if (address == NULL || storage == NULL) + { + SOPC_Free(storage); + SOPC_Free(address); + return NULL; + } + + length = sizeof(*storage); + if (getpeername(sock->sock, (FAR struct sockaddr *)storage, &length) < 0) + { + SOPC_Free(storage); + SOPC_Free(address); + return NULL; + } + + address->address.ai_family = storage->ss_family; + address->address.ai_addrlen = length; + address->address.ai_addr = (FAR struct sockaddr *)storage; + return address; +} + +/**************************************************************************** + * Name: SOPC_SocketAddress_GetNameInfo + ****************************************************************************/ + +SOPC_ReturnStatus +SOPC_SocketAddress_GetNameInfo(FAR const SOPC_Socket_Address *addr, + FAR char **host, FAR char **service) +{ + FAR char *host_result = NULL; + FAR char *service_result = NULL; + socklen_t host_length = 0; + socklen_t service_length = 0; + int flags = 0; + int ret; + + if (addr == NULL || (host == NULL && service == NULL)) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + if (host != NULL) + { + *host = NULL; + host_result = SOPC_Calloc(NI_MAXHOST, sizeof(char)); + if (host_result == NULL) + { + return SOPC_STATUS_OUT_OF_MEMORY; + } + + host_length = NI_MAXHOST; + flags |= NI_NUMERICHOST; + } + + if (service != NULL) + { + *service = NULL; + service_result = SOPC_Calloc(NI_MAXSERV, sizeof(char)); + if (service_result == NULL) + { + SOPC_Free(host_result); + return SOPC_STATUS_OUT_OF_MEMORY; + } + + service_length = NI_MAXSERV; + flags |= NI_NUMERICSERV; + } + + ret = getnameinfo(addr->address.ai_addr, addr->address.ai_addrlen, + host_result, host_length, service_result, + service_length, flags); + if (ret != 0) + { + SOPC_Free(host_result); + SOPC_Free(service_result); + return SOPC_STATUS_NOK; + } + + if (host != NULL) + { + *host = host_result; + } + + if (service != NULL) + { + *service = service_result; + } + + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: SOPC_Socket_Clear + ****************************************************************************/ + +void SOPC_Socket_Clear(FAR SOPC_Socket *sock) +{ + if (sock != NULL) + { + *sock = SOPC_INVALID_SOCKET; + } +} + +/**************************************************************************** + * Name: SOPC_Socket_CreateNew + ****************************************************************************/ + +SOPC_ReturnStatus +SOPC_Socket_CreateNew(FAR SOPC_Socket_AddressInfo *addr, bool reuse, + bool nonblocking, FAR SOPC_Socket *sock) +{ + FAR SOPC_Socket_Impl *implementation; + int enabled = 1; + + if (addr == NULL || sock == NULL) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + *sock = SOPC_INVALID_SOCKET; + implementation = SOPC_Calloc(1, sizeof(*implementation)); + if (implementation == NULL) + { + return SOPC_STATUS_OUT_OF_MEMORY; + } + + implementation->sock = socket(addr->addrinfo.ai_family, + addr->addrinfo.ai_socktype, + addr->addrinfo.ai_protocol); + implementation->family = addr->addrinfo.ai_family; + if (implementation->sock < 0 || + s2opc_socket_configure(implementation->sock, nonblocking) != + SOPC_STATUS_OK || + (reuse && setsockopt(implementation->sock, SOL_SOCKET, SO_REUSEADDR, + &enabled, sizeof(enabled)) < 0)) + { + if (implementation->sock >= 0) + { + close(implementation->sock); + } + + SOPC_Free(implementation); + return SOPC_STATUS_NOK; + } + + *sock = implementation; + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: SOPC_Socket_Listen + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Socket_Listen(SOPC_Socket sock, + FAR SOPC_Socket_AddressInfo *addr) +{ + if (sock == SOPC_INVALID_SOCKET || addr == NULL) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + if (bind(sock->sock, addr->addrinfo.ai_addr, + addr->addrinfo.ai_addrlen) < 0 || + listen(sock->sock, SOMAXCONN) < 0) + { + return SOPC_STATUS_NOK; + } + + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: SOPC_Socket_Accept + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Socket_Accept(SOPC_Socket listening_sock, + bool nonblocking, + FAR SOPC_Socket *accepted_sock) +{ + FAR SOPC_Socket_Impl *implementation; + struct sockaddr_storage remote; + socklen_t length; + int ret; + + if (listening_sock == SOPC_INVALID_SOCKET || accepted_sock == NULL) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + *accepted_sock = SOPC_INVALID_SOCKET; + implementation = SOPC_Calloc(1, sizeof(*implementation)); + if (implementation == NULL) + { + return SOPC_STATUS_OUT_OF_MEMORY; + } + + length = sizeof(remote); + do + { + ret = accept(listening_sock->sock, (FAR struct sockaddr *)&remote, + &length); + } + while (ret < 0 && errno == EINTR); + + implementation->sock = ret; + if (ret < 0 || s2opc_socket_configure(ret, nonblocking) != SOPC_STATUS_OK) + { + if (ret >= 0) + { + close(ret); + } + + SOPC_Free(implementation); + return SOPC_STATUS_NOK; + } + + implementation->family = remote.ss_family; + *accepted_sock = implementation; + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: SOPC_Socket_Connect + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Socket_Connect(SOPC_Socket sock, + FAR SOPC_Socket_AddressInfo *addr) +{ + int ret; + int error; + + if (sock == SOPC_INVALID_SOCKET || addr == NULL) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + do + { + ret = connect(sock->sock, addr->addrinfo.ai_addr, + addr->addrinfo.ai_addrlen); + } + while (ret < 0 && errno == EINTR); + + error = errno; + if (ret == 0 || error == EISCONN || error == EINPROGRESS || + error == EALREADY) + { + return SOPC_STATUS_OK; + } + + return SOPC_STATUS_NOK; +} + +/**************************************************************************** + * Name: SOPC_Socket_ConnectToLocal + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Socket_ConnectToLocal(SOPC_Socket from, + SOPC_Socket to) +{ + SOPC_Socket_AddressInfo addr; + struct sockaddr_storage storage; + + if (from == SOPC_INVALID_SOCKET || to == SOPC_INVALID_SOCKET) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + memset(&addr, 0, sizeof(addr)); + memset(&storage, 0, sizeof(storage)); + addr.addrinfo.ai_addr = (FAR struct sockaddr *)&storage; + addr.addrinfo.ai_addrlen = sizeof(storage); + if (getsockname(to->sock, addr.addrinfo.ai_addr, + &addr.addrinfo.ai_addrlen) < 0) + { + return SOPC_STATUS_NOK; + } + + return SOPC_Socket_Connect(from, &addr); +} + +/**************************************************************************** + * Name: SOPC_Socket_CheckAckConnect + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Socket_CheckAckConnect(SOPC_Socket sock) +{ + socklen_t length; + int error = 0; + + if (sock == SOPC_INVALID_SOCKET) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + length = sizeof(error); + if (getsockopt(sock->sock, SOL_SOCKET, SO_ERROR, &error, &length) < 0 || + error != 0) + { + return SOPC_STATUS_NOK; + } + + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: SOPC_SocketSet_Create + ****************************************************************************/ + +FAR SOPC_SocketSet *SOPC_SocketSet_Create(void) +{ + FAR SOPC_SocketSet *set; + + set = SOPC_Calloc(1, sizeof(*set)); + if (set != NULL) + { + SOPC_SocketSet_Clear(set); + } + + return set; +} + +/**************************************************************************** + * Name: SOPC_SocketSet_Delete + ****************************************************************************/ + +void SOPC_SocketSet_Delete(FAR SOPC_SocketSet **set) +{ + if (set != NULL) + { + SOPC_Free(*set); + *set = NULL; + } +} + +/**************************************************************************** + * Name: SOPC_SocketSet_Add + ****************************************************************************/ + +void SOPC_SocketSet_Add(SOPC_Socket sock, FAR SOPC_SocketSet *set) +{ + if (sock != SOPC_INVALID_SOCKET && set != NULL) + { + FD_SET(sock->sock, &set->set); + if (sock->sock > set->fdmax) + { + set->fdmax = sock->sock; + } + } +} + +/**************************************************************************** + * Name: SOPC_SocketSet_IsPresent + ****************************************************************************/ + +bool SOPC_SocketSet_IsPresent(SOPC_Socket sock, FAR SOPC_SocketSet *set) +{ + return sock != SOPC_INVALID_SOCKET && set != NULL && + FD_ISSET(sock->sock, &set->set); +} + +/**************************************************************************** + * Name: SOPC_SocketSet_Clear + ****************************************************************************/ + +void SOPC_SocketSet_Clear(FAR SOPC_SocketSet *set) +{ + if (set != NULL) + { + FD_ZERO(&set->set); + set->fdmax = 0; + } +} + +/**************************************************************************** + * Name: SOPC_Socket_WaitSocketEvents + ****************************************************************************/ + +int32_t SOPC_Socket_WaitSocketEvents(FAR SOPC_SocketSet *read_set, + FAR SOPC_SocketSet *write_set, + FAR SOPC_SocketSet *except_set, + uint32_t wait_ms) +{ + FAR struct timeval *timeout_ptr; + struct timeval timeout; + int fdmax; + int ret; + + if (read_set == NULL || write_set == NULL || except_set == NULL) + { + return -1; + } + + fdmax = read_set->fdmax; + if (write_set->fdmax > fdmax) + { + fdmax = write_set->fdmax; + } + + if (except_set->fdmax > fdmax) + { + fdmax = except_set->fdmax; + } + + if (wait_ms == 0) + { + timeout_ptr = NULL; + } + else + { + timeout.tv_sec = wait_ms / 1000; + timeout.tv_usec = (wait_ms % 1000) * 1000; + timeout_ptr = &timeout; + } + + do + { + ret = select(fdmax + 1, &read_set->set, &write_set->set, + &except_set->set, timeout_ptr); + } + while (ret < 0 && errno == EINTR); + + return ret; +} + +/**************************************************************************** + * Name: SOPC_Socket_Write + ****************************************************************************/ + +SOPC_ReturnStatus +SOPC_Socket_Write(SOPC_Socket sock, FAR const uint8_t *data, + uint32_t count, FAR uint32_t *sent_bytes) +{ + ssize_t ret; + int error; + + if (sock == SOPC_INVALID_SOCKET || data == NULL || sent_bytes == NULL || + count > INT32_MAX) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + do + { + ret = send(sock->sock, data, count, MSG_NOSIGNAL); + } + while (ret < 0 && errno == EINTR); + + error = errno; + if (ret >= 0) + { + *sent_bytes = ret; + return SOPC_STATUS_OK; + } + + *sent_bytes = 0; + return s2opc_socket_would_block(error) ? SOPC_STATUS_WOULD_BLOCK : + SOPC_STATUS_NOK; +} + +/**************************************************************************** + * Name: SOPC_Socket_Read + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Socket_Read(SOPC_Socket sock, FAR uint8_t *data, + uint32_t size, FAR uint32_t *read_count) +{ + ssize_t ret; + int error; + + if (sock == SOPC_INVALID_SOCKET || data == NULL || size == 0 || + read_count == NULL) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + do + { + ret = recv(sock->sock, data, size, 0); + } + while (ret < 0 && errno == EINTR); + + error = errno; + if (ret > 0) + { + *read_count = ret; + return SOPC_STATUS_OK; + } + + *read_count = 0; + if (ret == 0) + { + return SOPC_STATUS_CLOSED; + } + + return s2opc_socket_would_block(error) ? SOPC_STATUS_WOULD_BLOCK : + SOPC_STATUS_NOK; +} + +/**************************************************************************** + * Name: SOPC_Socket_BytesToRead + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Socket_BytesToRead(SOPC_Socket sock, + FAR uint32_t *bytes_to_read) +{ + int available; + int ret; + + if (sock == SOPC_INVALID_SOCKET || bytes_to_read == NULL) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + do + { + ret = ioctl(sock->sock, FIONREAD, &available); + } + while (ret < 0 && errno == EINTR); + + if (ret < 0 || available < 0) + { + return SOPC_STATUS_NOK; + } + + *bytes_to_read = available; + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: SOPC_Socket_Close + ****************************************************************************/ + +void SOPC_Socket_Close(FAR SOPC_Socket *sock) +{ + int ret; + + if (sock == NULL || *sock == SOPC_INVALID_SOCKET) + { + return; + } + + do + { + ret = close((*sock)->sock); + } + while (ret < 0 && errno == EINTR); + + SOPC_Free(*sock); + *sock = SOPC_INVALID_SOCKET; +} diff --git a/netutils/s2opc/port/nuttx/p_sopc_sockets.h b/netutils/s2opc/port/nuttx/p_sopc_sockets.h new file mode 100644 index 000000000..f38901caa --- /dev/null +++ b/netutils/s2opc/port/nuttx/p_sopc_sockets.h @@ -0,0 +1,64 @@ +/**************************************************************************** + * apps/netutils/s2opc/port/nuttx/p_sopc_sockets.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to Systerel under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. Systerel licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the + * License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +#ifndef __APPS_NETUTILS_S2OPC_PORT_NUTTX_P_SOPC_SOCKETS_H +#define __APPS_NETUTILS_S2OPC_PORT_NUTTX_P_SOPC_SOCKETS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct SOPC_Socket_Impl +{ + int sock; + sa_family_t family; +}; + +/* Keep addrinfo as the first member. S2OPC walks the resolver-owned list by + * casting its addrinfo elements to SOPC_Socket_AddressInfo. + */ + +struct SOPC_Socket_AddressInfo +{ + struct addrinfo addrinfo; +}; + +struct SOPC_Socket_Address +{ + struct addrinfo address; +}; + +struct SOPC_SocketSet +{ + int fdmax; + fd_set set; +}; + +#endif /* __APPS_NETUTILS_S2OPC_PORT_NUTTX_P_SOPC_SOCKETS_H */ diff --git a/netutils/s2opc/port/nuttx/p_sopc_threads.c b/netutils/s2opc/port/nuttx/p_sopc_threads.c new file mode 100644 index 000000000..30bf96c35 --- /dev/null +++ b/netutils/s2opc/port/nuttx/p_sopc_threads.c @@ -0,0 +1,473 @@ +/**************************************************************************** + * apps/netutils/s2opc/port/nuttx/p_sopc_threads.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to Systerel under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. Systerel licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the + * License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "p_sopc_threads.h" +#include "sopc_mem_alloc.h" +#include "sopc_mutexes.h" +#include "sopc_threads.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define S2OPC_NSEC_PER_SEC 1000000000l +#define S2OPC_NSEC_PER_MSEC 1000000l + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: s2opc_thread_start + ****************************************************************************/ + +static SOPC_ReturnStatus +s2opc_thread_start(FAR pthread_t *thread, + FAR void *(*entry)(FAR void *), FAR void *arg, + FAR const char *name, int priority, int affinity) +{ + struct sched_param param; + pthread_attr_t attr; +#ifdef CONFIG_SMP + cpu_set_t cpuset; +#endif + char taskname[CONFIG_TASK_NAME_SIZE + 1]; + int ret; + + ret = pthread_attr_init(&attr); + if (ret != 0) + { + return SOPC_STATUS_NOK; + } + + ret = pthread_attr_setstacksize(&attr, CONFIG_S2OPC_THREAD_STACKSIZE); + if (ret == 0) + { + param.sched_priority = priority > 0 ? priority : + SCHED_PRIORITY_DEFAULT; + ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + if (ret == 0) + { + ret = pthread_attr_setschedpolicy(&attr, priority > 0 ? + SCHED_FIFO : SCHED_RR); + } + + if (ret == 0) + { + ret = pthread_attr_setschedparam(&attr, ¶m); + } + } + +#ifdef CONFIG_SMP + if (ret == 0 && affinity >= 0) + { + CPU_ZERO(&cpuset); + CPU_SET((size_t)affinity, &cpuset); + ret = pthread_attr_setaffinity_np(&attr, sizeof(cpuset), &cpuset); + } +#endif + + if (ret == 0) + { + ret = pthread_create(thread, &attr, entry, arg); + } + + pthread_attr_destroy(&attr); + if (ret != 0) + { + return SOPC_STATUS_NOK; + } + + if (name != NULL) + { + strlcpy(taskname, name, sizeof(taskname)); + pthread_setname_np(*thread, taskname); + } + + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: s2opc_thread_create + ****************************************************************************/ + +static SOPC_ReturnStatus +s2opc_thread_create(FAR SOPC_Thread *thread, + FAR void *(*entry)(FAR void *), FAR void *arg, + FAR const char *name, int priority, int affinity) +{ + FAR SOPC_Thread_Impl *implementation; + SOPC_ReturnStatus status; + + if (thread == NULL || entry == NULL || priority < 0 || + priority > SCHED_PRIORITY_MAX || affinity >= CONFIG_SMP_NCPUS) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + *thread = SOPC_INVALID_THREAD; + implementation = SOPC_Calloc(1, sizeof(*implementation)); + if (implementation == NULL) + { + return SOPC_STATUS_OUT_OF_MEMORY; + } + + status = s2opc_thread_start(&implementation->thread, entry, arg, name, + priority, affinity); + if (status == SOPC_STATUS_OK) + { + *thread = implementation; + } + else + { + SOPC_Free(implementation); + } + + return status; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: SOPC_Condition_Init + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Condition_Init(FAR SOPC_Condition *cond) +{ + FAR SOPC_Condition_Impl *implementation; + + if (cond == NULL) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + *cond = SOPC_INVALID_COND; + implementation = SOPC_Calloc(1, sizeof(*implementation)); + if (implementation == NULL) + { + return SOPC_STATUS_OUT_OF_MEMORY; + } + + if (pthread_cond_init(&implementation->cond, NULL) != 0) + { + SOPC_Free(implementation); + return SOPC_STATUS_NOK; + } + + *cond = implementation; + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: SOPC_Condition_Clear + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Condition_Clear(FAR SOPC_Condition *cond) +{ + FAR SOPC_Condition_Impl *implementation; + + if (cond == NULL || *cond == SOPC_INVALID_COND) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + implementation = *cond; + if (pthread_cond_destroy(&implementation->cond) != 0) + { + return SOPC_STATUS_NOK; + } + + SOPC_Free(implementation); + *cond = SOPC_INVALID_COND; + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: SOPC_Condition_SignalAll + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Condition_SignalAll(FAR SOPC_Condition *cond) +{ + FAR SOPC_Condition_Impl *implementation; + + if (cond == NULL || *cond == SOPC_INVALID_COND) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + implementation = *cond; + return pthread_cond_broadcast(&implementation->cond) == 0 ? + SOPC_STATUS_OK : SOPC_STATUS_NOK; +} + +/**************************************************************************** + * Name: SOPC_Mutex_Initialization + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Mutex_Initialization(FAR SOPC_Mutex *mutex) +{ + FAR SOPC_Mutex_Impl *implementation; + pthread_mutexattr_t attr; + int ret; + + if (mutex == NULL) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + *mutex = SOPC_INVALID_MUTEX; + ret = pthread_mutexattr_init(&attr); + if (ret != 0) + { + return SOPC_STATUS_NOK; + } + + ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + if (ret == 0) + { + implementation = SOPC_Calloc(1, sizeof(*implementation)); + if (implementation == NULL) + { + ret = ENOMEM; + } + else if (pthread_mutex_init(&implementation->mutex, &attr) != 0) + { + SOPC_Free(implementation); + ret = EINVAL; + } + else + { + *mutex = implementation; + } + } + + pthread_mutexattr_destroy(&attr); + if (ret == ENOMEM) + { + return SOPC_STATUS_OUT_OF_MEMORY; + } + + return ret == 0 ? SOPC_STATUS_OK : SOPC_STATUS_NOK; +} + +/**************************************************************************** + * Name: SOPC_Mutex_Clear + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Mutex_Clear(FAR SOPC_Mutex *mutex) +{ + FAR SOPC_Mutex_Impl *implementation; + + if (mutex == NULL || *mutex == SOPC_INVALID_MUTEX) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + implementation = *mutex; + if (pthread_mutex_destroy(&implementation->mutex) != 0) + { + return SOPC_STATUS_NOK; + } + + SOPC_Free(implementation); + *mutex = SOPC_INVALID_MUTEX; + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: SOPC_Mutex_Lock + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Mutex_Lock(FAR SOPC_Mutex *mutex) +{ + FAR SOPC_Mutex_Impl *implementation; + + if (mutex == NULL || *mutex == SOPC_INVALID_MUTEX) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + implementation = *mutex; + return pthread_mutex_lock(&implementation->mutex) == 0 ? + SOPC_STATUS_OK : SOPC_STATUS_NOK; +} + +/**************************************************************************** + * Name: SOPC_Mutex_Unlock + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Mutex_Unlock(FAR SOPC_Mutex *mutex) +{ + FAR SOPC_Mutex_Impl *implementation; + + if (mutex == NULL || *mutex == SOPC_INVALID_MUTEX) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + implementation = *mutex; + return pthread_mutex_unlock(&implementation->mutex) == 0 ? + SOPC_STATUS_OK : SOPC_STATUS_NOK; +} + +/**************************************************************************** + * Name: SOPC_Mutex_UnlockAndWaitCond + ****************************************************************************/ + +SOPC_ReturnStatus +SOPC_Mutex_UnlockAndWaitCond(FAR SOPC_Condition *cond, + FAR SOPC_Mutex *mutex) +{ + FAR SOPC_Condition_Impl *condition; + FAR SOPC_Mutex_Impl *implementation; + + if (cond == NULL || *cond == SOPC_INVALID_COND || mutex == NULL || + *mutex == SOPC_INVALID_MUTEX) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + condition = *cond; + implementation = *mutex; + return pthread_cond_wait(&condition->cond, &implementation->mutex) == 0 ? + SOPC_STATUS_OK : SOPC_STATUS_NOK; +} + +/**************************************************************************** + * Name: SOPC_Mutex_UnlockAndTimedWaitCond + ****************************************************************************/ + +SOPC_ReturnStatus +SOPC_Mutex_UnlockAndTimedWaitCond(FAR SOPC_Condition *cond, + FAR SOPC_Mutex *mutex, + uint32_t milliseconds) +{ + FAR SOPC_Condition_Impl *condition; + FAR SOPC_Mutex_Impl *implementation; + struct timespec abstime; + uint64_t nanoseconds; + int ret; + + if (cond == NULL || *cond == SOPC_INVALID_COND || mutex == NULL || + *mutex == SOPC_INVALID_MUTEX || milliseconds == 0) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + if (clock_gettime(CLOCK_REALTIME, &abstime) < 0) + { + return SOPC_STATUS_NOK; + } + + abstime.tv_sec += milliseconds / 1000; + nanoseconds = abstime.tv_nsec + + (uint64_t)(milliseconds % 1000) * S2OPC_NSEC_PER_MSEC; + abstime.tv_sec += nanoseconds / S2OPC_NSEC_PER_SEC; + abstime.tv_nsec = nanoseconds % S2OPC_NSEC_PER_SEC; + + condition = *cond; + implementation = *mutex; + ret = pthread_cond_timedwait(&condition->cond, &implementation->mutex, + &abstime); + if (ret == ETIMEDOUT) + { + return SOPC_STATUS_TIMEOUT; + } + + return ret == 0 ? SOPC_STATUS_OK : SOPC_STATUS_NOK; +} + +/**************************************************************************** + * Name: SOPC_Thread_Create + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Thread_Create(FAR SOPC_Thread *thread, + FAR void *(*entry)(FAR void *), + FAR void *arg, FAR const char *name) +{ + return s2opc_thread_create(thread, entry, arg, name, 0, -1); +} + +/**************************************************************************** + * Name: SOPC_Thread_CreatePrioritized + ****************************************************************************/ + +SOPC_ReturnStatus +SOPC_Thread_CreatePrioritized(FAR SOPC_Thread *thread, + FAR void *(*entry)(FAR void *), FAR void *arg, + int priority, int affinity, + FAR const char *name) +{ + return s2opc_thread_create(thread, entry, arg, name, priority, affinity); +} + +/**************************************************************************** + * Name: SOPC_Thread_Join + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_Thread_Join(FAR SOPC_Thread *thread) +{ + FAR SOPC_Thread_Impl *implementation; + + if (thread == NULL || *thread == SOPC_INVALID_THREAD) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + implementation = *thread; + if (pthread_join(implementation->thread, NULL) != 0) + { + return SOPC_STATUS_NOK; + } + + SOPC_Free(implementation); + *thread = SOPC_INVALID_THREAD; + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: SOPC_Sleep + ****************************************************************************/ + +void SOPC_Sleep(unsigned int milliseconds) +{ + struct timespec delay; + + delay.tv_sec = milliseconds / 1000; + delay.tv_nsec = (milliseconds % 1000) * S2OPC_NSEC_PER_MSEC; + while (nanosleep(&delay, &delay) < 0 && errno == EINTR) + { + } +} diff --git a/netutils/s2opc/port/nuttx/p_sopc_threads.h b/netutils/s2opc/port/nuttx/p_sopc_threads.h new file mode 100644 index 000000000..1474bafb7 --- /dev/null +++ b/netutils/s2opc/port/nuttx/p_sopc_threads.h @@ -0,0 +1,52 @@ +/**************************************************************************** + * apps/netutils/s2opc/port/nuttx/p_sopc_threads.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to Systerel under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. Systerel licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the + * License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +#ifndef __APPS_NETUTILS_S2OPC_PORT_NUTTX_P_SOPC_THREADS_H +#define __APPS_NETUTILS_S2OPC_PORT_NUTTX_P_SOPC_THREADS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct SOPC_Mutex_Impl +{ + pthread_mutex_t mutex; +}; + +struct SOPC_Condition_Impl +{ + pthread_cond_t cond; +}; + +struct SOPC_Thread_Impl +{ + pthread_t thread; +}; + +#endif /* __APPS_NETUTILS_S2OPC_PORT_NUTTX_P_SOPC_THREADS_H */ diff --git a/netutils/s2opc/port/nuttx/p_sopc_time_reference.c b/netutils/s2opc/port/nuttx/p_sopc_time_reference.c new file mode 100644 index 000000000..a5b702abd --- /dev/null +++ b/netutils/s2opc/port/nuttx/p_sopc_time_reference.c @@ -0,0 +1,309 @@ +/**************************************************************************** + * apps/netutils/s2opc/port/nuttx/p_sopc_time_reference.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to Systerel under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. Systerel licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the + * License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include "sopc_assert.h" +#include "sopc_date_time.h" +#include "sopc_logger.h" +#include "sopc_mem_alloc.h" +#include "sopc_time_reference.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define S2OPC_NSEC_PER_SEC 1000000000l +#define S2OPC_NSEC_PER_MSEC 1000000l +#define S2OPC_NSEC_PER_USEC 1000l +#define S2OPC_USEC_PER_SEC 1000000l + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct SOPC_HighRes_TimeReference +{ + struct timespec time; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: s2opc_highres_add + ****************************************************************************/ + +static void +s2opc_highres_add(FAR SOPC_HighRes_TimeReference *reference, + uint64_t duration_us) +{ + reference->time.tv_sec += duration_us / S2OPC_USEC_PER_SEC; + reference->time.tv_nsec += + (duration_us % S2OPC_USEC_PER_SEC) * S2OPC_NSEC_PER_USEC; + + if (reference->time.tv_nsec >= S2OPC_NSEC_PER_SEC) + { + reference->time.tv_sec++; + reference->time.tv_nsec -= S2OPC_NSEC_PER_SEC; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: SOPC_TimeReference_GetCurrent + ****************************************************************************/ + +SOPC_TimeReference SOPC_TimeReference_GetCurrent(void) +{ + struct timespec current; + uint64_t milliseconds; + + if (clock_gettime(CLOCK_MONOTONIC, ¤t) < 0 || current.tv_sec < 0 || + current.tv_nsec < 0 || current.tv_nsec >= S2OPC_NSEC_PER_SEC) + { + return UINT64_MAX; + } + + if ((uint64_t)current.tv_sec > + (UINT64_MAX - current.tv_nsec / S2OPC_NSEC_PER_MSEC) / 1000) + { + return UINT64_MAX; + } + + milliseconds = (uint64_t)current.tv_sec * 1000; + return milliseconds + current.tv_nsec / S2OPC_NSEC_PER_MSEC; +} + +/**************************************************************************** + * Name: SOPC_HighRes_TimeReference_Create + ****************************************************************************/ + +FAR SOPC_HighRes_TimeReference *SOPC_HighRes_TimeReference_Create(void) +{ + FAR SOPC_HighRes_TimeReference *reference; + + reference = SOPC_Calloc(1, sizeof(*reference)); + if (reference != NULL) + { + SOPC_HighRes_TimeReference_GetTime(reference); + } + + return reference; +} + +/**************************************************************************** + * Name: SOPC_HighRes_TimeReference_Delete + ****************************************************************************/ + +void SOPC_HighRes_TimeReference_Delete( + FAR SOPC_HighRes_TimeReference **reference) +{ + if (reference != NULL) + { + SOPC_Free(*reference); + *reference = NULL; + } +} + +/**************************************************************************** + * Name: SOPC_HighRes_TimeReference_Copy + ****************************************************************************/ + +void SOPC_HighRes_TimeReference_Copy( + FAR SOPC_HighRes_TimeReference *destination, + FAR const SOPC_HighRes_TimeReference *source) +{ + if (destination != NULL && source != NULL) + { + *destination = *source; + } +} + +/**************************************************************************** + * Name: SOPC_HighRes_TimeReference_GetTime + ****************************************************************************/ + +void SOPC_HighRes_TimeReference_GetTime( + FAR SOPC_HighRes_TimeReference *reference) +{ + int ret; + + if (reference != NULL) + { + ret = clock_gettime(CLOCK_MONOTONIC, &reference->time); + SOPC_ASSERT(ret == 0); + } +} + +/**************************************************************************** + * Name: SOPC_HighRes_TimeReference_AddSynchedDuration + ****************************************************************************/ + +void SOPC_HighRes_TimeReference_AddSynchedDuration( + FAR SOPC_HighRes_TimeReference *reference, uint64_t duration_us, + int32_t offset_us) +{ + uint64_t window_offset; + uint64_t current_remainder; + uint64_t increment; + + SOPC_ASSERT(reference != NULL); + increment = duration_us; + if (offset_us >= 0) + { + SOPC_ASSERT(duration_us > 0); + current_remainder = SOPC_Time_GetCurrentTimeUTC() / 10; + current_remainder %= duration_us; + window_offset = (duration_us + current_remainder - + (uint32_t)offset_us) % duration_us; + increment -= window_offset; + + /* A wakeup in the last fifth of a period belongs to the next period. + * This absorbs small differences between the realtime and monotonic + * clocks. + */ + + if (increment < duration_us / 5) + { + increment += duration_us; + } + } + + s2opc_highres_add(reference, increment); +} + +/**************************************************************************** + * Name: SOPC_HighRes_TimeReference_IsExpired + ****************************************************************************/ + +bool SOPC_HighRes_TimeReference_IsExpired( + FAR const SOPC_HighRes_TimeReference *reference, + FAR const SOPC_HighRes_TimeReference *now) +{ + SOPC_HighRes_TimeReference current; + + SOPC_ASSERT(reference != NULL); + if (now == NULL) + { + SOPC_HighRes_TimeReference_GetTime(¤t); + now = ¤t; + } + + return reference->time.tv_sec < now->time.tv_sec || + (reference->time.tv_sec == now->time.tv_sec && + reference->time.tv_nsec <= now->time.tv_nsec); +} + +/**************************************************************************** + * Name: SOPC_HighRes_TimeReference_DeltaUs + ****************************************************************************/ + +int64_t SOPC_HighRes_TimeReference_DeltaUs( + FAR const SOPC_HighRes_TimeReference *reference, + FAR const SOPC_HighRes_TimeReference *time) +{ + SOPC_HighRes_TimeReference current; + int64_t delta_seconds; + int64_t delta_nanoseconds; + + SOPC_ASSERT(reference != NULL); + if (time == NULL) + { + SOPC_HighRes_TimeReference_GetTime(¤t); + time = ¤t; + } + + delta_seconds = time->time.tv_sec - reference->time.tv_sec; + delta_nanoseconds = time->time.tv_nsec - reference->time.tv_nsec; + return delta_seconds * S2OPC_USEC_PER_SEC + + delta_nanoseconds / S2OPC_NSEC_PER_USEC; +} + +/**************************************************************************** + * Name: SOPC_HighRes_TimeReference_SleepUntil + ****************************************************************************/ + +void SOPC_HighRes_TimeReference_SleepUntil( + FAR const SOPC_HighRes_TimeReference *date) +{ + struct timespec current; + struct timespec delay; + static bool warned; + int ret; + + SOPC_ASSERT(date != NULL); + for (; ; ) + { + ret = clock_gettime(CLOCK_MONOTONIC, ¤t); + if (ret < 0) + { + break; + } + + if (date->time.tv_sec < current.tv_sec || + (date->time.tv_sec == current.tv_sec && + date->time.tv_nsec <= current.tv_nsec)) + { + return; + } + + delay.tv_sec = date->time.tv_sec - current.tv_sec; + delay.tv_nsec = date->time.tv_nsec - current.tv_nsec; + if (delay.tv_nsec < 0) + { + delay.tv_sec--; + delay.tv_nsec += S2OPC_NSEC_PER_SEC; + } + + ret = nanosleep(&delay, NULL); + if (ret == 0) + { + return; + } + + if (errno != EINTR) + { + break; + } + } + + if (!warned) + { + warned = true; + SOPC_Logger_TraceError(SOPC_LOG_MODULE_COMMON, + "high-resolution sleep failed: %d (%s)", + errno, strerror(errno)); + } +} diff --git a/netutils/s2opc/port/nuttx/p_sopc_udp_sockets.c b/netutils/s2opc/port/nuttx/p_sopc_udp_sockets.c new file mode 100644 index 000000000..2586d64b9 --- /dev/null +++ b/netutils/s2opc/port/nuttx/p_sopc_udp_sockets.c @@ -0,0 +1,498 @@ +/**************************************************************************** + * apps/netutils/s2opc/port/nuttx/p_sopc_udp_sockets.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to Systerel under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. Systerel licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the + * License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_NETDEV_IFINDEX +# include +# include +#endif + +#include "p_sopc_sockets.h" +#include "sopc_buffer.h" +#include "sopc_mem_alloc.h" +#include "sopc_udp_sockets.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: s2opc_udp_would_block + ****************************************************************************/ + +static bool s2opc_udp_would_block(int error) +{ + return error == EAGAIN; +} + +/**************************************************************************** + * Name: s2opc_udp_resolve + ****************************************************************************/ + +static SOPC_ReturnStatus +s2opc_udp_resolve(bool ipv6, FAR const char *node, FAR const char *port, + FAR SOPC_Socket_AddressInfo **addrs) +{ + SOPC_Socket_AddressInfo hints; + FAR struct addrinfo *result; + + if ((node == NULL && port == NULL) || addrs == NULL) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + *addrs = NULL; + memset(&hints, 0, sizeof(hints)); + hints.addrinfo.ai_family = ipv6 ? AF_INET6 : AF_INET; + hints.addrinfo.ai_socktype = SOCK_DGRAM; + hints.addrinfo.ai_protocol = IPPROTO_UDP; + hints.addrinfo.ai_flags = AI_PASSIVE; + + result = NULL; + if (getaddrinfo(node, port, &hints.addrinfo, &result) != 0) + { + return SOPC_STATUS_NOK; + } + + *addrs = (FAR SOPC_Socket_AddressInfo *)result; + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: s2opc_udp_bind_device + ****************************************************************************/ + +static SOPC_ReturnStatus +s2opc_udp_bind_device(int sock, FAR const char *interface_name) +{ + if (interface_name == NULL) + { + return SOPC_STATUS_OK; + } + +#ifdef CONFIG_NET_BINDTODEVICE + if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, interface_name, + strlen(interface_name) + 1) < 0) + { + return SOPC_STATUS_NOK; + } + + return SOPC_STATUS_OK; +#else + return SOPC_STATUS_NOT_SUPPORTED; +#endif +} + +/**************************************************************************** + * Name: s2opc_udp_create + ****************************************************************************/ + +static SOPC_ReturnStatus +s2opc_udp_create(FAR const SOPC_Socket_AddressInfo *addr, + FAR const char *interface_name, bool reuse, + bool nonblocking, FAR SOPC_Socket *sock) +{ + FAR SOPC_Socket_Impl *implementation; + int enabled = 1; + int flags; + + if (addr == NULL || sock == NULL) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + *sock = SOPC_INVALID_SOCKET; + implementation = SOPC_Calloc(1, sizeof(*implementation)); + if (implementation == NULL) + { + return SOPC_STATUS_OUT_OF_MEMORY; + } + + implementation->sock = socket(addr->addrinfo.ai_family, + addr->addrinfo.ai_socktype, + addr->addrinfo.ai_protocol); + implementation->family = addr->addrinfo.ai_family; + if (implementation->sock < 0) + { + SOPC_Free(implementation); + return SOPC_STATUS_NOK; + } + + if ((reuse && + setsockopt(implementation->sock, SOL_SOCKET, SO_REUSEADDR, &enabled, + sizeof(enabled)) < 0) || + s2opc_udp_bind_device(implementation->sock, interface_name) != + SOPC_STATUS_OK) + { + SOPC_Socket_Close(&implementation); + return SOPC_STATUS_NOK; + } + + if (nonblocking) + { + flags = fcntl(implementation->sock, F_GETFL); + if (flags < 0 || fcntl(implementation->sock, F_SETFL, + flags | O_NONBLOCK) < 0) + { + SOPC_Socket_Close(&implementation); + return SOPC_STATUS_NOK; + } + } + + *sock = implementation; + return SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: s2opc_udp_is_multicast + ****************************************************************************/ + +static bool +s2opc_udp_is_multicast(FAR const SOPC_Socket_AddressInfo *address) +{ + FAR const struct sockaddr_in *addr4; + FAR const struct sockaddr_in6 *addr6; + + if (address->addrinfo.ai_family == AF_INET) + { + addr4 = (FAR const struct sockaddr_in *)address->addrinfo.ai_addr; + return IN_MULTICAST(ntohl(addr4->sin_addr.s_addr)); + } + + if (address->addrinfo.ai_family == AF_INET6) + { + addr6 = (FAR const struct sockaddr_in6 *)address->addrinfo.ai_addr; + return IN6_IS_ADDR_MULTICAST(&addr6->sin6_addr); + } + + return false; +} + +/**************************************************************************** + * Name: s2opc_udp_ipv4_interface + ****************************************************************************/ + +static SOPC_ReturnStatus +s2opc_udp_ipv4_interface(FAR const char *interface_name, + FAR struct in_addr *address) +{ +#ifdef CONFIG_NETDEV_IFINDEX + FAR struct ifaddrs *interfaces; + FAR struct ifaddrs *current; + FAR struct sockaddr_in *addr4; +#endif + + address->s_addr = htonl(INADDR_ANY); + if (interface_name == NULL) + { + return SOPC_STATUS_OK; + } + +#ifdef CONFIG_NETDEV_IFINDEX + interfaces = NULL; + if (getifaddrs(&interfaces) < 0) + { + return SOPC_STATUS_NOT_SUPPORTED; + } + + for (current = interfaces; current != NULL; current = current->ifa_next) + { + if (current->ifa_addr != NULL && + current->ifa_addr->sa_family == AF_INET && + strcmp(current->ifa_name, interface_name) == 0) + { + addr4 = (FAR struct sockaddr_in *)current->ifa_addr; + *address = addr4->sin_addr; + freeifaddrs(interfaces); + return SOPC_STATUS_OK; + } + } + + freeifaddrs(interfaces); + return SOPC_STATUS_NOK; +#else + return SOPC_STATUS_NOT_SUPPORTED; +#endif +} + +/**************************************************************************** + * Name: s2opc_udp_join_multicast + ****************************************************************************/ + +static SOPC_ReturnStatus +s2opc_udp_join_multicast(int sock, FAR const char *interface_name, + FAR const SOPC_Socket_AddressInfo *multicast) +{ + FAR const struct sockaddr_in *addr4; + FAR const struct sockaddr_in6 *addr6; + struct ipv6_mreq request6; + struct ip_mreq request4; + SOPC_ReturnStatus status; + + if (multicast->addrinfo.ai_family == AF_INET6) + { + addr6 = (FAR const struct sockaddr_in6 *)multicast->addrinfo.ai_addr; + memset(&request6, 0, sizeof(request6)); + request6.ipv6mr_multiaddr = addr6->sin6_addr; + if (interface_name != NULL) + { +#ifdef CONFIG_NETDEV_IFINDEX + request6.ipv6mr_interface = if_nametoindex(interface_name); + if (request6.ipv6mr_interface == 0) + { + return SOPC_STATUS_NOK; + } +#else + return SOPC_STATUS_NOT_SUPPORTED; +#endif + } + + return setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &request6, + sizeof(request6)) == 0 ? SOPC_STATUS_OK : + SOPC_STATUS_NOT_SUPPORTED; + } + + if (multicast->addrinfo.ai_family != AF_INET) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + addr4 = (FAR const struct sockaddr_in *)multicast->addrinfo.ai_addr; + memset(&request4, 0, sizeof(request4)); + request4.imr_multiaddr = addr4->sin_addr; + status = s2opc_udp_ipv4_interface(interface_name, + &request4.imr_interface); + if (status != SOPC_STATUS_OK) + { + return status; + } + + return setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &request4, + sizeof(request4)) == 0 ? SOPC_STATUS_OK : + SOPC_STATUS_NOT_SUPPORTED; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: SOPC_UDP_SocketAddress_Create + ****************************************************************************/ + +FAR SOPC_Socket_AddressInfo * +SOPC_UDP_SocketAddress_Create(bool ipv6, FAR const char *node, + FAR const char *service) +{ + FAR SOPC_Socket_AddressInfo *address = NULL; + + if (s2opc_udp_resolve(ipv6, node, service, &address) != SOPC_STATUS_OK) + { + return NULL; + } + + return address; +} + +/**************************************************************************** + * Name: SOPC_UDP_SocketAddress_Delete + ****************************************************************************/ + +void SOPC_UDP_SocketAddress_Delete(FAR SOPC_Socket_AddressInfo **address) +{ + SOPC_Socket_AddrInfoDelete(address); +} + +/**************************************************************************** + * Name: SOPC_UDP_Socket_Set_MulticastTTL + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_UDP_Socket_Set_MulticastTTL(SOPC_Socket sock, + uint8_t scope) +{ + int value = scope; + int level; + int option; + + if (sock == SOPC_INVALID_SOCKET) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + if (sock->family == AF_INET6) + { + level = IPPROTO_IPV6; + option = IPV6_MULTICAST_HOPS; + } + else + { + level = IPPROTO_IP; + option = IP_MULTICAST_TTL; + } + + return setsockopt(sock->sock, level, option, &value, sizeof(value)) == 0 ? + SOPC_STATUS_OK : SOPC_STATUS_NOK; +} + +/**************************************************************************** + * Name: SOPC_UDP_Socket_CreateToReceive + ****************************************************************************/ + +SOPC_ReturnStatus +SOPC_UDP_Socket_CreateToReceive(FAR SOPC_Socket_AddressInfo *listen_address, + FAR const char *interface_name, bool reuse, + bool nonblocking, FAR SOPC_Socket *sock) +{ + SOPC_ReturnStatus status; + + status = s2opc_udp_create(listen_address, interface_name, reuse, + nonblocking, sock); + if (status != SOPC_STATUS_OK) + { + return status; + } + + if (bind((*sock)->sock, listen_address->addrinfo.ai_addr, + listen_address->addrinfo.ai_addrlen) < 0) + { + SOPC_UDP_Socket_Close(sock); + return SOPC_STATUS_NOK; + } + + if (s2opc_udp_is_multicast(listen_address)) + { + status = s2opc_udp_join_multicast((*sock)->sock, interface_name, + listen_address); + if (status != SOPC_STATUS_OK) + { + SOPC_UDP_Socket_Close(sock); + } + } + + return status; +} + +/**************************************************************************** + * Name: SOPC_UDP_Socket_CreateToSend + ****************************************************************************/ + +SOPC_ReturnStatus +SOPC_UDP_Socket_CreateToSend(FAR SOPC_Socket_AddressInfo *destination, + FAR const char *interface_name, + bool nonblocking, FAR SOPC_Socket *sock) +{ + return s2opc_udp_create(destination, interface_name, false, nonblocking, + sock); +} + +/**************************************************************************** + * Name: SOPC_UDP_Socket_SendTo + ****************************************************************************/ + +SOPC_ReturnStatus +SOPC_UDP_Socket_SendTo(SOPC_Socket sock, + FAR const SOPC_Socket_AddressInfo *destination, + FAR SOPC_Buffer *buffer) +{ + ssize_t ret; + int error; + + if (sock == SOPC_INVALID_SOCKET || destination == NULL || buffer == NULL || + buffer->position != 0) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + do + { + ret = sendto(sock->sock, buffer->data, buffer->length, 0, + destination->addrinfo.ai_addr, + destination->addrinfo.ai_addrlen); + } + while (ret < 0 && errno == EINTR); + + error = errno; + if (ret >= 0 && (uint32_t)ret == buffer->length) + { + return SOPC_STATUS_OK; + } + + return ret < 0 && s2opc_udp_would_block(error) ? + SOPC_STATUS_WOULD_BLOCK : SOPC_STATUS_NOK; +} + +/**************************************************************************** + * Name: SOPC_UDP_Socket_ReceiveFrom + ****************************************************************************/ + +SOPC_ReturnStatus SOPC_UDP_Socket_ReceiveFrom(SOPC_Socket sock, + FAR SOPC_Buffer *buffer) +{ + struct sockaddr_storage source; + socklen_t source_length; + ssize_t ret; + int error; + + if (sock == SOPC_INVALID_SOCKET || buffer == NULL) + { + return SOPC_STATUS_INVALID_PARAMETERS; + } + + source_length = sizeof(source); + do + { + ret = recvfrom(sock->sock, buffer->data, buffer->current_size, 0, + (FAR struct sockaddr *)&source, &source_length); + } + while (ret < 0 && errno == EINTR); + + error = errno; + if (ret < 0) + { + return s2opc_udp_would_block(error) ? SOPC_STATUS_WOULD_BLOCK : + SOPC_STATUS_NOK; + } + + buffer->length = ret; + return buffer->length == buffer->current_size ? + SOPC_STATUS_OUT_OF_MEMORY : SOPC_STATUS_OK; +} + +/**************************************************************************** + * Name: SOPC_UDP_Socket_Close + ****************************************************************************/ + +void SOPC_UDP_Socket_Close(FAR SOPC_Socket *sock) +{ + SOPC_Socket_Close(sock); +} diff --git a/netutils/s2opc/port/nuttx/s2opc_common_export.h b/netutils/s2opc/port/nuttx/s2opc_common_export.h new file mode 100644 index 000000000..95a7233fa --- /dev/null +++ b/netutils/s2opc/port/nuttx/s2opc_common_export.h @@ -0,0 +1,31 @@ +/**************************************************************************** + * apps/netutils/s2opc/port/nuttx/s2opc_common_export.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +#ifndef __APPS_NETUTILS_S2OPC_PORT_NUTTX_S2OPC_COMMON_EXPORT_H +#define __APPS_NETUTILS_S2OPC_PORT_NUTTX_S2OPC_COMMON_EXPORT_H + +#define S2OPC_COMMON_EXPORT +#define S2OPC_COMMON_NO_EXPORT +#define S2OPC_COMMON_DEPRECATED __attribute__((deprecated)) +#define S2OPC_COMMON_DEPRECATED_EXPORT S2OPC_COMMON_DEPRECATED +#define S2OPC_COMMON_DEPRECATED_NO_EXPORT S2OPC_COMMON_DEPRECATED + +#endif /* __APPS_NETUTILS_S2OPC_PORT_NUTTX_S2OPC_COMMON_EXPORT_H */ diff --git a/netutils/s2opc/port/nuttx/sopc_build_info.c b/netutils/s2opc/port/nuttx/sopc_build_info.c new file mode 100644 index 000000000..0cf2247cd --- /dev/null +++ b/netutils/s2opc/port/nuttx/sopc_build_info.c @@ -0,0 +1,90 @@ +/**************************************************************************** + * apps/netutils/s2opc/port/nuttx/sopc_build_info.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "sopc_common_build_info.h" +#include "sopc_pubsub_build_info.h" +#include "sopc_toolkit_build_info.h" +#include "sopc_version.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const SOPC_Build_Info sopc_common_build_info = +{ + .buildVersion = SOPC_TOOLKIT_VERSION, + .buildSrcCommit = "S2OPC_Toolkit_" CONFIG_S2OPC_VERSION, + .buildDockerId = "", + .buildBuildDate = "" +}; + +const SOPC_Build_Info sopc_client_server_build_info = +{ + .buildVersion = SOPC_TOOLKIT_VERSION, + .buildSrcCommit = "S2OPC_Toolkit_" CONFIG_S2OPC_VERSION, + .buildDockerId = "", + .buildBuildDate = "" +}; + +const SOPC_Build_Info sopc_pubsub_build_info = +{ + .buildVersion = SOPC_TOOLKIT_VERSION, + .buildSrcCommit = "S2OPC_Toolkit_" CONFIG_S2OPC_VERSION, + .buildDockerId = "", + .buildBuildDate = "" +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: SOPC_Common_GetBuildInfo + ****************************************************************************/ + +SOPC_Build_Info SOPC_Common_GetBuildInfo(void) +{ + return sopc_common_build_info; +} + +/**************************************************************************** + * Name: SOPC_ClientServer_GetBuildInfo + ****************************************************************************/ + +SOPC_Build_Info SOPC_ClientServer_GetBuildInfo(void) +{ + return sopc_client_server_build_info; +} + +/**************************************************************************** + * Name: SOPC_PubSub_GetBuildInfo + ****************************************************************************/ + +SOPC_Build_Info SOPC_PubSub_GetBuildInfo(void) +{ + return sopc_pubsub_build_info; +} diff --git a/netutils/s2opc/port/nuttx/sopc_nuttx_config.h b/netutils/s2opc/port/nuttx/sopc_nuttx_config.h new file mode 100644 index 000000000..a0d0d1e3c --- /dev/null +++ b/netutils/s2opc/port/nuttx/sopc_nuttx_config.h @@ -0,0 +1,79 @@ +/**************************************************************************** + * apps/netutils/s2opc/port/nuttx/sopc_nuttx_config.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +#ifndef __APPS_NETUTILS_S2OPC_PORT_NUTTX_SOPC_NUTTX_CONFIG_H +#define __APPS_NETUTILS_S2OPC_PORT_NUTTX_SOPC_NUTTX_CONFIG_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* S2OPC uses Boolean constants in preprocessor expressions. NuttX + * deliberately defines false and true as typed expressions. Preserve the + * standard Boolean type, but provide untyped values to the S2OPC sources. + */ + +#undef false +#undef true +#define false 0 +#define true 1 + +#ifndef IPV6_ADD_MEMBERSHIP +# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#endif + +#ifndef IPV6_DROP_MEMBERSHIP +# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +#endif + +#define SOPC_MAX_NB_ELEMENTS_ASYNC_QUEUE_WARNING_ONLY 1 +#define SOPC_HAS_FILESYSTEM 1 +#define SOPC_LISTENER_LISTEN_ALL_INTERFACES 1 + +#ifdef CONFIG_S2OPC_NODE_MANAGEMENT +# define S2OPC_NODE_MANAGEMENT 1 +#endif + +#ifdef CONFIG_S2OPC_NODE_ADD_OPTIONAL +# define S2OPC_NODE_ADD_OPTIONAL 1 +#endif + +#ifdef CONFIG_S2OPC_HISTORY_READ +# define S2OPC_EXTERNAL_HISTORY_RAW_READ_SERVICE 1 +#endif + +#ifdef CONFIG_S2OPC_EVENT_MANAGEMENT +# define S2OPC_EVENT_MANAGEMENT 1 +#endif + +#ifdef CONFIG_S2OPC_AUDITING +# define S2OPC_HAS_AUDITING 1 +#endif + +#endif /* __APPS_NETUTILS_S2OPC_PORT_NUTTX_SOPC_NUTTX_CONFIG_H */