Skip to content

Commit 7901791

Browse files
committed
feat(flash): Switch to sector-by-sector flashing
This flashing flow has some advantages: - It is more interactive - the long erase command is avoided - It seems to be less sensitive to hardware malfunctions. See issue #45 where this mode was used to flash an EV3 brick with what looks like faulty RAM. As some functions are now called repeatedly, I tried to go through them and eliminate the memory leaks in them.
1 parent 271134c commit 7901791

File tree

1 file changed

+109
-116
lines changed

1 file changed

+109
-116
lines changed

src/bl_install.c

Lines changed: 109 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,22 @@
1515
#include "error.h"
1616
#include "funcs.h"
1717

18-
/**
19-
* @brief Erase internal flash
20-
* @retval error according to enum #ERR
21-
*/
22-
static int bootloader_erase(void);
23-
2418
/**
2519
* @brief Start the programming operation
2620
* @param offset Start of programmed region
2721
* @param length Length of programmed region
2822
* @retval error according to enum #ERR
2923
*/
30-
static int bootloader_start(int offset, int length);
24+
static int bootloader_erase_and_start(int offset, int length);
3125

3226
/**
3327
* @brief Send firmware to bootloader
34-
* @param fp Firmware file
28+
* @param firmware Firmware image
3529
* @param length Length of programmed region
3630
* @param pCrc32 Locally-calculated CRC32 of the firmware
3731
* @retval error according to enum #ERR
3832
*/
39-
static int bootloader_send(FILE *fp, int length, u32* pCrc32);
33+
static int bootloader_send(u8 *firmware, int length, u32* pCrc32);
4034

4135
/**
4236
* @brief Request CRC32 verification from the bootloader
@@ -70,98 +64,92 @@ static bool sector_is_mutable(u32 sector_number);
7064
*/
7165
int bootloader_install(FILE *fp)
7266
{
73-
int err = ERR_UNK;
74-
u32 local_crc32 = 0;
75-
u32 remote_crc32 = 0;
67+
u8 *firmware = calloc(FLASH_SIZE, 1);
68+
int read_bytes = fread(firmware, 1, FLASH_SIZE, fp); // this may be less than FLASH_SIZE (e.g. Pybricks has a small firmware image)
69+
int sector_count = (read_bytes + FLASH_SECTOR_SIZE - 1) / FLASH_SECTOR_SIZE;
7670

77-
puts("Erasing flash... (takes 1 minute 48 seconds)"); // 108 seconds
78-
err = bootloader_erase();
79-
if (err != ERR_UNK)
80-
return err;
71+
printf("Programming %d EV3 flash sectors...\n", sector_count);
8172

82-
puts("Downloading...");
83-
err = bootloader_start(FLASH_START, FLASH_SIZE); // extremely short
84-
if (err != ERR_UNK)
85-
return err;
86-
err = bootloader_send(fp, FLASH_SIZE, &local_crc32); // variable time
87-
if (err != ERR_UNK)
88-
return err;
89-
90-
puts("Calculating flash checksum... (takes 17 seconds)");
91-
err = bootloader_checksum(FLASH_START, FLASH_SIZE, &remote_crc32); // 16.7 seconds
73+
int err = ERR_UNK;
74+
const char *last_area = NULL;
75+
for (int sector = 0; sector < sector_count; sector++) {
76+
u32 local_crc32 = 0;
77+
u32 remote_crc32 = 0;
9278

93-
// handle usb 3.0 bug
94-
if (err == ERR_USBLOOP)
95-
{
96-
puts("Checksum not checked, assuming update was OK. Rebooting.");
97-
}
98-
else if (err == ERR_UNK) // no error occurred
99-
{
100-
if (local_crc32 != remote_crc32) {
101-
fprintf(stderr, "error: checksums do not match: remote %08X != local %08X\n",
102-
remote_crc32, local_crc32);
103-
return ERR_IO;
104-
} else {
105-
puts("Success! Local and remote checksums match. Rebooting.");
79+
const char *area = sector_explanation(sector);
80+
if (last_area != area) {
81+
printf("=== %s ===\n", area);
82+
last_area = area;
10683
}
107-
}
108-
else // other error occurred
109-
{
110-
return err;
111-
}
11284

113-
return bootloader_exit();
114-
}
85+
printf("- Sector %3d (%2d %%)\n", sector, 100*sector/sector_count);
86+
err = bootloader_erase_and_start(sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE);
87+
if (err != ERR_UNK) {
88+
puts("Flash erase returned an error!");
89+
break;
90+
}
11591

116-
static int bootloader_erase(void)
117-
{
118-
FW_ERASEFLASH *request = packet_alloc(FW_ERASEFLASH, 0);
119-
int res = ev3_write(handle, (u8 *) request, request->packetLen + PREFIX_SIZE);
120-
if (res < 0)
121-
{
122-
errmsg = "Unable to write FW_ERASEFLASH.";
123-
return ERR_COMM;
124-
}
92+
err = bootloader_send(firmware + sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE, &local_crc32);
93+
if (err != ERR_UNK) {
94+
puts("Programming returned an error!");
95+
break;
96+
}
12597

126-
FW_ERASEFLASH_REPLY *reply = malloc(sizeof(FW_ERASEFLASH_REPLY));
127-
res = ev3_read_timeout(handle, (u8 *) reply, sizeof(FW_ERASEFLASH_REPLY), -1);
128-
if (res <= 0)
129-
{
130-
errmsg = "Unable to read FW_ERASEFLASH";
131-
return ERR_COMM;
98+
err = bootloader_checksum(sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE, &remote_crc32);
99+
if (err == ERR_USBLOOP && sector == 0)
100+
{
101+
puts("NOTE: CRC not checked, because the brick is likely plugged to a USB 3.0 port.");
102+
}
103+
else if (err == ERR_UNK) // no error occurred
104+
{
105+
if (local_crc32 != remote_crc32) {
106+
printf("Checksum does not match: remote %08X != local %08X\n", remote_crc32, local_crc32);
107+
err = ERR_COMM;
108+
break;
109+
}
110+
}
111+
else // other error occurred
112+
{
113+
puts("Checksum computation returned an error!");
114+
break;
115+
}
132116
}
133117

134-
// note: accept looped-back packets (usb 3.0 bug; reply not required here)
135-
if (reply->type != VM_OK && reply->type != VM_SYS_RQ)
136-
{
137-
errno = reply->ret;
138-
fputs("Operation failed.\nlast_reply=", stderr);
139-
print_bytes(reply, reply->packetLen + 2);
140-
141-
errmsg = "`FW_ERASEFLASH` was denied.";
142-
return ERR_VM;
118+
free(firmware);
119+
if (err == ERR_UNK) {
120+
puts("Flashing finished, rebooting the brick.");
121+
return bootloader_exit();
122+
} else {
123+
puts("Some error occurred, leaving the brick in the bootloader mode.");
124+
puts("To exit it manually, simply remove the EV3 battery and then put it back in.");
125+
return err;
143126
}
144-
return ERR_UNK;
145127
}
146128

147-
static int bootloader_start(int offset, int length)
129+
static int bootloader_erase_and_start(int offset, int length)
148130
{
149-
FW_START_DOWNLOAD *request = packet_alloc(FW_START_DOWNLOAD, 0);
131+
FW_START_DOWNLOAD_WITH_ERASE *request = NULL;
132+
FW_START_DOWNLOAD_WITH_ERASE_REPLY *reply = NULL;
133+
int err;
134+
135+
request = packet_alloc(FW_START_DOWNLOAD_WITH_ERASE, 0);
150136
request->flashStart = offset;
151137
request->flashLength = length;
152138
int res = ev3_write(handle, (u8 *) request, request->packetLen + PREFIX_SIZE);
153139
if (res < 0)
154140
{
155141
errmsg = "Unable to write FW_START_DOWNLOAD.";
156-
return ERR_COMM;
142+
err = ERR_COMM;
143+
goto exit;
157144
}
158145

159-
FW_START_DOWNLOAD_REPLY *reply = malloc(sizeof(FW_START_DOWNLOAD_REPLY));
160-
res = ev3_read_timeout(handle, (u8 *) reply, sizeof(FW_START_DOWNLOAD_REPLY), -1);
146+
reply = malloc(sizeof(FW_START_DOWNLOAD_WITH_ERASE_REPLY));
147+
res = ev3_read_timeout(handle, (u8 *) reply, sizeof(FW_START_DOWNLOAD_WITH_ERASE_REPLY), -1);
161148
if (res <= 0)
162149
{
163150
errmsg = "Unable to read FW_START_DOWNLOAD";
164-
return ERR_COMM;
151+
err = ERR_COMM;
152+
goto exit;
165153
}
166154

167155
// note: accept looped-back packets (usb 3.0 bug; reply not required here)
@@ -171,43 +159,35 @@ static int bootloader_start(int offset, int length)
171159
fputs("Operation failed.\nlast_reply=", stderr);
172160
print_bytes(reply, reply->packetLen + 2);
173161

174-
errmsg = "`FW_START_DOWNLOAD` was denied.";
175-
return ERR_VM;
162+
errmsg = "`FW_START_DOWNLOAD_WITH_ERASE_REPLY` was denied.";
163+
err = ERR_VM;
164+
goto exit;
176165
}
177-
return ERR_UNK;
166+
err = ERR_UNK;
167+
168+
exit:
169+
free(request);
170+
free(reply);
171+
return err;
178172
}
179173

180-
static int bootloader_send(FILE *fp, int length, u32* pCrc32)
174+
static int bootloader_send(u8 *buffer, int length, u32* pCrc32)
181175
{
182-
*pCrc32 = 0;
183-
184-
int max_payload = 1024 - (sizeof(FW_DOWNLOAD_DATA) - PREFIX_SIZE + 2);
176+
const int max_payload = 1024 - (sizeof(FW_DOWNLOAD_DATA) - PREFIX_SIZE + 2);
185177
FW_DOWNLOAD_DATA *request = packet_alloc(FW_DOWNLOAD_DATA, max_payload);
186178
FW_DOWNLOAD_DATA_REPLY *reply = malloc(sizeof(FW_DOWNLOAD_DATA_REPLY));
187179

188180
int total = length;
189181
int sent_so_far = 0;
190-
int file_ended = 0;
191-
int res = 0;
192-
int last_percent = 0;
182+
int err = ERR_UNK;
193183
u32 crc = 0;
194184

195-
printf("Progress: %3d %%\n", 0);
196-
197185
while (sent_so_far < total) {
198186
int remaining = total - sent_so_far;
199187
int this_block = remaining <= max_payload ? remaining : max_payload;
188+
int res;
200189

201-
if (!file_ended) {
202-
int real = fread(request->payload, 1, this_block, fp);
203-
if (real < this_block) {
204-
file_ended = 1;
205-
memset(request->payload + real, 0, this_block - real);
206-
}
207-
} else {
208-
memset(request->payload, 0, this_block);
209-
}
210-
190+
memcpy(request->payload, buffer + sent_so_far, this_block);
211191
crc = crc32(crc, request->payload, this_block);
212192

213193
request->packetLen = sizeof(FW_DOWNLOAD_DATA) - PREFIX_SIZE + this_block;
@@ -216,14 +196,16 @@ static int bootloader_send(FILE *fp, int length, u32* pCrc32)
216196
if (res < 0)
217197
{
218198
errmsg = "Unable to write FW_DOWNLOAD_DATA.";
219-
return ERR_COMM;
199+
err = ERR_COMM;
200+
break;
220201
}
221202

222203
res = ev3_read_timeout(handle, (u8 *) reply, sizeof(FW_DOWNLOAD_DATA_REPLY), -1);
223204
if (res <= 0)
224205
{
225206
errmsg = "Unable to read FW_DOWNLOAD_DATA";
226-
return ERR_COMM;
207+
err = ERR_COMM;
208+
break;
227209
}
228210

229211
// note: accept looped-back packets (usb 3.0 bug; reply not required here)
@@ -234,19 +216,17 @@ static int bootloader_send(FILE *fp, int length, u32* pCrc32)
234216
print_bytes(reply, reply->packetLen + 2);
235217

236218
errmsg = "`FW_DOWNLOAD_DATA` was denied.";
237-
return ERR_VM;
219+
err = ERR_VM;
220+
break;
238221
}
239222

240223
sent_so_far += this_block;
241-
242-
int new_percent = sent_so_far * 100 / total;
243-
if (new_percent >= last_percent + 5) {
244-
last_percent += ((new_percent - last_percent) / 5) * 5;
245-
printf("Progress: %3d %%\n", last_percent);
246-
}
247224
}
225+
248226
*pCrc32 = crc;
249-
return ERR_UNK;
227+
free(request);
228+
free(reply);
229+
return err;
250230
}
251231

252232
/**
@@ -358,30 +338,37 @@ static bool sector_is_mutable(u32 sector_number)
358338

359339
static int bootloader_checksum(int offset, int length, u32 *pCrc32)
360340
{
341+
FW_GETCRC32 *request = NULL;
342+
FW_GETCRC32_REPLY *reply = NULL;
343+
int err;
344+
361345
*pCrc32 = 0;
362346

363-
FW_GETCRC32 *request = packet_alloc(FW_GETCRC32, 0);
347+
request = packet_alloc(FW_GETCRC32, 0);
364348
request->flashStart = offset;
365349
request->flashLength = length;
366350
int res = ev3_write(handle, (u8 *) request, request->packetLen + PREFIX_SIZE);
367351
if (res < 0)
368352
{
369353
errmsg = "Unable to write FW_GETCRC32.";
370-
return ERR_COMM;
354+
err = ERR_COMM;
355+
goto exit;
371356
}
372357

373-
FW_GETCRC32_REPLY *reply = malloc(sizeof(FW_GETCRC32_REPLY));
358+
reply = malloc(sizeof(FW_GETCRC32_REPLY));
374359
res = ev3_read_timeout(handle, (u8 *) reply, sizeof(FW_GETCRC32_REPLY), -1);
375360
if (res <= 0)
376361
{
377362
errmsg = "Unable to read FW_GETCRC32";
378-
return ERR_COMM;
363+
err = ERR_COMM;
364+
goto exit;
379365
}
380366

381367
// note: report loopback bug to outer code
382368
if (reply->type == VM_SYS_RQ)
383369
{
384-
return ERR_USBLOOP;
370+
err = ERR_USBLOOP;
371+
goto exit;
385372
}
386373

387374
if (reply->type != VM_OK)
@@ -391,8 +378,14 @@ static int bootloader_checksum(int offset, int length, u32 *pCrc32)
391378
print_bytes(reply, reply->packetLen + 2);
392379

393380
errmsg = "`FW_GETCRC32` was denied.";
394-
return ERR_VM;
381+
err = ERR_VM;
382+
goto exit;
395383
}
396384
*pCrc32 = reply->crc32;
397-
return ERR_UNK;
385+
err = ERR_UNK;
386+
387+
exit:
388+
free(request);
389+
free(reply);
390+
return err;
398391
}

0 commit comments

Comments
 (0)