Skip to content

Commit d718535

Browse files
defining OTAInterface
1 parent bbfe624 commit d718535

File tree

4 files changed

+776
-0
lines changed

4 files changed

+776
-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+
"OtaHeaterMagicNumberFail",
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+
, context(nullptr) {
70+
}
71+
72+
OTACloudProcessInterface::~OTACloudProcessInterface() {
73+
clean();
74+
}
75+
76+
void OTACloudProcessInterface::handleMessage(Message* msg) {
77+
78+
if ((state >= OtaAvailable || state < 0) && previous_state != state) {
79+
reportStatus();
80+
}
81+
82+
// this allows to do status report only when the state changes
83+
previous_state = state;
84+
85+
switch(state) {
86+
case Resume: updateState(resume(msg)); break;
87+
case OtaBegin: updateState(otaBegin()); break;
88+
case Idle: updateState(idle(msg)); break;
89+
case OtaAvailable: updateState(otaAvailable()); break;
90+
case StartOTA: updateState(startOTA()); break;
91+
case Fetch: updateState(fetch()); break;
92+
case FlashOTA: updateState(flashOTA()); break;
93+
case Reboot: updateState(reboot()); break;
94+
default: updateState(fail()); // all the states that are not defined are failures
95+
}
96+
}
97+
98+
OTACloudProcessInterface::State OTACloudProcessInterface::otaBegin() {
99+
if(!isOtaCapable()) {
100+
// FIXME What do we have to do in this case?
101+
DEBUG_WARNING("OTA is not available on this board");
102+
return OtaBegin;
103+
}
104+
105+
struct OtaBeginUp msg = {
106+
OtaBeginUpId,
107+
{}
108+
};
109+
110+
SHA256 sha256_calc;
111+
calculateSHA256(sha256_calc);
112+
113+
sha256_calc.finalize(sha256);
114+
memcpy(msg.params.sha, sha256, SHA256::HASH_SIZE);
115+
116+
DEBUG_VERBOSE("calculated SHA256: 0x%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
117+
this->sha256[0], this->sha256[1], this->sha256[2], this->sha256[3], this->sha256[4], this->sha256[5], this->sha256[6], this->sha256[7], this->sha256[8], this->sha256[9], this->sha256[10], this->sha256[11], this->sha256[12], this->sha256[13], this->sha256[14], this->sha256[15], this->sha256[16], this->sha256[17], this->sha256[18], this->sha256[19], this->sha256[20], this->sha256[21], this->sha256[22], this->sha256[23], this->sha256[24], this->sha256[25], this->sha256[26], this->sha256[27], this->sha256[28], this->sha256[29], this->sha256[30], this->sha256[31]
118+
);
119+
120+
deliver((Message*)&msg);
121+
// TODO msg object do not exist after this call make sure it gets copied somewhere
122+
123+
return Idle;
124+
}
125+
126+
void OTACloudProcessInterface::calculateSHA256(SHA256& sha256_calc) {
127+
DEBUG_VERBOSE("calculate sha on: 0x%X len 0x%X %d", appStartAddress(), appSize(), appSize());
128+
auto res = appFlashOpen();
129+
if(!res) {
130+
// TODO return error
131+
return;
132+
}
133+
134+
sha256_calc.begin();
135+
sha256_calc.update(
136+
reinterpret_cast<const uint8_t*>(appStartAddress()),
137+
appSize());
138+
appFlashClose();
139+
}
140+
141+
OTACloudProcessInterface::State OTACloudProcessInterface::idle(Message* msg) {
142+
// if a msg arrived, it may be an OTAavailable, then go to otaAvailable
143+
// otherwise do nothing
144+
if(msg!=nullptr && msg->id == OtaUpdateCmdDownId) {
145+
// save info coming from this message
146+
assert(context == nullptr); // This should never fail
147+
148+
struct OtaUpdateCmdDown* ota_msg = (struct OtaUpdateCmdDown*)msg;
149+
150+
context = new OtaContext(
151+
ota_msg->params.id, ota_msg->params.url,
152+
ota_msg->params.initialSha256, ota_msg->params.finalSha256
153+
);
154+
155+
// TODO verify that initialSha256 is the sha256 on board
156+
// TODO verify that final sha is not the current sha256 (?)
157+
return OtaAvailable;
158+
}
159+
160+
return Idle;
161+
}
162+
163+
OTACloudProcessInterface::State OTACloudProcessInterface::otaAvailable() {
164+
// depending on the policy decided on this device the ota process can start immediately
165+
// or wait for confirmation from the user
166+
if((policies & (ApprovalRequired | Approved)) == ApprovalRequired ) {
167+
return OtaAvailable;
168+
} else {
169+
policies &= ~Approved;
170+
return StartOTA;
171+
} // TODO add an abortOTA command? in this case delete the context
172+
}
173+
174+
OTACloudProcessInterface::State OTACloudProcessInterface::fail() {
175+
reset();
176+
clean();
177+
178+
return Idle;
179+
}
180+
181+
void OTACloudProcessInterface::clean() {
182+
// free the context pointer
183+
if(context != nullptr) {
184+
delete context;
185+
context = nullptr;
186+
}
187+
}
188+
189+
void OTACloudProcessInterface::reportStatus() {
190+
if(context == nullptr) {
191+
// FIXME handle this case: ota not in progress
192+
return;
193+
}
194+
static uint32_t last_timestamp = getTime();
195+
static uint32_t counter = 0;
196+
uint32_t new_timestamp = getTime();
197+
198+
struct OtaProgressCmdUp msg = {
199+
OtaProgressCmdUpId,
200+
{}
201+
};
202+
203+
memcpy(msg.params.id, context->id, ID_SIZE);
204+
msg.params.state = state>=0 ? state : State::Fail;
205+
206+
if(new_timestamp == last_timestamp) {
207+
msg.params.time = new_timestamp*1e6 + ++counter;
208+
} else {
209+
msg.params.time = new_timestamp*1e6;
210+
counter = 0;
211+
last_timestamp = new_timestamp;
212+
}
213+
214+
msg.params.state_data = static_cast<int32_t>(state<0? state : 0);
215+
216+
deliver((Message*)&msg);
217+
// TODO msg object do not exist after this call make sure it gets copied somewhere
218+
}
219+
220+
OTACloudProcessInterface::OtaContext::OtaContext(
221+
uint8_t id[ID_SIZE], const char* url,
222+
uint8_t* initialSha256, uint8_t* finalSha256
223+
) : url((char*) 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)