Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers etx.c Source File

etx.c

00001 /*
00002  * Copyright (c) 2014-2019, Arm Limited and affiliates.
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 #include "nsconfig.h"
00018 #include "ns_types.h"
00019 #include "common_functions.h"
00020 #include "ns_trace.h"
00021 #include "string.h"
00022 #include "nsdynmemLIB.h"
00023 #include "platform/arm_hal_phy.h"
00024 #include "net_interface.h"
00025 
00026 #include "Core/include/ns_address_internal.h"
00027 #include "MLE/mle.h"
00028 #include "NWK_INTERFACE/Include/protocol_abstract.h"
00029 #include "NWK_INTERFACE/Include/protocol.h"
00030 #include "NWK_INTERFACE/Include/protocol_stats.h"
00031 #include "Service_Libs/etx/etx.h"
00032 #include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h"
00033 #include "Service_Libs/utils/isqrt.h"
00034 
00035 //TODO: Refactor this away!
00036 #include "MAC/rf_driver_storage.h"
00037 
00038 #define TRACE_GROUP "etx"
00039 
00040 static uint16_t etx_current_calc(uint16_t etx, uint8_t accumulated_failures);
00041 static uint16_t etx_dbm_lqi_calc(uint8_t lqi, int8_t dbm);
00042 static void etx_value_change_callback_needed_check(uint16_t etx, uint16_t *stored_diff_etx, uint8_t accumulated_failures, uint8_t attribute_index);
00043 static void etx_accum_failures_callback_needed_check(etx_storage_t *entry, uint8_t attribute_index);
00044 static void etx_cache_entry_init(uint8_t attribute_index);
00045 
00046 #if ETX_ACCELERATED_SAMPLE_COUNT == 0 || ETX_ACCELERATED_SAMPLE_COUNT > 6
00047 #error "ETX_ACCELERATED_SAMPLE_COUNT accepted values 1-6"
00048 #endif
00049 
00050 #if ETX_ACCELERATED_INTERVAL == 0
00051 #error "ETX_ACCELERATED_INTERVAL can't be zero"
00052 #endif
00053 
00054 #if ETX_ACCELERATED_INTERVAL >= ETX_ACCELERATED_SAMPLE_COUNT
00055 #error "ETX_ACCELERATED_INTERVAL must be < ETX_ACCELERATED_SAMPLE_COUNT"
00056 #endif
00057 
00058 
00059 typedef struct {
00060     etx_value_change_handler_t *callback_ptr;
00061     etx_accum_failures_handler_t *accum_cb_ptr;
00062     etx_storage_t *etx_storage_list;
00063     etx_sample_storage_t *etx_cache_storage_list;
00064     uint32_t max_etx_update;
00065     uint16_t hysteresis;                            // 12 bit fraction
00066     uint8_t accum_threshold;
00067     uint8_t etx_min_sampling_time;
00068     uint8_t ext_storage_list_size;
00069     uint8_t min_sample_count;
00070     bool cache_sample_requested;
00071     int8_t interface_id;
00072 } ext_info_t;
00073 
00074 static ext_info_t etx_info = {
00075     .hysteresis = 0,
00076     .accum_threshold = 0,
00077     .callback_ptr = NULL,
00078     .accum_cb_ptr = NULL,
00079     .etx_storage_list = NULL,
00080     .etx_cache_storage_list = NULL,
00081     .ext_storage_list_size = 0,
00082     .min_sample_count = 0,
00083     .max_etx_update = 0,
00084     .cache_sample_requested = false,
00085     .etx_min_sampling_time = 0,
00086     .interface_id = -1
00087 };
00088 
00089 static void etx_calculation(etx_storage_t *entry, uint16_t attempts, uint8_t acks_rx, uint8_t attribute_index)
00090 {
00091     if (etx_info.hysteresis && !entry->stored_diff_etx) {
00092         entry->stored_diff_etx = entry->etx;
00093     }
00094 
00095     uint32_t etx = attempts << (12 - ETX_MOVING_AVERAGE_FRACTION);
00096 
00097     if (acks_rx) {
00098         etx /= acks_rx;
00099     } else  {
00100         etx = 0xffff;
00101     }
00102     if ((etx_info.max_etx_update) && etx > etx_info.max_etx_update) {
00103         etx = etx_info.max_etx_update;
00104     }
00105 
00106     if (etx_info.cache_sample_requested && entry->etx_samples == 1) {
00107         // skip the initial value as RSSI generated ETX is not valid
00108         etx = etx << 3;
00109     } else {
00110         //Add old etx 7/8 to new one
00111         etx += entry->etx - (entry->etx >> ETX_MOVING_AVERAGE_FRACTION);
00112     }
00113 
00114     if (etx > 0xffff) {
00115         etx = 0xffff;
00116     }
00117 
00118     // If real ETX value has been received do not update based on LQI or dBm
00119     entry->tmp_etx = false;
00120     entry->etx = etx;
00121 
00122     etx_cache_entry_init(attribute_index);
00123 
00124     // Checks if ETX value change callback is needed
00125     etx_value_change_callback_needed_check(entry->etx, &(entry->stored_diff_etx), entry->accumulated_failures, attribute_index);
00126 }
00127 
00128 static void etx_cache_entry_init(uint8_t attribute_index)
00129 {
00130     if (!etx_info.cache_sample_requested) {
00131         return;
00132     }
00133 
00134     etx_sample_storage_t *storage = etx_info.etx_cache_storage_list + attribute_index;
00135     storage->attempts_count = 0;
00136     storage->etx_timer = etx_info.etx_min_sampling_time;
00137     storage->received_acks = 0;
00138     storage->sample_count = 0;
00139 }
00140 
00141 static bool etx_update_possible(etx_sample_storage_t *storage, etx_storage_t *entry, uint16_t time_update)
00142 {
00143     if (storage->etx_timer && time_update) {
00144         if (time_update >= storage->etx_timer) {
00145             storage->etx_timer = 0;
00146         } else {
00147             storage->etx_timer -= time_update;
00148         }
00149     }
00150 
00151     if (entry->etx_samples > ETX_ACCELERATED_SAMPLE_COUNT) {
00152         //Slower ETX update phase
00153         if (storage->sample_count < etx_info.min_sample_count || storage->etx_timer) {
00154             if (storage->sample_count < 0xff) {
00155                 return false;
00156             }
00157         }
00158     } else {
00159         //Accelerated ETX at for new neighbor
00160         if (storage->sample_count < ETX_ACCELERATED_INTERVAL) {
00161             return false;
00162         }
00163     }
00164 
00165     //tr_debug("ETX update possible %u attempts, %u rx ack", storage->attempts_count, storage->received_acks);
00166 
00167     return true;
00168 
00169 }
00170 
00171 
00172 static etx_sample_storage_t *etx_cache_sample_update(uint8_t attribute_index, uint8_t attempts, bool ack_rx)
00173 {
00174     etx_sample_storage_t *storage = etx_info.etx_cache_storage_list + attribute_index;
00175     storage->attempts_count += attempts;
00176     if (ack_rx) {
00177         storage->received_acks++;
00178     }
00179     storage->sample_count++;
00180     return storage;
00181 
00182 }
00183 
00184 
00185 
00186 /**
00187  * \brief A function to update ETX value based on transmission attempts
00188  *
00189  *  Update is made based on failed and successful message sending
00190  *  attempts for a message.
00191  *
00192  * \param attempts number of attempts to send message
00193  * \param success was message sending successful
00194  * \param addr_type address type, ADDR_802_15_4_SHORT or ADDR_802_15_4_LONG
00195  * \param addr_ptr PAN ID with 802.15.4 address
00196  */
00197 void etx_transm_attempts_update(int8_t interface_id, uint8_t attempts, bool success, uint8_t attribute_index)
00198 {
00199     uint8_t accumulated_failures;
00200     // Gets table entry
00201     etx_storage_t *entry = etx_storage_entry_get(interface_id, attribute_index);
00202     if (!entry) {
00203         return;
00204     }
00205     if (entry->etx_samples < 7) {
00206         entry->etx_samples++;
00207     }
00208 
00209     if (etx_info.cache_sample_requested) {
00210 
00211         etx_sample_storage_t *storage = etx_cache_sample_update(attribute_index, attempts, success);
00212         entry->accumulated_failures = 0;
00213 
00214         if (!entry->etx || (entry->etx_samples > 1 &&  !etx_update_possible(storage, entry, 0))) {
00215             return;
00216         }
00217 
00218         etx_calculation(entry, storage->attempts_count, storage->received_acks, attribute_index);
00219         return;
00220     }
00221 
00222     accumulated_failures = entry->accumulated_failures;
00223 
00224     if (!success) {
00225         /* Stores failed attempts to estimate ETX and to calculate
00226            new ETX after successful sending */
00227         if (accumulated_failures + attempts < 32) {
00228             entry->accumulated_failures += attempts;
00229         } else {
00230             success = true;
00231         }
00232     }
00233 
00234     if (success) {
00235         entry->accumulated_failures = 0;
00236     } else {
00237         etx_accum_failures_callback_needed_check(entry, attribute_index);
00238     }
00239 
00240     if (entry->etx) {
00241 
00242         if (success) {
00243             etx_calculation(entry, attempts + accumulated_failures, 1, attribute_index);
00244         }
00245     }
00246 }
00247 
00248 /**
00249  * \brief A function to update ETX value based on remote incoming IDR
00250  *
00251  *  Update is made based on remote incoming IDR received from
00252  *  neighbor.
00253  *
00254  * \param remote_incoming_idr Remote incoming IDR
00255  * \param mac64_addr_ptr long MAC address
00256  */
00257 void etx_remote_incoming_idr_update(int8_t interface_id, uint8_t remote_incoming_idr, uint8_t attribute_index)
00258 {
00259     etx_storage_t *entry = etx_storage_entry_get(interface_id, attribute_index);
00260 
00261     if (entry) {
00262         // If ETX has been set
00263         if (entry->etx) {
00264             // If hysteresis is set stores ETX value to enable comparison
00265             if (etx_info.hysteresis && !entry->stored_diff_etx) {
00266                 entry->stored_diff_etx = entry->etx;
00267             }
00268             // remote EXT = remote incoming IDR^2 (12 bit fraction)
00269             uint32_t remote_ext = ((uint32_t)remote_incoming_idr * remote_incoming_idr) << 2;
00270 
00271             // ETX = 7/8 * current ETX + 1/8 * remote ETX */
00272             uint32_t etx = entry->etx - (entry->etx >> ETX_MOVING_AVERAGE_FRACTION);
00273             etx += remote_ext >> ETX_MOVING_AVERAGE_FRACTION;
00274 
00275             if (etx > 0xffff) {
00276                 entry->etx = 0xffff;
00277             } else {
00278                 entry->etx = etx;
00279             }
00280 
00281             // Checks if ETX value change callback is needed
00282             etx_value_change_callback_needed_check(entry->etx, &(entry->stored_diff_etx), entry->accumulated_failures, attribute_index);
00283         }
00284         entry->remote_incoming_idr = remote_incoming_idr;
00285     }
00286 }
00287 
00288 /**
00289  * \brief A function to read ETX value
00290  *
00291  *  Returns ETX value for an address
00292  *
00293  * \param interface_id network interface id
00294  * \param addr_type address type, ADDR_802_15_4_SHORT or ADDR_802_15_4_LONG
00295  * \param addr_ptr PAN ID with 802.15.4 address
00296  *
00297  * \return 0x0100 to 0xFFFF ETX value (8 bit fraction)
00298  * \return 0xFFFF address not associated
00299  * \return 0x0000 address unknown or other error
00300  * \return 0x0001 no ETX statistics on this interface
00301  */
00302 uint16_t etx_read(int8_t interface_id, addrtype_t addr_type, const uint8_t *addr_ptr)
00303 {
00304     protocol_interface_info_entry_t *interface = protocol_stack_interface_info_get_by_id(interface_id);
00305 
00306     if (!addr_ptr || !interface) {
00307         return 0;
00308     }
00309 
00310     if (interface->etx_read_override) {
00311         // Interface has modified ETX calculation
00312         return interface->etx_read_override(interface, addr_type, addr_ptr);
00313     }
00314 
00315     uint8_t attribute_index;
00316     if (interface->nwk_id == IF_IPV6) {
00317         return 1;
00318     }
00319 
00320     //Must Support old MLE table and new still same time
00321     mac_neighbor_table_entry_t *mac_neighbor = mac_neighbor_table_address_discover(mac_neighbor_info(interface), addr_ptr + PAN_ID_LEN, addr_type);
00322     if (!mac_neighbor) {
00323         return 0xffff;
00324     }
00325     attribute_index = mac_neighbor->index ;
00326 
00327     etx_storage_t *entry = etx_storage_entry_get(interface_id, attribute_index);
00328 
00329     if (!entry) {
00330         return 0xffff;
00331     }
00332 
00333     uint16_t etx  = etx_current_calc(entry->etx, entry->accumulated_failures);
00334     etx >>= 4;
00335 
00336     return etx;
00337 }
00338 
00339 /**
00340  * \brief A function to read local incoming IDR value
00341  *
00342  *  Returns local incoming IDR value for an address
00343  *
00344  * \param mac64_addr_ptr long MAC address
00345  *
00346  * \return 0x0100 to 0xFFFF incoming IDR value (8 bit fraction)
00347  * \return 0x0000 address unknown
00348  */
00349 uint16_t etx_local_incoming_idr_read(int8_t interface_id, uint8_t attribute_index)
00350 {
00351     uint32_t local_incoming_idr = 0;
00352     etx_storage_t *entry = etx_storage_entry_get(interface_id, attribute_index);
00353     if (entry) {
00354         uint16_t local_etx = etx_current_calc(entry->etx, entry->accumulated_failures);
00355 
00356         local_incoming_idr = isqrt32((uint32_t)local_etx << 16);
00357         // divide by sqrt(2^12)
00358         local_incoming_idr = local_incoming_idr >> 6;
00359     }
00360 
00361     return local_incoming_idr;
00362 }
00363 
00364 /**
00365  * \brief A function to read local incoming IDR value
00366  *
00367  *  Returns local incoming IDR value for an address
00368  *
00369  * \param mac64_addr_ptr long MAC address
00370  *
00371  * \return 0x0100 to 0xFFFF incoming IDR value (8 bit fraction)
00372  * \return 0x0000 address unknown
00373  */
00374 uint16_t etx_local_etx_read(int8_t interface_id, uint8_t attribute_index)
00375 {
00376     etx_storage_t *entry = etx_storage_entry_get(interface_id, attribute_index);
00377     if (!entry) {
00378         return 0;
00379     }
00380     return etx_current_calc(entry->etx, entry->accumulated_failures) >> 4;
00381 }
00382 
00383 /**
00384  * \brief A function to calculate current ETX
00385  *
00386  *  Returns current ETX value based on ETX and failed attempts. Return
00387  *  value is scaled by scaling factor
00388  *
00389  * \param etx ETX (12 bit fraction)
00390  * \param accumulated_failures failed attempts
00391  *
00392  * \return ETX value (12 bit fraction)
00393  */
00394 static uint16_t etx_current_calc(uint16_t etx, uint8_t accumulated_failures)
00395 {
00396     uint32_t current_etx;
00397 
00398     // If there is no failed attempts
00399     if (accumulated_failures == 0) {
00400         current_etx = etx;
00401     } else {
00402         /* Calculates ETX estimate based on failed attempts
00403            ETX = current ETX + 1/8 * (failed attempts << 12) */
00404         current_etx = etx + (accumulated_failures << (12 - ETX_MOVING_AVERAGE_FRACTION));
00405         if (current_etx > 0xffff) {
00406             current_etx = 0xffff;
00407         }
00408     }
00409 
00410     return current_etx;
00411 }
00412 
00413 /**
00414  * \brief A function to update ETX value based on LQI and dBm
00415  *
00416  *  Update is made based on dBM and LQI of received message.
00417  *
00418  * \param lqi link quality indicator
00419  * \param dbm measured dBm
00420  * \param mac64_addr_ptr long MAC address
00421  *
00422  * \return 0x0100 to 0xFFFF local incoming IDR value (8 bit fraction)
00423  */
00424 uint16_t etx_lqi_dbm_update(int8_t interface_id, uint8_t lqi, int8_t dbm, uint8_t attribute_index)
00425 {
00426     uint32_t local_incoming_idr = 0;
00427     uint32_t etx = 0;
00428 
00429     etx_storage_t *entry = etx_storage_entry_get(interface_id, attribute_index);
00430 
00431     if (entry) {
00432         // If local ETX is not set calculate it based on LQI and dBm
00433         if (!entry->etx) {
00434             etx = etx_dbm_lqi_calc(lqi, dbm);
00435             entry->etx = etx;
00436             entry->tmp_etx = true;
00437         }
00438         // If local ETX has been calculated without remote incoming IDR and
00439         // remote incoming IDR is available update it by remote incoming IDR value
00440         if (entry->remote_incoming_idr && entry->tmp_etx) {
00441             entry->tmp_etx = false;
00442 
00443             local_incoming_idr = isqrt32((uint32_t)entry->etx << 16);
00444             // divide by sqrt(2^12) and scale to 12 bit fraction
00445             local_incoming_idr = local_incoming_idr >> 2;
00446 
00447             etx = local_incoming_idr * (((uint16_t)entry->remote_incoming_idr) << 7);
00448             entry->etx = etx >> 12;
00449 
00450             local_incoming_idr >>= 4;
00451         }
00452 
00453         // If local ETX has been calculated indicates new neighbor
00454         if (etx) {
00455             etx_neighbor_add(interface_id, attribute_index);
00456         }
00457     }
00458 
00459     // If local ETX is not set return temporary ETX based on LQI and dB,
00460     if (!local_incoming_idr) {
00461         if (!etx) {
00462             etx = etx_dbm_lqi_calc(lqi, dbm);
00463         }
00464 
00465         local_incoming_idr = isqrt32(etx << 16);
00466         // divide by sqrt(2^12)
00467         local_incoming_idr >>= 6;
00468     }
00469 
00470     return local_incoming_idr;
00471 }
00472 
00473 /**
00474  * \brief A function to calculate ETX value based on dBm and LQI
00475  *
00476  *  Calculation is made using RF driver service. If service does not
00477  *  exists then local function is used.
00478  *
00479  * \param lqi link quality indicator
00480  * \param dbm measured dBm
00481  *
00482  * \return ETX value (12 bit fraction)
00483  */
00484 static uint16_t etx_dbm_lqi_calc(uint8_t lqi, int8_t dbm)
00485 {
00486     protocol_interface_info_entry_t *cur = 0;
00487     int8_t driver_ret_value = -1;
00488     uint16_t etx;
00489 
00490     phy_signal_info_s signal_info;
00491     signal_info.type = PHY_SIGNAL_INFO_ETX;
00492     signal_info.lqi = lqi;
00493     signal_info.dbm = dbm;
00494     signal_info.result = 0xffff;
00495 
00496     //TODO: This is needed, but RF driver cannot be accessed directly! Figure out MAC extension for this.
00497     cur = protocol_stack_interface_info_get(IF_6LoWPAN);
00498     if ((cur) && (cur->dev_driver) && (cur->dev_driver->phy_driver)) {
00499         phy_device_driver_s *dev_driver = cur->dev_driver->phy_driver;
00500         if (dev_driver->extension) {
00501             driver_ret_value = dev_driver->extension(PHY_EXTENSION_CONVERT_SIGNAL_INFO, (uint8_t *)&signal_info);
00502         }
00503     }
00504 
00505     if ((driver_ret_value != -1) && (signal_info.result != 0xffff)) {
00506         etx = signal_info.result;
00507         etx <<= 4;
00508     } else {
00509         /* Atmel version
00510            dBm = RSSI base value [dBm] + 1.03 [dB] x ED level
00511            LQI = errors in received frame */
00512 
00513         // for dBm -90 and LQI 0 ETX will be 2.4
00514         etx = ((dbm * -1) * (256 - lqi));
00515         etx >>= 1; // scale result to 12 bits
00516         etx += 1 << 12; // add one (perfect link)
00517     }
00518 
00519     return etx;
00520 }
00521 
00522 /**
00523  * \brief A function to register ETX value change callback
00524  *
00525  *  Register ETX value change callback. When ETX value has changed more or equal
00526  *  to hysteresis value ETX module calls ETX value change callback.
00527  *
00528  * \param nwk_interface_id network interface id
00529  * \param hysteresis hysteresis value (8 bit fraction)
00530  * \param callback_ptr callback function pointer
00531  *
00532  * \return 0 not 6LowPAN interface
00533  * \return 1 success
00534  */
00535 uint8_t etx_value_change_callback_register(nwk_interface_id nwk_id, int8_t interface_id, uint16_t hysteresis, etx_value_change_handler_t *callback_ptr)
00536 {
00537     if ((nwk_id == IF_6LoWPAN) && hysteresis && callback_ptr) {
00538         etx_info.hysteresis = hysteresis << 4;
00539         etx_info.callback_ptr = callback_ptr;
00540         etx_info.interface_id = interface_id;
00541         return 1;
00542     } else {
00543         return 0;
00544     }
00545 }
00546 
00547 bool etx_storage_list_allocate(int8_t interface_id, uint8_t etx_storage_size)
00548 {
00549     if (!etx_storage_size) {
00550         ns_dyn_mem_free(etx_info.etx_storage_list);
00551         ns_dyn_mem_free(etx_info.etx_cache_storage_list);
00552         etx_info.etx_cache_storage_list = NULL;
00553         etx_info.cache_sample_requested = false;
00554         etx_info.etx_storage_list = NULL;
00555         etx_info.ext_storage_list_size = 0;
00556         return true;
00557     }
00558 
00559     if (etx_info.ext_storage_list_size == etx_storage_size) {
00560         return true;
00561     }
00562 
00563     ns_dyn_mem_free(etx_info.etx_storage_list);
00564     etx_info.cache_sample_requested = false;
00565     etx_info.ext_storage_list_size = 0;
00566     etx_info.etx_storage_list = ns_dyn_mem_alloc(sizeof(etx_storage_t) * etx_storage_size);
00567 
00568     if (!etx_info.etx_storage_list) {
00569         ns_dyn_mem_free(etx_info.etx_storage_list);
00570         etx_info.etx_storage_list = NULL;
00571         etx_info.ext_storage_list_size = 0;
00572         return false;
00573     }
00574 
00575 
00576     etx_info.ext_storage_list_size = etx_storage_size;
00577     etx_info.interface_id = interface_id;
00578     etx_storage_t *list_ptr = etx_info.etx_storage_list;
00579     for (uint8_t i = 0; i < etx_storage_size; i++) {
00580         memset(list_ptr, 0, sizeof(etx_storage_t));
00581 
00582         list_ptr++;
00583     }
00584     return true;
00585 
00586 }
00587 
00588 bool etx_cached_etx_parameter_set(uint8_t min_wait_time, uint8_t etx_min_sample_count)
00589 {
00590     //No ini ETX allocation done yet
00591     if (etx_info.ext_storage_list_size == 0) {
00592         return false;
00593     }
00594 
00595     if (min_wait_time || etx_min_sample_count) {
00596         if (!etx_info.etx_cache_storage_list) {
00597             //allocate
00598             etx_info.etx_cache_storage_list = ns_dyn_mem_alloc(sizeof(etx_sample_storage_t) * etx_info.ext_storage_list_size);
00599 
00600             if (!etx_info.etx_cache_storage_list) {
00601                 return false;
00602             }
00603             etx_info.cache_sample_requested = true;
00604             etx_sample_storage_t *sample_list = etx_info.etx_cache_storage_list;
00605             for (uint8_t i = 0; i < etx_info.ext_storage_list_size; i++) {
00606                 memset(sample_list, 0, sizeof(etx_sample_storage_t));
00607                 sample_list++;
00608             }
00609         }
00610 
00611     } else {
00612         //Free Cache table we not need that anymore
00613         etx_info.cache_sample_requested = false;
00614         ns_dyn_mem_free(etx_info.etx_cache_storage_list);
00615         etx_info.etx_cache_storage_list = NULL;
00616     }
00617 
00618     etx_info.min_sample_count = etx_min_sample_count;
00619     etx_info.etx_min_sampling_time = min_wait_time;
00620 
00621     return true;
00622 }
00623 
00624 void etx_max_update_set(uint16_t etx_max_update)
00625 {
00626     if (etx_max_update) {
00627         etx_info.max_etx_update = (etx_max_update / 128) << (12 - ETX_MOVING_AVERAGE_FRACTION);
00628     } else {
00629         etx_info.max_etx_update = 0;
00630     }
00631 }
00632 
00633 etx_storage_t *etx_storage_entry_get(int8_t interface_id, uint8_t attribute_index)
00634 {
00635     if (etx_info.interface_id != interface_id || !etx_info.etx_storage_list || attribute_index >= etx_info.ext_storage_list_size) {
00636         return NULL;
00637     }
00638 
00639     etx_storage_t *entry = etx_info.etx_storage_list + attribute_index;
00640     return entry;
00641 }
00642 
00643 
00644 /**
00645  * \brief A function to register accumulated failures callback
00646  *
00647  *  When the number of accumulated failures has reached the threshold
00648  *  value, the ETX module calls the accumulated failures callback on
00649  *  every transmission failure.
00650  *
00651  * \param nwk_id network ID (6LoWPAN)
00652  * \param interface_id interface ID
00653  * \param threshold threshold value for accumulated failures
00654  * \param callback_ptr callback function pointer
00655  *
00656  * \return 0 not 6LowPAN interface
00657  * \return 1 success
00658  */
00659 uint8_t etx_accum_failures_callback_register(nwk_interface_id nwk_id, int8_t interface_id, uint8_t threshold, etx_accum_failures_handler_t *callback_ptr)
00660 {
00661     if ((nwk_id == IF_6LoWPAN) && threshold && callback_ptr) {
00662         etx_info.interface_id = interface_id;
00663         etx_info.accum_threshold = threshold;
00664         etx_info.accum_cb_ptr = callback_ptr;
00665         return 1;
00666     } else {
00667         return 0;
00668     }
00669 }
00670 
00671 /**
00672  * \brief A function to check if ETX value change callback is needed
00673  *
00674  *  Calculates current ETX and compares it against stored ETX. If change
00675  *  of the values is more than hysteresis calls ETX value change
00676  *  callback.
00677  *
00678  * \param etx ETX (12 bit fraction)
00679  * \param stored_diff_etx stored ETX value
00680  * \param accumulated_failures failed attempts
00681  * \param mac64_addr_ptr long MAC address
00682  * \param mac16_addr short MAC address or 0xffff address is not set
00683  *
00684  * \return ETX value (12 bit fraction)
00685  */
00686 static void etx_value_change_callback_needed_check(uint16_t etx, uint16_t *stored_diff_etx, uint8_t accumulated_failures, uint8_t attribute_index)
00687 {
00688     uint16_t current_etx;
00689     bool callback = false;
00690     if (!etx_info.hysteresis) {
00691         return;
00692     }
00693 
00694     // Calculates current ETX
00695     current_etx = etx_current_calc(etx, accumulated_failures);
00696 
00697     // If difference is more than hysteresis
00698     if (current_etx > *stored_diff_etx) {
00699         if (current_etx - *stored_diff_etx >= etx_info.hysteresis) {
00700             callback = true;
00701         }
00702     } else if (current_etx < *stored_diff_etx) {
00703         if (*stored_diff_etx - current_etx >= etx_info.hysteresis) {
00704             callback = true;
00705         }
00706     }
00707 
00708     // Calls callback function
00709     if (callback) {
00710         etx_info.callback_ptr(etx_info.interface_id, (*stored_diff_etx) >> 4, current_etx >> 4, attribute_index);
00711         *stored_diff_etx = current_etx;
00712     }
00713 }
00714 
00715 /**
00716  * \brief A function to check if accumulated failures callback is needed
00717  *
00718  *  If the current number of accumulated failures is equal or greater than
00719  *  the set threshold value, the function calls accumulated failures callback.
00720  *
00721  * \param neigh_table_ptr the neighbor node in question
00722  */
00723 static void etx_accum_failures_callback_needed_check(etx_storage_t *entry, uint8_t attribute_index)
00724 {
00725     if (!etx_info.accum_threshold) {
00726         return;
00727     }
00728 
00729     if (entry->accumulated_failures < etx_info.accum_threshold) {
00730         return;
00731     }
00732 
00733     etx_info.accum_cb_ptr(etx_info.interface_id, entry->accumulated_failures, attribute_index);
00734 }
00735 
00736 /**
00737  * \brief A function to remove ETX neighbor
00738  *
00739  *  Notifies ETX module that neighbor has been removed. Calls ETX value change callback
00740  *  if that is set.
00741  *
00742  * \param mac64_addr_ptr long MAC address
00743  *
00744  */
00745 void etx_neighbor_remove(int8_t interface_id, uint8_t attribute_index)
00746 {
00747 
00748     //tr_debug("Remove attribute %u", attribute_index);
00749     uint16_t stored_diff_etx;
00750     etx_storage_t *entry = etx_storage_entry_get(interface_id, attribute_index);
00751     if (entry && etx_info.callback_ptr) {
00752 
00753         if (entry->etx) {
00754             stored_diff_etx = entry->stored_diff_etx >> 4;
00755             if (!stored_diff_etx) {
00756                 stored_diff_etx = 0xffff;
00757             }
00758             etx_info.callback_ptr(etx_info.interface_id, stored_diff_etx, 0xffff, attribute_index);
00759         }
00760 
00761         if (etx_info.cache_sample_requested) {
00762             //Clear cached values
00763             etx_sample_storage_t *cache_entry = etx_info.etx_cache_storage_list + attribute_index;
00764             memset(cache_entry, 0, sizeof(etx_sample_storage_t));
00765         }
00766         //Clear all data base back to zero for new user
00767         memset(entry, 0, sizeof(etx_storage_t));
00768     }
00769 }
00770 
00771 /**
00772  * \brief A function to add ETX neighbor
00773  *
00774  *  Notifies ETX module that neighbor has been added. Calls ETX value change callback
00775  *  if that is set.
00776  *
00777  * \param mac64_addr_ptr long MAC address
00778  *
00779  */
00780 void etx_neighbor_add(int8_t interface_id, uint8_t attribute_index)
00781 {
00782 
00783     //tr_debug("Add attribute %u", attribute_index);
00784     uint16_t stored_diff_etx;
00785     etx_storage_t *entry = etx_storage_entry_get(interface_id, attribute_index);
00786     if (entry && etx_info.callback_ptr) {
00787         // Gets table entry
00788 
00789         if (entry->etx) {
00790             stored_diff_etx = entry->stored_diff_etx;
00791             if (!stored_diff_etx) {
00792                 stored_diff_etx = entry->etx;
00793             }
00794             etx_info.callback_ptr(etx_info.interface_id, stored_diff_etx >> 4, entry->etx >> 4,
00795                                   attribute_index);
00796         }
00797     }
00798 }
00799 
00800 void etx_cache_timer(int8_t interface_id, uint16_t seconds_update)
00801 {
00802     if (!etx_info.cache_sample_requested) {
00803         return;
00804     }
00805 
00806     protocol_interface_info_entry_t *interface = protocol_stack_interface_info_get_by_id(interface_id);
00807     if (!interface || !mac_neighbor_info(interface)) {
00808         return;
00809     }
00810 
00811     ns_list_foreach(mac_neighbor_table_entry_t, neighbour, &mac_neighbor_info(interface)->neighbour_list) {
00812 
00813         etx_storage_t *etx_entry = etx_storage_entry_get(interface_id, neighbour->index);
00814 
00815         if (!etx_entry || etx_entry->tmp_etx) {
00816             continue;
00817         }
00818         etx_sample_storage_t *storage = etx_info.etx_cache_storage_list + neighbour->index;
00819 
00820         if (etx_update_possible(storage, etx_entry, seconds_update)) {
00821             etx_calculation(etx_entry, storage->attempts_count, storage->received_acks, neighbour->index);
00822         }
00823     }
00824 
00825 }