/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 | } |