Skip to content

Commit a227a71

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 58dfbfc commit a227a71

File tree

1 file changed

+101
-117
lines changed

1 file changed

+101
-117
lines changed

src/bl_install.c

Lines changed: 101 additions & 117 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,83 @@ 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+
int read_bytes = fread(firmware, 1, FLASH_SIZE, fp); // this may be less than FLASH_SIZE (e.g. Pybricks has a small firmware image)
55+
int sector_count = (read_bytes + FLASH_SECTOR_SIZE - 1) / FLASH_SECTOR_SIZE;
6256

63-
puts("Erasing flash... (takes 1 minute 48 seconds)"); // 108 seconds
64-
err = bootloader_erase();
65-
if (err != ERR_UNK)
66-
return err;
57+
int err = ERR_UNK;
58+
for (int sector = 0; sector < sector_count; sector++) {
59+
u32 local_crc32 = 0;
60+
u32 remote_crc32 = 0;
6761

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;
62+
printf("Programming sector %d out of %d...\n", sector, sector_count);
63+
err = bootloader_erase_and_start(sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE);
64+
if (err != ERR_UNK) {
65+
puts("Flash erase returned an error!");
66+
break;
67+
}
7568

76-
puts("Calculating flash checksum... (takes 17 seconds)");
77-
err = bootloader_checksum(FLASH_START, FLASH_SIZE, &remote_crc32); // 16.7 seconds
69+
err = bootloader_send(firmware + sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE, &local_crc32);
70+
if (err != ERR_UNK) {
71+
puts("Programming returned an error!");
72+
break;
73+
}
7874

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.");
75+
err = bootloader_checksum(sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE, &remote_crc32);
76+
if (err == ERR_USBLOOP && sector == 0)
77+
{
78+
puts("NOTE: CRC not checked, because the brick is likely plugged to a USB 3.0 port.");
79+
}
80+
else if (err == ERR_UNK) // no error occurred
81+
{
82+
if (local_crc32 != remote_crc32) {
83+
printf("Checksum failed: remote %08X != local %08X\n", remote_crc32, local_crc32);
84+
err = ERR_COMM;
85+
break;
86+
}
87+
}
88+
else // other error occurred
89+
{
90+
puts("Checksum computation returned an error!");
91+
break;
9292
}
9393
}
94-
else // other error occurred
95-
{
94+
95+
free(firmware);
96+
if (err == ERR_UNK) {
97+
puts("Flashing finished, rebooting the brick.");
98+
return bootloader_exit();
99+
} else {
100+
puts("Some error occurred, leaving the brick in the bootloader mode.");
101+
puts("To exit it manually, simply remove the EV3 battery and then put it back in.");
96102
return err;
97103
}
98-
99-
return bootloader_exit();
100104
}
101105

102-
static int bootloader_erase(void)
106+
static int bootloader_erase_and_start(int offset, int length)
103107
{
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-
}
108+
FW_START_DOWNLOAD_WITH_ERASE *request = NULL;
109+
FW_START_DOWNLOAD_WITH_ERASE_REPLY *reply = NULL;
110+
int err;
111111

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;
118-
}
119-
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;
129-
}
130-
return ERR_UNK;
131-
}
132-
133-
static int bootloader_start(int offset, int length)
134-
{
135-
FW_START_DOWNLOAD *request = packet_alloc(FW_START_DOWNLOAD, 0);
112+
request = packet_alloc(FW_START_DOWNLOAD_WITH_ERASE, 0);
136113
request->flashStart = offset;
137114
request->flashLength = length;
138115
int res = ev3_write(handle, (u8 *) request, request->packetLen + PREFIX_SIZE);
139116
if (res < 0)
140117
{
141118
errmsg = "Unable to write FW_START_DOWNLOAD.";
142-
return ERR_COMM;
119+
err = ERR_COMM;
120+
goto exit;
143121
}
144122

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);
123+
reply = malloc(sizeof(FW_START_DOWNLOAD_WITH_ERASE_REPLY));
124+
res = ev3_read_timeout(handle, (u8 *) reply, sizeof(FW_START_DOWNLOAD_WITH_ERASE_REPLY), -1);
147125
if (res <= 0)
148126
{
149127
errmsg = "Unable to read FW_START_DOWNLOAD";
150-
return ERR_COMM;
128+
err = ERR_COMM;
129+
goto exit;
151130
}
152131

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

160-
errmsg = "`FW_START_DOWNLOAD` was denied.";
161-
return ERR_VM;
139+
errmsg = "`FW_START_DOWNLOAD_WITH_ERASE_REPLY` was denied.";
140+
err = ERR_VM;
141+
goto exit;
162142
}
163-
return ERR_UNK;
143+
err = ERR_UNK;
144+
145+
exit:
146+
free(request);
147+
free(reply);
148+
return err;
164149
}
165150

166-
static int bootloader_send(FILE *fp, int length, u32* pCrc32)
151+
static int bootloader_send(u8 *buffer, int length, u32* pCrc32)
167152
{
168-
*pCrc32 = 0;
169-
170-
int max_payload = 1024 - (sizeof(FW_DOWNLOAD_DATA) - PREFIX_SIZE + 2);
153+
const int max_payload = 1024 - (sizeof(FW_DOWNLOAD_DATA) - PREFIX_SIZE + 2);
171154
FW_DOWNLOAD_DATA *request = packet_alloc(FW_DOWNLOAD_DATA, max_payload);
172155
FW_DOWNLOAD_DATA_REPLY *reply = malloc(sizeof(FW_DOWNLOAD_DATA_REPLY));
173156

174157
int total = length;
175158
int sent_so_far = 0;
176-
int file_ended = 0;
177-
int res = 0;
178-
int last_percent = 0;
159+
int err = ERR_UNK;
179160
u32 crc = 0;
180161

181-
printf("Progress: %3d %%\n", 0);
182-
183162
while (sent_so_far < total) {
184163
int remaining = total - sent_so_far;
185164
int this_block = remaining <= max_payload ? remaining : max_payload;
165+
int res;
186166

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-
167+
memcpy(request->payload, buffer + sent_so_far, this_block);
197168
crc = crc32(crc, request->payload, this_block);
198169

199170
request->packetLen = sizeof(FW_DOWNLOAD_DATA) - PREFIX_SIZE + this_block;
@@ -202,14 +173,16 @@ static int bootloader_send(FILE *fp, int length, u32* pCrc32)
202173
if (res < 0)
203174
{
204175
errmsg = "Unable to write FW_DOWNLOAD_DATA.";
205-
return ERR_COMM;
176+
err = ERR_COMM;
177+
break;
206178
}
207179

208180
res = ev3_read_timeout(handle, (u8 *) reply, sizeof(FW_DOWNLOAD_DATA_REPLY), -1);
209181
if (res <= 0)
210182
{
211183
errmsg = "Unable to read FW_DOWNLOAD_DATA";
212-
return ERR_COMM;
184+
err = ERR_COMM;
185+
break;
213186
}
214187

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

222195
errmsg = "`FW_DOWNLOAD_DATA` was denied.";
223-
return ERR_VM;
196+
err = ERR_VM;
197+
break;
224198
}
225199

226200
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-
}
233201
}
202+
234203
*pCrc32 = crc;
235-
return ERR_UNK;
204+
free(request);
205+
free(reply);
206+
return err;
236207
}
237208

238209
/**
@@ -290,30 +261,37 @@ int bootloader_crc(FILE *fp, u32 starting_sector, u32 num_sectors, bool verbose)
290261

291262
static int bootloader_checksum(int offset, int length, u32 *pCrc32)
292263
{
264+
FW_GETCRC32 *request = NULL;
265+
FW_GETCRC32_REPLY *reply = NULL;
266+
int err;
267+
293268
*pCrc32 = 0;
294269

295-
FW_GETCRC32 *request = packet_alloc(FW_GETCRC32, 0);
270+
request = packet_alloc(FW_GETCRC32, 0);
296271
request->flashStart = offset;
297272
request->flashLength = length;
298273
int res = ev3_write(handle, (u8 *) request, request->packetLen + PREFIX_SIZE);
299274
if (res < 0)
300275
{
301276
errmsg = "Unable to write FW_GETCRC32.";
302-
return ERR_COMM;
277+
err = ERR_COMM;
278+
goto exit;
303279
}
304280

305-
FW_GETCRC32_REPLY *reply = malloc(sizeof(FW_GETCRC32_REPLY));
281+
reply = malloc(sizeof(FW_GETCRC32_REPLY));
306282
res = ev3_read_timeout(handle, (u8 *) reply, sizeof(FW_GETCRC32_REPLY), -1);
307283
if (res <= 0)
308284
{
309285
errmsg = "Unable to read FW_GETCRC32";
310-
return ERR_COMM;
286+
err = ERR_COMM;
287+
goto exit;
311288
}
312289

313290
// note: report loopback bug to outer code
314291
if (reply->type == VM_SYS_RQ)
315292
{
316-
return ERR_USBLOOP;
293+
err = ERR_USBLOOP;
294+
goto exit;
317295
}
318296

319297
if (reply->type != VM_OK)
@@ -323,8 +301,14 @@ static int bootloader_checksum(int offset, int length, u32 *pCrc32)
323301
print_bytes(reply, reply->packetLen + 2);
324302

325303
errmsg = "`FW_GETCRC32` was denied.";
326-
return ERR_VM;
304+
err = ERR_VM;
305+
goto exit;
327306
}
328307
*pCrc32 = reply->crc32;
329-
return ERR_UNK;
308+
err = ERR_UNK;
309+
310+
exit:
311+
free(request);
312+
free(reply);
313+
return err;
330314
}

0 commit comments

Comments
 (0)