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 Energy Scan Server.
32 */
33
34 #include "energy_scan_server.hpp"
35
36 #include "coap/coap_message.hpp"
37 #include "common/as_core_type.hpp"
38 #include "common/code_utils.hpp"
39 #include "common/debug.hpp"
40 #include "common/locator_getters.hpp"
41 #include "common/log.hpp"
42 #include "instance/instance.hpp"
43 #include "meshcop/meshcop.hpp"
44 #include "meshcop/meshcop_tlvs.hpp"
45 #include "thread/thread_netif.hpp"
46
47 namespace ot {
48
49 RegisterLogModule("EnergyScanSrv");
50
EnergyScanServer(Instance & aInstance)51 EnergyScanServer::EnergyScanServer(Instance &aInstance)
52 : InstanceLocator(aInstance)
53 , mChannelMask(0)
54 , mChannelMaskCurrent(0)
55 , mPeriod(0)
56 , mScanDuration(0)
57 , mCount(0)
58 , mReportMessage(nullptr)
59 , mTimer(aInstance)
60 {
61 }
62
63 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)64 void EnergyScanServer::HandleTmf<kUriEnergyScan>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
65 {
66 uint8_t count;
67 uint16_t period;
68 uint16_t scanDuration;
69 uint32_t mask;
70 MeshCoP::Tlv tlv;
71
72 VerifyOrExit(aMessage.IsPostRequest());
73
74 SuccessOrExit(Tlv::Find<MeshCoP::CountTlv>(aMessage, count));
75 SuccessOrExit(Tlv::Find<MeshCoP::PeriodTlv>(aMessage, period));
76 SuccessOrExit(Tlv::Find<MeshCoP::ScanDurationTlv>(aMessage, scanDuration));
77
78 SuccessOrExit(MeshCoP::ChannelMaskTlv::FindIn(aMessage, mask));
79
80 FreeMessage(mReportMessage);
81 mReportMessage = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriEnergyReport);
82 VerifyOrExit(mReportMessage != nullptr);
83
84 SuccessOrExit(MeshCoP::ChannelMaskTlv::AppendTo(*mReportMessage, mask));
85
86 tlv.SetType(MeshCoP::Tlv::kEnergyList);
87 SuccessOrExit(mReportMessage->Append(tlv));
88
89 mNumScanResults = 0;
90 mChannelMask = mask;
91 mChannelMaskCurrent = mChannelMask;
92 mCount = count;
93 mPeriod = period;
94 mScanDuration = scanDuration;
95 mTimer.Start(kScanDelay);
96
97 mCommissioner = aMessageInfo.GetPeerAddr();
98
99 if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast())
100 {
101 SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
102 LogInfo("Sent %s ack", UriToString<kUriEnergyScan>());
103 }
104
105 exit:
106 return;
107 }
108
HandleTimer(void)109 void EnergyScanServer::HandleTimer(void)
110 {
111 VerifyOrExit(mReportMessage != nullptr);
112
113 if (mCount)
114 {
115 // grab the lowest channel to scan
116 uint32_t channelMask = mChannelMaskCurrent & ~(mChannelMaskCurrent - 1);
117
118 IgnoreError(Get<Mac::Mac>().EnergyScan(channelMask, mScanDuration, HandleScanResult, this));
119 }
120 else
121 {
122 SendReport();
123 }
124
125 exit:
126 return;
127 }
128
HandleScanResult(Mac::EnergyScanResult * aResult,void * aContext)129 void EnergyScanServer::HandleScanResult(Mac::EnergyScanResult *aResult, void *aContext)
130 {
131 static_cast<EnergyScanServer *>(aContext)->HandleScanResult(aResult);
132 }
133
HandleScanResult(Mac::EnergyScanResult * aResult)134 void EnergyScanServer::HandleScanResult(Mac::EnergyScanResult *aResult)
135 {
136 VerifyOrExit(mReportMessage != nullptr);
137
138 if (aResult)
139 {
140 if (mReportMessage->Append<int8_t>(aResult->mMaxRssi) != kErrorNone)
141 {
142 FreeMessage(mReportMessage);
143 mReportMessage = nullptr;
144 ExitNow();
145 }
146
147 mNumScanResults++;
148
149 if (mNumScanResults == NumericLimits<uint8_t>::kMax)
150 {
151 // If we reach the max length that fit in the Energy List
152 // TLV we send the current set of energy scan data.
153
154 mCount = 0;
155 mTimer.Start(kReportDelay);
156 }
157 }
158 else
159 {
160 // Clear the lowest channel to scan
161 mChannelMaskCurrent &= mChannelMaskCurrent - 1;
162
163 if (mChannelMaskCurrent == 0)
164 {
165 mChannelMaskCurrent = mChannelMask;
166 mCount--;
167 }
168
169 if (mCount)
170 {
171 mTimer.Start(mPeriod);
172 }
173 else
174 {
175 mTimer.Start(kReportDelay);
176 }
177 }
178
179 exit:
180 return;
181 }
182
SendReport(void)183 void EnergyScanServer::SendReport(void)
184 {
185 Error error = kErrorNone;
186 Tmf::MessageInfo messageInfo(GetInstance());
187 uint16_t offset;
188
189 // Update the Energy List TLV length in Report message
190 offset = mReportMessage->GetLength() - mNumScanResults - sizeof(uint8_t);
191 mReportMessage->Write(offset, mNumScanResults);
192
193 messageInfo.SetSockAddrToRlocPeerAddrTo(mCommissioner);
194
195 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*mReportMessage, messageInfo));
196
197 LogInfo("Sent %s", UriToString<kUriEnergyReport>());
198
199 exit:
200 FreeMessageOnError(mReportMessage, error);
201 LogWarnOnError(error, "send scan results");
202 mReportMessage = nullptr;
203 }
204
HandleNotifierEvents(Events aEvents)205 void EnergyScanServer::HandleNotifierEvents(Events aEvents)
206 {
207 uint16_t borderAgentRloc;
208
209 if (aEvents.Contains(kEventThreadNetdataChanged) && (mReportMessage != nullptr) &&
210 Get<NetworkData::Leader>().FindBorderAgentRloc(borderAgentRloc) != kErrorNone)
211 {
212 mReportMessage->Free();
213 mReportMessage = nullptr;
214 mTimer.Stop();
215 }
216 }
217
218 } // namespace ot
219