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 */
5751int 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
311295static 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