diff --git a/Vendors/Renesas/Readme.md b/Vendors/Renesas/Readme.md
new file mode 100644
index 0000000..6ffc5a3
--- /dev/null
+++ b/Vendors/Renesas/Readme.md
@@ -0,0 +1,43 @@
+# Renesas Wi-SUN FAN CSMP Agent Integration
+
+
+
+
+
+## Summary
+
+The integration targets Renesas Wi-SUN FAN products based on the Renesas RX MCU family.
+Renesas provides Wi-SUN FAN-compliant Sub-GHz solutions for Wi-SUN FAN applications.
+For the list of officially supported devices, stack versions, configuration options, and available features, please refer to the Renesas Wi-SUN FAN product and software documentation.
+
+The purpose of this integration is to enable a Renesas Wi-SUN FAN node to communicate with Cisco Field Network Director (FND) using CSMP.
+It provides platform-specific functionality required by the CSMP Agent Library, including:
+
+- UDP/IPv6 communication over the Renesas Wi-SUN FAN protocol stack,
+- access to platform, stack, and firmware information,
+- mapping of Wi-SUN FAN network and firmware information to CSMP TLV objects,
+- firmware update integration.
+
+## Requirements
+
+The following components are required to build and run the Renesas Wi-SUN FAN CSMP Agent integration:
+
+- A running instance of Cisco Field Network Director (FND). For a development setup, please refer to the [CSMP Developer Tutorial](../../docs/CSMP%20Developer%20Tutorial%20-%200v11.pdf).
+
+- At least two [Renesas Wi-SUN FAN Development Kits](https://www.renesas.com/en/design-resources/boards-kits/rtk0ee0013d10002bj).
+
+- The latest Renesas [Wi-SUN FAN Protocol Stack](https://www.renesas.com/en/software-tool/sub-ghzwi-sun-protocol-stack).
+
+## Renesas Wi-SUN FAN Stack
+
+This Renesas CSMP integration layer is designed to be used in conjunction with the latest version of the [Renesas Wi-SUN FAN Protocol Stack](https://www.renesas.com/en/software-tool/sub-ghzwi-sun-protocol-stack).
+Different versions may not work, so please make sure that this CSMP integration layer is used with the correct version of the Renesas Wi-SUN FAN Protocol Stack!
+
+Further information about the CSMP usage with the Renesas Wi-SUN FAN protocol stack, APIs, configuration, sample applications, and supported platforms is provided in the user manual of the Renesas Wi-SUN FAN Protocol Stack and accompanying documentation, which are located in the release package of the Renesas Wi-SUN FAN Protocol Stack.
+It also includes instructions for configuring and running a sample setup for CSMP with Renesas Wi-SUN FAN nodes.
+
+Relevant Renesas resources:
+
+- [Renesas Sub-GHz/Wi-SUN FAN Transceivers](https://www.renesas.com/en/products/wireless-connectivity/sub-ghz-Wi-SUN-transceivers)
+
+- [Renesas Sub-GHz/Wi-SUN FAN Protocol Stack](https://www.renesas.com/en/software-tool/sub-ghzWi-SUN-protocol-stack)
diff --git a/Vendors/Renesas/resources/renesas-logo.jpg b/Vendors/Renesas/resources/renesas-logo.jpg
new file mode 100644
index 0000000..42706f5
Binary files /dev/null and b/Vendors/Renesas/resources/renesas-logo.jpg differ
diff --git a/include/iana_pen.h b/include/iana_pen.h
index 11642eb..5905a4f 100644
--- a/include/iana_pen.h
+++ b/include/iana_pen.h
@@ -42,8 +42,8 @@ enum {
BC_HYDRO = 39480,
LANDIS_GYR = 42830,
TELLABS = 1397,
- EXEGIN = 39682
- //RENESAS
+ EXEGIN = 39682,
+ RENESAS = 63257
//ROHM
};
diff --git a/osal/efr32_wisun/osal_platform_types.h b/osal/efr32_wisun/osal_platform_types.h
index 66ba588..444cc0f 100644
--- a/osal/efr32_wisun/osal_platform_types.h
+++ b/osal/efr32_wisun/osal_platform_types.h
@@ -56,4 +56,9 @@ typedef int osal_sd_set_t;
#define GECKO_BTL_UPLOAD_SLOT_ID 0
#define GECKO_BTL_BACKUP_SLOT_ID 1
+// Firmware Management Configuration
+#define CSMP_FWMGMT_ACTIVE_SLOTS 3 // 0-RUN, 1-UPLOAD, 2-BACKUP
+#define CSMP_FWMGMT_SLOTIMG_SIZE (512*1024) // 512 Kb
+#define CSMP_FWMGMT_BLKMAP_CNT (32)
+
#endif
diff --git a/osal/freertos/osal_platform_types.h b/osal/freertos/osal_platform_types.h
index 597a709..b109b33 100644
--- a/osal/freertos/osal_platform_types.h
+++ b/osal/freertos/osal_platform_types.h
@@ -54,4 +54,9 @@ typedef fd_set osal_sd_set_t;
#define OSAL_AF_INET6 AF_INET6
#define OSAL_SOCK_DGRAM SOCK_DGRAM
+// Firmware Management Configuration
+#define CSMP_FWMGMT_ACTIVE_SLOTS 3 // 0-RUN, 1-UPLOAD, 2-BACKUP
+#define CSMP_FWMGMT_SLOTIMG_SIZE (30*1024) // 30 Kb
+#define CSMP_FWMGMT_BLKMAP_CNT (32)
+
#endif
diff --git a/osal/linux/osal_platform_types.h b/osal/linux/osal_platform_types.h
index 359aa9f..09da7ac 100644
--- a/osal/linux/osal_platform_types.h
+++ b/osal/linux/osal_platform_types.h
@@ -55,6 +55,9 @@ typedef fd_set osal_sd_set_t;
#define OSAL_AF_INET6 AF_INET6
#define OSAL_SOCK_DGRAM SOCK_DGRAM
-
+// Firmware Management Configuration
+#define CSMP_FWMGMT_ACTIVE_SLOTS 3 // 0-RUN, 1-UPLOAD, 2-BACKUP
+#define CSMP_FWMGMT_SLOTIMG_SIZE (30*1024) // 30 Kb
+#define CSMP_FWMGMT_BLKMAP_CNT (32)
#endif
diff --git a/osal/osal.h b/osal/osal.h
index 15f35cc..9138155 100644
--- a/osal/osal.h
+++ b/osal/osal.h
@@ -35,15 +35,6 @@
#define HWID_SIZE 32
#define BLOCK_SIZE 1024
-// IMAGE SLOT INFO
-#define CSMP_FWMGMT_ACTIVE_SLOTS 3 // 0-RUN, 1-UPLOAD, 2-BACKUP
-#if defined(OSAL_EFR32_WISUN)
-#define CSMP_FWMGMT_SLOTIMG_SIZE (512*1024) // 512 Kb
-#else
-#define CSMP_FWMGMT_SLOTIMG_SIZE (30*1024) // 30 Kb
-#endif
-#define CSMP_FWMGMT_BLKMAP_CNT (32)
-
#define CSMP_IMAGE_HDR_SIZE 256
#define REBOOT_DELAY 5
diff --git a/osal/renesas_wisun/osal_platform_types.h b/osal/renesas_wisun/osal_platform_types.h
new file mode 100644
index 0000000..179224c
--- /dev/null
+++ b/osal/renesas_wisun/osal_platform_types.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2024 Cisco Systems, Inc.
+ *
+ * Licensed 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 __OSAL_PLATFORM_TYPES_H
+#define __OSAL_PLATFORM_TYPES_H
+
+#include
+#include
+#include "FreeRTOS.h"
+#include "task.h"
+#include "semphr.h"
+#include "r_byte_swap.h"
+#include "r_header_utils.h"
+#if defined(__i386) || defined(__x86_64)
+
+/* The Wi-SUN FAN simulator requires Unix sockets for basic functionality -> avoid redefinition */
+#include
+#include
+#include
+#else
+
+/* Supported address families. */
+#define AF_INET6 10 /* IP version 6 */
+
+/* Type to represent a port. */
+typedef uint16_t in_port_t;
+
+typedef uint16_t sa_family_t;
+
+typedef uint32_t socklen_t;
+
+struct in6_addr
+{
+ unsigned char s6_addr[16]; /* IPv6 address */
+};
+
+/* Structure describing a generic socket address. */
+struct sockaddr
+{
+ sa_family_t sa_family; /* Common data: address family and length. */
+ char sa_data[14]; /* Address data. */
+};
+
+struct sockaddr_in6
+{
+ sa_family_t sin6_family; /* AF_INET6 */
+ in_port_t sin6_port; /* port number */
+ uint32_t sin6_flowinfo; /* IPv6 flow information */
+ struct in6_addr sin6_addr; /* IPv6 address */
+ uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */
+};
+
+/* Structure for scatter/gather I/O. */
+struct iovec
+{
+ void* iov_base; /* Pointer to data. */
+ size_t iov_len; /* Length of data. */
+};
+
+/* Structure describing messages sent by
+ `sendmsg' and received by `recvmsg'. */
+struct msghdr
+{
+ void* msg_name; /* Address to send to/receive from. */
+ socklen_t msg_namelen; /* Length of address data. */
+
+ struct iovec* msg_iov; /* Vector of data to send/receive into. */
+ size_t msg_iovlen; /* Number of elements in the vector. */
+
+ void* msg_control; /* Ancillary data (eg BSD filedesc passing). */
+ size_t msg_controllen; /* Ancillary data buffer length.
+ !! The type should be socklen_t but the
+ definition of the kernel is incompatible
+ with this. */
+
+ int msg_flags; /* Flags on received message. */
+};
+
+#endif
+
+typedef void (*osal_sighandler_t)(int);
+
+typedef struct sockaddr_in6 osal_sockaddr_t;
+typedef SemaphoreHandle_t osal_sem_t;
+typedef int osal_sigset_t;
+typedef socklen_t osal_socklen_t;
+typedef TaskHandle_t osal_task_t;
+typedef TaskFunction_t osal_task_fnc_t;
+typedef int32_t osal_ssize_t;
+typedef uint64_t osal_time_t;
+typedef BaseType_t osal_basetype_t;
+typedef int osal_socket_handle_t;
+typedef int osal_sd_set_t;
+
+#define OSAL_AF_INET6 AF_INET6
+#define OSAL_SOCK_DGRAM 0
+
+#define OSAL_ATTR_PACKED R_HEADER_UTILS_ATTR_PACKED
+
+#define CSMP_MAX_SOCKETS 2
+
+#if !defined(__i386) && !defined(__x86_64)
+struct timeval
+{
+ uint32_t tv_sec; /* Seconds. */
+ uint32_t tv_usec; /* Microseconds. */
+};
+#endif
+
+struct timezone {
+ int tz_minuteswest; /* minutes west of Greenwich */
+ int tz_dsttime; /* type of DST correction */
+};
+
+#endif
\ No newline at end of file
diff --git a/osal/renesas_wisun/osal_renesas_config.h b/osal/renesas_wisun/osal_renesas_config.h
new file mode 100644
index 0000000..659abe9
--- /dev/null
+++ b/osal/renesas_wisun/osal_renesas_config.h
@@ -0,0 +1,57 @@
+/******************************************************************************
+ * DISCLAIMER
+ * This software is supplied by Renesas Electronics Corporation and is only
+ * intended for use with Renesas products. No other uses are authorized. This
+ * software is owned by Renesas Electronics Corporation and is protected under
+ * all applicable laws, including copyright laws.
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
+ * THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT
+ * LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.
+ * TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS
+ * ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR
+ * ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE
+ * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ * Renesas reserves the right, without notice, to make changes to this
+ * software and to discontinue the availability of this software. By using this
+ * software, you agree to the additional terms and conditions found by
+ * accessing the following link:
+ * http://www.renesas.com/disclaimer
+ *
+ * Copyright (C) 2025 Renesas Electronics Corporation. All rights reserved.
+ *****************************************************************************/
+
+/**
+ * @file osal_renesas_config.h
+ * @brief Configuration of the CSMP module running within the Renesas Wi-SUN FAN sample application.
+ */
+
+#ifndef OSAL_RENESAS_CONFIG_H
+#define OSAL_RENESAS_CONFIG_H
+
+/** the major version of the CSMP module */
+#define R_CSMP_MAJOR_VERSION "0"
+
+/** the major version of the CSMP module */
+#define R_CSMP_MINOR_VERSION "1"
+
+/** the build version of the CSMP module */
+#define R_CSMP_BUILD_VERSION "0"
+
+/** string to identify this version of the CSMP module (in format "major.minor.build") */
+#define R_CSMP_VERSION_STRING R_CSMP_MAJOR_VERSION "." R_CSMP_MINOR_VERSION "." R_CSMP_BUILD_VERSION
+
+/** string to identify the running firmware */
+#define R_CSMP_FILE_NAME "Wi-SUN-FAN-SampleApp"
+
+/** string to identify this hardware platform as used by FND */
+#define R_CSMP_HARDWARE_ID "renesastest"
+
+/** max number of ip addresses */
+#define R_TLV_IPADDRESS_MAX_NUM 3
+
+/** max number of routes */
+#define R_TLV_ROUTES_MAX_NUM 2
+
+#endif /* OSAL_RENESAS_CONFIG_H */
diff --git a/osal/renesas_wisun/osal_renesas_wisun.c b/osal/renesas_wisun/osal_renesas_wisun.c
new file mode 100644
index 0000000..e9b9555
--- /dev/null
+++ b/osal/renesas_wisun/osal_renesas_wisun.c
@@ -0,0 +1,540 @@
+/******************************************************************************
+* DISCLAIMER
+ * This software is supplied by Renesas Electronics Corporation and is only
+ * intended for use with Renesas products. No other uses are authorized. This
+ * software is owned by Renesas Electronics Corporation and is protected under
+ * all applicable laws, including copyright laws.
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
+ * THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT
+ * LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.
+ * TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS
+ * ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR
+ * ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE
+ * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ * Renesas reserves the right, without notice, to make changes to this
+ * software and to discontinue the availability of this software. By using this
+ * software, you agree to the additional terms and conditions found by
+ * accessing the following link:
+ * http://www.renesas.com/disclaimer
+ *
+ * Copyright (C) 2025 Renesas Electronics Corporation. All rights reserved.
+ *****************************************************************************/
+
+/**
+ * @file osal_renesas_wisun.c
+ * @brief Implementation of the OSAL CSMP interface to be used within the Renesas Wi-SUN FAN sample application.
+ */
+
+#include "osal.h"
+#include "csmp.h"
+#include "csmp_service.h"
+#include "CsmpAgentLib_sample_tlvs.h"
+#include "signature_verify.h"
+#include "r_nwk_api.h"
+#include "r_mem_tools.h"
+#include "r_impl_utils.h"
+#include "r_ipv6_helper.h"
+#if R_FWUP_SUPPORT
+#include "r_fwup_wrapper.h"
+#endif
+#include "lib/random.h"
+#include "osal_renesas_wrapper.h"
+
+#define MY_HEAP_ID R_HEAP_ID_CSMP
+#include "r_heap.h"
+
+// R_LOG_PREFIX is processed by RLog code generator
+#define R_LOG_PREFIX CSMP
+#include "r_log_internal.h"
+#if R_LOG_THRESHOLD > R_LOG_SEVERITY_OFF
+#include "r_loggen_osal_renesas_wisun.h"
+#endif
+
+typedef struct r_trickle_timer_s
+{
+ r_timer_id_t id;
+ uint32_t t0;
+ uint32_t tfire;
+ uint32_t icur;
+ uint32_t imin;
+ uint32_t imax;
+ uint8_t is_running : 1;
+ r_timer_t cb_timer;
+ trickle_timer_fired_t csmp_cb; //!< The CSMP callback function that should be executed once this timer expires
+} r_trickle_timer_t;
+
+static r_trickle_timer_t timers[timer_num];
+
+uint16_t socket_ports[CSMP_MAX_SOCKETS];
+
+csmp_handle_t r_csmp_handle;
+
+extern uint32_t m_GroupIds[CSMP_GROUP_NUM_TYPES];
+STATIC_ASSERT(R_FLASH_CSMP_GROUPS_SIZE == sizeof(m_GroupIds), Size_of_csmp_group_array_must_equal_R_FLASH_CSMP_GROUPS_SIZE);
+
+/** The csmp module is uninitialized by default (R_FALSE) */
+static r_boolean_t csmpIsInitialized = R_FALSE;
+
+osal_ssize_t osal_sendmsg(osal_socket_handle_t sockd, const struct msghdr msg, osal_basetype_t flags)
+{
+ if (msg.msg_iov == NULL)
+ {
+ R_LOG_ERR("r_sendmsg failed: data vector is null");
+ return R_RESULT_INVALID_PARAMETER;
+ }
+
+ if (msg.msg_name == NULL)
+ {
+ R_LOG_ERR("r_sendmsg failed: address is null");
+ return R_RESULT_INVALID_PARAMETER;
+ }
+
+ const osal_sockaddr_t *addr = msg.msg_name;
+ osal_socklen_t addrlen = msg.msg_namelen;
+
+ osal_ssize_t total_sent = 0;
+
+ for (size_t i = 0; i < msg.msg_iovlen; ++i)
+ {
+ const struct iovec *iov = &msg.msg_iov[i];
+ const uint8_t *buf = (const uint8_t *)iov->iov_base;
+ size_t len = iov->iov_len;
+
+ while (len > 0)
+ {
+ osal_ssize_t n = osal_sendto(sockd, buf, len, flags, addr, addrlen);
+
+ if (n <= 0)
+ {
+ return OSAL_FAILURE;
+ }
+
+ buf += (size_t)n;
+ len -= (size_t)n;
+ total_sent += n;
+ }
+ }
+
+ return total_sent;
+}
+
+osal_ssize_t osal_sendto(osal_socket_handle_t sockd, const void *buf, size_t len, osal_basetype_t flags,
+ const osal_sockaddr_t *dest_addr, osal_socklen_t addrlen)
+{
+ if (sockd < 0 || sockd >= CSMP_MAX_SOCKETS || addrlen != sizeof(struct sockaddr_in6))
+ {
+ return R_RESULT_INVALID_PARAMETER;
+ }
+
+ struct sockaddr_in6* dest = (struct sockaddr_in6*)dest_addr;
+ if (dest->sin6_family != AF_INET6)
+ {
+ return R_RESULT_INVALID_PARAMETER;
+ }
+
+ uint16_t dstPort = ntohs(dest->sin6_port); // port in socket struct is network byte order but R_UDP_DataRequest expects host byte order
+ uint16_t srcPort = ntohs(socket_ports[sockd]); // port in socket struct is network byte order but R_UDP_DataRequest expects host byte order
+ r_result_t res = R_UDP_DataRequest(dest->sin6_addr.s6_addr, dstPort, srcPort, buf, len, 0);
+ if (res == R_RESULT_SUCCESS)
+ {
+ R_LOG_DBG("Transmission of %{u16} bytes to %{ipv6addr}:%{u16} from port %{u16}", len, dest->sin6_addr.s6_addr, dstPort, srcPort);
+ return len;
+ }
+ R_LOG_ERR("Transmission of %{u16} bytes to %{ipv6addr} failed: 0x%{hex8}", len, dest->sin6_addr.s6_addr, res);
+ return OSAL_FAILURE;
+}
+
+void osal_print_formatted_ip(const osal_sockaddr_t *sockadd)
+{
+ R_LOG_DBG("[%{ipv6addr}]:%{u16}\n", sockadd->sin6_addr.s6_addr, sockadd->sin6_port);
+}
+
+static void R_TrickleTimer_HandleExpired(void* timer)
+{
+ r_trickle_timer_t* trickleTimer = timer;
+ if (trickleTimer->id == reg_timer)
+ {
+ R_LOG_DBG("Register trickle timer fired");
+ }
+ else if (trickleTimer->id == rpt_timer)
+ {
+ R_LOG_DBG("Periodic metrics report trickle timer fired");
+ }
+ else if (trickleTimer->id == lrq_timer)
+ {
+ R_LOG_DBG("Firmware load request timer fired");
+ }
+ else if (trickleTimer->id == async_timer)
+ {
+ R_LOG_DBG("Async CSMP Reponse timer fired");
+ }
+ else
+ {
+ R_LOG_ERR("Invalid trickle timer id");
+ }
+
+ /* Set timer again */
+ trickleTimer->t0 += trickleTimer->icur; // update t0 to next interval
+
+ /* Double interval size */
+ trickleTimer->icur <<= 1;
+ if (trickleTimer->icur > trickleTimer->imax)
+ {
+ trickleTimer->icur = trickleTimer->imax;
+ }
+
+ uint32_t min = trickleTimer->icur >> 1;
+ trickleTimer->tfire = trickleTimer->t0 + min + (random32() % (trickleTimer->icur - min));
+ r_result_t res = R_Timer_StartSecs(&trickleTimer->cb_timer, trickleTimer->icur, R_TrickleTimer_HandleExpired, trickleTimer);
+ if (res != R_RESULT_SUCCESS)
+ {
+ R_LOG_ERR("Fatal error: Trickle timer could not be restarted");
+ }
+
+ /* Execute CSMP callback */
+ trickleTimer->csmp_cb();
+}
+
+void osal_trickle_timer_start(osal_timerid_t timerid, uint32_t imin, uint32_t imax, trickle_timer_fired_t trickle_timer_fired)
+{
+ if (timerid >= timer_num)
+ {
+ R_LOG_ERR("Invalid trickle timer id");
+ return; // Invalid timer ID
+ }
+
+ if (timerid == reg_timer)
+ {
+ R_LOG_DBG("Register trickle timer start");
+ }
+ else if (timerid == rpt_timer)
+ {
+ R_LOG_DBG("Periodic metrics report trickle timer start");
+ }
+ else if (timerid == lrq_timer)
+ {
+ R_LOG_DBG("Firmware load request timer start");
+ }
+ else if (timerid == async_timer)
+ {
+ R_LOG_DBG("Async CSMP Reponse timer start");
+ }
+
+ timers[timerid].t0 = clock_seconds() + (random32() % imin);
+ timers[timerid].icur = imin;
+ timers[timerid].imin = imin;
+ timers[timerid].imax = imax;
+ uint32_t min = timers[timerid].icur >> 1;
+ timers[timerid].tfire = timers[timerid].t0 + min + (random32() % (timers[timerid].icur - min));
+ timers[timerid].id = timerid;
+ timers[timerid].csmp_cb = trickle_timer_fired;
+
+ r_result_t timerRes = R_Timer_StartSecs(&timers[timerid].cb_timer, timers[timerid].icur, R_TrickleTimer_HandleExpired, &timers[timerid]);
+ if (timerRes != R_RESULT_SUCCESS)
+ {
+ R_LOG_ERR("Fatal error: Timer %{u8} could not be started: 0x%{hex8}", (uint8_t)timerid, timerRes);
+ return;
+ }
+
+ R_LOG_DBG("Start timer %{u8} (expires in %{u32}s)", timerid, timers[timerid].icur);
+ timers[timerid].is_running = R_TRUE;
+}
+
+void osal_trickle_timer_stop(osal_timerid_t timerid)
+{
+ if (timerid >= timer_num)
+ {
+ R_LOG_ERR("Invalid trickle timer id");
+ return; // Invalid timer ID
+ }
+
+ r_result_t res = R_Timer_Stop(&timers[timerid].cb_timer);
+
+ if (timerid == reg_timer)
+ {
+ R_LOG_DBG("Register trickle timer stop");
+ }
+ else if (timerid == rpt_timer)
+ {
+ R_LOG_DBG("Periodic metrics report trickle timer stop");
+ }
+ else if (timerid == lrq_timer)
+ {
+ R_LOG_DBG("Firmware load request timer stop");
+ }
+ else if (timerid == async_timer)
+ {
+ R_LOG_DBG("Async CSMP Reponse timer stop");
+ }
+
+ switch (res)
+ {
+ case R_RESULT_SUCCESS:
+ R_LOG_DBG("Timer %{u8} stopped", timerid);
+ timers[timerid].is_running = R_FALSE;
+ break;
+
+ case R_RESULT_NOTHING_TO_DO:
+ break; // Do nothing (timer was not started)
+
+ default:
+ R_LOG_ERR("Trickle timer could not be stopped: 0x%{hex8}", res);
+ break;
+ }
+}
+
+void *osal_malloc(size_t size)
+{
+ return my_alloc(size);
+}
+
+void osal_free(void *ptr)
+{
+ my_free(ptr);
+}
+
+osal_basetype_t osal_gettime(struct timeval *tv, struct timezone *tz)
+{
+ if (tv == NULL)
+ {
+ return OSAL_FAILURE;
+ }
+
+ tv->tv_sec = R_CLOCK_GetUnixTimestamp();;
+ tv->tv_usec = 0UL;
+
+ if (tz != NULL)
+ {
+ tz->tz_minuteswest = 0;
+ tz->tz_dsttime = 0;
+ }
+
+ return OSAL_SUCCESS;
+}
+
+osal_basetype_t osal_settime(struct timeval *tv, struct timezone *tz)
+{
+ if (tv == NULL)
+ {
+ return OSAL_FAILURE;
+ }
+ uint64_t currentUnixTime = tv->tv_sec + tv->tv_usec / 1000000;
+
+ if (tz != NULL)
+ {
+ currentUnixTime += tz->tz_minuteswest * 60;
+ if (tz->tz_dsttime)
+ {
+ currentUnixTime -= 3600;
+ }
+ }
+
+ R_CLOCK_SetUnixTimestampReference(currentUnixTime);
+
+ return OSAL_SUCCESS;
+}
+
+/*
+ * The following functions are not used in any CSMP platform independent code.
+ * For the Renesas platform, the functionalities are already provided by the dedicated
+ * FW update module. The corresponding methods are then directly called when processing
+ * the relevant TLVs.
+ */
+osal_basetype_t osal_system_reboot(struct in6_addr *NMSaddr)
+{
+ return OSAL_FAILURE;
+}
+
+osal_basetype_t osal_read_firmware(uint8_t slotid, uint8_t *data, uint32_t size)
+{
+ return OSAL_FAILURE;
+}
+
+osal_basetype_t osal_write_firmware(uint8_t slotid, uint8_t *data, uint32_t size)
+{
+ return OSAL_FAILURE;
+}
+
+osal_basetype_t osal_write_slothdr(uint8_t slotid, Csmp_Slothdr *slot)
+{
+ return OSAL_FAILURE;
+}
+
+osal_basetype_t osal_read_slothdr(uint8_t slotid, Csmp_Slothdr *slot)
+{
+ return OSAL_FAILURE;
+}
+
+osal_basetype_t osal_copy_firmware(uint8_t source_slotid, uint8_t dest_slotid, Csmp_Slothdr *slot)
+{
+ return OSAL_FAILURE;
+}
+
+osal_basetype_t osal_task_create(osal_task_t * thread,
+ const char * name,
+ uint32_t priority,
+ size_t stacksize,
+ osal_task_fnc_t entry,
+ void * arg)
+{
+ return OSAL_SUCCESS;
+}
+
+osal_basetype_t osal_task_cancel(osal_task_t thread)
+{
+ return OSAL_SUCCESS;
+}
+
+osal_socket_handle_t osal_socket(osal_basetype_t domain,
+ osal_basetype_t type,
+ osal_basetype_t protocol)
+{
+ osal_socket_handle_t sockd = 0;
+ uint16_t port = CSMP_DEFAULT_PORT + 1;
+ while (sockd < CSMP_MAX_SOCKETS)
+ {
+ if (socket_ports[sockd] == 0)
+ {
+ socket_ports[sockd] = htons(port);
+ return sockd;
+ }
+ port = ntohs(socket_ports[sockd]) + 1;
+ sockd++;
+ }
+ return OSAL_FAILURE;
+}
+
+osal_basetype_t osal_socket_close(osal_socket_handle_t sockd)
+{
+ if (sockd < 0 || sockd >= CSMP_MAX_SOCKETS)
+ {
+ return OSAL_FAILURE;
+ }
+ socket_ports[sockd] = 0;
+ return OSAL_SUCCESS;
+}
+
+osal_basetype_t osal_bind(osal_socket_handle_t sockd, osal_sockaddr_t *addr, osal_socklen_t addrlen)
+{
+ if (sockd >= CSMP_MAX_SOCKETS || sockd < 0)
+ {
+ return OSAL_FAILURE;
+ }
+ socket_ports[sockd] = addr->sin6_port;
+ return OSAL_SUCCESS;
+}
+
+osal_basetype_t osal_task_setcanceltype()
+{
+ return OSAL_FAILURE;
+}
+
+void osal_update_sockaddr(osal_sockaddr_t *listen_addr, uint16_t sport)
+{
+ listen_addr->sin6_port = htons(sport);
+}
+
+osal_basetype_t osal_read_groups(uint32_t *groups, uint8_t num_groups)
+{
+ if (num_groups < CSMP_GROUP_NUM_TYPES)
+ {
+ return R_RESULT_INSUFFICIENT_OUTPUT_BUFFER;
+ }
+
+ uint32_t csmpGroups[CSMP_GROUP_NUM_TYPES] = {0};
+ r_result_t flashRes = R_Flash_Read(R_FLASH_CSMP_GROUPS, &csmpGroups, R_FLASH_CSMP_GROUPS_SIZE);
+ if (flashRes == R_RESULT_SUCCESS)
+ {
+ R_LOG_DBG("Restored CSMP group information from NVM");
+ R_memcpy(groups, csmpGroups, R_FLASH_CSMP_GROUPS_SIZE);
+ return OSAL_SUCCESS;
+ }
+ return OSAL_FAILURE;
+}
+
+osal_basetype_t osal_write_groups(uint32_t *groups, uint8_t num_groups)
+{
+ if (num_groups > CSMP_GROUP_NUM_TYPES)
+ {
+ return R_RESULT_INSUFFICIENT_OUTPUT_BUFFER;
+ }
+
+ R_Flash_Write(R_FLASH_CSMP_GROUPS, groups, num_groups * sizeof(groups[0]));
+ return OSAL_SUCCESS;
+}
+
+int R_CSMP_Start(const r_ipv6addr_t* serverAddr, const uint8_t* localEui64, uint32_t regImin, uint32_t regImax)
+{
+ dev_config_t devConfig = { 0 };
+ if (!serverAddr || R_IPv6_IsUnspecified(serverAddr) || R_IPv6_IsMulticast(serverAddr))
+ {
+ R_LOG_WARN("CSMP could not be started due to invalid NMS address: %{ipv6addr}", serverAddr->bytes);
+ return R_RESULT_INVALID_PARAMETER;
+ }
+ R_memcpy(devConfig.NMSaddr.s6_addr, serverAddr->bytes, R_IPV6_ADDRESS_LENGTH);
+ R_memcpy(devConfig.ieee_eui64.data, localEui64, R_EUI64_LEN);
+ devConfig.reginterval_min = regImin;
+ devConfig.reginterval_max = regImax;
+
+ if (devConfig.reginterval_max < devConfig.reginterval_min || devConfig.reginterval_min == 0
+ || devConfig.reginterval_max > 36000)
+ {
+ R_LOG_ERR("reg interval error (min=%{u32}; max=%{u32})", devConfig.reginterval_min, devConfig.reginterval_max);
+ return R_RESULT_INVALID_PARAMETER;
+ }
+
+ /* Enable the CSMP signature parameters */
+ devConfig.csmp_sig_settings.reqsignedpost = true;
+ devConfig.csmp_sig_settings.reqvalidcheckpost = true;
+ devConfig.csmp_sig_settings.reqtimesyncpost = true;
+ devConfig.csmp_sig_settings.reqseclocalpost = true;
+ devConfig.csmp_sig_settings.reqsignedresp = true;
+ devConfig.csmp_sig_settings.reqvalidcheckresp = true;
+ devConfig.csmp_sig_settings.reqtimesyncresp = true;
+ devConfig.csmp_sig_settings.reqseclocalresp = true;
+
+ /*************************************************************
+ init the csmp_handle parameter of csmp_service_start func:
+ * callback function for the GET TLV request
+ * callback function for the POST TLV request
+ * callback function for the signature verification
+ **************************************************************/
+ r_csmp_handle.csmptlvs_get = (csmptlvs_get_t)csmptlvs_get;
+ r_csmp_handle.csmptlvs_post = (csmptlvs_post_t)csmptlvs_post;
+ r_csmp_handle.signature_verify = (signature_verify_t)signature_verify;
+
+ if (csmp_service_start(&devConfig, &r_csmp_handle) < 0)
+ {
+ R_LOG_ERR("start csmp agent service: fail!");
+ return 1;
+ }
+ R_LOG_DBG("start csmp agent service: success!");
+
+ /* Initialize csmpIsInitialized for the subsequent stop */
+ csmpIsInitialized = R_TRUE;
+ return 0;
+}
+
+int R_CSMP_Stop()
+{
+ if (csmpIsInitialized)
+ {
+ /* Set csmpIsInitialized to FALSE to avoid a double reset. */
+ csmpIsInitialized = R_FALSE;
+
+ if (csmp_service_stop())
+ {
+ R_LOG_DBG("stop csmp agent service: success!");
+ return 0;
+ }
+ else
+ {
+ R_LOG_ERR("stop csmp agent service: fail!");
+ return 1;
+ }
+ }
+ /* return immediately */
+ return 0;
+}
diff --git a/osal/renesas_wisun/osal_renesas_wrapper.h b/osal/renesas_wisun/osal_renesas_wrapper.h
new file mode 100644
index 0000000..717efe8
--- /dev/null
+++ b/osal/renesas_wisun/osal_renesas_wrapper.h
@@ -0,0 +1,49 @@
+/******************************************************************************
+ * DISCLAIMER
+ * This software is supplied by Renesas Electronics Corporation and is only
+ * intended for use with Renesas products. No other uses are authorized. This
+ * software is owned by Renesas Electronics Corporation and is protected under
+ * all applicable laws, including copyright laws.
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
+ * THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT
+ * LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.
+ * TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS
+ * ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR
+ * ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE
+ * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ * Renesas reserves the right, without notice, to make changes to this
+ * software and to discontinue the availability of this software. By using this
+ * software, you agree to the additional terms and conditions found by
+ * accessing the following link:
+ * http://www.renesas.com/disclaimer
+ *
+ * Copyright (C) 2025 Renesas Electronics Corporation. All rights reserved.
+ *****************************************************************************/
+
+/**
+ * @file osal_renesas_wrapper.h
+ * @brief API of the Renesas wrapper around the CsmpAgentLib implementation by Cisco so that it may be used within the
+ * Renesas Wi-SUN FAN sample application.
+ */
+
+#ifndef OSAL_RENESAS_WRAPPER_H
+#define OSAL_RENESAS_WRAPPER_H
+
+/**
+ * Start the CSMP module. Several periodic timers will be started.
+ * @param serverAddr The IPv6 address of the server (Cisco IoT Field Network Director).
+ * @param localEui64 The EUI-64 of this device.
+ * @param regImin The minimum registration interval of this device.
+ * @param regImax The maximum registration interval of this device.
+ * @return 0 if the CSMP module was successfully started.
+ */
+int R_CSMP_Start(const r_ipv6addr_t* serverAddr, const uint8_t* localEui64, uint32_t regImin, uint32_t regImax);
+
+/**
+ * Stop the CSMP module and related timers.
+ * @return 0 if the CSMP module was successfully stopped.
+ */
+int R_CSMP_Stop();
+#endif /* OSAL_RENESAS_WRAPPER_H */
diff --git a/sample/tlvs/renesas_tlvs.c b/sample/tlvs/renesas_tlvs.c
new file mode 100644
index 0000000..6c6476c
--- /dev/null
+++ b/sample/tlvs/renesas_tlvs.c
@@ -0,0 +1,1708 @@
+/******************************************************************************
+ * DISCLAIMER
+ * This software is supplied by Renesas Electronics Corporation and is only
+ * intended for use with Renesas products. No other uses are authorized. This
+ * software is owned by Renesas Electronics Corporation and is protected under
+ * all applicable laws, including copyright laws.
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
+ * THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT
+ * LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.
+ * TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS
+ * ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR
+ * ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE
+ * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ * Renesas reserves the right, without notice, to make changes to this
+ * software and to discontinue the availability of this software. By using this
+ * software, you agree to the additional terms and conditions found by
+ * accessing the following link:
+ * http://www.renesas.com/disclaimer
+ *
+ * Copyright (C) 2026 Renesas Electronics Corporation. All rights reserved.
+ *****************************************************************************/
+
+/**
+ * @file renesas_wisun_tlvs.c
+ * @brief Renesas implementation of the individual CSMP get and post TLV requests.
+ */
+
+
+#include
+#include
+
+#include "r_nwk_api.h"
+#include "r_impl_utils.h"
+#include "r_mem_tools.h"
+#include "r_app_config.h"
+
+#include "osal_renesas_config.h"
+
+#include "csmp_service.h"
+#include "csmp_info.h"
+
+#include "signature_verify.h"
+#if R_FWUP_SUPPORT
+#include "r_flash_wrapper.h"
+#include "r_fwup_wrapper.h"
+#if !defined(__i386) && !defined(__x86_64)
+#include "r_fwup_private.h"
+#include "r_fwup_wrap_flash.h"
+#include "r_fwup_config.h"
+#endif
+#endif
+
+// R_LOG_PREFIX is processed by RLog code generator
+#define R_LOG_PREFIX CSMP
+#include "r_log_internal.h"
+#if R_LOG_THRESHOLD > R_LOG_SEVERITY_OFF
+#include "r_loggen_renesas_wisun_tlvs.h"
+#endif
+
+/** The hardware information */
+Hardware_Desc g_tlv_hardwareDesc = HARDWARE_DESC_INIT;
+
+/** The interface information */
+Interface_Desc g_tlv_interfaceDesc = INTERFACE_DESC_INIT;
+
+/** The ipaddress information */
+IP_Address g_tlv_ipAddress[R_TLV_IPADDRESS_MAX_NUM] = {IPADDRESS_INIT};
+
+/** The ip route information */
+IP_Route g_tlv_ipRoute[R_TLV_ROUTES_MAX_NUM] = {IPROUTE_INIT};
+
+/** The current time information */
+Current_Time g_tlv_currentTime = CURRENT_TIME_INIT;
+
+/** The up time information */
+Up_Time g_tlv_upTime = UPTIME_INIT;
+
+/** The interface metrics data */
+Interface_Metrics g_tlv_interfaceMetrics = INTERFACE_METRICS_INIT;
+
+/** The ip route rpl metrics data */
+IPRoute_RPLMetrics g_tlv_iprouteRplmetrics[R_TLV_ROUTES_MAX_NUM] = {IPROUTE_RPLMETRICS_INIT};
+
+/** The wpan status data */
+WPAN_Status g_tlv_wpanStatus = WPANSTATUS_INIT;
+
+/** The rpl data */
+RPL_Instance g_tlv_rplInstance = RPLINSTANCE_INIT;
+
+/** The signature settings data */
+Signature_Settings g_SignatureSettings = SIGNATURE_SETTINGS_INIT;
+
+/** The transfer request data */
+Transfer_Request g_transferRequest = TRANSFER_REQUEST_INIT;
+
+/** The image block data */
+Image_Block g_imageBlock = IMAGE_BLOCK_INIT;
+
+/** The load request data */
+Load_Request g_loadRequest = LOAD_REQUEST_INIT;
+
+/** The cancel load request data */
+Cancel_Load_Request g_cancelLoadRequest = CANCEL_LOAD_REQUEST_INIT;
+
+/** The firmware info data */
+Firmware_Image_Info g_firmwareImageInfo[CSMP_FWMGMT_ACTIVE_SLOTS] = {FIRMWARE_IMAGE_INFO_INIT};
+
+bool g_downloadbusy = false; // Track ongoing download
+
+#if R_FWUP_SUPPORT
+static const char baseHwId[] = R_CSMP_HARDWARE_ID;
+static const char baseFileName[] = R_CSMP_FILE_NAME;
+static const char baseCsmpVersion[] = R_CSMP_VERSION_STRING;
+static const char baseLibraryVersion[] = R_NWK_BASE_VERSION_STRING;
+static bool initxfer = false; // Track transfer request
+static bool initload = false; // Track load request
+static uint32_t curloadtime = 0; // Track current loadtime
+
+static r_boolean_t uploadSlotValid = R_FALSE;
+extern r_fwup_state_t fwupState;
+/** The information about the firmware image being uploaded */
+static Csmp_Slothdr uploadSlotHdr = CSMP_SLOTHDR_INIT;
+
+/** The file hash is either read from non-volatile memory or from the running firmware header */
+static uint8_t runImageFileHash[SHA256_HASH_SIZE] = { 0 };
+/** The file name is either read from non-volatile memory or computed from the app name and the file hash */
+static char runImageFileName[FILE_NAME_SIZE] = "";
+/**
+ * The file size is read from non-volatile memory. It is valid only after switching to an image transferred by FND;
+ * otherwise it should be 0.
+ */
+static uint32_t runImageFileSize = 0;
+#endif
+
+extern const uint8_t* AppIpGetMacAddr(void);
+
+typedef union
+{
+ r_nwk_nd_cache_t nd_cache;
+ uint8_t placeholder[sizeof(r_nwk_nd_cache_t) + R_NWK_MAX_NEIGHBORS_PER_CONFIRM * sizeof(r_nwk_nd_cache_entry_t)];
+} nd_cache_buffer_t;
+
+/** public key */
+static const char pubkey[PUBLIC_KEY_LEN] = {
+ 0x04, 0x23, 0xD2, 0x83, 0x45, 0xE8, 0xD5, 0xDF, 0x86, 0x9D,
+ 0x6E, 0xE7, 0x58, 0x0D, 0xC1, 0x8F, 0x35, 0x9D, 0x57, 0xB1,
+ 0x3D, 0x50, 0x4A, 0x16, 0x01, 0x15, 0xC4, 0x81, 0x19, 0xB0,
+ 0xE6, 0x60, 0xB8, 0x64, 0x14, 0x01, 0x5D, 0x56, 0x83, 0xBE,
+ 0xE1, 0x85, 0x98, 0xCB, 0x90, 0xE1, 0xF7, 0x9B, 0xF4, 0x33,
+ 0x5A, 0x4B, 0x29, 0xAD, 0x35, 0x69, 0x9B, 0x4F, 0xDC, 0x42,
+ 0x7F, 0xEB, 0xC2, 0x99, 0xA5
+};
+
+void pubkey_get(const char** key, size_t len)
+{
+ if (key && (len == sizeof(pubkey)))
+ {
+ *key = pubkey;
+ }
+}
+
+void* hardware_desc_get(uint32_t* num)
+{
+ *num = 1;
+ MEMZERO_S(&g_tlv_hardwareDesc);
+
+ g_tlv_hardwareDesc.has_entphysicalindex = true;
+ g_tlv_hardwareDesc.has_entphysicaldescr = true;
+ g_tlv_hardwareDesc.has_entphysicalclass = true;
+ g_tlv_hardwareDesc.has_entphysicalname = true;
+ g_tlv_hardwareDesc.has_entphysicalhardwarerev = true;
+ g_tlv_hardwareDesc.has_entphysicalfirmwarerev = true;
+ g_tlv_hardwareDesc.has_entphysicalsoftwarerev = true;
+ g_tlv_hardwareDesc.has_entphysicalserialnum = true;
+ g_tlv_hardwareDesc.has_entphysicalmfgname = true;
+ g_tlv_hardwareDesc.has_entphysicalmodelname = true;
+ g_tlv_hardwareDesc.has_entphysicalmfgdate = true;
+ g_tlv_hardwareDesc.has_entphysicalfunction = true;
+ g_tlv_hardwareDesc.has_entphysicaloui = true;
+
+ g_tlv_hardwareDesc.entphysicalindex = 1;
+ sprintf(g_tlv_hardwareDesc.entphysicaldescr, "Renesas Wi-SUN FAN CSMP Agent");
+ g_tlv_hardwareDesc.entphysicalclass = CLASS_MODULE;
+ sprintf(g_tlv_hardwareDesc.entphysicalname, "lowpan"); // Other values ("Wi-SUN FAN Node") seem to conflict with the server
+ sprintf(g_tlv_hardwareDesc.entphysicalhardwarerev, "1.0");
+ sprintf(g_tlv_hardwareDesc.entphysicalfirmwarerev, R_CSMP_VERSION_STRING);
+ const char* appName = R_APP_GetAppName();
+ strncpy(g_tlv_hardwareDesc.entphysicalsoftwarerev, appName, sizeof(g_tlv_hardwareDesc.entphysicalsoftwarerev));
+ g_tlv_hardwareDesc.entphysicalsoftwarerev[sizeof(g_tlv_hardwareDesc.entphysicalsoftwarerev) - 1] = '\0';
+ const uint8_t* p_macAddr = AppIpGetMacAddr();
+ snprintf(g_tlv_hardwareDesc.entphysicalserialnum, sizeof(g_tlv_hardwareDesc.entphysicalserialnum),
+ "%02X%02X%02X%02X%02X%02X%02X%02X",
+ p_macAddr[0], p_macAddr[1], p_macAddr[2], p_macAddr[3],
+ p_macAddr[4], p_macAddr[5], p_macAddr[6], p_macAddr[7]);
+ snprintf(g_tlv_hardwareDesc.entphysicaloui, sizeof(g_tlv_hardwareDesc.entphysicaloui),
+ "%02X%02X%02X",
+ p_macAddr[0], p_macAddr[1], p_macAddr[2]);
+ sprintf(g_tlv_hardwareDesc.entphysicalmfgname, "Renesas Electronics");
+ const char* hardwareName = R_APP_GetHardwareName();
+ strncpy(g_tlv_hardwareDesc.entphysicalmodelname, hardwareName, sizeof(g_tlv_hardwareDesc.entphysicalmodelname));
+ g_tlv_hardwareDesc.entphysicalmodelname[sizeof(g_tlv_hardwareDesc.entphysicalmodelname) - 1] = '\0';
+ g_tlv_hardwareDesc.entphysicalmfgdate = R_APP_RELEASE_DATE;
+ g_tlv_hardwareDesc.entphysicalfunction = 1;
+
+ return &g_tlv_hardwareDesc;
+}
+
+void* interface_desc_get(uint32_t* num)
+{
+ *num = 1;
+ MEMZERO_S(&g_tlv_interfaceDesc);
+
+ g_tlv_interfaceDesc.has_ifindex = true;
+ g_tlv_interfaceDesc.has_ifname = true;
+ g_tlv_interfaceDesc.has_ifdescr = true;
+ g_tlv_interfaceDesc.has_iftype = true;
+ g_tlv_interfaceDesc.has_ifmtu = true;
+ g_tlv_interfaceDesc.has_ifphysaddress = true;
+
+ g_tlv_interfaceDesc.ifindex = 1;
+ sprintf(g_tlv_interfaceDesc.ifname, "lowpan");
+ sprintf(g_tlv_interfaceDesc.ifdescr, "ieee802154");
+ g_tlv_interfaceDesc.iftype = 259;
+ g_tlv_interfaceDesc.ifmtu = R_MAX_MTU_SIZE;
+ const uint8_t* p_macAddr = AppIpGetMacAddr();
+ g_tlv_interfaceDesc.ifphysaddress.len = 8;
+ MEMCPY_A(g_tlv_interfaceDesc.ifphysaddress.data, p_macAddr);
+
+ return &g_tlv_interfaceDesc;
+}
+
+void* ipaddress_get(uint32_t* num)
+{
+ uint32_t i = 0;
+ uint8_t ipv6Addr[R_IPV6_ADDRESS_LENGTH];
+ const uint8_t* p_macAddr = AppIpGetMacAddr();
+ MEMZERO_A(g_tlv_ipAddress);
+
+ g_tlv_ipAddress[i].ipaddressaddrtype = IPV6;
+ g_tlv_ipAddress[i].ipaddressaddr.len = R_IPV6_ADDRESS_LENGTH;
+ g_tlv_ipAddress[i].ipaddressaddr.data[0] = 0xfe;
+ g_tlv_ipAddress[i].ipaddressaddr.data[1] = 0x80;
+ R_memcpy(g_tlv_ipAddress[i].ipaddressaddr.data + 8, p_macAddr, 8);
+ g_tlv_ipAddress[i].ipaddressaddr.data[8] ^= 0x02;
+ g_tlv_ipAddress[i].ipaddressifindex = 1;
+ g_tlv_ipAddress[i].ipaddresstype = UNICAST;
+ g_tlv_ipAddress[i].ipaddressorigin = LINKLAYER;
+ g_tlv_ipAddress[i].ipaddresspfxlen = R_IPV6_PREFIX_LEN * 8u;
+ g_tlv_ipAddress[i].ipaddressindex = (int32_t)i + 1;
+ i++;
+
+ if (R_NWK_GetRequest(R_NWK_nwkIpv6Address, ipv6Addr, sizeof(ipv6Addr)) == R_RESULT_SUCCESS
+ && MEMISNOTZERO_S(&ipv6Addr))
+ {
+ g_tlv_ipAddress[i].ipaddressaddrtype = IPV6;
+ g_tlv_ipAddress[i].ipaddressaddr.len = R_IPV6_ADDRESS_LENGTH;
+ MEMCPY_A(g_tlv_ipAddress[i].ipaddressaddr.data, ipv6Addr);
+ g_tlv_ipAddress[i].ipaddressifindex = 1;
+ g_tlv_ipAddress[i].ipaddresstype = UNICAST;
+ g_tlv_ipAddress[i].ipaddressorigin = DHCP;
+ g_tlv_ipAddress[i].ipaddresspfxlen = R_IPV6_PREFIX_LEN * 8u;
+ g_tlv_ipAddress[i].ipaddressindex = (int32_t)i + 1;
+ i++;
+ }
+ *num = i;
+ for (i = 0; i < *num; i++)
+ {
+ g_tlv_ipAddress[i].has_ipaddressindex = true;
+ g_tlv_ipAddress[i].has_ipaddressaddrtype = true;
+ g_tlv_ipAddress[i].has_ipaddressaddr = true;
+ g_tlv_ipAddress[i].has_ipaddressifindex = true;
+ g_tlv_ipAddress[i].has_ipaddresstype = true;
+ g_tlv_ipAddress[i].has_ipaddressorigin = true;
+ g_tlv_ipAddress[i].has_ipaddresspfxlen = true;
+ }
+
+ if (*num == 0)
+ {
+ return NULL;
+ }
+
+ return &g_tlv_ipAddress;
+}
+
+void* iproute_get(uint32_t* num)
+{
+ uint32_t i = 0;
+ r_ipv6addr_t apAddr;
+ MEMZERO_A(g_tlv_ipRoute);
+
+#if R_BORDER_ROUTER_ENABLED
+ uint8_t deviceType;
+ if (R_NWK_GetRequest(R_NWK_deviceType, &deviceType, sizeof(deviceType)) == R_RESULT_SUCCESS &&
+ deviceType != R_BORDERROUTER)
+#endif
+ {
+ if (R_NWK_GetRequest(R_NWK_preferredParentAddress, &apAddr, sizeof(apAddr)) == R_RESULT_SUCCESS
+ && MEMISNOTZERO_S(&apAddr))
+ {
+ g_tlv_ipRoute[i].inetcidrroutedesttype = IPV6;
+ g_tlv_ipRoute[i].inetcidrroutedest.len = R_IPV6_ADDRESS_LENGTH;
+ MEMCPY_A(g_tlv_ipRoute[i].inetcidrroutedest.data, apAddr.bytes);
+ g_tlv_ipRoute[i].inetcidrroutepfxlen = R_IPV6_PREFIX_LEN * 8u;
+ g_tlv_ipRoute[i].inetcidrroutenexthoptype = IPV6;
+ g_tlv_ipRoute[i].inetcidrroutenexthop.len = R_IPV6_ADDRESS_LENGTH;
+ MEMCPY_A(g_tlv_ipRoute[i].inetcidrroutenexthop.data, apAddr.bytes);
+ g_tlv_ipRoute[i].inetcidrrouteifindex = 1;
+ g_tlv_ipRoute[i].inetcidrrouteindex = (int32_t)i + 1;
+ i++;
+ }
+ if (R_NWK_GetRequest(R_NWK_alternateParentAddress, &apAddr, sizeof(apAddr)) == R_RESULT_SUCCESS
+ && MEMISNOTZERO_S(&apAddr))
+ {
+ g_tlv_ipRoute[i].inetcidrroutedesttype = IPV6;
+ g_tlv_ipRoute[i].inetcidrroutedest.len = R_IPV6_ADDRESS_LENGTH;
+ MEMCPY_A(g_tlv_ipRoute[i].inetcidrroutedest.data, apAddr.bytes);
+ g_tlv_ipRoute[i].inetcidrroutepfxlen = R_IPV6_PREFIX_LEN * 8u;
+ g_tlv_ipRoute[i].inetcidrroutenexthoptype = IPV6;
+ g_tlv_ipRoute[i].inetcidrroutenexthop.len = R_IPV6_ADDRESS_LENGTH;
+ MEMCPY_A(g_tlv_ipRoute[i].inetcidrroutenexthop.data, apAddr.bytes);
+ g_tlv_ipRoute[i].inetcidrrouteifindex = 1;
+ g_tlv_ipRoute[i].inetcidrrouteindex = (int32_t)i + 1;
+ i++;
+ }
+ }
+
+ *num = i;
+ for (i = 0; i < *num; i++)
+ {
+ g_tlv_ipRoute[i].has_inetcidrrouteindex = true;
+ g_tlv_ipRoute[i].has_inetcidrroutedesttype = true;
+ g_tlv_ipRoute[i].has_inetcidrroutedest = true;
+ g_tlv_ipRoute[i].has_inetcidrroutepfxlen = true;
+ g_tlv_ipRoute[i].has_inetcidrroutenexthoptype = true;
+ g_tlv_ipRoute[i].has_inetcidrroutenexthop = true;
+ g_tlv_ipRoute[i].has_inetcidrrouteifindex = true;
+ }
+
+ if (*num == 0)
+ {
+ return NULL;
+ }
+
+ return &g_tlv_ipRoute;
+}
+
+void* currenttime_get(uint32_t* num)
+{
+ struct timeval tv = {0};
+
+ *num = 1;
+ MEMZERO_S(&g_tlv_currentTime);
+
+ g_tlv_currentTime.has_posix = true;
+ g_tlv_currentTime.has_source = true;
+
+ osal_gettime(&tv, NULL);
+ g_tlv_currentTime.posix = tv.tv_sec;
+ g_tlv_currentTime.source = 1; // Local
+
+ return &g_tlv_currentTime;
+}
+
+/**
+ * @brief Parse exactly @p n decimal digits from a character buffer.
+ *
+ * @param s Pointer to the input character buffer containing at least @p n characters.
+ * @param n Number of digits to parse.
+ * @param parsed The parsed numeric value.
+ *
+ * @return r_result_t R_RESULT_SUCCESS on success, R_RESULT_FAILED on parse error.
+ */
+static r_result_t parse_ndigits(const char *s, uint8_t n, uint32_t *parsed)
+{
+ *parsed = 0;
+ for (int i = 0; i < n; ++i)
+ {
+ unsigned char c = (unsigned char)s[i];
+ if (c < '0' || c > '9')
+ {
+ return R_RESULT_FAILED;
+ }
+ *parsed = *parsed * 10 + (c - '0');
+ }
+ return R_RESULT_SUCCESS;
+}
+
+/**
+ * @brief Determine whether a given year is a leap year.
+ *
+ * @param year Year to evaluate.
+ *
+ * @return r_boolean_t TRUE if @p year is a leap year, FALSE otherwise.
+ */
+static r_boolean_t is_leap(uint16_t year)
+{
+ return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
+}
+
+/**
+ * @brief Get the number of days in a given month of a specific year.
+ *
+ * @param year Year used to determine leap-year behavior.
+ * @param month Month index (1–12) for which the day count is requested.
+ *
+ * @return uint8_t Number of days in the specified month.
+ */
+static uint8_t days_in_month(const uint16_t year, const uint8_t month)
+{
+ static const uint8_t base_days[12] = {
+ 31, 28, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31
+ };
+ uint8_t d = base_days[month - 1];
+ if (month == 2 && is_leap(year))
+ {
+ d = 29;
+ }
+ return d;
+}
+
+/**
+ * @brief Compute the number of days since the Unix epoch (1970-01-01).
+ *
+ * @param year Year of the input date.
+ * @param month Month (1–12) of the input date.
+ * @param day Day (1–31) of the input date.
+ * @param days_since_epoch Pointer receiving the computed day count.
+ *
+ * @return r_result_t R_RESULT_SUCCESS on success, R_RESULT_FAILED on validation error.
+ */
+static r_result_t days_since_epoch(const uint16_t year, const uint8_t month, const uint8_t day, uint32_t *days_since_epoch)
+{
+ *days_since_epoch = 0;
+
+ if (year >= 1970)
+ {
+ uint16_t y;
+ for (y = 1970; y < year; ++y)
+ {
+ *days_since_epoch += 365 + is_leap(y);
+ }
+ }
+ else
+ {
+ return R_RESULT_FAILED;
+ }
+
+ for (uint8_t m = 1; m < month; ++m)
+ {
+ *days_since_epoch += days_in_month(year, m);
+ }
+
+ *days_since_epoch += (day - 1); // 1970-01-01 -> day index 0
+
+ return R_RESULT_SUCCESS;
+}
+
+/**
+ * @brief Parse an ISO 8601 timestamp string and convert it to a Unix timestamp.
+ *
+ * Supported formats:
+ * - "YYYY-MM-DDTHH:MM:SSZ"
+ * - "YYYY-MM-DDTHH:MM:SS+HH:MM"
+ * - "YYYY-MM-DDTHH:MM:SS-HH:MM"
+ * - "YYYY-MM-DD HH:MM:SSZ" (space instead of 'T')
+ *
+ * @param from Pointer to a zero-terminated ISO 8601 input string.
+ * @param to Pointer receiving the computed Unix timestamp in seconds.
+ *
+ * @return r_result_t R_RESULT_SUCCESS on success, R_RESULT_FAILED on parse or validation error.
+ */
+r_result_t iso8601_to_unix(const char *from, uint32_t *to)
+{
+ if (from == NULL)
+ {
+ return R_RESULT_FAILED;
+ }
+
+ const size_t len = strlen(from);
+ if (len < 19)
+ {
+ // Minimal length: "YYYY-MM-DDTHH:MM:SS"
+ return R_RESULT_FAILED;
+ }
+
+ // Parse date/time components
+ uint32_t year, month, day, hour, min, sec;
+ r_result_t result = parse_ndigits(from + 0, 4, &year);
+ result |= parse_ndigits(from + 5, 2, &month);
+ result |= parse_ndigits(from + 8, 2, &day);
+ const char sep1 = from[4]; // '-'
+ const char sep2 = from[7]; // '-'
+ const char sep3 = from[10]; // 'T' or ' '
+ result |= parse_ndigits(from + 11, 2, &hour);
+ const char sep4 = from[13]; // ':'
+ result |= parse_ndigits(from + 14, 2, &min);
+ const char sep5 = from[16]; // ':'
+ result |= parse_ndigits(from + 17, 2, &sec);
+
+ if (result != R_RESULT_SUCCESS)
+ {
+ return result;
+ }
+
+ if (month < 1 || month > 12 || day < 1 || day > 31 ||
+ hour > 23 || min > 59 || sec > 60)
+ {
+ return R_RESULT_FAILED;
+ }
+
+ if (sep1 != '-' || sep2 != '-' ||
+ (sep3 != 'T' && sep3 != ' ') ||
+ sep4 != ':' || sep5 != ':')
+ {
+ return R_RESULT_FAILED;
+ }
+
+ // Validate day against month/year
+ if (day > days_in_month(year, month))
+ {
+ return R_RESULT_FAILED;
+ }
+
+ // Timezone offset in seconds (relative to UTC)
+ int32_t offset_seconds = 0;
+
+ // Position after "YYYY-MM-DDTHH:MM:SS" (19 chars)
+ const uint8_t pos = 19;
+ if (pos < len)
+ {
+ const char c = from[pos];
+ if (c == 'Z' || c == 'z')
+ {
+ // UTC, offset 0
+ offset_seconds = 0;
+ }
+ else if (c == '+' || c == '-')
+ {
+ // +HH:MM or -HH:MM
+ if ((uint16_t) len < pos + 6)
+ {
+ return R_RESULT_FAILED; // not enough room for "+HH:MM"
+ }
+ const int32_t sign = (c == '+') ? 1 : -1;
+
+ uint32_t off_hour, off_min;
+ result = parse_ndigits(from + pos + 1, 2, &off_hour);
+ const char colon = from[pos + 3];
+ result |= parse_ndigits(from + pos + 4, 2, &off_min);
+
+ if (result != R_RESULT_SUCCESS ||
+ off_hour > 23 || off_min > 59 ||
+ colon != ':')
+ {
+ return R_RESULT_FAILED;
+ }
+
+ offset_seconds = sign * (int32_t) (off_hour * 3600 + off_min * 60);
+ }
+ else
+ {
+ // Unexpected trailing character
+ return R_RESULT_FAILED;
+ }
+ }
+
+ // Compute days since epoch and then total seconds
+ uint32_t days;
+ result = days_since_epoch(year, month, day, &days);
+
+ if (result != R_RESULT_SUCCESS)
+ {
+ return result;
+ }
+
+ *to = days * 86400 + hour * 3600 + min * 60 + sec;
+
+ /*
+ * Interpretation of timezone: "2025-11-15T10:30:00+02:00"
+ * means local_time = UTC + 2h => UTC = local_time - 2h
+ *
+ * We have parsed the *local* time (10:30).
+ * So we must subtract the offset to get UTC.
+ */
+ *to -= offset_seconds;
+
+ return R_RESULT_SUCCESS;
+}
+
+void currenttime_post(Current_Time* tlv)
+{
+ struct timeval tv = {0};
+
+ if (tlv->has_posix)
+ {
+ tv.tv_sec = tlv->posix;
+ osal_settime(&tv, NULL);
+ }
+ else if (tlv->has_iso8601)
+ {
+ uint32_t posix_seconds = 0;
+ iso8601_to_unix(tlv->iso8601, &posix_seconds);
+ tv.tv_sec = posix_seconds;
+ osal_settime(&tv, NULL);
+ }
+}
+
+void* uptime_get(uint32_t* num)
+{
+ *num = 1;
+ MEMZERO_S(&g_tlv_upTime);
+
+ g_tlv_upTime.has_sysuptime = true;
+
+ g_tlv_upTime.sysuptime = clock_seconds();
+
+ return &g_tlv_upTime;
+}
+
+void* interface_metrics_get(uint32_t* num)
+{
+ uint16_t phy_DataRate;
+ *num = 1;
+ MEMZERO_S(&g_tlv_interfaceMetrics);
+
+ g_tlv_interfaceMetrics.has_ifindex = true;
+ g_tlv_interfaceMetrics.has_ifinspeed = true;
+ g_tlv_interfaceMetrics.has_ifoutspeed = true;
+ g_tlv_interfaceMetrics.has_ifadminstatus = true;
+ g_tlv_interfaceMetrics.has_ifoperstatus = true;
+ g_tlv_interfaceMetrics.has_iflastchange = true;
+ g_tlv_interfaceMetrics.has_ifinoctets = true;
+ g_tlv_interfaceMetrics.has_ifoutoctets = true;
+ g_tlv_interfaceMetrics.has_ifindiscards = true;
+ g_tlv_interfaceMetrics.has_ifinerrors = true;
+ g_tlv_interfaceMetrics.has_ifoutdiscards = true;
+ g_tlv_interfaceMetrics.has_ifouterrors = true;
+
+ g_tlv_interfaceMetrics.ifindex = 1;
+ if (R_NWK_GetRequest(R_NWK_phyDataRate, &phy_DataRate, sizeof(phy_DataRate)) == R_RESULT_SUCCESS)
+ {
+ g_tlv_interfaceMetrics.ifinspeed = phy_DataRate;
+ g_tlv_interfaceMetrics.ifoutspeed = phy_DataRate;
+ }
+ else
+ {
+ g_tlv_interfaceMetrics.has_ifinspeed = false;
+ g_tlv_interfaceMetrics.has_ifoutspeed = false;
+ }
+ g_tlv_interfaceMetrics.ifadminstatus = IF_ADMIN_STATUS_UP;
+ g_tlv_interfaceMetrics.ifoperstatus = IF_OPER_STATUS_UP;
+ g_tlv_interfaceMetrics.iflastchange = clock_seconds();
+ r_nwk_if_mib_t mgmt;
+ if (R_NWK_GetRequest(R_NWK_fanInterfaceMgmtInfo, &mgmt, sizeof(mgmt)) == R_RESULT_SUCCESS)
+ {
+ g_tlv_interfaceMetrics.ifinoctets = mgmt.stats.ifinoctets;
+ g_tlv_interfaceMetrics.ifoutoctets = mgmt.stats.ifoutoctets;
+ g_tlv_interfaceMetrics.ifindiscards = mgmt.stats.ifindiscards + mgmt.stats.ifinunknownprotos;
+ g_tlv_interfaceMetrics.ifinerrors = mgmt.stats.ifinerrors;
+ g_tlv_interfaceMetrics.ifoutdiscards = mgmt.stats.ifoutdiscards;
+ g_tlv_interfaceMetrics.ifouterrors = mgmt.stats.ifouterrors;
+ }
+ else
+ {
+ g_tlv_interfaceMetrics.has_ifinoctets = false;
+ g_tlv_interfaceMetrics.has_ifoutoctets = false;
+ g_tlv_interfaceMetrics.has_ifindiscards = false;
+ g_tlv_interfaceMetrics.has_ifinerrors = false;
+ g_tlv_interfaceMetrics.has_ifoutdiscards = false;
+ g_tlv_interfaceMetrics.has_ifouterrors = false;
+ }
+
+ return &g_tlv_interfaceMetrics;
+}
+
+/**
+ * Search in the neighbor cache for a given address and fills the RPL metric information
+ * @param index The index of the metric information in the reported TLV
+ * @param apAddr The address for which the metrics needs to be retrieved
+ * @param metric The metric structure to be filled
+ * @return R_RESULT_SUCCESS if the address was found, R_RESULT_FAILED otherwise
+ */
+r_result_t fillIpRouteRplMetric(uint32_t index, r_ipv6addr_t* apAddr, IPRoute_RPLMetrics* metric)
+{
+ r_result_t res;
+ r_boolean_t isContinuation = R_FALSE;
+ do
+ {
+ nd_cache_buffer_t cfm;
+ res = R_NWK_GetRequestMultipart(R_NWK_ndCache, &cfm, sizeof(cfm), isContinuation);
+ if (res == R_RESULT_SUCCESS || res == R_RESULT_SUCCESS_ADDITIONAL_DATA)
+ {
+ isContinuation = R_TRUE;
+
+ for (uint16_t i = 0; i < cfm.nd_cache.count; i++)
+ {
+ r_boolean_t check = R_TRUE;
+ for (uint16_t j = 0; j < sizeof(cfm.nd_cache.entries[i].ipAddress.bytes); j++)
+ {
+ if (cfm.nd_cache.entries[i].ipAddress.bytes[j] != apAddr->bytes[j])
+ {
+ check = R_FALSE;
+ break;
+ }
+ }
+ if (check == R_TRUE)
+ {
+ metric->inetcidrrouteindex = (int32_t)index + 1;
+ metric->instanceindex = 1;
+ metric->rank = cfm.nd_cache.entries[i].rank;
+ metric->pathetx = cfm.nd_cache.entries[i].routingCost;
+ metric->linketx = cfm.nd_cache.entries[i].linkMetric;
+ metric->lqiforward = cfm.nd_cache.entries[i].nodeToNeighRsl;
+ metric->lqireverse = cfm.nd_cache.entries[i].neighToNodeRsl;
+ metric->rssiforward = -174 + cfm.nd_cache.entries[i].nodeToNeighRsl;
+ metric->rssireverse = -174 + cfm.nd_cache.entries[i].neighToNodeRsl;
+ metric->dagsize = cfm.nd_cache.entries[i].panSize;
+ return R_RESULT_SUCCESS;
+ }
+ }
+ }
+ else
+ {
+ return R_RESULT_FAILED;
+ }
+ }
+ while (res == R_RESULT_SUCCESS_ADDITIONAL_DATA);
+ return R_RESULT_FAILED;
+}
+
+void* iproute_rplmetrics_get(uint32_t* num)
+{
+ uint32_t index = 0;
+ r_ipv6addr_t apAddr;
+ MEMZERO_A(g_tlv_iprouteRplmetrics);
+
+#if R_BORDER_ROUTER_ENABLED
+ uint8_t deviceType;
+ if (R_NWK_GetRequest(R_NWK_deviceType, &deviceType, sizeof(deviceType)) == R_RESULT_SUCCESS &&
+ deviceType != R_BORDERROUTER)
+#endif
+ {
+ if (R_NWK_GetRequest(R_NWK_preferredParentAddress, &apAddr, sizeof(apAddr)) == R_RESULT_SUCCESS
+ && MEMISNOTZERO_S(&apAddr))
+ {
+ if (fillIpRouteRplMetric(index, &apAddr, &g_tlv_iprouteRplmetrics[index]) == R_RESULT_SUCCESS)
+ {
+ index++;
+ }
+ }
+
+ if (R_NWK_GetRequest(R_NWK_alternateParentAddress, &apAddr, sizeof(apAddr)) == R_RESULT_SUCCESS
+ && MEMISNOTZERO_S(&apAddr))
+ {
+ if (fillIpRouteRplMetric(index, &apAddr, &g_tlv_iprouteRplmetrics[index]) == R_RESULT_SUCCESS)
+ {
+ index++;
+ }
+ }
+ }
+
+ *num = index;
+ for (uint32_t i = 0; i < *num; i++)
+ {
+ g_tlv_iprouteRplmetrics[i].has_inetcidrrouteindex = true;
+ g_tlv_iprouteRplmetrics[i].has_instanceindex = true;
+ g_tlv_iprouteRplmetrics[i].has_rank = true;
+ g_tlv_iprouteRplmetrics[i].has_pathetx = true;
+ g_tlv_iprouteRplmetrics[i].has_linketx = true;
+ g_tlv_iprouteRplmetrics[i].has_rssiforward = true;
+ g_tlv_iprouteRplmetrics[i].has_rssireverse = true;
+ g_tlv_iprouteRplmetrics[i].has_lqiforward = true;
+ g_tlv_iprouteRplmetrics[i].has_lqireverse = true;
+ g_tlv_iprouteRplmetrics[i].has_dagsize = true;
+ }
+
+ if (*num == 0)
+ {
+ return NULL;
+ }
+
+ return &g_tlv_iprouteRplmetrics;
+}
+
+void* wpanstatus_get(uint32_t* num)
+{
+ *num = 1;
+ MEMZERO_S(&g_tlv_wpanStatus);
+
+ g_tlv_wpanStatus.has_ifindex = true;
+ g_tlv_wpanStatus.has_ssid = true;
+ g_tlv_wpanStatus.has_panid = true;
+ g_tlv_wpanStatus.has_dot1xenabled = true;
+ g_tlv_wpanStatus.has_securitylevel = true;
+ g_tlv_wpanStatus.has_rank = true;
+ g_tlv_wpanStatus.has_beaconvalid = true;
+ g_tlv_wpanStatus.has_beaconversion = true;
+ g_tlv_wpanStatus.has_beaconage = true;
+ g_tlv_wpanStatus.has_txpower = true;
+ g_tlv_wpanStatus.has_dagsize = true;
+ g_tlv_wpanStatus.has_metric = true;
+ g_tlv_wpanStatus.has_lastchanged = true;
+ g_tlv_wpanStatus.has_lastchangedreason = true;
+ g_tlv_wpanStatus.has_demomodeenabled = false;
+
+ g_tlv_wpanStatus.ifindex = 1;
+ const char* netName = R_APP_GetNetworkName();
+ strncpy((char *) g_tlv_wpanStatus.ssid.data, netName, sizeof(g_tlv_wpanStatus.ssid.data));
+ g_tlv_wpanStatus.ssid.data[sizeof(g_tlv_wpanStatus.ssid.data) - 1] = '\0';
+ g_tlv_wpanStatus.ssid.len = strlen(netName) + 1;
+ uint16_t panId;
+ if (R_NWK_GetRequest(R_NWK_macPANId, &panId, sizeof(panId)) == R_RESULT_SUCCESS)
+ {
+ g_tlv_wpanStatus.panid = panId;
+ }
+ else
+ {
+ g_tlv_wpanStatus.has_panid = false;
+ }
+ g_tlv_wpanStatus.dot1xenabled = true;
+ g_tlv_wpanStatus.securitylevel = IEEE154_SEC_MIC_32;
+#if !R_LEAF_NODE_ENABLED
+ r_nwk_rpl_info_t rplInfo;
+ if (R_NWK_GetRequest(R_NWK_rplInfo, &rplInfo, sizeof(rplInfo)) == R_RESULT_SUCCESS)
+ {
+ g_tlv_wpanStatus.rank = rplInfo.rank;
+ }
+ else
+#endif
+ {
+ g_tlv_wpanStatus.has_rank = false;
+ }
+#if !R_LEAF_NODE_ENABLED
+ uint16_t panVersion;
+ if (R_NWK_GetRequest(R_NWK_panVersion, &panVersion, sizeof(panVersion)) == R_RESULT_SUCCESS)
+ {
+ g_tlv_wpanStatus.beaconversion = panVersion;
+ }
+ else
+#endif
+ {
+ g_tlv_wpanStatus.has_beaconversion = false;
+ }
+ g_tlv_wpanStatus.beaconvalid = true;
+ clock_time_t beaconAge;
+ if (R_NWK_GetRequest(R_NWK_lastPanConfigRecvTime, &beaconAge, sizeof(beaconAge)) == R_RESULT_SUCCESS)
+ {
+ g_tlv_wpanStatus.beaconage = clock_seconds() - beaconAge;
+ }
+ else
+ {
+ g_tlv_wpanStatus.has_beaconage = false;
+ }
+
+ int32_t csmpTxPower = INT32_MAX;
+#if R_PHY_TYPE_CWX_M
+ int8_t cwxTxPower;
+ if (R_NWK_GetRequest(R_NWK_phyFskTransmitPower, &cwxTxPower, sizeof(cwxTxPower)) == R_RESULT_SUCCESS)
+ {
+ /* Convert CWX value to actual TX power output at antenna connector in dBm */
+ csmpTxPower = cwxTxPower / 2; // CWX value is in units of 0.5 dBm
+ csmpTxPower = csmpTxPower + 22; // 20-24 dB FEM gain depending on CWX board type -> Add avg FEM gain
+ }
+#else
+ uint8_t trgTxPower;
+ if (R_NWK_GetRequest(R_NWK_phyTransmitPower, &trgTxPower, sizeof(trgTxPower)) == R_RESULT_SUCCESS)
+ {
+ csmpTxPower = trgTxPower; // TODO convert TRG value to dBm (requires mapping table depending on region)
+ }
+#endif
+ if (csmpTxPower != INT32_MAX)
+ {
+ g_tlv_wpanStatus.txpower = csmpTxPower;
+ }
+ else
+ {
+ g_tlv_wpanStatus.has_txpower = false;
+ }
+ uint16_t panSize = 0;
+ if (R_NWK_GetRequest(R_NWK_panSize, &panSize, sizeof(panSize)))
+ {
+ g_tlv_wpanStatus.dagsize = panSize;
+ }
+ else
+ {
+ g_tlv_wpanStatus.has_dagsize = false;
+ }
+
+#if R_BORDER_ROUTER_ENABLED
+ uint8_t deviceType;
+ if (R_NWK_GetRequest(R_NWK_deviceType, &deviceType, sizeof(deviceType)) == R_RESULT_SUCCESS &&
+ deviceType == R_BORDERROUTER)
+ {
+ g_tlv_wpanStatus.has_metric = false;
+ }
+ else
+#endif
+ {
+ r_result_t res;
+ r_ipv6addr_t addrPreferred;
+ r_boolean_t foundPreferred = R_FALSE;
+ if (R_NWK_GetRequest(R_NWK_preferredParentAddress, &addrPreferred, sizeof(addrPreferred)) == R_RESULT_SUCCESS
+ && MEMISNOTZERO_S(&addrPreferred))
+ {
+ r_boolean_t isContinuation = R_FALSE;
+ do
+ {
+ nd_cache_buffer_t cfm;
+ res = R_NWK_GetRequestMultipart(R_NWK_ndCache, &cfm, sizeof(cfm), isContinuation);
+ if (res == R_RESULT_SUCCESS || res == R_RESULT_SUCCESS_ADDITIONAL_DATA)
+ {
+ if (!isContinuation)
+ {
+ isContinuation = R_TRUE;
+ }
+ for (uint16_t i = 0; i < cfm.nd_cache.count; i++)
+ {
+ r_boolean_t check = R_TRUE;
+ for (uint16_t j = 0; j < sizeof(cfm.nd_cache.entries[i].ipAddress.bytes); j++)
+ {
+ if (cfm.nd_cache.entries[i].ipAddress.bytes[j] != addrPreferred.bytes[j])
+ {
+ check = R_FALSE;
+ break;
+ }
+ }
+ if (check == R_TRUE)
+ {
+ g_tlv_wpanStatus.metric = cfm.nd_cache.entries[i].routingCost + cfm.nd_cache.entries[i].linkMetric;
+ foundPreferred = R_TRUE;
+ break;
+ }
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ while (res == R_RESULT_SUCCESS_ADDITIONAL_DATA);
+ if (!foundPreferred)
+ {
+ g_tlv_wpanStatus.has_metric = false;
+ }
+ }
+ else
+ {
+ g_tlv_wpanStatus.has_metric = false;
+ }
+ }
+
+ g_tlv_wpanStatus.lastchanged = 0;
+ g_tlv_wpanStatus.lastchangedreason = 0;
+
+ return &g_tlv_wpanStatus;
+}
+
+void* rplinstance_get(uint32_t* num)
+{
+ *num = 1;
+ MEMZERO_S(&g_tlv_rplInstance);
+
+ g_tlv_rplInstance.has_instanceindex = true;
+ g_tlv_rplInstance.has_instanceid = true;
+ g_tlv_rplInstance.has_dodagid = true;
+ g_tlv_rplInstance.has_dodagversionnumber = true;
+ g_tlv_rplInstance.has_rank = true;
+ g_tlv_rplInstance.has_parentcount = true;
+ g_tlv_rplInstance.has_dagsize = true;
+
+ g_tlv_rplInstance.instanceindex = 1;
+#if !R_LEAF_NODE_ENABLED
+ r_nwk_rpl_info_t rplInfo;
+ if (R_NWK_GetRequest(R_NWK_rplInfo, &rplInfo, sizeof(rplInfo)) == R_RESULT_SUCCESS)
+ {
+ MEMCPY_A(g_tlv_rplInstance.dodagid.data, rplInfo.dodag_id.bytes);
+ g_tlv_rplInstance.dodagid.len = 16;
+ g_tlv_rplInstance.dodagversionnumber = rplInfo.dodag_version;
+ g_tlv_rplInstance.rank = rplInfo.rank;
+ g_tlv_rplInstance.instanceid = rplInfo.instance_id;
+ }
+ else
+#endif
+ {
+ g_tlv_rplInstance.has_dodagid = false;
+ g_tlv_rplInstance.has_dodagversionnumber = false;
+ g_tlv_rplInstance.has_rank = false;
+ g_tlv_rplInstance.has_instanceid = false;
+ }
+
+#if R_BORDER_ROUTER_ENABLED
+ uint8_t deviceType;
+ if (R_NWK_GetRequest(R_NWK_deviceType, &deviceType, sizeof(deviceType)) == R_RESULT_SUCCESS &&
+ deviceType == R_BORDERROUTER)
+ {
+ g_tlv_rplInstance.parentcount = 0;
+ }
+ else
+#endif
+ {
+ g_tlv_rplInstance.parentcount = 1; // We must have a Preferred Parent (otherwise we could not communicate with NMS)
+ r_ipv6addr_t apAddr;
+ if (R_NWK_GetRequest(R_NWK_alternateParentAddress, &apAddr, sizeof(apAddr)) == R_RESULT_SUCCESS
+ && MEMISNOTZERO_S(&apAddr))
+ {
+ g_tlv_rplInstance.parentcount++; // We also have an Alternate Parent -> Increment counter
+ }
+ }
+
+ uint16_t nwkPanSize = 0;
+ if (R_NWK_GetRequest(R_NWK_panSize, &nwkPanSize, sizeof(nwkPanSize)) == R_RESULT_SUCCESS)
+ {
+ g_tlv_rplInstance.dagsize = nwkPanSize;
+ }
+ else
+ {
+ g_tlv_rplInstance.has_dagsize = false;
+ }
+
+ return &g_tlv_rplInstance;
+}
+
+void* signature_settings_get(uint32_t* num)
+{
+ *num = 1;
+
+ return &g_SignatureSettings;
+}
+
+void signature_settings_post(Signature_Settings* tlv)
+{
+ g_SignatureSettings.has_reqsignedpost = true;
+ g_SignatureSettings.reqsignedpost = tlv->reqsignedpost;
+
+ g_SignatureSettings.has_reqvalidcheckpost = true;
+ g_SignatureSettings.reqvalidcheckpost = tlv->reqvalidcheckpost;
+
+ g_SignatureSettings.has_reqtimesyncpost = true;
+ g_SignatureSettings.reqtimesyncpost = tlv->reqtimesyncpost;
+
+ g_SignatureSettings.has_reqseclocalpost = true;
+ g_SignatureSettings.reqseclocalpost = tlv->reqseclocalpost;
+
+ g_SignatureSettings.has_reqsignedresp = true;
+ g_SignatureSettings.reqsignedresp = tlv->reqsignedresp;
+
+ g_SignatureSettings.has_reqvalidcheckresp = true;
+ g_SignatureSettings.reqvalidcheckresp = tlv->reqvalidcheckresp;
+
+ g_SignatureSettings.has_reqtimesyncresp = true;
+ g_SignatureSettings.reqtimesyncresp = tlv->reqtimesyncresp;
+
+ g_SignatureSettings.has_reqseclocalresp = true;
+ g_SignatureSettings.reqseclocalresp = tlv->reqseclocalresp;
+
+ g_SignatureSettings.has_cert = true;
+ g_SignatureSettings.cert.len = tlv->cert.len;
+ MEMCPY_A(g_SignatureSettings.cert.data, tlv->cert.data);
+}
+
+void* vendorTlv_get(tlvid_t tlvid, uint32_t *num)
+{
+ return NULL;
+}
+
+void vendorTlv_post(tlvid_t tlvid, Vendor_Tlv *tlv)
+{
+ return;
+}
+
+void* transferRequest_get(tlvid_t tlvid, uint32_t* num)
+{
+#if R_FWUP_SUPPORT
+ (void)tlvid;
+ *num = 1;
+ R_LOG_DBG("## transferRequest_get: GET for TLV %{u32}", tlvid.type);
+
+ // Check upload slot download status
+ if (uploadSlotValid && uploadSlotHdr.status != (uint32_t) FWHDR_STATUS_DOWNLOAD)
+ {
+ g_transferRequest.status = uploadSlotHdr.status;
+ R_LOG_DBG("transferRequest_get: Transfer request download status = 0x%{hex32}", g_transferRequest.status);
+ return &g_transferRequest;
+ }
+ // Update g_transferRequest fields
+ MEMCPY_A(g_transferRequest.filehash.data, uploadSlotHdr.filehash);
+ strncpy(g_transferRequest.filename, uploadSlotHdr.filename, sizeof(g_transferRequest.filename));
+ g_transferRequest.filename[sizeof(g_transferRequest.filename) - 1] = '\0';
+ strncpy(g_transferRequest.version, uploadSlotHdr.version, sizeof(g_transferRequest.version));
+ g_transferRequest.version[sizeof(g_transferRequest.version) -1] = '\0';
+ strncpy(g_transferRequest.hwinfo.hwid, uploadSlotHdr.hwid, sizeof(g_transferRequest.hwinfo.hwid));
+ g_transferRequest.hwinfo.hwid[sizeof(g_transferRequest.hwinfo.hwid) -1] = '\0';
+ g_transferRequest.filesize = uploadSlotHdr.filesize;
+ g_transferRequest.blocksize = uploadSlotHdr.blocksize;
+ g_transferRequest.report_int_min = uploadSlotHdr.reportintervalmin;
+ g_transferRequest.report_int_max = uploadSlotHdr.reportintervalmax;
+ g_transferRequest.status = uploadSlotHdr.status;
+
+ R_LOG_DBG("## transferRequest_get: GET for TLV %{u32} done.", tlvid.type);
+ return &g_transferRequest;
+#else
+ *num = 0;
+ return NULL;
+#endif
+}
+
+void transferRequest_post(tlvid_t tlvid, Transfer_Request* tlv)
+{
+#if R_FWUP_SUPPORT
+ (void)tlvid;
+ R_LOG_DBG("## transferRequest_post: POST for TLV %{u32}.", tlvid.type);
+
+ if (!tlv)
+ {
+ R_LOG_DBG("## transferRequest_post: Transfer request tlv context is NULL");
+ return;
+ }
+
+ uint32_t tmin = tlv->report_int_min * 1000;
+ uint32_t tmax = tlv->report_int_max * 1000;
+
+ // Check hardware id
+ if (!(tlv->hwinfo.has_hwid))
+ {
+ tlv->response = RESPONSE_INCOMPATIBLE_HW;
+ R_LOG_DBG("## transferRequest_post: Invalid hardware id: %s", tlv->hwinfo.hwid);
+ return;
+ }
+ // Check filehash len
+ if (tlv->filehash.len != SHA256_HASH_SIZE)
+ {
+ tlv->response = RESPONSE_INVALID_REQ;
+ R_LOG_DBG("## transferRequest_post: Invalid filehash size: %{u16}", tlv->filehash.len);
+ return;
+ }
+ // Check filesize
+ if (tlv->filesize == 0 ||
+ tlv->filesize > CSMP_FWMGMT_SLOTIMG_SIZE)
+ {
+ tlv->response = RESPONSE_FILE_SIZE_TOO_BIG;
+ R_LOG_DBG("## transferRequest_post: Invalid file size: %{u32}", tlv->filesize);
+ return;
+ }
+ // blocksize should be smaller than csmp's MTU (1024)
+ // blocksize should be larger than filesize/1024 since there is only 1024 bitmaps
+ if (tlv->blocksize == 0 ||
+ tlv->blocksize > BLOCK_SIZE ||
+ tlv->blocksize < tlv->filesize / (CSMP_FWMGMT_BLKMAP_CNT * 32))
+ {
+ tlv->response = RESPONSE_INVALID_BLOCK_SIZE;
+ R_LOG_DBG("## transferRequest_post: Invalid block size: %{u32}", tlv->blocksize);
+ return;
+ }
+ // Check pending reboot
+ if (initload)
+ {
+ tlv->response = RESPONSE_PENDING_REBOOT;
+ R_LOG_DBG("## transferRequest_post: Pending reboot for upload image");
+ return;
+ }
+ // Check duplicate request on Upload slot
+ if ((memcmp(tlv->filehash.data, uploadSlotHdr.filehash, tlv->filehash.len)) == 0)
+ {
+ tlv->response = RESPONSE_DUP_XFER;
+ R_LOG_DBG("## transferRequest_post: Duplicate transfer request");
+ return;
+ }
+ // Check duplicate request on Run slot
+ if ((memcmp(tlv->filehash.data, runImageFileHash, tlv->filehash.len)) == 0)
+ {
+ tlv->response = RESPONSE_MATCH_RUN_XFER;
+ R_LOG_DBG("## transferRequest_post: Transfer request matches Run image");
+ return;
+ }
+
+ // Initiliase new transfer - start
+ tlv->has_response = true;
+ tlv->response = RESPONSE_OK;
+ MEMCPY_S(&g_transferRequest, tlv);
+ if (!initxfer)
+ {
+ initxfer = true;
+ }
+
+ // Init report intervals
+ tmin = (tmin >= MIN_REPORT_MIN) ? tmin : MIN_REPORT_MIN;
+ tmax = (tmax >= MAX_REPORT_MIN) ? tmax : MAX_REPORT_MIN;
+ tmax = (tmax < tmin) ? tmin : tmax;
+
+ // Erase upload slot
+ memset(&uploadSlotHdr, 0xFF, sizeof(uploadSlotHdr));
+
+ // Init upload slot from tlv context
+ MEMCPY_A(uploadSlotHdr.filehash, tlv->filehash.data);
+ strncpy(uploadSlotHdr.filename, tlv->filename, sizeof(uploadSlotHdr.filename));
+ uploadSlotHdr.filename[sizeof(uploadSlotHdr.filename) - 1] = '\0';
+ strncpy(uploadSlotHdr.version, tlv->version, sizeof(uploadSlotHdr.version));
+ uploadSlotHdr.version[sizeof(uploadSlotHdr.version) - 1] = '\0';
+ strncpy(uploadSlotHdr.hwid, tlv->hwinfo.hwid, sizeof(uploadSlotHdr.hwid));
+ uploadSlotHdr.hwid[sizeof(uploadSlotHdr.hwid) - 1] = '\0';
+ uploadSlotHdr.filesize = tlv->filesize;
+ uploadSlotHdr.blocksize = tlv->blocksize;
+ uploadSlotHdr.filesizelastblk = uploadSlotHdr.filesize %
+ uploadSlotHdr.blocksize;
+ if (uploadSlotHdr.filesizelastblk == 0)
+ {
+ uploadSlotHdr.filesizelastblk = uploadSlotHdr.blocksize;
+ }
+ uploadSlotHdr.blockcnt = (uploadSlotHdr.filesize + (uploadSlotHdr.blocksize - 1)) /
+ uploadSlotHdr.blocksize;
+ uploadSlotHdr.reportintervalmin = tmin;
+ uploadSlotHdr.reportintervalmax = tmax;
+ uploadSlotHdr.status = (uint32_t) FWHDR_STATUS_DOWNLOAD;
+
+ r_fwup_result_t result = R_APP_FWUP_TransferRequest(uploadSlotHdr.filehash, uploadSlotHdr.filename, uploadSlotHdr.version,
+ uploadSlotHdr.filesize, uploadSlotHdr.blocksize);
+
+ if (result == R_FWUP_RESULT_OK)
+ {
+ // Initiliase new transfer - done
+ initxfer = false;
+ uploadSlotValid = R_TRUE;
+ R_LOG_DBG("## transferRequest_post: POST for TLV %{u32} done.", tlvid.type);
+ }
+ else
+ {
+ tlv->response = RESPONSE_INVALID_REQ;
+ R_LOG_ERR("## transferRequest_post: R_APP_FWUP_TransferRequest failed: 0x%{hex8}", result);
+ }
+#endif
+}
+
+void imageBlock_post(tlvid_t tlvid, Image_Block* tlv)
+{
+#if R_FWUP_SUPPORT
+ (void)tlvid;
+ R_LOG_DBG("## imageBlock_post: POST for TLV %{u32}.", tlvid.type);
+
+ if (!tlv)
+ {
+ R_LOG_ERR("## imageBlock_post: Image block tlv context is NULL");
+ return;
+ }
+
+ /* Update g_imageBlock structure from tlv context */
+ MEMCPY_S(&g_imageBlock, tlv);
+
+ /* Check file hash length, file hash data, block data length with upload slot (as from transfer request) */
+ if ((g_imageBlock.filehash.len >= MIN_HASH_COMPARE_LEN) && (g_imageBlock.filehash.len <= sizeof(uploadSlotHdr.filehash)) &&
+ (memcmp(g_imageBlock.filehash.data, uploadSlotHdr.filehash, g_imageBlock.filehash.len) == 0) &&
+ (g_imageBlock.blockdata.len <= uploadSlotHdr.blocksize))
+ {
+ tlv->retval = true;
+ R_LOG_DBG("## imageBlock_post: Writing image block %{u32} to upload slot", g_imageBlock.blocknum);
+ /* Check the initialization of the transfer request */
+ if (initxfer)
+ {
+ tlv->retval = false;
+ R_LOG_DBG("## imageBlock_post: Transfer still initializing ");
+ g_downloadbusy = false;
+ return;
+ }
+
+ /* Note: FND sends only a small Hash while the FWUP library expects the full filehash */
+ r_fwup_result_t result = R_APP_FWUP_BlockRequest(uploadSlotHdr.filehash, g_imageBlock.blocknum,
+ g_imageBlock.blockdata.len, g_imageBlock.blockdata.data);
+ if (result != R_FWUP_RESULT_OK)
+ {
+ R_LOG_ERR("## imageBlock_post: R_APP_FWUP_BlockRequest failed: 0x%{hex8}", result);
+ tlv->retval = false;
+ g_downloadbusy = false;
+ return;
+ }
+ R_LOG_DBG("## imageBlock_post: Successful write of image block %{u32} (len=%{u32})", g_imageBlock.blocknum, g_imageBlock.blockdata.len);
+ if (g_imageBlock.blocknum == uploadSlotHdr.filesize / uploadSlotHdr.blocksize)
+ {
+ R_LOG_DBG("## imageBlock_post: Image transfer completed!");
+ uploadSlotHdr.status = (uint32_t) FWHDR_STATUS_COMPLETE;
+ }
+ }
+ else
+ {
+ tlv->retval = false;
+ R_LOG_DBG("## imageBlock_post: Image block POST failed!");
+ }
+
+ g_downloadbusy = false;
+ R_LOG_DBG("## imageBlock_post: POST for TLV %{u32} done.", tlvid.type);
+#endif
+}
+
+void* loadRequest_get(tlvid_t tlvid, uint32_t* num)
+{
+#if R_FWUP_SUPPORT
+ (void)tlvid;
+ *num = 1;
+ R_LOG_DBG("## loadRequest_get: GET for TLV %{u32}.", tlvid.type);
+
+ // Check for pending active load requests
+ if (!initload)
+ {
+ R_LOG_DBG("## loadRequest_get: No active load requests pending");
+ return NULL;
+ }
+
+ MEMCPY_A(g_loadRequest.filehash.data, uploadSlotHdr.filehash);
+ g_loadRequest.filehash.len = SHA256_HASH_SIZE;
+ g_loadRequest.loadtime = curloadtime;
+
+ R_LOG_DBG("## loadRequest_get: GET for TLV %{u32} done.", tlvid.type);
+ return &g_loadRequest;
+#else
+ *num = 0;
+ return NULL;
+#endif
+}
+
+void loadRequest_post(tlvid_t tlvid, Load_Request* tlv)
+{
+#if R_FWUP_SUPPORT
+ (void)tlvid;
+ R_LOG_DBG("## loadRequest_post: POST for TLV %{u32}.", tlvid.type);
+
+ if (!tlv)
+ {
+ R_LOG_ERR("## loadRequest_post: Load request tlv context is NULL");
+ return;
+ }
+
+ // Reference g_loadRequest via tlv context
+ g_loadRequest = *tlv;
+
+ if (memcmp(g_loadRequest.filehash.data, uploadSlotHdr.filehash,
+ g_loadRequest.filehash.len) == 0)
+ {
+ // Filehash matches UPLOAD image
+ if (uploadSlotHdr.status == (uint32_t) FWHDR_STATUS_COMPLETE)
+ {
+ R_LOG_DBG("## loadRequest_post: Load request ok for upload slot image");
+ tlv->response = RESPONSE_OK;
+ }
+ else if (uploadSlotHdr.status == (uint32_t) FWHDR_STATUS_BADIMAGE ||
+ uploadSlotHdr.status == (uint32_t) FWHDR_STATUS_BADHASH)
+ {
+ R_LOG_DBG("## loadRequest_post: Load request on bad upload slot image");
+ tlv->response = RESPONSE_SIGNATURE_FAILED;
+ }
+ else
+ {
+ R_LOG_DBG("## loadRequest_post: Load request incomplete");
+ tlv->response = RESPONSE_INCOMPLETE;
+ }
+ if (tlv->response != RESPONSE_OK)
+ {
+ R_LOG_ERR("## loadRequest_post: Load request failed");
+ return;
+ }
+ }
+ else if (memcmp(g_loadRequest.filehash.data, runImageFileHash,
+ g_loadRequest.filehash.len) == 0)
+ {
+ // Filehash matches RUN image
+ R_LOG_DBG("## loadRequest_post: Load request on running image");
+ tlv->response = RESPONSE_IMAGE_RUNNING;
+ return;
+ }
+ else
+ {
+ // Load request for unknown filehash
+ R_LOG_ERR("## loadRequest_post: Load request with unknown filehash");
+ tlv->response = RESPONSE_UNKNOWN_HASH;
+ return;
+ }
+ // Check for redundant load request
+ if (initload && (g_loadRequest.loadtime == curloadtime))
+ {
+ R_LOG_DBG("## loadRequest_post: Redundant load request");
+ return;
+ }
+
+ curloadtime = 0;
+ initload = true;
+
+ r_fwup_result_t result = R_APP_FWUP_LoadRequest(g_loadRequest.filehash.data, g_loadRequest.loadtime);
+ if (result != R_FWUP_RESULT_OK)
+ {
+ initload = false;
+ R_LOG_ERR("## loadRequest_post: R_APP_FWUP_LoadRequest failed: 0x%{hex8}", result);
+ return;
+ }
+
+ // Upon success update g_loadRequest structure from tlv context
+ MEMCPY_S(&g_loadRequest, tlv);
+ // Save new load time
+ curloadtime = g_loadRequest.loadtime;
+
+ R_LOG_DBG("## loadRequest_post: POST for TLV 0x%{hex32} done.", tlvid.type);
+#endif
+}
+
+void cancelLoadRequest_post(tlvid_t tlvid, Cancel_Load_Request* tlv)
+{
+#if R_FWUP_SUPPORT
+ (void)tlvid;
+ R_LOG_DBG("## cancelLoadRequest_post: POST for TLV 0x%{hex32}.", tlvid.type);
+ if (!tlv)
+ {
+ R_LOG_DBG("## cancelLoadRequest_post: Cancel load request tlv context is NULL");
+ return;
+ }
+
+ if (memcmp(tlv->filehash.data, uploadSlotHdr.filehash, SHA256_HASH_SIZE) == 0)
+ {
+ R_LOG_DBG("## cancelLoadRequest_post: Cancel load request valid, cancelling current load request");
+
+ // Cancel current load request
+ r_fwup_result_t result = R_APP_FWUP_CancelRequest(g_loadRequest.filehash.data);
+ if (result != R_FWUP_RESULT_OK)
+ {
+ R_LOG_ERR("## cancelLoadRequest_post: R_APP_FWUP_CancelRequest failed: 0x%{hex8}", result);
+ }
+ else
+ {
+ initload = false;
+ curloadtime = 0;
+ }
+ }
+
+ R_LOG_DBG("## cancelLoadRequest_post: POST for TLV 0x%{hex32} done.", tlvid.type);
+#endif
+}
+
+void setBackupRequest_post(tlvid_t tlvid, Set_Backup_Request *tlv)
+{
+#if R_FWUP_SUPPORT
+ R_LOG_ERR("## setBackupRequest_post: Backup image slot not supported");
+ return;
+#endif
+}
+
+#if R_FWUP_SUPPORT
+static uint8_t reverse_bits8(uint8_t x)
+{
+ x = (uint8_t)((x >> 4) | (x << 4)); // swap nibbles
+ x = (uint8_t)(((x & 0xCCu) >> 2) | ((x & 0x33u) << 2)); // swap bit pairs
+ x = (uint8_t)(((x & 0xAAu) >> 1) | ((x & 0x55u) << 1)); // swap individual bits
+ return x;
+}
+
+static void initialize_file_hash()
+{
+ // Initialize the file hash through the firmware hash attribute stored in flash
+ r_result_t flashRes = R_Flash_Read(R_FLASH_FIRMWARE_HASH, runImageFileHash, SHA256_HASH_SIZE);
+ if (flashRes != R_RESULT_SUCCESS)
+ {
+ R_LOG_ERR("Unable to read the firmware hash from flash");
+ }
+
+ if (MEMISZERO_A(runImageFileHash))
+ {
+#if !defined(__i386) && !defined(__x86_64)
+ uint8_t buf[128u];
+ st_fw_header_t *p_hdr = (st_fw_header_t *)buf;
+ r_fwup_wrap_flash_read((uint32_t) buf, FWUP_CFG_MAIN_AREA_ADDR_L, 128u);
+ R_memcpy(runImageFileHash, p_hdr->sig, SHA256_HASH_SIZE);
+#else
+ memset(runImageFileHash, 0x12, SHA256_HASH_SIZE);
+#endif
+ }
+}
+
+static char hex_digit(uint8_t v)
+{
+ v &= 0x0F;
+ return (v < 10) ? (char)('0' + v) : (char)('a' + (v - 10));
+}
+
+static void initialize_file_name()
+{
+ // Initialize the file hash through the firmware hash attribute stored in flash
+ r_result_t flashRes = R_Flash_Read(R_FLASH_FIRMWARE_NAME, runImageFileName, FILE_NAME_SIZE);
+ if (flashRes != R_RESULT_SUCCESS)
+ {
+ R_LOG_ERR("Unable to read the firmware name from flash");
+ }
+
+ /**
+ * If no firmware file name is stored in data flash (meaning that the current firmware image was not uploaded by
+ * FND), create a file name using the same syntax as FND so that images that are uploaded via a programmer will
+ * report a similar file name as firmware images uploaded by FND
+ */
+ if (runImageFileName[0] == 0)
+ {
+ if (MEMISZERO_A(runImageFileHash))
+ {
+ initialize_file_hash();
+ }
+ if (sizeof(baseFileName) + sizeof(baseLibraryVersion) + sizeof(baseCsmpVersion) + 16 >= sizeof(runImageFileName))
+ {
+ R_LOG_ERR("File name initialization failed: File name too long");
+ strncpy(runImageFileName, baseFileName, sizeof(runImageFileName));
+ runImageFileName[sizeof(runImageFileName) - 1] = '\0';
+ }
+ R_memcpy(runImageFileName, baseFileName, sizeof(baseFileName));
+ size_t j = sizeof(baseFileName) - 1;
+ runImageFileName[j++] = '-';
+ R_memcpy(&runImageFileName[j], baseLibraryVersion, sizeof(baseLibraryVersion));
+ j += sizeof(baseLibraryVersion) - 1;
+ runImageFileName[j++] = '-';
+ R_memcpy(&runImageFileName[j], baseCsmpVersion, sizeof(baseCsmpVersion));
+ j += sizeof(baseCsmpVersion) - 1;
+ runImageFileName[j++] = '-';
+ for (size_t i = 0; i < 8; i++) {
+ uint8_t b = runImageFileHash[i];
+ runImageFileName[j++] = hex_digit(b >> 4);
+ runImageFileName[j++] = hex_digit(b);
+ }
+ runImageFileName[j] = '\0';
+ }
+}
+
+static void initialize_file_size()
+{
+ // Initialize the file size through the attribute stored in flash
+ r_result_t flashRes = R_Flash_Read(R_FLASH_FIRMWARE_SIZE, &runImageFileSize, sizeof(runImageFileSize));
+ if (flashRes != R_RESULT_SUCCESS)
+ {
+ R_LOG_ERR("Unable to read the firmware size from flash");
+ }
+}
+#endif
+
+void* firmwareImageInfo_get(tlvid_t tlvid, uint32_t* num)
+{
+#if R_FWUP_SUPPORT
+ (void)tlvid;
+ *num = 0;
+ R_LOG_DBG("## firmwareImageInfo_get: GET for TLV %{u32}.", tlvid.type);
+
+ R_LOG_DBG("## firmwareImageInfo_get: Reading firmware image info for running image");
+ Firmware_Image_Info* imageInfo = &g_firmwareImageInfo[RUN_IMAGE];
+ MEMZERO_S(imageInfo);
+ (*num)++;
+
+ // Index
+ imageInfo->has_index = true;
+ imageInfo->index = RUN_IMAGE + 1;
+ // Filehash
+ imageInfo->has_filehash = true;
+ if (MEMISZERO_A(runImageFileHash))
+ {
+ initialize_file_hash();
+ initialize_file_name();
+ initialize_file_size();
+ }
+ MEMCPY_A(imageInfo->filehash.data, runImageFileHash);
+ imageInfo->filehash.len = SHA256_HASH_SIZE;
+ // Filename
+ imageInfo->has_filename = true;
+ strncpy(imageInfo->filename, runImageFileName, sizeof(imageInfo->filename));
+ imageInfo->filename[sizeof(imageInfo->filename) - 1] = '\0'; // Ensure null-termination of filename string
+ // Version
+ imageInfo->has_version = true;
+ strncpy(imageInfo->version, baseCsmpVersion, sizeof(imageInfo->version));
+ imageInfo->version[sizeof(imageInfo->version) - 1] = '\0'; // Ensure null-termination of version string
+ // Filesize
+ if (runImageFileSize != 0)
+ {
+ imageInfo->has_filesize = true;
+ imageInfo->filesize = runImageFileSize;
+ }
+ else
+ {
+ imageInfo->has_filesize = false;
+ }
+ // Blocksize
+ imageInfo->has_blocksize = false;
+ // Blockcount
+ imageInfo->blockcnt = 0xFFFFFFFF;
+ // Bitmap
+ imageInfo->has_bitmap = false;
+ // Default image?
+ imageInfo->has_isdefault = true;
+ imageInfo->isdefault = false;
+ // Running image?
+ imageInfo->has_isrunning = true;
+ imageInfo->isrunning = true;
+ // Loadtime
+ imageInfo->has_loadtime = false;
+ // Hardware Id
+ imageInfo->has_hwinfo = true;
+ imageInfo->hwinfo.has_hwid = true;
+ strncpy(imageInfo->hwinfo.hwid, baseHwId, sizeof(imageInfo->hwinfo.hwid));
+ imageInfo->hwinfo.hwid[sizeof(imageInfo->hwinfo.hwid) - 1] = '\0'; // Ensure null-termination of hardware string
+ // Vendor Hardware Id
+ imageInfo->hwinfo.has_vendorhwid = false;
+ // Kernel version
+ imageInfo->has_kernelversion = false;
+ // Subkernel version
+ imageInfo->has_subkernelversion = false;
+ // Loader error
+ imageInfo->has_loaderrorcode = false;
+ // Subloader error
+ imageInfo->has_subloaderrorcode = false;
+ // Download status
+ imageInfo->status = FWHDR_STATUS_COMPLETE;
+
+ R_LOG_DBG("## firmwareImageInfo_get: Reading firmware image info for upload image");
+ imageInfo = &g_firmwareImageInfo[UPLOAD_IMAGE];
+ memset(imageInfo, 0, sizeof(Firmware_Image_Info));
+ (*num)++;
+
+ // Index
+ imageInfo->has_index = true;
+ imageInfo->index = UPLOAD_IMAGE + 1;
+ // Filehash
+ imageInfo->has_filehash = true;
+ imageInfo->filehash.len = sizeof(uploadSlotHdr.filehash);
+ MEMCPY_A(imageInfo->filehash.data, uploadSlotHdr.filehash);
+ // Filename
+ imageInfo->has_filename = true;
+ strncpy(imageInfo->filename, uploadSlotHdr.filename, sizeof(imageInfo->filename));
+ imageInfo->filename[sizeof(imageInfo->filename) - 1] = '\0';
+ // Version
+ imageInfo->has_version = true;
+ strncpy(imageInfo->version, uploadSlotHdr.version, sizeof(imageInfo->version));
+ imageInfo->version[sizeof(imageInfo->version) - 1] = '\0';
+ // Filesize
+ imageInfo->has_filesize = true;
+ imageInfo->filesize = uploadSlotHdr.filesize;
+ // Blocksize
+ imageInfo->has_blocksize = true;
+ imageInfo->blocksize = uploadSlotHdr.blocksize;
+ // Blockcount
+ imageInfo->blockcnt = uploadSlotHdr.blockcnt;
+ // Bitmap
+ if (uploadSlotHdr.status == (uint32_t) FWHDR_STATUS_DOWNLOAD)
+ {
+ imageInfo->has_bitmap = true;
+ imageInfo->bitmap.len =
+ (((uploadSlotHdr.filesize + uploadSlotHdr.blocksize - 1u) / uploadSlotHdr.blocksize) + 7u) / 8u;
+ for (uint8_t i = 0; i < imageInfo->bitmap.len; i++)
+ {
+ imageInfo->bitmap.data[i] = reverse_bits8(fwupState.imageBitmap[sizeof(fwupState.imageBitmap) - i - 1]);
+ }
+ }
+ else
+ {
+ imageInfo->has_bitmap = false;
+ }
+ // Default image?
+ imageInfo->has_isdefault = true;
+ imageInfo->isdefault = false;
+ // Running image?
+ imageInfo->has_isrunning = true;
+ imageInfo->isrunning = false;
+ // Loadtime
+ if (curloadtime != 0)
+ {
+ imageInfo->has_loadtime = true;
+ imageInfo->loadtime = curloadtime;
+ }
+ // Hardware Id
+ imageInfo->has_hwinfo = true;
+ imageInfo->hwinfo.has_hwid = true;
+ strncpy(imageInfo->hwinfo.hwid, uploadSlotHdr.hwid, sizeof(imageInfo->hwinfo.hwid));
+ imageInfo->hwinfo.hwid[sizeof(imageInfo->hwinfo.hwid) - 1] = '\0';
+ // Vendor Hardware Id
+ imageInfo->hwinfo.has_vendorhwid = false;
+ // Kernel version
+ imageInfo->has_kernelversion = false;
+ // Subkernel version
+ imageInfo->has_subkernelversion = false;
+ // Loader error
+ imageInfo->has_loaderrorcode = false;
+ // Subloader error
+ imageInfo->has_subloaderrorcode = false;
+ // Download status
+ imageInfo->status = uploadSlotHdr.status;
+
+ DPRINTF("## firmwareImageInfo_get: GET for TLV %{u32} done.", tlvid.type);
+ return &g_firmwareImageInfo;
+#else
+ *num = 0;
+ return NULL;
+#endif
+}
+
+void rebootRequest_post(tlvid_t tlvid, Reboot_Request* tlv)
+{
+#if R_FWUP_SUPPORT
+ switch (tlv->flag)
+ {
+ case REBOOT:
+ R_LOG_DBG("Rebooting system...\n");
+ R_FWUP_SoftwareReset();
+ // Should not reach here
+ break;
+ default:
+ R_LOG_ERR("csmp rebootRequest: Reboot flag not supported!");
+ }
+#else
+ R_LOG_ERR("csmp rebootRequest: Reboot not supported!");
+#endif
+}
\ No newline at end of file
diff --git a/src/csmpagent/csmp_firmwareMgmt.c b/src/csmpagent/csmp_firmwareMgmt.c
index c1e632f..baabb76 100755
--- a/src/csmpagent/csmp_firmwareMgmt.c
+++ b/src/csmpagent/csmp_firmwareMgmt.c
@@ -49,7 +49,7 @@ int csmp_get_transferRequest(tlvid_t tlvid, uint8_t *buf, size_t len,
if(xfer_req) {
// Firmware status check
- if (!xfer_req->has_status || xfer_req->status != FWHDR_STATUS_DOWNLOAD) {
+ if (!xfer_req->has_status || xfer_req->status != (uint32_t) FWHDR_STATUS_DOWNLOAD) {
DPRINTF("csmpagent_firmwaremgmt: Transfer request status error! (%x)\n",
xfer_req->status);
return CSMP_OP_TLV_RD_EMPTY;
diff --git a/src/csmpapi/csmpservice.c b/src/csmpapi/csmpservice.c
index ccd20d2..2fd8956 100644
--- a/src/csmpapi/csmpservice.c
+++ b/src/csmpapi/csmpservice.c
@@ -22,7 +22,7 @@
#include "cgmsagent.h"
#include "csmpserver.h"
-uint8_t g_csmplib_status = SERVICE_NOT_START;
+csmp_service_status_t g_csmplib_status = SERVICE_NOT_START;
uint8_t g_csmplib_eui64[8];
uint32_t g_csmplib_reginterval_min = 0;
diff --git a/src/csmpservice/cgmsagent.c b/src/csmpservice/cgmsagent.c
index 1bef450..898c785 100644
--- a/src/csmpservice/cgmsagent.c
+++ b/src/csmpservice/cgmsagent.c
@@ -42,7 +42,7 @@ enum {
REASON_OUTAGE_RECOVERY = 8
};
-extern uint8_t g_csmplib_status;
+extern csmp_service_status_t g_csmplib_status;
extern uint32_t g_csmplib_reginterval_min;
extern uint32_t g_csmplib_reginterval_max;
extern csmp_subscription_list_t g_csmplib_report_list;
@@ -287,6 +287,8 @@ void response_handler(struct sockaddr_in6 *from, uint16_t status, const void *bo
g_csmplib_stats.reg_fails_stats.error_process++;
}
break;
+ default:
+ break;
}
return;
}
diff --git a/src/lib/protobuf-c/protobuf-c.c b/src/lib/protobuf-c/protobuf-c.c
index 945f9ab..e0736fe 100644
--- a/src/lib/protobuf-c/protobuf-c.c
+++ b/src/lib/protobuf-c/protobuf-c.c
@@ -2892,7 +2892,7 @@ parse_member(ScannedMember *scanned_member,
message->unknown_fields +
(message->n_unknown_fields++);
ufield->tag = scanned_member->tag;
- ufield->wire_type = scanned_member->wire_type;
+ ufield->wire_type = (ProtobufCWireType) scanned_member->wire_type;
ufield->len = scanned_member->len;
ufield->data = do_alloc(allocator, scanned_member->len);
if (ufield->data == NULL)