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 + +

+ Renesas logo +

+ +## 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)