Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers thread_nvm_store.c Source File

thread_nvm_store.c

00001 /*
00002  * Copyright (c) 2017, Arm Limited and affiliates.
00003  * SPDX-License-Identifier: BSD-3-Clause
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holder nor the
00014  *    names of its contributors may be used to endorse or promote products
00015  *    derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 /*
00030  * \file thread_nvm_store.c
00031  *
00032  */
00033 
00034 #include "nsconfig.h"
00035 
00036 #include <string.h>
00037 #include <stdio.h>
00038 #include "Core/include/address.h"
00039 #include "ns_file_system.h"
00040 #include "thread_config.h"
00041 #include "thread_common.h"
00042 #include "thread_nvm_store.h"
00043 #include "ns_trace.h"
00044 
00045 #define TRACE_GROUP "tnvm"
00046 const char *FAST_DATA_FILE = "f_d";
00047 #define FAST_DATA_VERSION 1
00048 #define LINK_INFO_WRITE_DELAY 2
00049 #define LINK_INFO_SHORT_ADDR_NOT_SET 0xffff
00050 #define LINK_INFO_WRITE_DONE 0xffff
00051 
00052 const char *LINK_INFO_FILE = "l_i";
00053 #define LINK_INFO_DATA_VERSION 1
00054 
00055 typedef struct {
00056     uint8_t mac[8];
00057     uint16_t short_addr;
00058 } nvm_link_info_t;
00059 
00060 typedef struct {
00061     nvm_link_info_t nvm_link_info;
00062     uint16_t write_delay;
00063     bool loaded;
00064 } thread_nvm_store_link_info_t;
00065 
00066 const char *THREAD_NVM_ACTIVE_CONF_FILE = "a_c";
00067 #define ACTIVE_CONF_DATA_VERSION 1
00068 
00069 const char *DEVICE_CONF_FILE = "s_d";
00070 #define DEVICE_CONF_VERSION 1
00071 
00072 const char *THREAD_NVM_PENDING_CONF_FILE = "p_c";
00073 #define PENDING_CONF_DATA_VERSION 1
00074 
00075 static const char* thread_nvm_store_get_root_path(void);
00076 static int root_path_valid(void);
00077 static int thread_nvm_store_read(const char *file_name, void *data, uint32_t data_size, uint32_t *version);
00078 static int thread_nvm_store_write(const char *file_name, void *data, uint32_t data_size, uint32_t version);
00079 static void thread_nvm_store_create_path(char* fast_data_path, const char* file_name);
00080 static int thread_nvm_store_fast_data_save(thread_nvm_fast_data_t* fast_data_to_set);
00081 static int thread_nvm_store_all_counters_store(uint32_t mac_frame_counter, uint32_t mle_frame_counter, uint32_t seq_counter);
00082 static void thread_nvm_store_link_info_delayed_write(uint32_t seconds);
00083 
00084 #define MAX_ROOT_PATH_LEN 150
00085 
00086 #define FAST_DATA_STRING_LEN (strlen(FAST_DATA_FILE)+strlen(thread_nvm_store_get_root_path())+1)
00087 #define ACTIVE_CONF_STRING_LEN (strlen(THREAD_NVM_ACTIVE_CONF_FILE)+strlen(thread_nvm_store_get_root_path())+1)
00088 #define DEVICE_CONF_STRING_LEN (strlen(DEVICE_CONF_FILE)+strlen(thread_nvm_store_get_root_path())+1)
00089 #define PENDING_CONF_STRING_LEN (strlen(THREAD_NVM_PENDING_CONF_FILE)+strlen(thread_nvm_store_get_root_path())+1)
00090 #define LINK_INFO_STRING_LEN (strlen(LINK_INFO_FILE)+strlen(thread_nvm_store_get_root_path())+1)
00091 
00092 
00093 thread_nvm_fast_data_t cached_fast_data;
00094 thread_nvm_store_link_info_t cached_link_info = {
00095     .nvm_link_info.short_addr = LINK_INFO_SHORT_ADDR_NOT_SET,
00096     .nvm_link_info.mac = {0,0,0,0,0,0,0,0},
00097     .write_delay = LINK_INFO_WRITE_DELAY,
00098     .loaded = false
00099 };
00100 
00101 static const char* thread_nvm_store_get_root_path(void)
00102 {
00103     char* path = ns_file_system_get_root_path();
00104     if (NULL==path) {
00105         return "";
00106     }
00107     return path;
00108 }
00109 
00110 static int root_path_valid(void)
00111 {
00112     if (NULL==ns_file_system_get_root_path())
00113         return 0;
00114     int path_len = strlen(thread_nvm_store_get_root_path());
00115     if(path_len==0 || path_len>MAX_ROOT_PATH_LEN) {
00116         return 0;
00117     }
00118     return 1;
00119 }
00120 int thread_nvm_store_device_configuration_write(uint8_t *mac_ptr, uint8_t *mleid_ptr)
00121 {
00122    thread_nvm_device_conf_t d_c;
00123    if (!root_path_valid()) {
00124        return THREAD_NVM_FILE_ROOT_PATH_INVALID;
00125    }
00126    memcpy(d_c.mac, mac_ptr, sizeof(d_c.mac));
00127    memcpy(d_c.mle_id, mleid_ptr, sizeof(d_c.mle_id));
00128    char device_conf_path[DEVICE_CONF_STRING_LEN];
00129    thread_nvm_store_create_path(device_conf_path, DEVICE_CONF_FILE);
00130    return thread_nvm_store_write(device_conf_path, &d_c, sizeof(thread_nvm_device_conf_t), DEVICE_CONF_VERSION);
00131 }
00132 
00133 int thread_nvm_store_device_configuration_read(uint8_t *mac_ptr, uint8_t *mleid_ptr)
00134 {
00135     int ret = THREAD_NVM_FILE_READ_ERROR;
00136     if (!root_path_valid()) {
00137         return THREAD_NVM_FILE_ROOT_PATH_INVALID;
00138     }
00139     char device_conf_path[DEVICE_CONF_STRING_LEN];
00140     thread_nvm_store_create_path(device_conf_path, DEVICE_CONF_FILE);
00141     uint32_t version;
00142     thread_nvm_device_conf_t d_c;
00143 
00144     ret = thread_nvm_store_read(device_conf_path, &d_c, sizeof(thread_nvm_device_conf_t), &version);
00145     if(THREAD_NVM_FILE_SUCCESS==ret) {
00146         if (THREAD_NVM_FILE_SUCCESS==ret && DEVICE_CONF_VERSION!=version) {
00147             tr_info("fast data version mismatch %"PRIu32, version);
00148             ret = THREAD_NVM_FILE_VERSION_WRONG;
00149         }
00150         else {
00151             memcpy(mac_ptr, d_c.mac, sizeof(d_c.mac));
00152             memcpy(mleid_ptr, d_c.mle_id, sizeof(d_c.mle_id));
00153         }
00154     }
00155     return ret;
00156 }
00157 
00158 int thread_nvm_store_pending_configuration_write(void *data, uint16_t size)
00159 {
00160     char pc_data_path[PENDING_CONF_STRING_LEN];
00161     if (NULL==data) {
00162         return THREAD_NVM_FILE_PARAMETER_INVALID;
00163     }
00164     if (!root_path_valid()) {
00165         return THREAD_NVM_FILE_ROOT_PATH_INVALID;
00166     }
00167     thread_nvm_store_create_path(pc_data_path, THREAD_NVM_PENDING_CONF_FILE);
00168     return thread_nvm_store_write(pc_data_path, data, size, PENDING_CONF_DATA_VERSION);
00169 }
00170 
00171 int thread_nvm_store_pending_configuration_read(void *data, uint16_t size)
00172 {
00173     char pc_data_path[PENDING_CONF_STRING_LEN];
00174     uint32_t version;
00175     if (NULL==data) {
00176         return THREAD_NVM_FILE_PARAMETER_INVALID;
00177     }
00178     if (!root_path_valid()) {
00179         return THREAD_NVM_FILE_ROOT_PATH_INVALID;
00180     }
00181     thread_nvm_store_create_path(pc_data_path, THREAD_NVM_PENDING_CONF_FILE);
00182 
00183     int ret = thread_nvm_store_read(pc_data_path, data, size, &version);
00184     if (THREAD_NVM_FILE_SUCCESS==ret && PENDING_CONF_DATA_VERSION!=version) {
00185         tr_info("Pending configuration version mismatch %"PRIu32, version);
00186         return THREAD_NVM_FILE_VERSION_WRONG;
00187     }
00188     return ret;
00189 }
00190 
00191 int thread_nvm_store_active_configuration_write(void *data, uint16_t data_size)
00192 {
00193     char ac_data_path[ACTIVE_CONF_STRING_LEN];
00194     if (NULL==data) {
00195         return THREAD_NVM_FILE_PARAMETER_INVALID;
00196     }
00197     if (!root_path_valid()) {
00198         return THREAD_NVM_FILE_ROOT_PATH_INVALID;
00199     }
00200 
00201     thread_nvm_store_create_path(ac_data_path, THREAD_NVM_ACTIVE_CONF_FILE);
00202     return thread_nvm_store_write(ac_data_path, data, data_size, ACTIVE_CONF_DATA_VERSION);
00203 }
00204 
00205 int thread_nvm_store_active_configuration_read(void *data, uint16_t data_size)
00206 {
00207     char ac_data_path[ACTIVE_CONF_STRING_LEN];
00208     uint32_t version;
00209     if (NULL==data) {
00210         return THREAD_NVM_FILE_PARAMETER_INVALID;
00211     }
00212     if (!root_path_valid()) {
00213         return THREAD_NVM_FILE_ROOT_PATH_INVALID;
00214     }
00215     thread_nvm_store_create_path(ac_data_path, THREAD_NVM_ACTIVE_CONF_FILE);
00216 
00217     int ret = thread_nvm_store_read(ac_data_path, data, data_size, &version);
00218     if (THREAD_NVM_FILE_SUCCESS==ret && ACTIVE_CONF_DATA_VERSION!=version) {
00219         tr_info("active configuration version mismatch %"PRIu32, version);
00220         return THREAD_NVM_FILE_VERSION_WRONG;
00221     }
00222     return ret;
00223 }
00224 
00225 int thread_nvm_store_active_configuration_remove(void)
00226 {
00227     if (!root_path_valid()) {
00228         return THREAD_NVM_FILE_ROOT_PATH_INVALID;
00229     }
00230     char ac_data_path[ACTIVE_CONF_STRING_LEN];
00231     thread_nvm_store_create_path(ac_data_path, THREAD_NVM_ACTIVE_CONF_FILE);
00232     return remove(ac_data_path);
00233 }
00234 
00235 int thread_nvm_store_pending_configuration_remove(void)
00236 {
00237     if (!root_path_valid()) {
00238         return THREAD_NVM_FILE_ROOT_PATH_INVALID;
00239     }
00240     char ac_data_path[PENDING_CONF_STRING_LEN];
00241     thread_nvm_store_create_path(ac_data_path, THREAD_NVM_PENDING_CONF_FILE);
00242     return remove(ac_data_path);
00243 }
00244 
00245 
00246 int thread_nvm_store_seq_counter_write(uint32_t network_seq_counter)
00247 {
00248     int ret = THREAD_NVM_FILE_SUCCESS;
00249     if (cached_fast_data.seq_counter!=network_seq_counter) {
00250         ret = thread_nvm_store_all_counters_store(cached_fast_data.mac_frame_counter, cached_fast_data.mle_frame_counter, network_seq_counter);
00251         cached_fast_data.seq_counter=network_seq_counter;
00252     }
00253     return ret;
00254 }
00255 
00256 int thread_nvm_store_fast_data_check_and_write(uint32_t mac_frame_counter, uint32_t mle_frame_counter, uint32_t network_seq_counter)
00257 {
00258     int ret = THREAD_NVM_FILE_SUCCESS;
00259     if( ((int)(mac_frame_counter - cached_fast_data.mac_frame_counter) > MAC_FRAME_COUNTER_LIMIT) ||
00260         ((int)(mle_frame_counter - cached_fast_data.mle_frame_counter) > MLE_FRAME_COUNTER_LIMIT) ||
00261         cached_fast_data.seq_counter!=network_seq_counter) {
00262             ret = thread_nvm_store_all_counters_store(mac_frame_counter, mle_frame_counter, network_seq_counter);
00263             cached_fast_data.mac_frame_counter = mac_frame_counter;
00264             cached_fast_data.mle_frame_counter = mle_frame_counter;
00265             cached_fast_data.seq_counter=network_seq_counter;
00266     }
00267     return ret;
00268 }
00269 
00270 
00271 int thread_nvm_store_frame_counters_check_and_write(uint32_t mac_frame_counter, uint32_t mle_frame_counter)
00272 {
00273     int ret = THREAD_NVM_FILE_SUCCESS;
00274     if( ((int)(mac_frame_counter - cached_fast_data.mac_frame_counter) > MAC_FRAME_COUNTER_LIMIT) ||
00275         ((int)(mle_frame_counter - cached_fast_data.mle_frame_counter) > MLE_FRAME_COUNTER_LIMIT)) {
00276             ret = thread_nvm_store_all_counters_store(mac_frame_counter, mle_frame_counter, cached_fast_data.seq_counter);
00277             cached_fast_data.mac_frame_counter = mac_frame_counter;
00278             cached_fast_data.mle_frame_counter = mle_frame_counter;
00279     }
00280     return ret;
00281 }
00282 
00283 static int thread_nvm_store_all_counters_store(uint32_t mac_frame_counter, uint32_t mle_frame_counter, uint32_t network_seq_counter)
00284 {
00285     thread_nvm_fast_data_t fast_data;
00286     fast_data.mac_frame_counter = mac_frame_counter;
00287     fast_data.mle_frame_counter = mle_frame_counter;
00288     fast_data.seq_counter = network_seq_counter;
00289     if (root_path_valid()) {
00290         return thread_nvm_store_fast_data_save(&fast_data);
00291     }
00292     else{
00293         return THREAD_NVM_FILE_ROOT_PATH_INVALID;
00294     }
00295 }
00296 
00297 int thread_nvm_store_fast_data_write(thread_nvm_fast_data_t* fast_data)
00298 {
00299     cached_fast_data.mac_frame_counter = fast_data->mac_frame_counter;
00300     cached_fast_data.mle_frame_counter = fast_data->mle_frame_counter;
00301     cached_fast_data.seq_counter = fast_data->seq_counter;
00302 
00303     if (root_path_valid()) {
00304         return thread_nvm_store_fast_data_save(fast_data);
00305     }
00306     else {
00307         return THREAD_NVM_FILE_ROOT_PATH_INVALID;
00308     }
00309 }
00310 
00311 static void thread_nvm_store_create_path(char* fast_data_path, const char* file_name)
00312 {
00313     strcpy(fast_data_path, thread_nvm_store_get_root_path());
00314     strcat(fast_data_path, file_name);
00315 }
00316 
00317 int thread_nvm_store_fast_data_read(thread_nvm_fast_data_t* fast_data)
00318 {
00319     int ret = THREAD_NVM_FILE_SUCCESS;
00320 
00321     if (root_path_valid()) {
00322         char fast_data_path[FAST_DATA_STRING_LEN];
00323         thread_nvm_store_create_path(fast_data_path, FAST_DATA_FILE);
00324         uint32_t version;
00325         ret = thread_nvm_store_read(fast_data_path, fast_data, sizeof(thread_nvm_fast_data_t), &version);
00326         if (THREAD_NVM_FILE_SUCCESS==ret && FAST_DATA_VERSION!=version) {
00327             tr_info("fast data version mismatch %"PRIu32, version);
00328             return THREAD_NVM_FILE_VERSION_WRONG;
00329         }
00330     }
00331     else {
00332         fast_data->mac_frame_counter = cached_fast_data.mac_frame_counter;
00333         fast_data->mle_frame_counter = cached_fast_data.mle_frame_counter;
00334         fast_data->seq_counter = cached_fast_data.seq_counter;
00335     }
00336     return ret;
00337 }
00338 
00339 static int thread_nvm_store_fast_data_save(thread_nvm_fast_data_t* fast_data_to_set)
00340 {
00341     char fast_data_path[FAST_DATA_STRING_LEN];
00342     thread_nvm_store_create_path(fast_data_path, FAST_DATA_FILE);
00343     return thread_nvm_store_write(fast_data_path, fast_data_to_set, sizeof(thread_nvm_fast_data_t), FAST_DATA_VERSION);
00344 }
00345 
00346 static int thread_nvm_store_write(const char *file_name, void *data, uint32_t data_size, uint32_t version)
00347 {
00348     FILE *fp = fopen(file_name, "w");
00349     if(fp == NULL) {
00350         tr_error("NVM open error: %s", file_name);
00351         return THREAD_NVM_FILE_CANNOT_OPEN;
00352     }
00353 
00354     size_t n_bytes = fwrite(&version, 1, sizeof(uint32_t), fp);
00355     if (n_bytes!=sizeof(uint32_t)) {
00356         tr_warning("NVM version write error");
00357         fclose(fp);
00358         return THREAD_NVM_FILE_WRITE_ERROR;
00359     }
00360 
00361     n_bytes = fwrite(data, 1, data_size, fp);
00362     fclose(fp);
00363     if (n_bytes!=data_size) {
00364         tr_error("NVM write error %s", file_name);
00365         return THREAD_NVM_FILE_WRITE_ERROR;
00366     }
00367     else {
00368         return THREAD_NVM_FILE_SUCCESS;
00369     }
00370 }
00371 
00372 // returns 0 when ok
00373 static int thread_nvm_store_read(const char *file_name, void *data, uint32_t data_size, uint32_t *version)
00374 {
00375     FILE *fp = fopen(file_name, "r");
00376     if(fp == NULL) {
00377         tr_warning("File not found: %s", file_name);
00378         return THREAD_NVM_FILE_CANNOT_OPEN;
00379     }
00380 
00381     size_t n_bytes = fread(version, 1, sizeof(uint32_t), fp);
00382     if (n_bytes!=sizeof(uint32_t)) {
00383         tr_warning("NVM version read error %s", file_name);
00384         fclose(fp);
00385         return THREAD_NVM_FILE_READ_ERROR;
00386     }
00387 
00388     n_bytes = fread(data, 1, data_size, fp);
00389     fclose(fp);
00390     if (n_bytes!=data_size) {
00391         tr_error("NVM read error %s", file_name);
00392         return THREAD_NVM_FILE_READ_ERROR;
00393     }
00394     else {
00395         return THREAD_NVM_FILE_SUCCESS; // return how many bytes was written.
00396     }
00397 }
00398 
00399 int thread_nvm_store_link_info_read(void)
00400 {
00401     nvm_link_info_t nvm_link_info_tmp;
00402     int status;
00403 
00404     if (!ns_file_system_get_root_path()) {
00405         if (!memcmp(cached_link_info.nvm_link_info.mac, ADDR_UNSPECIFIED, 8) &&
00406             cached_link_info.nvm_link_info.short_addr == LINK_INFO_SHORT_ADDR_NOT_SET) {
00407             tr_info("link info not cached");
00408             return THREAD_NVM_FILE_READ_ERROR;
00409         }
00410     }
00411     cached_link_info.loaded = true;
00412     char link_info_path[LINK_INFO_STRING_LEN];
00413     strcpy(link_info_path, thread_nvm_store_get_root_path());
00414     strcat(link_info_path, LINK_INFO_FILE);
00415 
00416     uint32_t version=0;
00417     status = thread_nvm_store_read(link_info_path, &nvm_link_info_tmp, sizeof(nvm_link_info_t), &version);
00418 
00419     if (status != THREAD_NVM_FILE_SUCCESS) {
00420         if (!memcmp(cached_link_info.nvm_link_info.mac, ADDR_UNSPECIFIED, 8) &&
00421             cached_link_info.nvm_link_info.short_addr == LINK_INFO_SHORT_ADDR_NOT_SET) {
00422             tr_info("link info not cached and read error %d", status);
00423             cached_link_info.loaded = false;
00424             return THREAD_NVM_FILE_READ_ERROR;
00425         }
00426         return status;
00427     }
00428     else if (ACTIVE_CONF_DATA_VERSION != version) {
00429         tr_info("link info version mismatch %"PRIu32, version);
00430         return THREAD_NVM_FILE_VERSION_WRONG;
00431     }
00432     memcpy(cached_link_info.nvm_link_info.mac, nvm_link_info_tmp.mac, 8);
00433     cached_link_info.nvm_link_info.short_addr = nvm_link_info_tmp.short_addr;
00434     tr_info("info read: %s parent short addr: %"PRIu16, trace_array(cached_link_info.nvm_link_info.mac, 8), cached_link_info.nvm_link_info.short_addr);
00435     return THREAD_NVM_FILE_SUCCESS;
00436 }
00437 
00438 int thread_nvm_store_link_info_get(uint8_t *parent_mac64, uint16_t *my_short_address)
00439 {
00440     if (!memcmp(cached_link_info.nvm_link_info.mac, ADDR_UNSPECIFIED, 8) &&
00441         cached_link_info.nvm_link_info.short_addr == LINK_INFO_SHORT_ADDR_NOT_SET) {
00442         tr_info("thread_nvm_store_link_info_get addr zeros");
00443         return THREAD_NVM_FILE_READ_ERROR;
00444     }
00445 
00446     if (!cached_link_info.loaded) {
00447         return THREAD_NVM_FILE_READ_ERROR;
00448     }
00449     // read data from cache if cached data is available
00450     if (parent_mac64) {
00451         memcpy(parent_mac64, cached_link_info.nvm_link_info.mac, 8);
00452     }
00453     if (my_short_address) {
00454         *my_short_address = cached_link_info.nvm_link_info.short_addr;
00455     }
00456     return THREAD_NVM_FILE_SUCCESS;
00457 }
00458 
00459 int thread_nvm_store_link_info_clear(void)
00460 {
00461     int status;
00462     tr_info("thread_nvm_store_link_info_clear");
00463     memset(cached_link_info.nvm_link_info.mac, 0, 8);
00464     cached_link_info.nvm_link_info.short_addr = LINK_INFO_SHORT_ADDR_NOT_SET;
00465 
00466     cached_link_info.loaded = false;
00467 
00468     if (!ns_file_system_get_root_path()) {
00469         return THREAD_NVM_FILE_ROOT_PATH_INVALID;
00470     }
00471 
00472     char link_info_path[LINK_INFO_STRING_LEN];
00473     strcpy(link_info_path, thread_nvm_store_get_root_path());
00474     strcat(link_info_path, LINK_INFO_FILE);
00475 
00476     status = remove(link_info_path);
00477 
00478     if (status != 0) {
00479       return THREAD_NVM_FILE_REMOVE_ERROR;
00480     }
00481 
00482     return THREAD_NVM_FILE_SUCCESS;
00483 }
00484 
00485 int thread_nvm_store_link_info_write(uint8_t *parent_mac, uint16_t short_addr)
00486 {
00487     //tr_info("write mac: %s parent short addr: %"PRIu16, trace_array(parent_mac, 8), short_addr);
00488     if (parent_mac) {
00489         memcpy(cached_link_info.nvm_link_info.mac, parent_mac, 8);
00490     } else {
00491         memset(cached_link_info.nvm_link_info.mac, 0, 8);
00492         tr_info("setting to zero");
00493     }
00494     // when router parent is zeros, but my short address is the actual routing address.
00495     cached_link_info.nvm_link_info.short_addr = short_addr;
00496 
00497     if (cached_link_info.write_delay == LINK_INFO_WRITE_DONE) {
00498         cached_link_info.write_delay = LINK_INFO_WRITE_DELAY; // delay writing some seconds
00499     }
00500 
00501     if (!ns_file_system_get_root_path()) {
00502         return THREAD_NVM_FILE_ROOT_PATH_INVALID;
00503     }
00504 
00505     return THREAD_NVM_FILE_SUCCESS;
00506 }
00507 
00508 static void thread_nvm_store_link_info_delayed_write(uint32_t seconds)
00509 {
00510     if (cached_link_info.write_delay == LINK_INFO_WRITE_DONE) {
00511         return;
00512     }
00513     else if (cached_link_info.write_delay > seconds) {
00514         cached_link_info.write_delay -= seconds;
00515         return;
00516     }
00517     cached_link_info.write_delay = LINK_INFO_WRITE_DONE;
00518 
00519     if (!ns_file_system_get_root_path()) {
00520         return;
00521     }
00522 
00523     char link_info_path[LINK_INFO_STRING_LEN];
00524     strcpy(link_info_path, thread_nvm_store_get_root_path());
00525     strcat(link_info_path, LINK_INFO_FILE);
00526     tr_info("link info write parent mac: %s parent short addr: %"PRIu16, trace_array(cached_link_info.nvm_link_info.mac, 8), cached_link_info.nvm_link_info.short_addr);
00527     thread_nvm_store_write(link_info_path, &cached_link_info.nvm_link_info, sizeof(nvm_link_info_t), LINK_INFO_DATA_VERSION);
00528 }
00529 
00530 void thread_nvm_store_seconds_timer(uint32_t seconds)
00531 {
00532     thread_nvm_store_link_info_delayed_write(seconds);
00533 }