Skip to content

Commit 032cb28

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 3d0ee68 commit 032cb28

File tree

1 file changed

+113
-116
lines changed

1 file changed

+113
-116
lines changed

src/bl_install.c

Lines changed: 113 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
@@ -56,98 +50,96 @@ static int bootloader_checksum(int offset, int length, u32 *pCrc32);
5650
*/
5751
int bootloader_install(FILE *fp)
5852
{
59-
int err = ERR_UNK;
60-
u32 local_crc32 = 0;
61-
u32 remote_crc32 = 0;
53+
u8 *firmware = calloc(FLASH_SIZE, 1);
54+
if (!firmware) {
55+
errmsg = "out of memory";
56+
return ERR_NOMEM;
57+
}
6258

63-
puts("Erasing flash... (takes 1 minute 48 seconds)"); // 108 seconds
64-
err = bootloader_erase();
65-
if (err != ERR_UNK)
66-
return err;
59+
int read_bytes = fread(firmware, 1, FLASH_SIZE, fp); // this may be less than FLASH_SIZE (e.g. Pybricks has a small firmware image)
60+
if (ferror(fp)) {
61+
errmsg = "Reading of firmware file failed";
62+
free(firmware);
63+
return ERR_IO;
64+
}
6765

68-
puts("Downloading...");
69-
err = bootloader_start(FLASH_START, FLASH_SIZE); // extremely short
70-
if (err != ERR_UNK)
71-
return err;
72-
err = bootloader_send(fp, FLASH_SIZE, &local_crc32); // variable time
73-
if (err != ERR_UNK)
74-
return err;
66+
int sector_count = (read_bytes + FLASH_SECTOR_SIZE - 1) / FLASH_SECTOR_SIZE;
7567

76-
puts("Calculating flash checksum... (takes 17 seconds)");
77-
err = bootloader_checksum(FLASH_START, FLASH_SIZE, &remote_crc32); // 16.7 seconds
68+
printf("Programming %d EV3 flash sectors...\n", sector_count);
7869

79-
// handle usb 3.0 bug
80-
if (err == ERR_USBLOOP)
81-
{
82-
puts("Checksum not checked, assuming update was OK. Rebooting.");
83-
}
84-
else if (err == ERR_UNK) // no error occurred
85-
{
86-
if (local_crc32 != remote_crc32) {
87-
fprintf(stderr, "error: checksums do not match: remote %08X != local %08X\n",
88-
remote_crc32, local_crc32);
89-
return ERR_IO;
90-
} else {
91-
puts("Success! Local and remote checksums match. Rebooting.");
92-
}
93-
}
94-
else // other error occurred
95-
{
96-
return err;
97-
}
70+
int err = ERR_UNK;
71+
for (int sector = 0; sector < sector_count; sector++) {
72+
u32 local_crc32 = 0;
73+
u32 remote_crc32 = 0;
9874

99-
return bootloader_exit();
100-
}
75+
printf("- Sector %3d (%2d %%)\n", sector, 100*sector/sector_count);
76+
err = bootloader_erase_and_start(sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE);
77+
if (err != ERR_UNK) {
78+
puts("Flash erase returned an error!");
79+
break;
80+
}
10181

102-
static int bootloader_erase(void)
103-
{
104-
FW_ERASEFLASH *request = packet_alloc(FW_ERASEFLASH, 0);
105-
int res = ev3_write(handle, (u8 *) request, request->packetLen + PREFIX_SIZE);
106-
if (res < 0)
107-
{
108-
errmsg = "Unable to write FW_ERASEFLASH.";
109-
return ERR_COMM;
110-
}
82+
err = bootloader_send(firmware + sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE, &local_crc32);
83+
if (err != ERR_UNK) {
84+
puts("Programming returned an error!");
85+
break;
86+
}
11187

112-
FW_ERASEFLASH_REPLY *reply = malloc(sizeof(FW_ERASEFLASH_REPLY));
113-
res = ev3_read_timeout(handle, (u8 *) reply, sizeof(FW_ERASEFLASH_REPLY), -1);
114-
if (res <= 0)
115-
{
116-
errmsg = "Unable to read FW_ERASEFLASH";
117-
return ERR_COMM;
88+
err = bootloader_checksum(sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE, &remote_crc32);
89+
if (err == ERR_USBLOOP && sector == 0)
90+
{
91+
puts("NOTE: CRC not checked, because the brick is likely plugged to a USB 3.0 port.");
92+
}
93+
else if (err == ERR_UNK) // no error occurred
94+
{
95+
if (local_crc32 != remote_crc32) {
96+
printf("Checksum does not match: remote %08X != local %08X\n", remote_crc32, local_crc32);
97+
err = ERR_COMM;
98+
break;
99+
}
100+
}
101+
else // other error occurred
102+
{
103+
puts("Checksum computation returned an error!");
104+
break;
105+
}
118106
}
119107

120-
// note: accept looped-back packets (usb 3.0 bug; reply not required here)
121-
if (reply->type != VM_OK && reply->type != VM_SYS_RQ)
122-
{
123-
errno = reply->ret;
124-
fputs("Operation failed.\nlast_reply=", stderr);
125-
print_bytes(reply, reply->packetLen + 2);
126-
127-
errmsg = "`FW_ERASEFLASH` was denied.";
128-
return ERR_VM;
108+
free(firmware);
109+
if (err == ERR_UNK) {
110+
puts("Flashing finished, rebooting the brick.");
111+
return bootloader_exit();
112+
} else {
113+
puts("Some error occurred, leaving the brick in the bootloader mode.");
114+
puts("To exit it manually, simply remove the EV3 battery and then put it back in.");
115+
return err;
129116
}
130-
return ERR_UNK;
131117
}
132118

133-
static int bootloader_start(int offset, int length)
119+
static int bootloader_erase_and_start(int offset, int length)
134120
{
135-
FW_START_DOWNLOAD *request = packet_alloc(FW_START_DOWNLOAD, 0);
121+
FW_START_DOWNLOAD_WITH_ERASE *request = NULL;
122+
FW_START_DOWNLOAD_WITH_ERASE_REPLY *reply = NULL;
123+
int err;
124+
125+
request = packet_alloc(FW_START_DOWNLOAD_WITH_ERASE, 0);
136126
request->flashStart = offset;
137127
request->flashLength = length;
138128
int res = ev3_write(handle, (u8 *) request, request->packetLen + PREFIX_SIZE);
139129
if (res < 0)
140130
{
141131
errmsg = "Unable to write FW_START_DOWNLOAD.";
142-
return ERR_COMM;
132+
err = ERR_COMM;
133+
goto exit;
143134
}
144135

145-
FW_START_DOWNLOAD_REPLY *reply = malloc(sizeof(FW_START_DOWNLOAD_REPLY));
146-
res = ev3_read_timeout(handle, (u8 *) reply, sizeof(FW_START_DOWNLOAD_REPLY), -1);
136+
reply = malloc(sizeof(FW_START_DOWNLOAD_WITH_ERASE_REPLY));
137+
res = ev3_read_timeout(handle, (u8 *) reply, sizeof(FW_START_DOWNLOAD_WITH_ERASE_REPLY), -1);
147138
if (res <= 0)
148139
{
149140
errmsg = "Unable to read FW_START_DOWNLOAD";
150-
return ERR_COMM;
141+
err = ERR_COMM;
142+
goto exit;
151143
}
152144

153145
// note: accept looped-back packets (usb 3.0 bug; reply not required here)
@@ -157,43 +149,35 @@ static int bootloader_start(int offset, int length)
157149
fputs("Operation failed.\nlast_reply=", stderr);
158150
print_bytes(reply, reply->packetLen + 2);
159151

160-
errmsg = "`FW_START_DOWNLOAD` was denied.";
161-
return ERR_VM;
152+
errmsg = "`FW_START_DOWNLOAD_WITH_ERASE_REPLY` was denied.";
153+
err = ERR_VM;
154+
goto exit;
162155
}
163-
return ERR_UNK;
156+
err = ERR_UNK;
157+
158+
exit:
159+
free(request);
160+
free(reply);
161+
return err;
164162
}
165163

166-
static int bootloader_send(FILE *fp, int length, u32* pCrc32)
164+
static int bootloader_send(u8 *buffer, int length, u32* pCrc32)
167165
{
168-
*pCrc32 = 0;
169-
170-
int max_payload = 1024 - (sizeof(FW_DOWNLOAD_DATA) - PREFIX_SIZE + 2);
166+
const int max_payload = 1024 - (sizeof(FW_DOWNLOAD_DATA) - PREFIX_SIZE + 2);
171167
FW_DOWNLOAD_DATA *request = packet_alloc(FW_DOWNLOAD_DATA, max_payload);
172168
FW_DOWNLOAD_DATA_REPLY *reply = malloc(sizeof(FW_DOWNLOAD_DATA_REPLY));
173169

174170
int total = length;
175171
int sent_so_far = 0;
176-
int file_ended = 0;
177-
int res = 0;
178-
int last_percent = 0;
172+
int err = ERR_UNK;
179173
u32 crc = 0;
180174

181-
printf("Progress: %3d %%\n", 0);
182-
183175
while (sent_so_far < total) {
184176
int remaining = total - sent_so_far;
185177
int this_block = remaining <= max_payload ? remaining : max_payload;
178+
int res;
186179

187-
if (!file_ended) {
188-
int real = fread(request->payload, 1, this_block, fp);
189-
if (real < this_block) {
190-
file_ended = 1;
191-
memset(request->payload + real, 0, this_block - real);
192-
}
193-
} else {
194-
memset(request->payload, 0, this_block);
195-
}
196-
180+
memcpy(request->payload, buffer + sent_so_far, this_block);
197181
crc = crc32(crc, request->payload, this_block);
198182

199183
request->packetLen = sizeof(FW_DOWNLOAD_DATA) - PREFIX_SIZE + this_block;
@@ -202,14 +186,16 @@ static int bootloader_send(FILE *fp, int length, u32* pCrc32)
202186
if (res < 0)
203187
{
204188
errmsg = "Unable to write FW_DOWNLOAD_DATA.";
205-
return ERR_COMM;
189+
err = ERR_COMM;
190+
break;
206191
}
207192

208193
res = ev3_read_timeout(handle, (u8 *) reply, sizeof(FW_DOWNLOAD_DATA_REPLY), -1);
209194
if (res <= 0)
210195
{
211196
errmsg = "Unable to read FW_DOWNLOAD_DATA";
212-
return ERR_COMM;
197+
err = ERR_COMM;
198+
break;
213199
}
214200

215201
// note: accept looped-back packets (usb 3.0 bug; reply not required here)
@@ -220,19 +206,17 @@ static int bootloader_send(FILE *fp, int length, u32* pCrc32)
220206
print_bytes(reply, reply->packetLen + 2);
221207

222208
errmsg = "`FW_DOWNLOAD_DATA` was denied.";
223-
return ERR_VM;
209+
err = ERR_VM;
210+
break;
224211
}
225212

226213
sent_so_far += this_block;
227-
228-
int new_percent = sent_so_far * 100 / total;
229-
if (new_percent >= last_percent + 5) {
230-
last_percent += ((new_percent - last_percent) / 5) * 5;
231-
printf("Progress: %3d %%\n", last_percent);
232-
}
233214
}
215+
234216
*pCrc32 = crc;
235-
return ERR_UNK;
217+
free(request);
218+
free(reply);
219+
return err;
236220
}
237221

238222
/**
@@ -310,30 +294,37 @@ int bootloader_crc(FILE *fp, u32 starting_sector, u32 num_sectors, bool verbose)
310294

311295
static int bootloader_checksum(int offset, int length, u32 *pCrc32)
312296
{
297+
FW_GETCRC32 *request = NULL;
298+
FW_GETCRC32_REPLY *reply = NULL;
299+
int err;
300+
313301
*pCrc32 = 0;
314302

315-
FW_GETCRC32 *request = packet_alloc(FW_GETCRC32, 0);
303+
request = packet_alloc(FW_GETCRC32, 0);
316304
request->flashStart = offset;
317305
request->flashLength = length;
318306
int res = ev3_write(handle, (u8 *) request, request->packetLen + PREFIX_SIZE);
319307
if (res < 0)
320308
{
321309
errmsg = "Unable to write FW_GETCRC32.";
322-
return ERR_COMM;
310+
err = ERR_COMM;
311+
goto exit;
323312
}
324313

325-
FW_GETCRC32_REPLY *reply = malloc(sizeof(FW_GETCRC32_REPLY));
314+
reply = malloc(sizeof(FW_GETCRC32_REPLY));
326315
res = ev3_read_timeout(handle, (u8 *) reply, sizeof(FW_GETCRC32_REPLY), -1);
327316
if (res <= 0)
328317
{
329318
errmsg = "Unable to read FW_GETCRC32";
330-
return ERR_COMM;
319+
err = ERR_COMM;
320+
goto exit;
331321
}
332322

333323
// note: report loopback bug to outer code
334324
if (reply->type == VM_SYS_RQ)
335325
{
336-
return ERR_USBLOOP;
326+
err = ERR_USBLOOP;
327+
goto exit;
337328
}
338329

339330
if (reply->type != VM_OK)
@@ -343,8 +334,14 @@ static int bootloader_checksum(int offset, int length, u32 *pCrc32)
343334
print_bytes(reply, reply->packetLen + 2);
344335

345336
errmsg = "`FW_GETCRC32` was denied.";
346-
return ERR_VM;
337+
err = ERR_VM;
338+
goto exit;
347339
}
348340
*pCrc32 = reply->crc32;
349-
return ERR_UNK;
341+
err = ERR_UNK;
342+
343+
exit:
344+
free(request);
345+
free(reply);
346+
return err;
350347
}

0 commit comments

Comments
 (0)