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/code_utils.hpp"
38 #include "common/debug.hpp"
39 #include "common/instance.hpp"
40 #include "common/locator_getters.hpp"
41 #include "common/logging.hpp"
42 #include "meshcop/meshcop.hpp"
43 #include "meshcop/meshcop_tlvs.hpp"
44 #include "thread/thread_netif.hpp"
45 #include "thread/uri_paths.hpp"
46
47 namespace ot {
48
EnergyScanServer(Instance & aInstance)49 EnergyScanServer::EnergyScanServer(Instance &aInstance)
50 : InstanceLocator(aInstance)
51 , mChannelMask(0)
52 , mChannelMaskCurrent(0)
53 , mPeriod(0)
54 , mScanDuration(0)
55 , mCount(0)
56 , mActive(false)
57 , mScanResultsLength(0)
58 , mTimer(aInstance, EnergyScanServer::HandleTimer)
59 , mEnergyScan(UriPath::kEnergyScan, &EnergyScanServer::HandleRequest, this)
60 {
61 Get<Tmf::Agent>().AddResource(mEnergyScan);
62 }
63
HandleRequest(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)64 void EnergyScanServer::HandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
65 {
66 static_cast<EnergyScanServer *>(aContext)->HandleRequest(*static_cast<Coap::Message *>(aMessage),
67 *static_cast<const Ip6::MessageInfo *>(aMessageInfo));
68 }
69
HandleRequest(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)70 void EnergyScanServer::HandleRequest(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
71 {
72 uint8_t count;
73 uint16_t period;
74 uint16_t scanDuration;
75 Ip6::MessageInfo responseInfo(aMessageInfo);
76 uint32_t mask;
77
78 VerifyOrExit(aMessage.IsPostRequest());
79
80 SuccessOrExit(Tlv::Find<MeshCoP::CountTlv>(aMessage, count));
81 SuccessOrExit(Tlv::Find<MeshCoP::PeriodTlv>(aMessage, period));
82 SuccessOrExit(Tlv::Find<MeshCoP::ScanDurationTlv>(aMessage, scanDuration));
83
84 VerifyOrExit((mask = MeshCoP::ChannelMaskTlv::GetChannelMask(aMessage)) != 0);
85
86 mChannelMask = mask;
87 mChannelMaskCurrent = mChannelMask;
88 mCount = count;
89 mPeriod = period;
90 mScanDuration = scanDuration;
91 mScanResultsLength = 0;
92 mActive = true;
93 mTimer.Start(kScanDelay);
94
95 mCommissioner = aMessageInfo.GetPeerAddr();
96
97 if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast())
98 {
99 SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, responseInfo));
100 otLogInfoMeshCoP("sent energy scan query response");
101 }
102
103 exit:
104 return;
105 }
106
HandleTimer(Timer & aTimer)107 void EnergyScanServer::HandleTimer(Timer &aTimer)
108 {
109 aTimer.Get<EnergyScanServer>().HandleTimer();
110 }
111
HandleTimer(void)112 void EnergyScanServer::HandleTimer(void)
113 {
114 VerifyOrExit(mActive);
115
116 if (mCount)
117 {
118 // grab the lowest channel to scan
119 uint32_t channelMask = mChannelMaskCurrent & ~(mChannelMaskCurrent - 1);
120 IgnoreError(Get<Mac::Mac>().EnergyScan(channelMask, mScanDuration, HandleScanResult, this));
121 }
122 else
123 {
124 SendReport();
125 }
126
127 exit:
128 return;
129 }
130
HandleScanResult(Mac::EnergyScanResult * aResult,void * aContext)131 void EnergyScanServer::HandleScanResult(Mac::EnergyScanResult *aResult, void *aContext)
132 {
133 static_cast<EnergyScanServer *>(aContext)->HandleScanResult(aResult);
134 }
135
HandleScanResult(Mac::EnergyScanResult * aResult)136 void EnergyScanServer::HandleScanResult(Mac::EnergyScanResult *aResult)
137 {
138 VerifyOrExit(mActive);
139
140 if (aResult)
141 {
142 VerifyOrExit(mScanResultsLength < OPENTHREAD_CONFIG_TMF_ENERGY_SCAN_MAX_RESULTS);
143 mScanResults[mScanResultsLength++] = aResult->mMaxRssi;
144 }
145 else
146 {
147 // clear the lowest channel to scan
148 mChannelMaskCurrent &= mChannelMaskCurrent - 1;
149
150 if (mChannelMaskCurrent == 0)
151 {
152 mChannelMaskCurrent = mChannelMask;
153 mCount--;
154 }
155
156 if (mCount)
157 {
158 mTimer.Start(mPeriod);
159 }
160 else
161 {
162 mTimer.Start(kReportDelay);
163 }
164 }
165
166 exit:
167 return;
168 }
169
SendReport(void)170 void EnergyScanServer::SendReport(void)
171 {
172 Error error = kErrorNone;
173 MeshCoP::ChannelMaskTlv channelMask;
174 MeshCoP::EnergyListTlv energyList;
175 Ip6::MessageInfo messageInfo;
176 Coap::Message * message;
177
178 VerifyOrExit((message = MeshCoP::NewMeshCoPMessage(Get<Tmf::Agent>())) != nullptr, error = kErrorNoBufs);
179
180 SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kEnergyReport));
181 SuccessOrExit(error = message->SetPayloadMarker());
182
183 channelMask.Init();
184 channelMask.SetChannelMask(mChannelMask);
185 SuccessOrExit(error = channelMask.AppendTo(*message));
186
187 energyList.Init();
188 energyList.SetLength(mScanResultsLength);
189 SuccessOrExit(error = message->Append(energyList));
190 SuccessOrExit(error = message->AppendBytes(mScanResults, mScanResultsLength));
191
192 messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
193 messageInfo.SetPeerAddr(mCommissioner);
194 messageInfo.SetPeerPort(Tmf::kUdpPort);
195 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
196
197 otLogInfoMeshCoP("sent scan results");
198
199 exit:
200 FreeMessageOnError(message, error);
201 MeshCoP::LogError("send scan results", error);
202 mActive = false;
203 }
204
HandleNotifierEvents(Events aEvents)205 void EnergyScanServer::HandleNotifierEvents(Events aEvents)
206 {
207 if (aEvents.Contains(kEventThreadNetdataChanged) && !mActive &&
208 Get<NetworkData::Leader>().GetCommissioningData() == nullptr)
209 {
210 mActive = false;
211 mTimer.Stop();
212 }
213 }
214
215 } // namespace ot
216