| /* |
| * Copyright (c) 2016, The OpenThread Authors. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holder nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @file |
| * This file implements the jam detector feature. |
| */ |
| |
| #include <openthread/config.h> |
| |
| #include "jam_detector.hpp" |
| |
| #include <openthread/openthread.h> |
| #include <openthread/platform/random.h> |
| |
| #include "openthread-instance.h" |
| #include "common/code_utils.hpp" |
| #include "thread/thread_netif.hpp" |
| |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| |
| namespace ot { |
| namespace Utils { |
| |
| JamDetector::JamDetector(ThreadNetif &aNetif) : |
| ThreadNetifLocator(aNetif), |
| mHandler(NULL), |
| mContext(NULL), |
| mRssiThreshold(kDefaultRssiThreshold), |
| mTimer(aNetif.GetIp6().mTimerScheduler, &JamDetector::HandleTimer, this), |
| mHistoryBitmap(0), |
| mCurSecondStartTime(0), |
| mSampleInterval(0), |
| mWindow(kMaxWindow), |
| mBusyPeriod(kMaxWindow), |
| mEnabled(false), |
| mAlwaysAboveThreshold(false), |
| mJamState(false) |
| { |
| } |
| |
| otError JamDetector::Start(Handler aHandler, void *aContext) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(!mEnabled, error = OT_ERROR_ALREADY); |
| VerifyOrExit(aHandler != NULL, error = OT_ERROR_INVALID_ARGS); |
| |
| mHandler = aHandler; |
| mContext = aContext; |
| |
| mEnabled = true; |
| |
| mCurSecondStartTime = Timer::GetNow(); |
| mAlwaysAboveThreshold = true; |
| mHistoryBitmap = 0; |
| mJamState = false; |
| mSampleInterval = kMaxSampleInterval; |
| |
| mTimer.Start(kMinSampleInterval); |
| |
| exit: |
| return error; |
| } |
| |
| otError JamDetector::Stop(void) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(mEnabled, error = OT_ERROR_ALREADY); |
| |
| mEnabled = false; |
| mJamState = false; |
| |
| mTimer.Stop(); |
| |
| exit: |
| return error; |
| } |
| |
| otError JamDetector::SetRssiThreshold(int8_t aThreshold) |
| { |
| mRssiThreshold = aThreshold; |
| |
| return OT_ERROR_NONE; |
| } |
| |
| otError JamDetector::SetWindow(uint8_t aWindow) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(aWindow != 0, error = OT_ERROR_INVALID_ARGS); |
| VerifyOrExit(aWindow <= kMaxWindow, error = OT_ERROR_INVALID_ARGS); |
| |
| mWindow = aWindow; |
| |
| exit: |
| return error; |
| } |
| |
| otError JamDetector::SetBusyPeriod(uint8_t aBusyPeriod) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(aBusyPeriod != 0, error = OT_ERROR_INVALID_ARGS); |
| VerifyOrExit(aBusyPeriod <= mWindow, error = OT_ERROR_INVALID_ARGS); |
| |
| mBusyPeriod = aBusyPeriod; |
| |
| exit: |
| return error; |
| } |
| |
| void JamDetector::HandleTimer(Timer &aTimer) |
| { |
| GetOwner(aTimer).HandleTimer(); |
| } |
| |
| void JamDetector::HandleTimer(void) |
| { |
| int8_t rssi; |
| bool didExceedThreshold = true; |
| |
| VerifyOrExit(mEnabled); |
| |
| rssi = otPlatRadioGetRssi(GetNetif().GetInstance()); |
| |
| // If the RSSI is valid, check if it exceeds the threshold |
| // and try to update the history bit map |
| if (rssi != OT_RADIO_RSSI_INVALID) |
| { |
| didExceedThreshold = (rssi >= mRssiThreshold); |
| UpdateHistory(didExceedThreshold); |
| } |
| |
| // If the RSSI sample does not exceed the threshold, go back to max sample interval |
| // Otherwise, divide the sample interval by half while ensuring it does not go lower |
| // than minimum sample interval. |
| |
| if (!didExceedThreshold) |
| { |
| mSampleInterval = kMaxSampleInterval; |
| } |
| else |
| { |
| mSampleInterval /= 2; |
| |
| if (mSampleInterval < kMinSampleInterval) |
| { |
| mSampleInterval = kMinSampleInterval; |
| } |
| } |
| |
| mTimer.Start(mSampleInterval + (otPlatRandomGet() % kMaxRandomDelay)); |
| |
| exit: |
| return; |
| } |
| |
| void JamDetector::UpdateHistory(bool aDidExceedThreshold) |
| { |
| uint32_t now = Timer::GetNow(); |
| |
| // If the RSSI is ever below the threshold, update mAlwaysAboveThreshold |
| // for current second interval. |
| if (!aDidExceedThreshold) |
| { |
| mAlwaysAboveThreshold = false; |
| } |
| |
| // If we reached end of current one second interval, update the history bitmap |
| if (now - mCurSecondStartTime >= kOneSecondInterval) |
| { |
| mHistoryBitmap <<= 1; |
| |
| if (mAlwaysAboveThreshold) |
| { |
| mHistoryBitmap |= 0x1; |
| } |
| |
| mAlwaysAboveThreshold = true; |
| |
| while (now - mCurSecondStartTime >= kOneSecondInterval) |
| { |
| mCurSecondStartTime += kOneSecondInterval; |
| } |
| |
| UpdateJamState(); |
| } |
| } |
| |
| void JamDetector::UpdateJamState(void) |
| { |
| uint8_t numJammedSeconds = 0; |
| uint64_t bitmap = mHistoryBitmap; |
| bool oldJamState = mJamState; |
| |
| // Clear all history bits beyond the current window size |
| bitmap &= (static_cast<uint64_t>(1) << mWindow) - 1; |
| |
| // Count the number of bits in the bitmap |
| while (bitmap != 0) |
| { |
| numJammedSeconds++; |
| bitmap &= (bitmap - 1); |
| } |
| |
| // Update the Jam state |
| mJamState = (numJammedSeconds >= mBusyPeriod); |
| |
| // If there is a change, invoke the handler. |
| if ((mJamState != oldJamState) || (mJamState == true)) |
| { |
| mHandler(mJamState, mContext); |
| } |
| } |
| |
| JamDetector &JamDetector::GetOwner(const Context &aContext) |
| { |
| #if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES |
| JamDetector &detector = *static_cast<JamDetector *>(aContext.GetContext()); |
| #else |
| JamDetector &detector = otGetThreadNetif().GetJamDetector(); |
| OT_UNUSED_VARIABLE(aContext); |
| #endif |
| return detector; |
| } |
| |
| } // namespace Utils |
| } // namespace ot |
| |
| #endif // OPENTHREAD_ENABLE_JAM_DETECTION |