Skip to content

Commit f1e6c7e

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 ff9d1c9 commit f1e6c7e

File tree

1 file changed

+103
-117
lines changed

1 file changed

+103
-117
lines changed

src/bl_install.c

Lines changed: 103 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,85 @@ 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+
printf("Programming %d EV3 flash sectors...\n", sector_count);
6758

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;
75-
76-
puts("Calculating flash checksum... (takes 17 seconds)");
77-
err = bootloader_checksum(FLASH_START, FLASH_SIZE, &remote_crc32); // 16.7 seconds
59+
int err = ERR_UNK;
60+
for (int sector = 0; sector < sector_count; sector++) {
61+
u32 local_crc32 = 0;
62+
u32 remote_crc32 = 0;
7863

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.");
64+
printf("- sector %d of %d (%d %%)\n", sector, sector_count, 100*sector/sector_count);
65+
err = bootloader_erase_and_start(sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE);
66+
if (err != ERR_UNK) {
67+
puts("Flash erase returned an error!");
68+
break;
9269
}
93-
}
94-
else // other error occurred
95-
{
96-
return err;
97-
}
98-
99-
return bootloader_exit();
100-
}
10170

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-
}
71+
err = bootloader_send(firmware + sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE, &local_crc32);
72+
if (err != ERR_UNK) {
73+
puts("Programming returned an error!");
74+
break;
75+
}
11176

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;
77+
err = bootloader_checksum(sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE, &remote_crc32);
78+
if (err == ERR_USBLOOP && sector == 0)
79+
{
80+
puts("NOTE: CRC not checked, because the brick is likely plugged to a USB 3.0 port.");
81+
}
82+
else if (err == ERR_UNK) // no error occurred
83+
{
84+
if (local_crc32 != remote_crc32) {
85+
printf("Checksum does not match: remote %08X != local %08X\n", remote_crc32, local_crc32);
86+
err = ERR_COMM;
87+
break;
88+
}
89+
}
90+
else // other error occurred
91+
{
92+
puts("Checksum computation returned an error!");
93+
break;
94+
}
11895
}
11996

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;
97+
free(firmware);
98+
if (err == ERR_UNK) {
99+
puts("Flashing finished, rebooting the brick.");
100+
return bootloader_exit();
101+
} else {
102+
puts("Some error occurred, leaving the brick in the bootloader mode.");
103+
puts("To exit it manually, simply remove the EV3 battery and then put it back in.");
104+
return err;
129105
}
130-
return ERR_UNK;
131106
}
132107

133-
static int bootloader_start(int offset, int length)
108+
static int bootloader_erase_and_start(int offset, int length)
134109
{
135-
FW_START_DOWNLOAD *request = packet_alloc(FW_START_DOWNLOAD, 0);
110+
FW_START_DOWNLOAD_WITH_ERASE *request = NULL;
111+
FW_START_DOWNLOAD_WITH_ERASE_REPLY *reply = NULL;
112+
int err;
113+
114+
request = packet_alloc(FW_START_DOWNLOAD_WITH_ERASE, 0);
136115
request->flashStart = offset;
137116
request->flashLength = length;
138117
int res = ev3_write(handle, (u8 *) request, request->packetLen + PREFIX_SIZE);
139118
if (res < 0)
140119
{
141120
errmsg = "Unable to write FW_START_DOWNLOAD.";
142-
return ERR_COMM;
121+
err = ERR_COMM;
122+
goto exit;
143123
}
144124

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);
125+
reply = malloc(sizeof(FW_START_DOWNLOAD_WITH_ERASE_REPLY));
126+
res = ev3_read_timeout(handle, (u8 *) reply, sizeof(FW_START_DOWNLOAD_WITH_ERASE_REPLY), -1);
147127
if (res <= 0)
148128
{
149129
errmsg = "Unable to read FW_START_DOWNLOAD";
150-
return ERR_COMM;
130+
err = ERR_COMM;
131+
goto exit;
151132
}
152133

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

160-
errmsg = "`FW_START_DOWNLOAD` was denied.";
161-
return ERR_VM;
141+
errmsg = "`FW_START_DOWNLOAD_WITH_ERASE_REPLY` was denied.";
142+
err = ERR_VM;
143+
goto exit;
162144
}
163-
return ERR_UNK;
145+
err = ERR_UNK;
146+
147+
exit:
148+
free(request);
149+
free(reply);
150+
return err;
164151
}
165152

166-
static int bootloader_send(FILE *fp, int length, u32* pCrc32)
153+
static int bootloader_send(u8 *buffer, int length, u32* pCrc32)
167154
{
168-
*pCrc32 = 0;
169-
170-
int max_payload = 1024 - (sizeof(FW_DOWNLOAD_DATA) - PREFIX_SIZE + 2);
155+
const int max_payload = 1024 - (sizeof(FW_DOWNLOAD_DATA) - PREFIX_SIZE + 2);
171156
FW_DOWNLOAD_DATA *request = packet_alloc(FW_DOWNLOAD_DATA, max_payload);
172157
FW_DOWNLOAD_DATA_REPLY *reply = malloc(sizeof(FW_DOWNLOAD_DATA_REPLY));
173158

174159
int total = length;
175160
int sent_so_far = 0;
176-
int file_ended = 0;
177-
int res = 0;
178-
int last_percent = 0;
161+
int err = ERR_UNK;
179162
u32 crc = 0;
180163

181-
printf("Progress: %3d %%\n", 0);
182-
183164
while (sent_so_far < total) {
184165
int remaining = total - sent_so_far;
185166
int this_block = remaining <= max_payload ? remaining : max_payload;
167+
int res;
186168

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-
169+
memcpy(request->payload, buffer + sent_so_far, this_block);
197170
crc = crc32(crc, request->payload, this_block);
198171

199172
request->packetLen = sizeof(FW_DOWNLOAD_DATA) - PREFIX_SIZE + this_block;
@@ -202,14 +175,16 @@ static int bootloader_send(FILE *fp, int length, u32* pCrc32)
202175
if (res < 0)
203176
{
204177
errmsg = "Unable to write FW_DOWNLOAD_DATA.";
205-
return ERR_COMM;
178+
err = ERR_COMM;
179+
break;
206180
}
207181

208182
res = ev3_read_timeout(handle, (u8 *) reply, sizeof(FW_DOWNLOAD_DATA_REPLY), -1);
209183
if (res <= 0)
210184
{
211185
errmsg = "Unable to read FW_DOWNLOAD_DATA";
212-
return ERR_COMM;
186+
err = ERR_COMM;
187+
break;
213188
}
214189

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

222197
errmsg = "`FW_DOWNLOAD_DATA` was denied.";
223-
return ERR_VM;
198+
err = ERR_VM;
199+
break;
224200
}
225201

226202
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-
}
233203
}
204+
234205
*pCrc32 = crc;
235-
return ERR_UNK;
206+
free(request);
207+
free(reply);
208+
return err;
236209
}
237210

238211
/**
@@ -300,30 +273,37 @@ int bootloader_crc(FILE *fp, u32 starting_sector, u32 num_sectors, bool verbose)
300273

301274
static int bootloader_checksum(int offset, int length, u32 *pCrc32)
302275
{
276+
FW_GETCRC32 *request = NULL;
277+
FW_GETCRC32_REPLY *reply = NULL;
278+
int err;
279+
303280
*pCrc32 = 0;
304281

305-
FW_GETCRC32 *request = packet_alloc(FW_GETCRC32, 0);
282+
request = packet_alloc(FW_GETCRC32, 0);
306283
request->flashStart = offset;
307284
request->flashLength = length;
308285
int res = ev3_write(handle, (u8 *) request, request->packetLen + PREFIX_SIZE);
309286
if (res < 0)
310287
{
311288
errmsg = "Unable to write FW_GETCRC32.";
312-
return ERR_COMM;
289+
err = ERR_COMM;
290+
goto exit;
313291
}
314292

315-
FW_GETCRC32_REPLY *reply = malloc(sizeof(FW_GETCRC32_REPLY));
293+
reply = malloc(sizeof(FW_GETCRC32_REPLY));
316294
res = ev3_read_timeout(handle, (u8 *) reply, sizeof(FW_GETCRC32_REPLY), -1);
317295
if (res <= 0)
318296
{
319297
errmsg = "Unable to read FW_GETCRC32";
320-
return ERR_COMM;
298+
err = ERR_COMM;
299+
goto exit;
321300
}
322301

323302
// note: report loopback bug to outer code
324303
if (reply->type == VM_SYS_RQ)
325304
{
326-
return ERR_USBLOOP;
305+
err = ERR_USBLOOP;
306+
goto exit;
327307
}
328308

329309
if (reply->type != VM_OK)
@@ -333,8 +313,14 @@ static int bootloader_checksum(int offset, int length, u32 *pCrc32)
333313
print_bytes(reply, reply->packetLen + 2);
334314

335315
errmsg = "`FW_GETCRC32` was denied.";
336-
return ERR_VM;
316+
err = ERR_VM;
317+
goto exit;
337318
}
338319
*pCrc32 = reply->crc32;
339-
return ERR_UNK;
320+
err = ERR_UNK;
321+
322+
exit:
323+
free(request);
324+
free(reply);
325+
return err;
340326
}

0 commit comments

Comments
 (0)