BA / Mbed OS BaBoRo1
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers etx.c Source File

etx.c

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