1 /* 2 * Copyright (c) 2024, 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 includes definitions for the spinel based Thread controller. 32 */ 33 34 #ifndef OTBR_AGENT_NCP_SPINEL_HPP_ 35 #define OTBR_AGENT_NCP_SPINEL_HPP_ 36 37 #include <functional> 38 #include <memory> 39 40 #include <openthread/dataset.h> 41 #include <openthread/error.h> 42 #include <openthread/link.h> 43 #include <openthread/thread.h> 44 45 #include "lib/spinel/spinel.h" 46 #include "lib/spinel/spinel_buffer.hpp" 47 #include "lib/spinel/spinel_driver.hpp" 48 #include "lib/spinel/spinel_encoder.hpp" 49 50 #include "common/task_runner.hpp" 51 #include "common/types.hpp" 52 #include "ncp/async_task.hpp" 53 54 namespace otbr { 55 namespace Ncp { 56 57 /** 58 * This interface is an observer to subscribe the network properties from NCP. 59 */ 60 class PropsObserver 61 { 62 public: 63 /** 64 * Updates the device role. 65 * 66 * @param[in] aRole The device role. 67 */ 68 virtual void SetDeviceRole(otDeviceRole aRole) = 0; 69 70 /** 71 * Updates the active dataset. 72 * 73 * @param[in] aActiveOpDatasetTlvs The active dataset tlvs. 74 */ 75 virtual void SetDatasetActiveTlvs(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs) = 0; 76 77 /** 78 * The destructor. 79 */ 80 virtual ~PropsObserver(void) = default; 81 }; 82 83 /** 84 * The class provides methods for controlling the Thread stack on the network co-processor (NCP). 85 */ 86 class NcpSpinel 87 { 88 public: 89 using Ip6AddressTableCallback = std::function<void(const std::vector<Ip6AddressInfo> &)>; 90 using Ip6MulticastAddressTableCallback = std::function<void(const std::vector<Ip6Address> &)>; 91 using NetifStateChangedCallback = std::function<void(bool)>; 92 93 /** 94 * Constructor. 95 */ 96 NcpSpinel(void); 97 98 /** 99 * Do the initialization. 100 * 101 * @param[in] aSpinelDriver A reference to the SpinelDriver instance that this object depends. 102 * @param[in] aObserver A reference to the Network properties observer. 103 */ 104 void Init(ot::Spinel::SpinelDriver &aSpinelDriver, PropsObserver &aObserver); 105 106 /** 107 * Do the de-initialization. 108 */ 109 void Deinit(void); 110 111 /** 112 * Returns the Co-processor version string. 113 */ GetCoprocessorVersion(void)114 const char *GetCoprocessorVersion(void) { return mSpinelDriver->GetVersion(); } 115 116 /** 117 * This method sets the active dataset on the NCP. 118 * 119 * If this method is called again before the previous call completed, no action will be taken. 120 * The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY. 121 * 122 * @param[in] aActiveOpDatasetTlvs A reference to the active operational dataset of the Thread network. 123 * @param[in] aAsyncTask A pointer to an async result to receive the result of this operation. 124 */ 125 void DatasetSetActiveTlvs(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs, AsyncTaskPtr aAsyncTask); 126 127 /** 128 * This method instructs the NCP to send a MGMT_SET to set Thread Pending Operational Dataset. 129 * 130 * If this method is called again before the previous call completed, no action will be taken. 131 * The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY. 132 * 133 * @param[in] aPendingOpDatasetTlvsPtr A shared pointer to the pending operational dataset of the Thread network. 134 * @param[in] aAsyncTask A pointer to an async result to receive the result of this operation. 135 */ 136 void DatasetMgmtSetPending(std::shared_ptr<otOperationalDatasetTlvs> aPendingOpDatasetTlvsPtr, 137 AsyncTaskPtr aAsyncTask); 138 139 /** 140 * This method enableds/disables the IP6 on the NCP. 141 * 142 * If this method is called again before the previous call completed, no action will be taken. 143 * The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY. 144 * 145 * @param[in] aEnable TRUE to enable and FALSE to disable. 146 * @param[in] aAsyncTask A pointer to an async result to receive the result of this operation. 147 */ 148 void Ip6SetEnabled(bool aEnable, AsyncTaskPtr aAsyncTask); 149 150 /** 151 * This method sets the callback to receive the IPv6 address table from the NCP. 152 * 153 * The callback will be invoked when receiving an IPv6 address table from the NCP. When the 154 * callback is invoked, the callback MUST copy the otIp6AddressInfo objects and maintain it 155 * if it's not used immediately (within the callback). 156 * 157 * @param[in] aCallback The callback to handle the IP6 address table. 158 */ Ip6SetAddressCallback(const Ip6AddressTableCallback & aCallback)159 void Ip6SetAddressCallback(const Ip6AddressTableCallback &aCallback) { mIp6AddressTableCallback = aCallback; } 160 161 /** 162 * This method sets the callback to receive the IPv6 multicast address table from the NCP. 163 * 164 * @param[in] aCallback The callback to handle the IPv6 address table. 165 * 166 * The callback will be invoked when receiving an IPv6 multicast address table from the NCP. 167 * When the callback is invoked, the callback MUST copy the otIp6Address objects and maintain it 168 * if it's not used immediately (within the callback). 169 */ Ip6SetAddressMulticastCallback(const Ip6MulticastAddressTableCallback & aCallback)170 void Ip6SetAddressMulticastCallback(const Ip6MulticastAddressTableCallback &aCallback) 171 { 172 mIp6MulticastAddressTableCallback = aCallback; 173 } 174 175 /** 176 * This methods sends an IP6 datagram through the NCP. 177 * 178 * @param[in] aData A pointer to the beginning of the IP6 datagram. 179 * @param[in] aLength The length of the datagram. 180 * 181 * @retval OTBR_ERROR_NONE The datagram is sent to NCP successfully. 182 * @retval OTBR_ERROR_BUSY NcpSpinel is busy with other requests. 183 */ 184 otbrError Ip6Send(const uint8_t *aData, uint16_t aLength); 185 186 /** 187 * This method enableds/disables the Thread network on the NCP. 188 * 189 * If this method is called again before the previous call completed, no action will be taken. 190 * The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY. 191 * 192 * @param[in] aEnable TRUE to enable and FALSE to disable. 193 * @param[in] aAsyncTask A pointer to an async result to receive the result of this operation. 194 */ 195 void ThreadSetEnabled(bool aEnable, AsyncTaskPtr aAsyncTask); 196 197 /** 198 * This method instructs the device to leave the current network gracefully. 199 * 200 * If this method is called again before the previous call completed, no action will be taken. 201 * The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY. 202 * 203 * @param[in] aAsyncTask A pointer to an async result to receive the result of this operation. 204 */ 205 void ThreadDetachGracefully(AsyncTaskPtr aAsyncTask); 206 207 /** 208 * This method instructs the NCP to erase the persistent network info. 209 * 210 * If this method is called again before the previous call completed, no action will be taken. 211 * The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY. 212 * 213 * @param[in] aAsyncTask A pointer to an async result to receive the result of this operation. 214 */ 215 void ThreadErasePersistentInfo(AsyncTaskPtr aAsyncTask); 216 217 /** 218 * This method sets the callback invoked when the network interface state changes. 219 * 220 * @param[in] aCallback The callback invoked when the network interface state changes. 221 */ NetifSetStateChangedCallback(const NetifStateChangedCallback & aCallback)222 void NetifSetStateChangedCallback(const NetifStateChangedCallback &aCallback) 223 { 224 mNetifStateChangedCallback = aCallback; 225 } 226 227 private: 228 using FailureHandler = std::function<void(otError)>; 229 230 static constexpr uint8_t kMaxTids = 16; 231 SafeInvoke(Function & aFunc,Args &&...aArgs)232 template <typename Function, typename... Args> static void SafeInvoke(Function &aFunc, Args &&...aArgs) 233 { 234 if (aFunc) 235 { 236 aFunc(std::forward<Args>(aArgs)...); 237 } 238 } 239 CallAndClear(AsyncTaskPtr & aResult,otError aError,const std::string & aErrorInfo="")240 static void CallAndClear(AsyncTaskPtr &aResult, otError aError, const std::string &aErrorInfo = "") 241 { 242 if (aResult) 243 { 244 aResult->SetResult(aError, aErrorInfo); 245 aResult = nullptr; 246 } 247 } 248 249 static otbrError SpinelDataUnpack(const uint8_t *aDataIn, spinel_size_t aDataLen, const char *aPackFormat, ...); 250 251 static void HandleReceivedFrame(const uint8_t *aFrame, 252 uint16_t aLength, 253 uint8_t aHeader, 254 bool &aSave, 255 void *aContext); 256 void HandleReceivedFrame(const uint8_t *aFrame, uint16_t aLength, uint8_t aHeader, bool &aShouldSaveFrame); 257 static void HandleSavedFrame(const uint8_t *aFrame, uint16_t aLength, void *aContext); 258 259 static otDeviceRole SpinelRoleToDeviceRole(spinel_net_role_t aRole); 260 261 void HandleNotification(const uint8_t *aFrame, uint16_t aLength); 262 void HandleResponse(spinel_tid_t aTid, const uint8_t *aFrame, uint16_t aLength); 263 void HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength); 264 otbrError HandleResponseForPropSet(spinel_tid_t aTid, 265 spinel_prop_key_t aKey, 266 const uint8_t *aData, 267 uint16_t aLength); 268 269 spinel_tid_t GetNextTid(void); 270 void FreeTidTableItem(spinel_tid_t aTid); 271 272 using EncodingFunc = std::function<otError(void)>; 273 otError SetProperty(spinel_prop_key_t aKey, const EncodingFunc &aEncodingFunc); 274 otError SendEncodedFrame(void); 275 276 otError ParseIp6AddressTable(const uint8_t *aBuf, uint16_t aLength, std::vector<Ip6AddressInfo> &aAddressTable); 277 otError ParseIp6MulticastAddresses(const uint8_t *aBuf, uint8_t aLen, std::vector<Ip6Address> &aAddressList); 278 otError ParseIp6StreamNet(const uint8_t *aBuf, uint8_t aLen, const uint8_t *&aData, uint16_t &aDataLen); 279 otError ParseOperationalDatasetTlvs(const uint8_t *aBuf, uint8_t aLen, otOperationalDatasetTlvs &aDatasetTlvs); 280 281 ot::Spinel::SpinelDriver *mSpinelDriver; 282 uint16_t mCmdTidsInUse; ///< Used transaction ids. 283 spinel_tid_t mCmdNextTid; ///< Next available transaction id. 284 285 spinel_prop_key_t mWaitingKeyTable[kMaxTids]; ///< The property keys of ongoing transactions. 286 spinel_command_t mCmdTable[kMaxTids]; ///< The mapping of spinel command and tids when the response 287 ///< is LAST_STATUS. 288 289 static constexpr uint16_t kTxBufferSize = 2048; 290 uint8_t mTxBuffer[kTxBufferSize]; 291 ot::Spinel::Buffer mNcpBuffer; 292 ot::Spinel::Encoder mEncoder; 293 spinel_iid_t mIid; /// < Interface Id used to in Spinel header 294 295 TaskRunner mTaskRunner; 296 297 PropsObserver *mPropsObserver; 298 299 AsyncTaskPtr mDatasetSetActiveTask; 300 AsyncTaskPtr mDatasetMgmtSetPendingTask; 301 AsyncTaskPtr mIp6SetEnabledTask; 302 AsyncTaskPtr mThreadSetEnabledTask; 303 AsyncTaskPtr mThreadDetachGracefullyTask; 304 AsyncTaskPtr mThreadErasePersistentInfoTask; 305 306 Ip6AddressTableCallback mIp6AddressTableCallback; 307 Ip6MulticastAddressTableCallback mIp6MulticastAddressTableCallback; 308 NetifStateChangedCallback mNetifStateChangedCallback; 309 }; 310 311 } // namespace Ncp 312 } // namespace otbr 313 314 #endif // OTBR_AGENT_NCP_SPINEL_HPP_ 315