Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
etx.c
00001 /* 00002 * Copyright (c) 2014-2018, 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/address.h" 00027 #include "MLE/mle.h" 00028 #include "NWK_INTERFACE/Include/protocol.h" 00029 #include "NWK_INTERFACE/Include/protocol_stats.h" 00030 #include "Service_Libs/etx/etx.h" 00031 #include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h" 00032 #include "Service_Libs/utils/isqrt.h" 00033 00034 //TODO: Refactor this away! 00035 #include "MAC/rf_driver_storage.h" 00036 00037 #define TRACE_GROUP "etx" 00038 00039 static uint16_t etx_current_calc(uint16_t etx, uint8_t accumulated_failures); 00040 static uint16_t etx_dbm_lqi_calc(uint8_t lqi, int8_t dbm); 00041 static void etx_value_change_callback_needed_check(uint16_t etx, uint16_t *stored_diff_etx, uint8_t accumulated_failures, uint8_t attribute_index); 00042 static void etx_accum_failures_callback_needed_check(etx_storage_t * entry, uint8_t attribute_index); 00043 00044 typedef struct { 00045 uint16_t hysteresis; // 12 bit fraction 00046 uint8_t accum_threshold; 00047 etx_value_change_handler_t *callback_ptr; 00048 etx_accum_failures_handler_t *accum_cb_ptr; 00049 etx_storage_t *etx_storage_list; 00050 uint8_t ext_storage_list_size; 00051 int8_t interface_id; 00052 } ext_info_t; 00053 00054 static ext_info_t etx_info = { 00055 .hysteresis = 0, 00056 .accum_threshold = 0, 00057 .callback_ptr = NULL, 00058 .accum_cb_ptr = NULL, 00059 .etx_storage_list = NULL, 00060 .ext_storage_list_size = 0, 00061 .interface_id = -1 00062 }; 00063 00064 /** 00065 * \brief A function to update ETX value based on transmission attempts 00066 * 00067 * Update is made based on failed and successful message sending 00068 * attempts for a message. 00069 * 00070 * \param attempts number of attempts to send message 00071 * \param success was message sending successful 00072 * \param addr_type address type, ADDR_802_15_4_SHORT or ADDR_802_15_4_LONG 00073 * \param addr_ptr PAN ID with 802.15.4 address 00074 */ 00075 void etx_transm_attempts_update(int8_t interface_id, uint8_t attempts, bool success, uint8_t attribute_index) 00076 { 00077 uint32_t etx; 00078 uint8_t accumulated_failures; 00079 // Gets table entry 00080 etx_storage_t * entry = etx_storage_entry_get(interface_id, attribute_index); 00081 if (!entry) { 00082 return; 00083 } 00084 00085 accumulated_failures = entry->accumulated_failures; 00086 00087 if (!success) { 00088 /* Stores failed attempts to estimate ETX and to calculate 00089 new ETX after successful sending */ 00090 if (accumulated_failures + attempts < 32) { 00091 entry->accumulated_failures += attempts; 00092 } else { 00093 success = true; 00094 } 00095 } 00096 00097 if (success) { 00098 entry->accumulated_failures = 0; 00099 } else { 00100 etx_accum_failures_callback_needed_check(entry, attribute_index); 00101 } 00102 00103 if (entry->etx) { 00104 // If hysteresis is set stores ETX value for comparison 00105 if (etx_info.hysteresis && !entry->stored_diff_etx) { 00106 entry->stored_diff_etx = entry->etx; 00107 } 00108 00109 if (success) { 00110 // ETX = 7/8 * current ETX + 1/8 * ((attempts + failed attempts) << 12) 00111 etx = entry->etx - (entry->etx >> ETX_MOVING_AVERAGE_FRACTION); 00112 etx += (attempts + accumulated_failures) << (12 - ETX_MOVING_AVERAGE_FRACTION); 00113 00114 if (etx > 0xffff) { 00115 entry->etx = 0xffff; 00116 } else { 00117 entry->etx = etx; 00118 } 00119 } 00120 00121 // If real ETX value has been received do not update based on LQI or dBm 00122 entry->tmp_etx = false; 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 00129 /** 00130 * \brief A function to update ETX value based on remote incoming IDR 00131 * 00132 * Update is made based on remote incoming IDR received from 00133 * neighbor. 00134 * 00135 * \param remote_incoming_idr Remote incoming IDR 00136 * \param mac64_addr_ptr long MAC address 00137 */ 00138 void etx_remote_incoming_idr_update(int8_t interface_id, uint8_t remote_incoming_idr, uint8_t attribute_index) 00139 { 00140 etx_storage_t * entry = etx_storage_entry_get(interface_id, attribute_index); 00141 00142 if (entry) { 00143 // If ETX has been set 00144 if (entry->etx) { 00145 // If hysteresis is set stores ETX value to enable comparison 00146 if (etx_info.hysteresis && !entry->stored_diff_etx) { 00147 entry->stored_diff_etx = entry->etx; 00148 } 00149 // remote EXT = remote incoming IDR^2 (12 bit fraction) 00150 uint32_t remote_ext = ((uint32_t)remote_incoming_idr * remote_incoming_idr) << 2; 00151 00152 // ETX = 7/8 * current ETX + 1/8 * remote ETX */ 00153 uint32_t etx = entry->etx - (entry->etx >> ETX_MOVING_AVERAGE_FRACTION); 00154 etx += remote_ext >> ETX_MOVING_AVERAGE_FRACTION; 00155 00156 if (etx > 0xffff) { 00157 entry->etx = 0xffff; 00158 } else { 00159 entry->etx = etx; 00160 } 00161 00162 // Checks if ETX value change callback is needed 00163 etx_value_change_callback_needed_check(entry->etx, &(entry->stored_diff_etx), entry->accumulated_failures, attribute_index); 00164 } 00165 entry->remote_incoming_idr = remote_incoming_idr; 00166 } 00167 } 00168 00169 /** 00170 * \brief A function to read ETX value 00171 * 00172 * Returns ETX value for an address 00173 * 00174 * \param interface_id network interface id 00175 * \param addr_type address type, ADDR_802_15_4_SHORT or ADDR_802_15_4_LONG 00176 * \param addr_ptr PAN ID with 802.15.4 address 00177 * 00178 * \return 0x0100 to 0xFFFF ETX value (8 bit fraction) 00179 * \return 0xFFFF address not associated 00180 * \return 0x0000 address unknown or other error 00181 * \return 0x0001 no ETX statistics on this interface 00182 */ 00183 uint16_t etx_read(int8_t interface_id, addrtype_t addr_type, const uint8_t *addr_ptr) 00184 { 00185 protocol_interface_info_entry_t *interface = protocol_stack_interface_info_get_by_id(interface_id); 00186 00187 if (!addr_ptr || !interface) { 00188 return 0; 00189 } 00190 00191 if (interface->etx_read_override) { 00192 // Interface has modified ETX calculation 00193 return interface->etx_read_override(interface, addr_type, addr_ptr); 00194 } 00195 00196 uint8_t attribute_index; 00197 if (interface->nwk_id == IF_IPV6) { 00198 return 1; 00199 } 00200 00201 //Must Support old MLE table and new still same time 00202 mac_neighbor_table_entry_t *mac_neighbor = mac_neighbor_table_address_discover(mac_neighbor_info(interface), addr_ptr + PAN_ID_LEN, addr_type); 00203 if (!mac_neighbor) { 00204 return 0xffff; 00205 } 00206 attribute_index = mac_neighbor->index ; 00207 00208 00209 //tr_debug("Etx Read from atribute %u", attribute_index); 00210 00211 etx_storage_t * entry = etx_storage_entry_get(interface_id, attribute_index); 00212 00213 if (!entry) { 00214 return 0xffff; 00215 } 00216 00217 uint16_t etx = etx_current_calc(entry->etx, entry->accumulated_failures); 00218 etx >>= 4; 00219 00220 //tr_debug("Etx value %u", etx); 00221 00222 return etx; 00223 } 00224 00225 /** 00226 * \brief A function to read local incoming IDR value 00227 * 00228 * Returns local incoming IDR value for an address 00229 * 00230 * \param mac64_addr_ptr long MAC address 00231 * 00232 * \return 0x0100 to 0xFFFF incoming IDR value (8 bit fraction) 00233 * \return 0x0000 address unknown 00234 */ 00235 uint16_t etx_local_incoming_idr_read(int8_t interface_id, uint8_t attribute_index) 00236 { 00237 uint32_t local_incoming_idr = 0; 00238 etx_storage_t * entry = etx_storage_entry_get(interface_id, attribute_index); 00239 if (entry) { 00240 uint16_t local_etx = etx_current_calc(entry->etx, entry->accumulated_failures); 00241 00242 local_incoming_idr = isqrt32((uint32_t)local_etx << 16); 00243 // divide by sqrt(2^12) 00244 local_incoming_idr = local_incoming_idr >> 6; 00245 } 00246 00247 return local_incoming_idr; 00248 } 00249 00250 /** 00251 * \brief A function to read local incoming IDR value 00252 * 00253 * Returns local incoming IDR value for an address 00254 * 00255 * \param mac64_addr_ptr long MAC address 00256 * 00257 * \return 0x0100 to 0xFFFF incoming IDR value (8 bit fraction) 00258 * \return 0x0000 address unknown 00259 */ 00260 uint16_t etx_local_etx_read(int8_t interface_id, uint8_t attribute_index) 00261 { 00262 etx_storage_t * entry = etx_storage_entry_get(interface_id, attribute_index); 00263 if (!entry) { 00264 return 0; 00265 } 00266 return etx_current_calc(entry->etx, entry->accumulated_failures) >> 4; 00267 } 00268 00269 /** 00270 * \brief A function to calculate current ETX 00271 * 00272 * Returns current ETX value based on ETX and failed attempts. Return 00273 * value is scaled by scaling factor 00274 * 00275 * \param etx ETX (12 bit fraction) 00276 * \param accumulated_failures failed attempts 00277 * 00278 * \return ETX value (12 bit fraction) 00279 */ 00280 static uint16_t etx_current_calc(uint16_t etx, uint8_t accumulated_failures) 00281 { 00282 uint32_t current_etx; 00283 00284 // If there is no failed attempts 00285 if (accumulated_failures == 0) { 00286 current_etx = etx; 00287 } else { 00288 /* Calculates ETX estimate based on failed attempts 00289 ETX = current ETX + 1/8 * (failed attempts << 12) */ 00290 current_etx = etx + (accumulated_failures << (12 - ETX_MOVING_AVERAGE_FRACTION)); 00291 if (current_etx > 0xffff) { 00292 current_etx = 0xffff; 00293 } 00294 } 00295 00296 return current_etx; 00297 } 00298 00299 /** 00300 * \brief A function to update ETX value based on LQI and dBm 00301 * 00302 * Update is made based on dBM and LQI of received message. 00303 * 00304 * \param lqi link quality indicator 00305 * \param dbm measured dBm 00306 * \param mac64_addr_ptr long MAC address 00307 * 00308 * \return 0x0100 to 0xFFFF local incoming IDR value (8 bit fraction) 00309 */ 00310 uint16_t etx_lqi_dbm_update(int8_t interface_id, uint8_t lqi, int8_t dbm, uint8_t attribute_index) 00311 { 00312 uint32_t local_incoming_idr = 0; 00313 uint32_t etx = 0; 00314 00315 etx_storage_t *entry = etx_storage_entry_get(interface_id, attribute_index); 00316 00317 if (entry) { 00318 // If local ETX is not set calculate it based on LQI and dBm 00319 if (!entry->etx) { 00320 etx = etx_dbm_lqi_calc(lqi, dbm); 00321 entry->etx = etx; 00322 entry->tmp_etx = true; 00323 } 00324 // If local ETX has been calculated without remote incoming IDR and 00325 // remote incoming IDR is available update it by remote incoming IDR value 00326 if (entry->remote_incoming_idr && entry->tmp_etx) { 00327 entry->tmp_etx = false; 00328 00329 local_incoming_idr = isqrt32((uint32_t)entry->etx << 16); 00330 // divide by sqrt(2^12) and scale to 12 bit fraction 00331 local_incoming_idr = local_incoming_idr >> 2; 00332 00333 etx = local_incoming_idr * (((uint16_t)entry->remote_incoming_idr) << 7); 00334 entry->etx = etx >> 12; 00335 00336 local_incoming_idr >>= 4; 00337 } 00338 00339 // If local ETX has been calculated indicates new neighbor 00340 if (etx) { 00341 etx_neighbor_add(interface_id, attribute_index); 00342 } 00343 } 00344 00345 // If local ETX is not set return temporary ETX based on LQI and dB, 00346 if (!local_incoming_idr) { 00347 if (!etx) { 00348 etx = etx_dbm_lqi_calc(lqi, dbm); 00349 } 00350 00351 local_incoming_idr = isqrt32(etx << 16); 00352 // divide by sqrt(2^12) 00353 local_incoming_idr >>= 6; 00354 } 00355 00356 return local_incoming_idr; 00357 } 00358 00359 /** 00360 * \brief A function to calculate ETX value based on dBm and LQI 00361 * 00362 * Calculation is made using RF driver service. If service does not 00363 * exists then local function is used. 00364 * 00365 * \param lqi link quality indicator 00366 * \param dbm measured dBm 00367 * 00368 * \return ETX value (12 bit fraction) 00369 */ 00370 static uint16_t etx_dbm_lqi_calc(uint8_t lqi, int8_t dbm) 00371 { 00372 protocol_interface_info_entry_t *cur = 0; 00373 int8_t driver_ret_value = -1; 00374 uint16_t etx; 00375 00376 phy_signal_info_s signal_info; 00377 signal_info.type = PHY_SIGNAL_INFO_ETX; 00378 signal_info.lqi = lqi; 00379 signal_info.dbm = dbm; 00380 signal_info.result = 0xffff; 00381 00382 //TODO: This is needed, but RF driver cannot be accessed directly! Figure out MAC extension for this. 00383 cur = protocol_stack_interface_info_get(IF_6LoWPAN); 00384 if ((cur) && (cur->dev_driver) && (cur->dev_driver->phy_driver)) { 00385 phy_device_driver_s *dev_driver = cur->dev_driver->phy_driver; 00386 if (dev_driver->extension) { 00387 driver_ret_value = dev_driver->extension(PHY_EXTENSION_CONVERT_SIGNAL_INFO, (uint8_t *)&signal_info); 00388 } 00389 } 00390 00391 if ((driver_ret_value != -1) && (signal_info.result != 0xffff)) { 00392 etx = signal_info.result; 00393 etx <<= 4; 00394 } else { 00395 /* Atmel version 00396 dBm = RSSI base value [dBm] + 1.03 [dB] x ED level 00397 LQI = errors in received frame */ 00398 00399 // for dBm -90 and LQI 0 ETX will be 2.4 00400 etx = ((dbm * -1) * (256 - lqi)); 00401 etx >>= 1; // scale result to 12 bits 00402 etx += 1 << 12; // add one (perfect link) 00403 } 00404 00405 return etx; 00406 } 00407 00408 /** 00409 * \brief A function to register ETX value change callback 00410 * 00411 * Register ETX value change callback. When ETX value has changed more or equal 00412 * to hysteresis value ETX module calls ETX value change callback. 00413 * 00414 * \param nwk_interface_id network interface id 00415 * \param hysteresis hysteresis value (8 bit fraction) 00416 * \param callback_ptr callback function pointer 00417 * 00418 * \return 0 not 6LowPAN interface 00419 * \return 1 success 00420 */ 00421 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) 00422 { 00423 if ((nwk_id == IF_6LoWPAN) && hysteresis && callback_ptr) { 00424 etx_info.hysteresis = hysteresis << 4; 00425 etx_info.callback_ptr = callback_ptr; 00426 etx_info.interface_id = interface_id; 00427 return 1; 00428 } else { 00429 return 0; 00430 } 00431 } 00432 00433 bool etx_storage_list_allocate(int8_t interface_id, uint8_t etx_storage_size) 00434 { 00435 if (!etx_storage_size) { 00436 ns_dyn_mem_free(etx_info.etx_storage_list); 00437 etx_info.etx_storage_list = NULL; 00438 etx_info.ext_storage_list_size = 0; 00439 return true; 00440 } 00441 00442 if (etx_info.ext_storage_list_size == etx_storage_size) { 00443 return true; 00444 } 00445 00446 ns_dyn_mem_free(etx_info.etx_storage_list); 00447 etx_info.ext_storage_list_size = 0; 00448 etx_info.etx_storage_list = ns_dyn_mem_alloc(sizeof(etx_storage_t) * etx_storage_size); 00449 if (!etx_info.etx_storage_list) { 00450 return false; 00451 } 00452 00453 etx_info.ext_storage_list_size = etx_storage_size; 00454 etx_info.interface_id = interface_id; 00455 etx_storage_t * list_ptr = etx_info.etx_storage_list; 00456 for (uint8_t i = 0; i< etx_storage_size; i++) { 00457 memset(list_ptr, 0, sizeof(etx_storage_t)); 00458 00459 list_ptr++; 00460 } 00461 return true; 00462 00463 } 00464 00465 etx_storage_t *etx_storage_entry_get(int8_t interface_id, uint8_t attribute_index) 00466 { 00467 if (etx_info.interface_id != interface_id || !etx_info.etx_storage_list || attribute_index >= etx_info.ext_storage_list_size) { 00468 tr_debug("Unknow ID or un initilized ETX %u", attribute_index); 00469 return NULL; 00470 } 00471 00472 etx_storage_t *entry = etx_info.etx_storage_list + attribute_index; 00473 return entry; 00474 } 00475 00476 00477 /** 00478 * \brief A function to register accumulated failures callback 00479 * 00480 * When the number of accumulated failures has reached the threshold 00481 * value, the ETX module calls the accumulated failures callback on 00482 * every transmission failure. 00483 * 00484 * \param nwk_id network ID (6LoWPAN) 00485 * \param interface_id interface ID 00486 * \param threshold threshold value for accumulated failures 00487 * \param callback_ptr callback function pointer 00488 * 00489 * \return 0 not 6LowPAN interface 00490 * \return 1 success 00491 */ 00492 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) 00493 { 00494 if ((nwk_id == IF_6LoWPAN) && threshold && callback_ptr) { 00495 etx_info.interface_id = interface_id; 00496 etx_info.accum_threshold = threshold; 00497 etx_info.accum_cb_ptr = callback_ptr; 00498 return 1; 00499 } else { 00500 return 0; 00501 } 00502 } 00503 00504 /** 00505 * \brief A function to check if ETX value change callback is needed 00506 * 00507 * Calculates current ETX and compares it against stored ETX. If change 00508 * of the values is more than hysteresis calls ETX value change 00509 * callback. 00510 * 00511 * \param etx ETX (12 bit fraction) 00512 * \param stored_diff_etx stored ETX value 00513 * \param accumulated_failures failed attempts 00514 * \param mac64_addr_ptr long MAC address 00515 * \param mac16_addr short MAC address or 0xffff address is not set 00516 * 00517 * \return ETX value (12 bit fraction) 00518 */ 00519 static void etx_value_change_callback_needed_check(uint16_t etx, uint16_t *stored_diff_etx, uint8_t accumulated_failures, uint8_t attribute_index) 00520 { 00521 uint16_t current_etx; 00522 bool callback = false; 00523 if (!etx_info.hysteresis) { 00524 return; 00525 } 00526 00527 // Calculates current ETX 00528 current_etx = etx_current_calc(etx, accumulated_failures); 00529 00530 // If difference is more than hysteresis 00531 if (current_etx > *stored_diff_etx) { 00532 if (current_etx - *stored_diff_etx >= etx_info.hysteresis) { 00533 callback = true; 00534 } 00535 } else if (current_etx < *stored_diff_etx) { 00536 if (*stored_diff_etx - current_etx >= etx_info.hysteresis) { 00537 callback = true; 00538 } 00539 } 00540 00541 // Calls callback function 00542 if (callback) { 00543 etx_info.callback_ptr(etx_info.interface_id, (*stored_diff_etx) >> 4, current_etx >> 4, attribute_index); 00544 *stored_diff_etx = current_etx; 00545 } 00546 } 00547 00548 /** 00549 * \brief A function to check if accumulated failures callback is needed 00550 * 00551 * If the current number of accumulated failures is equal or greater than 00552 * the set threshold value, the function calls accumulated failures callback. 00553 * 00554 * \param neigh_table_ptr the neighbor node in question 00555 */ 00556 static void etx_accum_failures_callback_needed_check(etx_storage_t * entry, uint8_t attribute_index) 00557 { 00558 if (!etx_info.accum_threshold) { 00559 return; 00560 } 00561 00562 if (entry->accumulated_failures < etx_info.accum_threshold) { 00563 return; 00564 } 00565 00566 etx_info.accum_cb_ptr(etx_info.interface_id, entry->accumulated_failures, attribute_index); 00567 } 00568 00569 /** 00570 * \brief A function to remove ETX neighbor 00571 * 00572 * Notifies ETX module that neighbor has been removed. Calls ETX value change callback 00573 * if that is set. 00574 * 00575 * \param mac64_addr_ptr long MAC address 00576 * 00577 */ 00578 void etx_neighbor_remove(int8_t interface_id, uint8_t attribute_index) { 00579 00580 tr_debug("Remove attribute %u", attribute_index); 00581 uint16_t stored_diff_etx; 00582 etx_storage_t *entry = etx_storage_entry_get(interface_id, attribute_index); 00583 if (entry && etx_info.callback_ptr) { 00584 00585 if (entry->etx) { 00586 stored_diff_etx = entry->stored_diff_etx >> 4; 00587 if (!stored_diff_etx) { 00588 stored_diff_etx = 0xffff; 00589 } 00590 etx_info.callback_ptr(etx_info.interface_id, stored_diff_etx, 0xffff, attribute_index); 00591 } 00592 //Clear all data base back to zero for new user 00593 memset(entry, 0, sizeof(etx_storage_t)); 00594 } 00595 } 00596 00597 /** 00598 * \brief A function to add ETX neighbor 00599 * 00600 * Notifies ETX module that neighbor has been added. Calls ETX value change callback 00601 * if that is set. 00602 * 00603 * \param mac64_addr_ptr long MAC address 00604 * 00605 */ 00606 void etx_neighbor_add(int8_t interface_id, uint8_t attribute_index) { 00607 00608 tr_debug("Add attribute %u", attribute_index); 00609 uint16_t stored_diff_etx; 00610 etx_storage_t *entry = etx_storage_entry_get(interface_id, attribute_index); 00611 if (entry && etx_info.callback_ptr) { 00612 // Gets table entry 00613 00614 if (entry->etx) { 00615 stored_diff_etx = entry->stored_diff_etx; 00616 if (!stored_diff_etx) { 00617 stored_diff_etx = entry->etx; 00618 } 00619 etx_info.callback_ptr(etx_info.interface_id, stored_diff_etx >> 4, entry->etx >> 4, 00620 attribute_index); 00621 } 00622 } 00623 }
Generated on Tue Aug 9 2022 00:37:06 by
1.7.2