1 /*
2 * Copyright (c) 2016, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file implements the jam detector feature.
32 */
33
34 #include "jam_detector.hpp"
35
36 #if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE
37
38 #include "instance/instance.hpp"
39
40 namespace ot {
41 namespace Utils {
42
43 RegisterLogModule("JamDetector");
44
JamDetector(Instance & aInstance)45 JamDetector::JamDetector(Instance &aInstance)
46 : InstanceLocator(aInstance)
47 , mTimer(aInstance)
48 , mHistoryBitmap(0)
49 , mCurSecondStartTime(0)
50 , mSampleInterval(0)
51 , mWindow(kMaxWindow)
52 , mBusyPeriod(kMaxWindow)
53 , mEnabled(false)
54 , mAlwaysAboveThreshold(false)
55 , mJamState(false)
56 , mRssiThreshold(kDefaultRssiThreshold)
57 {
58 }
59
Start(Handler aHandler,void * aContext)60 Error JamDetector::Start(Handler aHandler, void *aContext)
61 {
62 Error error = kErrorNone;
63
64 VerifyOrExit(!mEnabled, error = kErrorAlready);
65 VerifyOrExit(aHandler != nullptr, error = kErrorInvalidArgs);
66
67 mCallback.Set(aHandler, aContext);
68 mEnabled = true;
69
70 LogInfo("Started");
71
72 CheckState();
73
74 exit:
75 return error;
76 }
77
Stop(void)78 Error JamDetector::Stop(void)
79 {
80 Error error = kErrorNone;
81
82 VerifyOrExit(mEnabled, error = kErrorAlready);
83
84 mEnabled = false;
85 mJamState = false;
86
87 mTimer.Stop();
88
89 LogInfo("Stopped");
90
91 exit:
92 return error;
93 }
94
CheckState(void)95 void JamDetector::CheckState(void)
96 {
97 VerifyOrExit(mEnabled);
98
99 switch (Get<Mle::MleRouter>().GetRole())
100 {
101 case Mle::kRoleDisabled:
102 VerifyOrExit(mTimer.IsRunning());
103 mTimer.Stop();
104 SetJamState(false);
105 break;
106
107 default:
108 VerifyOrExit(!mTimer.IsRunning());
109 mCurSecondStartTime = TimerMilli::GetNow();
110 mAlwaysAboveThreshold = true;
111 mHistoryBitmap = 0;
112 mJamState = false;
113 mSampleInterval = kMaxSampleInterval;
114 mTimer.Start(kMinSampleInterval);
115 break;
116 }
117
118 exit:
119 return;
120 }
121
SetRssiThreshold(int8_t aThreshold)122 void JamDetector::SetRssiThreshold(int8_t aThreshold)
123 {
124 mRssiThreshold = aThreshold;
125 LogInfo("RSSI threshold set to %d", mRssiThreshold);
126 }
127
SetWindow(uint8_t aWindow)128 Error JamDetector::SetWindow(uint8_t aWindow)
129 {
130 Error error = kErrorNone;
131
132 VerifyOrExit(aWindow != 0, error = kErrorInvalidArgs);
133 VerifyOrExit(aWindow <= kMaxWindow, error = kErrorInvalidArgs);
134
135 mWindow = aWindow;
136 LogInfo("window set to %d", mWindow);
137
138 exit:
139 return error;
140 }
141
SetBusyPeriod(uint8_t aBusyPeriod)142 Error JamDetector::SetBusyPeriod(uint8_t aBusyPeriod)
143 {
144 Error error = kErrorNone;
145
146 VerifyOrExit(aBusyPeriod != 0, error = kErrorInvalidArgs);
147 VerifyOrExit(aBusyPeriod <= mWindow, error = kErrorInvalidArgs);
148
149 mBusyPeriod = aBusyPeriod;
150 LogInfo("busy period set to %d", mBusyPeriod);
151
152 exit:
153 return error;
154 }
155
HandleTimer(void)156 void JamDetector::HandleTimer(void)
157 {
158 int8_t rssi;
159 bool didExceedThreshold = true;
160
161 VerifyOrExit(mEnabled);
162
163 rssi = Get<Radio>().GetRssi();
164
165 // If the RSSI is valid, check if it exceeds the threshold
166 // and try to update the history bit map
167 if (rssi != Radio::kInvalidRssi)
168 {
169 didExceedThreshold = (rssi >= mRssiThreshold);
170 UpdateHistory(didExceedThreshold);
171 }
172
173 // If the RSSI sample does not exceed the threshold, go back to max sample interval
174 // Otherwise, divide the sample interval by half while ensuring it does not go lower
175 // than minimum sample interval.
176
177 if (!didExceedThreshold)
178 {
179 mSampleInterval = kMaxSampleInterval;
180 }
181 else
182 {
183 mSampleInterval /= 2;
184
185 if (mSampleInterval < kMinSampleInterval)
186 {
187 mSampleInterval = kMinSampleInterval;
188 }
189 }
190
191 mTimer.Start(mSampleInterval + Random::NonCrypto::GetUint32InRange(0, kMaxRandomDelay));
192
193 exit:
194 return;
195 }
196
UpdateHistory(bool aDidExceedThreshold)197 void JamDetector::UpdateHistory(bool aDidExceedThreshold)
198 {
199 uint32_t interval = TimerMilli::GetNow() - mCurSecondStartTime;
200
201 // If the RSSI is ever below the threshold, update mAlwaysAboveThreshold
202 // for current second interval.
203 if (!aDidExceedThreshold)
204 {
205 mAlwaysAboveThreshold = false;
206 }
207
208 // If we reached end of current one second interval, update the history bitmap
209
210 if (interval >= Time::kOneSecondInMsec)
211 {
212 mHistoryBitmap <<= 1;
213
214 if (mAlwaysAboveThreshold)
215 {
216 mHistoryBitmap |= 0x1;
217 }
218
219 mAlwaysAboveThreshold = true;
220
221 mCurSecondStartTime += (interval / Time::kOneSecondInMsec) * Time::kOneSecondInMsec;
222
223 UpdateJamState();
224 }
225 }
226
UpdateJamState(void)227 void JamDetector::UpdateJamState(void)
228 {
229 uint8_t numJammedSeconds = 0;
230 uint64_t bitmap = mHistoryBitmap;
231
232 // Clear all history bits beyond the current window size
233 bitmap &= (static_cast<uint64_t>(1) << mWindow) - 1;
234
235 // Count the number of bits in the bitmap
236 while (bitmap != 0)
237 {
238 numJammedSeconds++;
239 bitmap &= (bitmap - 1);
240 }
241
242 SetJamState(numJammedSeconds >= mBusyPeriod);
243 }
244
SetJamState(bool aNewState)245 void JamDetector::SetJamState(bool aNewState)
246 {
247 bool shouldInvokeHandler = aNewState;
248
249 if (aNewState != mJamState)
250 {
251 mJamState = aNewState;
252 shouldInvokeHandler = true;
253 LogInfo("Jamming %s", mJamState ? "detected" : "cleared");
254 }
255
256 if (shouldInvokeHandler)
257 {
258 mCallback.Invoke(aNewState);
259 }
260 }
261
HandleNotifierEvents(Events aEvents)262 void JamDetector::HandleNotifierEvents(Events aEvents)
263 {
264 if (aEvents.Contains(kEventThreadRoleChanged))
265 {
266 CheckState();
267 }
268 }
269
270 } // namespace Utils
271 } // namespace ot
272
273 #endif // OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE
274