Coverage Report

Created: 2026-01-09 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wpantund/src/ncp-spinel/SpinelNCPTaskDeepSleep.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
 *    Description:
19
 *    This file contains the code that handles transitioning the
20
 *      NCP into a deep-sleep state.
21
 *
22
 */
23
24
#if HAVE_CONFIG_H
25
#include <config.h>
26
#endif
27
28
#include "assert-macros.h"
29
#include <syslog.h>
30
#include <errno.h>
31
#include "SpinelNCPTaskDeepSleep.h"
32
#include "SpinelNCPInstance.h"
33
#include "spinel-extra.h"
34
35
using namespace nl;
36
using namespace nl::wpantund;
37
38
nl::wpantund::SpinelNCPTaskDeepSleep::SpinelNCPTaskDeepSleep(
39
  SpinelNCPInstance* instance,
40
  CallbackWithStatusArg1 cb
41
0
):  SpinelNCPTask(instance, cb)
42
0
{
43
0
}
44
45
void
46
nl::wpantund::SpinelNCPTaskDeepSleep::finish(int status, const boost::any& value)
47
0
{
48
0
  mInstance->mResetIsExpected = false;
49
50
0
  SpinelNCPTask::finish(status, value);
51
0
}
52
53
54
int
55
nl::wpantund::SpinelNCPTaskDeepSleep::vprocess_event(int event, va_list args)
56
0
{
57
0
  int ret = kWPANTUNDStatus_Failure;
58
59
0
  EH_BEGIN();
60
61
  // The first event to a task is EVENT_STARTING_TASK. The following
62
  // line makes sure that we don't start processing this task
63
  // until it is properly scheduled. All tasks immediately receive
64
  // the initial `EVENT_STARTING_TASK` event, but further events
65
  // will only be received by that task once it is that task's turn
66
  // to execute.
67
0
  EH_WAIT_UNTIL(EVENT_STARTING_TASK != event);
68
69
  // If we are still initializing, wait until we are finished.
70
0
  EH_WAIT_UNTIL_WITH_TIMEOUT(mInstance->mDriverState == SpinelNCPInstance::NORMAL_OPERATION, NCP_DEFAULT_COMMAND_SEND_TIMEOUT);
71
72
0
  if (mInstance->can_set_ncp_power()) {
73
0
    mNextCommand = SpinelPackData(SPINEL_FRAME_PACK_CMD_NOOP);
74
0
    EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
75
76
    // Wait for half a second after the last ncp-generated event before
77
    // manually cutting the power, just to be conservative.
78
0
    do {
79
0
      EH_WAIT_UNTIL(!IS_EVENT_FROM_NCP(event));
80
0
      EH_WAIT_UNTIL_WITH_TIMEOUT(0.5, IS_EVENT_FROM_NCP(event));
81
0
    } while(!eh_did_timeout);
82
83
0
    if (mInstance->set_ncp_power(false) == kWPANTUNDStatus_Ok) {
84
0
      mInstance->change_ncp_state(DEEP_SLEEP);
85
0
    } else {
86
0
      syslog(LOG_ERR, "DeepSleep: set_ncp_power(false) failed.");
87
88
      // Turning off the power manually didn't work for some reason.
89
      // Turn it back on and we will try to do it via the API, below.
90
0
      mInstance->set_ncp_power(true);
91
0
    }
92
0
  }
93
94
0
  if (mInstance->get_ncp_state() != DEEP_SLEEP) {
95
96
0
    if (ncp_state_is_joining_or_joined(mInstance->get_ncp_state())) {
97
0
      mNextCommand = SpinelPackData(
98
0
        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_BOOL_S),
99
0
        SPINEL_PROP_NET_STACK_UP,
100
0
        false
101
0
      );
102
0
      EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
103
0
      ret = mNextCommandRet;
104
0
      require_noerr(ret, on_error);
105
106
0
      mNextCommand = SpinelPackData(
107
0
        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_BOOL_S),
108
0
        SPINEL_PROP_NET_IF_UP,
109
0
        false
110
0
      );
111
0
      EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
112
0
      ret = mNextCommandRet;
113
0
      require_noerr(ret, on_error);
114
0
    }
115
116
    // We only try to put the NCP into a deep sleep state
117
    // if it advertises that it supports this sort of
118
    // power management. Otherwise, we simply try to put
119
    // the chip into a low power state.
120
121
0
    if (mInstance->mCapabilities.count(SPINEL_CAP_MCU_POWER_STATE)) {
122
0
      syslog(LOG_NOTICE, "DeepSleep: Putting NCP to low-power.");
123
124
0
      mNextCommand = SpinelPackData(
125
0
        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT8_S),
126
0
        SPINEL_PROP_MCU_POWER_STATE,
127
0
        SPINEL_MCU_POWER_STATE_LOW_POWER
128
0
      );
129
0
      EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
130
0
      ret = mNextCommandRet;
131
0
      require_noerr(ret, on_error);
132
133
0
    } else {
134
0
      syslog(LOG_WARNING, "DeepSleep: No support for CAP_MCU_POWER_STATE. Will attempt to change configuration to reduce power.");
135
136
      // Turn off the phy
137
0
      mNextCommand = SpinelPackData(
138
0
        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_BOOL_S),
139
0
        SPINEL_PROP_PHY_ENABLED,
140
0
        false
141
0
      );
142
0
      EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
143
144
      // Wait for half a second after the last ncp-generated event
145
      // just to make sure we have received everything it wanted to
146
      // tell us. We have to do this because we aren't really putting
147
      // the NCP into a deep sleep mode, so it might still send us
148
      // some asynchronous updates triggered from the above three
149
      // commands.
150
0
      do {
151
0
        EH_WAIT_UNTIL(!IS_EVENT_FROM_NCP(event));
152
0
        EH_WAIT_UNTIL_WITH_TIMEOUT(0.5, IS_EVENT_FROM_NCP(event));
153
0
      } while(!eh_did_timeout);
154
155
0
      mInstance->change_ncp_state(DEEP_SLEEP);
156
0
    }
157
0
  }
158
159
0
on_error:
160
161
0
  if (mInstance->get_ncp_state() == DEEP_SLEEP) {
162
0
    syslog(LOG_NOTICE, "NCP is asleep.");
163
0
    ret = kWPANTUNDStatus_Ok;
164
0
  } else {
165
0
    syslog(LOG_WARNING, "NCP DID NOT GO TO SLEEP!");
166
0
    if (kWPANTUNDStatus_Ok == ret) {
167
0
      ret = kWPANTUNDStatus_Failure;
168
0
    }
169
0
  }
170
171
0
  finish(ret);
172
173
0
  EH_END();
174
0
}