Coverage Report

Created: 2026-01-10 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wpantund/src/ncp-spinel/SpinelNCPTaskSendCommand.cpp
Line
Count
Source
1
/*
2
 *
3
 * Copyright (c) 2016 Nest Labs, Inc.
4
 * All rights reserved.
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 *
18
 */
19
20
#if HAVE_CONFIG_H
21
#include <config.h>
22
#endif
23
24
#include "assert-macros.h"
25
#include <syslog.h>
26
#include <errno.h>
27
#include "SpinelNCPTaskSendCommand.h"
28
#include "SpinelNCPInstance.h"
29
#include "spinel-extra.h"
30
31
using namespace nl;
32
using namespace nl::wpantund;
33
34
static int simple_unpacker(const uint8_t* data_in, spinel_size_t data_len, const std::string& pack_format,
35
              boost::any& result);
36
37
SpinelNCPTaskSendCommand::Factory::Factory(SpinelNCPInstance* instance):
38
23.2k
  mInstance(instance),
39
23.2k
  mCb(NilReturn()),
40
23.2k
  mTimeout(NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT),
41
23.2k
  mLockProperty(0),
42
23.2k
  mCheckTimeout(0)
43
23.2k
{
44
23.2k
}
45
46
SpinelNCPTaskSendCommand::Factory&
47
SpinelNCPTaskSendCommand::Factory::set_callback(const CallbackWithStatusArg1 &cb)
48
4
{
49
4
  mCb = cb;
50
4
  return *this;
51
4
}
52
53
SpinelNCPTaskSendCommand::Factory&
54
SpinelNCPTaskSendCommand::Factory::set_callback(const CallbackWithStatus &cb)
55
18.6k
{
56
18.6k
  mCb = boost::bind(cb, _1);
57
18.6k
  return *this;
58
18.6k
}
59
60
SpinelNCPTaskSendCommand::Factory&
61
SpinelNCPTaskSendCommand::Factory::add_command(const Data& command)
62
34.7k
{
63
34.7k
  mCommandList.insert(mCommandList.end(), command);
64
34.7k
  return *this;
65
34.7k
}
66
67
SpinelNCPTaskSendCommand::Factory&
68
SpinelNCPTaskSendCommand::Factory::set_timeout(int timeout)
69
0
{
70
0
  mTimeout = timeout;
71
0
  return *this;
72
0
}
73
74
SpinelNCPTaskSendCommand::Factory&
75
SpinelNCPTaskSendCommand::Factory::set_reply_format(const std::string& packed_format)
76
0
{
77
0
  mReplyUnpacker = boost::bind(simple_unpacker, _1, _2, packed_format, _3);
78
0
  return *this;
79
0
}
80
81
SpinelNCPTaskSendCommand::Factory&
82
SpinelNCPTaskSendCommand::Factory::set_reply_unpacker(const ReplyUnpacker& reply_unpacker)
83
4
{
84
4
  mReplyUnpacker = reply_unpacker;
85
4
  return *this;
86
4
}
87
88
SpinelNCPTaskSendCommand::Factory&
89
SpinelNCPTaskSendCommand::Factory::set_lock_property(int lock_property)
90
0
{
91
0
  mLockProperty = lock_property;
92
0
  return *this;
93
0
}
94
95
SpinelNCPTaskSendCommand::Factory&
96
SpinelNCPTaskSendCommand::Factory::set_final_check(const CheckHandler &handler, int timeout)
97
0
{
98
0
  mCheckHandler = handler;
99
0
  mCheckTimeout = timeout;
100
0
  return *this;
101
0
}
102
103
boost::shared_ptr<SpinelNCPTask>
104
SpinelNCPTaskSendCommand::Factory::finish(void)
105
23.2k
{
106
23.2k
  return boost::shared_ptr<SpinelNCPTask>(new SpinelNCPTaskSendCommand(*this));
107
23.2k
}
108
109
nl::wpantund::SpinelNCPTaskSendCommand::SpinelNCPTaskSendCommand(
110
  const Factory& factory
111
23.2k
):  SpinelNCPTask(factory.mInstance, factory.mCb),
112
23.2k
  mCommandList(factory.mCommandList),
113
23.2k
  mLockProperty(factory.mLockProperty),
114
23.2k
  mReplyUnpacker(factory.mReplyUnpacker),
115
23.2k
  mCheckHandler(factory.mCheckHandler),
116
23.2k
  mCheckTimeout(factory.mCheckTimeout),
117
23.2k
  mRetVal(kWPANTUNDStatus_Failure)
118
23.2k
{
119
23.2k
  mNextCommandTimeout = factory.mTimeout;
120
23.2k
}
121
122
static boost::any
123
spinel_iter_to_any(spinel_datatype_iter_t *iter)
124
0
{
125
0
  boost::any ret;
126
0
  spinel_status_t status;
127
128
0
  switch(iter->pack_format[0]) {
129
0
  case SPINEL_DATATYPE_BOOL_C:
130
0
    {
131
0
      bool val(0);
132
0
      status = spinel_datatype_iter_unpack(iter, &val);
133
0
      require_noerr(status, bail);
134
0
      ret = val;
135
0
    }
136
0
    break;
137
138
0
  case SPINEL_DATATYPE_UINT8_C:
139
0
    {
140
0
      uint8_t val(0);
141
0
      status = spinel_datatype_iter_unpack(iter, &val);
142
0
      require_noerr(status, bail);
143
0
      ret = val;
144
0
    }
145
0
    break;
146
147
0
  case SPINEL_DATATYPE_INT8_C:
148
0
    {
149
0
      int8_t val(0);
150
0
      status = spinel_datatype_iter_unpack(iter, &val);
151
0
      require_noerr(status, bail);
152
0
      ret = (int)val;
153
0
    }
154
0
    break;
155
156
0
  case SPINEL_DATATYPE_UINT16_C:
157
0
    {
158
0
      uint16_t val(0);
159
0
      status = spinel_datatype_iter_unpack(iter, &val);
160
0
      require_noerr(status, bail);
161
0
      ret = val;
162
0
    }
163
0
    break;
164
165
0
  case SPINEL_DATATYPE_INT16_C:
166
0
    {
167
0
      int16_t val(0);
168
0
      status = spinel_datatype_iter_unpack(iter, &val);
169
0
      require_noerr(status, bail);
170
0
      ret = val;
171
0
    }
172
0
    break;
173
174
0
  case SPINEL_DATATYPE_UINT32_C:
175
0
    {
176
0
      uint32_t val(0);
177
0
      status = spinel_datatype_iter_unpack(iter, &val);
178
0
      require_noerr(status, bail);
179
0
      ret = val;
180
0
    }
181
0
    break;
182
183
0
  case SPINEL_DATATYPE_INT32_C:
184
0
    {
185
0
      int32_t val(0);
186
0
      status = spinel_datatype_iter_unpack(iter, &val);
187
0
      require_noerr(status, bail);
188
0
      ret = val;
189
0
    }
190
0
    break;
191
192
0
  case SPINEL_DATATYPE_UINT64_C:
193
0
    {
194
0
      uint64_t val(0);
195
0
      status = spinel_datatype_iter_unpack(iter, &val);
196
0
      require_noerr(status, bail);
197
0
      ret = val;
198
0
    }
199
0
    break;
200
201
0
  case SPINEL_DATATYPE_INT64_C:
202
0
    {
203
0
      int64_t val(0);
204
0
      status = spinel_datatype_iter_unpack(iter, &val);
205
0
      require_noerr(status, bail);
206
0
      ret = val;
207
0
    }
208
0
    break;
209
210
0
  case SPINEL_DATATYPE_UINT_PACKED_C:
211
0
    {
212
0
      unsigned int val(0);
213
0
      status = spinel_datatype_iter_unpack(iter, &val);
214
0
      require_noerr(status, bail);
215
0
      ret = val;
216
0
    }
217
0
    break;
218
219
0
  case SPINEL_DATATYPE_IPv6ADDR_C:
220
0
    {
221
0
      struct in6_addr *val = NULL;
222
0
      status = spinel_datatype_iter_unpack(iter, &val);
223
0
      require_noerr(status, bail);
224
0
      ret = in6_addr_to_string(*val);
225
0
    }
226
0
    break;
227
228
0
  case SPINEL_DATATYPE_EUI64_C:
229
0
    {
230
0
      const spinel_eui64_t *val(NULL);
231
0
      status = spinel_datatype_iter_unpack(iter, &val);
232
0
      require_noerr(status, bail);
233
0
      ret = Data(val->bytes, sizeof(val->bytes));
234
0
    }
235
0
    break;
236
237
0
  case SPINEL_DATATYPE_EUI48_C:
238
0
    {
239
0
      const spinel_eui48_t *val(NULL);
240
0
      status = spinel_datatype_iter_unpack(iter, &val);
241
0
      require_noerr(status, bail);
242
0
      ret = Data(val->bytes, sizeof(val->bytes));
243
0
    }
244
0
    break;
245
246
0
  case SPINEL_DATATYPE_DATA_C:
247
0
    {
248
0
      const uint8_t *val_ptr(NULL);
249
0
      spinel_size_t val_len;
250
0
      status = spinel_datatype_iter_unpack(iter, &val_ptr, &val_len);
251
0
      require_noerr(status, bail);
252
0
      ret = Data(val_ptr, val_len);
253
0
    }
254
0
    break;
255
256
0
  case SPINEL_DATATYPE_UTF8_C:
257
0
    {
258
0
      const char *val(NULL);
259
0
      status = spinel_datatype_iter_unpack(iter, &val);
260
0
      require_noerr(status, bail);
261
0
      ret = std::string(val);
262
0
    }
263
0
    break;
264
265
0
  case SPINEL_DATATYPE_STRUCT_C:
266
0
    goto bail;
267
268
0
  case SPINEL_DATATYPE_ARRAY_C:
269
    // TODO: Recursively parse this
270
0
    goto bail;
271
272
0
  default:
273
0
    goto bail;
274
275
0
  }
276
277
0
bail:
278
0
  return ret;
279
0
}
280
281
static boost::any
282
spinel_packed_to_any(const uint8_t* data_in, spinel_size_t data_len, const char* pack_format)
283
0
{
284
0
  spinel_datatype_iter_t spinel_iter = {};
285
0
  spinel_datatype_iter_start(&spinel_iter, data_in, data_len, pack_format);
286
287
0
  return spinel_iter_to_any(&spinel_iter);
288
0
}
289
290
static int
291
simple_unpacker(const uint8_t* data_in, spinel_size_t data_len, const std::string& pack_format, boost::any& result)
292
0
{
293
0
  int retval = kWPANTUNDStatus_Ok;
294
295
0
  try {
296
0
    result = spinel_packed_to_any(data_in, data_len, pack_format.c_str());
297
298
0
  } catch(...) {
299
0
    retval = kWPANTUNDStatus_Failure;
300
0
  }
301
302
0
  return retval;
303
0
}
304
305
int
306
nl::wpantund::SpinelNCPTaskSendCommand::vprocess_event(int event, va_list args)
307
534k
{
308
534k
  EH_BEGIN();
309
310
23.2k
  mRetVal = kWPANTUNDStatus_Failure;
311
312
  // The first event to a task is EVENT_STARTING_TASK. The following
313
  // line makes sure that we don't start processing this task
314
  // until it is properly scheduled. All tasks immediately receive
315
  // the initial `EVENT_STARTING_TASK` event, but further events
316
  // will only be received by that task once it is that task's turn
317
  // to execute.
318
23.2k
  EH_WAIT_UNTIL(EVENT_STARTING_TASK != event);
319
320
9.75k
  if (mLockProperty != 0) {
321
0
    mNextCommand = SpinelPackData(
322
0
      SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_BOOL_S),
323
0
      mLockProperty,
324
0
      true
325
0
    );
326
327
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
328
329
0
    mRetVal = mNextCommandRet;
330
331
    // In case of BUSY error status (meaning the `ALLOW_LOCAL_NET_DATA_CHANGE`
332
    // was already true), allow the operation to proceed.
333
334
0
    require((mRetVal == kWPANTUNDStatus_Ok) || (mRetVal == kWPANTUNDStatus_Busy), on_error);
335
0
  }
336
337
9.75k
  mRetVal = kWPANTUNDStatus_Ok;
338
339
9.75k
  mCommandIter = mCommandList.begin();
340
341
24.1k
  while ( (mRetVal == kWPANTUNDStatus_Ok)
342
24.1k
       && (mCommandList.end() != mCommandIter)
343
15.6k
  ) {
344
15.6k
    mNextCommand = *mCommandIter++;
345
346
15.6k
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
347
15.6k
  }
348
349
8.55k
  mRetVal = mNextCommandRet;
350
351
8.55k
  require_noerr(mRetVal, on_error);
352
353
5.43k
  if ( mReplyUnpacker
354
0
    && (kWPANTUNDStatus_Ok == mRetVal)
355
0
    && (EVENT_NCP_PROP_VALUE_IS == event)
356
5.43k
  ) {
357
    // Handle the packed response from the last command
358
359
0
    unsigned int key = va_arg(args, unsigned int);
360
0
    const uint8_t* data_in = va_arg(args, const uint8_t*);
361
0
    spinel_size_t data_len = va_arg_small(args, spinel_size_t);
362
0
    (void) key; // Ignored
363
364
0
    mRetVal = mReplyUnpacker(data_in, data_len, mReturnValue);
365
0
  }
366
367
5.43k
  if (mCheckTimeout != 0) {
368
    // We need to yield to allow other protothreads
369
    // to handle the event before starting to wait for
370
    // the check handler to become `true`.
371
0
    EH_YIELD();
372
373
0
    mRetVal = kWPANTUNDStatus_Timeout;
374
0
    EH_REQUIRE_WITHIN(mCheckTimeout, mCheckHandler() == true, on_error);
375
0
    mRetVal = kWPANTUNDStatus_Ok;
376
0
  }
377
378
8.55k
on_error:
379
380
8.55k
  if (mRetVal != kWPANTUNDStatus_Ok) {
381
3.12k
    syslog(LOG_ERR, "SendCommand task encountered an error: %d (0x%08X)", mRetVal, mRetVal);
382
3.12k
  }
383
384
  // Even in case of failure we proceed to set mLockProperty
385
  // to `false`. The error status is checked after this. It is stored in
386
  // a class instance variable `mRetVal` so that the value is preserved
387
  // over the protothread EH_SPAWN() call.
388
389
8.55k
  if (mLockProperty != 0) {
390
0
    mNextCommand = SpinelPackData(
391
0
      SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_BOOL_S),
392
0
      mLockProperty,
393
0
      false
394
0
    );
395
396
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
397
398
0
    if (mNextCommandRet != kWPANTUNDStatus_Ok)
399
0
    {
400
0
      mRetVal = mNextCommandRet;
401
0
    }
402
403
0
    check_noerr(mNextCommandRet);
404
0
  }
405
406
8.55k
  finish(mRetVal, mReturnValue);
407
408
534k
  EH_END();
409
0
}