Skip to content

Commit 4341a94

Browse files
andreagilardonipennam
authored andcommitted
defining OTAInterface
1 parent fe02f27 commit 4341a94

File tree

4 files changed

+785
-0
lines changed

4 files changed

+785
-0
lines changed

src/ota/interface/OTAInterface.cpp

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
/*
2+
This file is part of the ArduinoIoTCloud library.
3+
4+
Copyright (c) 2024 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
11+
/******************************************************************************
12+
* INCLUDE
13+
******************************************************************************/
14+
15+
#include <AIoTC_Config.h>
16+
17+
#if OTA_ENABLED
18+
#include "OTAInterface.h"
19+
#include "../OTA.h"
20+
21+
extern "C" unsigned long getTime();
22+
23+
/******************************************************************************
24+
* PUBLIC MEMBER FUNCTIONS
25+
******************************************************************************/
26+
27+
#ifdef DEBUG_VERBOSE
28+
const char* const OTACloudProcessInterface::STATE_NAMES[] = { // used only for debug purposes
29+
"Resume",
30+
"OtaBegin",
31+
"Idle",
32+
"OtaAvailable",
33+
"StartOTA",
34+
"Fetch",
35+
"FlashOTA",
36+
"Reboot",
37+
"Fail",
38+
"NoCapableBootloaderFail",
39+
"NoOtaStorageFail",
40+
"OtaStorageInitFail",
41+
"OtaStorageOpenFail",
42+
"OtaHeaderLengthFail",
43+
"OtaHeaderCrcFail",
44+
"OtaHeaderMagicNumberFail",
45+
"ParseHttpHeaderFail",
46+
"UrlParseErrorFail",
47+
"ServerConnectErrorFail",
48+
"HttpHeaderErrorFail",
49+
"OtaDownloadFail",
50+
"OtaHeaderTimeoutFail",
51+
"HttpResponseFail",
52+
"OtaStorageEndFail",
53+
"StorageConfigFail",
54+
"LibraryFail",
55+
"ModemFail",
56+
"ErrorOpenUpdateFileFail",
57+
"ErrorWriteUpdateFileFail",
58+
"ErrorReformatFail",
59+
"ErrorUnmountFail",
60+
"ErrorRenameFail",
61+
};
62+
#endif // DEBUG_VERBOSE
63+
64+
OTACloudProcessInterface::OTACloudProcessInterface(MessageStream *ms)
65+
: CloudProcess(ms)
66+
, policies(None)
67+
, state(Resume)
68+
, previous_state(Resume)
69+
, report_last_timestamp(0)
70+
, report_counter(0)
71+
, context(nullptr) {
72+
}
73+
74+
OTACloudProcessInterface::~OTACloudProcessInterface() {
75+
clean();
76+
}
77+
78+
void OTACloudProcessInterface::handleMessage(Message* msg) {
79+
80+
if ((state >= OtaAvailable || state < 0) && previous_state != state) {
81+
reportStatus(static_cast<int32_t>(state<0? state : 0));
82+
}
83+
84+
// this allows to do status report only when the state changes
85+
previous_state = state;
86+
87+
switch(state) {
88+
case Resume: updateState(resume(msg)); break;
89+
case OtaBegin: updateState(otaBegin()); break;
90+
case Idle: updateState(idle(msg)); break;
91+
case OtaAvailable: updateState(otaAvailable()); break;
92+
case StartOTA: updateState(startOTA()); break;
93+
case Fetch: updateState(fetch()); break;
94+
case FlashOTA: updateState(flashOTA()); break;
95+
case Reboot: updateState(reboot()); break;
96+
case OTAUnavailable: break;
97+
default: updateState(fail()); // all the states that are not defined are failures
98+
}
99+
}
100+
101+
OTACloudProcessInterface::State OTACloudProcessInterface::otaBegin() {
102+
if(!isOtaCapable()) {
103+
DEBUG_VERBOSE("OTA is not available on this board");
104+
return OTAUnavailable;
105+
}
106+
107+
struct OtaBeginUp msg = {
108+
OtaBeginUpId,
109+
};
110+
111+
SHA256 sha256_calc;
112+
calculateSHA256(sha256_calc);
113+
114+
sha256_calc.finalize(sha256);
115+
memcpy(msg.params.sha, sha256, SHA256::HASH_SIZE);
116+
117+
DEBUG_VERBOSE("calculated SHA256: "
118+
"0x%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X"
119+
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
120+
sha256[0], sha256[1], sha256[2], sha256[3], sha256[4], sha256[5], sha256[6], sha256[7],
121+
sha256[8], sha256[9], sha256[10], sha256[11], sha256[12], sha256[13], sha256[14], sha256[15],
122+
sha256[16], sha256[17], sha256[18], sha256[19], sha256[20], sha256[21], sha256[22], sha256[23],
123+
sha256[24], sha256[25], sha256[26], sha256[27], sha256[28], sha256[29], sha256[30], sha256[31]
124+
);
125+
126+
deliver((Message*)&msg);
127+
128+
return Idle;
129+
}
130+
131+
void OTACloudProcessInterface::calculateSHA256(SHA256& sha256_calc) {
132+
auto res = appFlashOpen();
133+
if(!res) {
134+
// TODO return error
135+
return;
136+
}
137+
138+
sha256_calc.begin();
139+
sha256_calc.update(
140+
reinterpret_cast<const uint8_t*>(appStartAddress()),
141+
appSize());
142+
appFlashClose();
143+
}
144+
145+
OTACloudProcessInterface::State OTACloudProcessInterface::idle(Message* msg) {
146+
// if a msg arrived, it may be an OTAavailable, then go to otaAvailable
147+
// otherwise do nothing
148+
if(msg!=nullptr && msg->id == OtaUpdateCmdDownId) {
149+
// save info coming from this message
150+
assert(context == nullptr); // This should never fail
151+
152+
struct OtaUpdateCmdDown* ota_msg = (struct OtaUpdateCmdDown*)msg;
153+
154+
context = new OtaContext(
155+
ota_msg->params.id, ota_msg->params.url,
156+
ota_msg->params.initialSha256, ota_msg->params.finalSha256
157+
);
158+
159+
// TODO verify that initialSha256 is the sha256 on board
160+
// TODO verify that final sha is not the current sha256 (?)
161+
return OtaAvailable;
162+
}
163+
164+
return Idle;
165+
}
166+
167+
OTACloudProcessInterface::State OTACloudProcessInterface::otaAvailable() {
168+
// depending on the policy decided on this device the ota process can start immediately
169+
// or wait for confirmation from the user
170+
if((policies & (ApprovalRequired | Approved)) == ApprovalRequired ) {
171+
return OtaAvailable;
172+
} else {
173+
policies &= ~Approved;
174+
return StartOTA;
175+
} // TODO add an abortOTA command? in this case delete the context
176+
}
177+
178+
OTACloudProcessInterface::State OTACloudProcessInterface::fail() {
179+
reset();
180+
clean();
181+
182+
return Idle;
183+
}
184+
185+
void OTACloudProcessInterface::clean() {
186+
// free the context pointer
187+
if(context != nullptr) {
188+
delete context;
189+
context = nullptr;
190+
}
191+
}
192+
193+
void OTACloudProcessInterface::reportStatus(int32_t state_data) {
194+
if(context == nullptr) {
195+
// FIXME handle this case: ota not in progress
196+
return;
197+
}
198+
uint32_t new_timestamp = getTime();
199+
200+
struct OtaProgressCmdUp msg = {
201+
OtaProgressCmdUpId,
202+
};
203+
204+
memcpy(msg.params.id, context->id, ID_SIZE);
205+
msg.params.state = state>=0 ? state : State::Fail;
206+
207+
if(new_timestamp == report_last_timestamp) {
208+
msg.params.time = new_timestamp*1e6 + ++report_counter;
209+
} else {
210+
msg.params.time = new_timestamp*1e6;
211+
report_counter = 0;
212+
report_last_timestamp = new_timestamp;
213+
}
214+
215+
msg.params.state_data = state_data;
216+
217+
deliver((Message*)&msg);
218+
}
219+
220+
OTACloudProcessInterface::OtaContext::OtaContext(
221+
uint8_t id[ID_SIZE], const char* url,
222+
uint8_t* initialSha256, uint8_t* finalSha256
223+
) : url(http://github.com/(%3Cspan%20class=%22pl-k%22%3Echar%3C/span%3E*) malloc(strlen(url) + 1)) {
224+
225+
memcpy(this->id, id, ID_SIZE);
226+
strcpy(this->url, url);
227+
memcpy(this->initialSha256, initialSha256, 32);
228+
memcpy(this->finalSha256, finalSha256, 32);
229+
}
230+
231+
OTACloudProcessInterface::OtaContext::~OtaContext() {
232+
free(url);
233+
}
234+
235+
#endif /* OTA_ENABLED */

0 commit comments

Comments
 (0)