|
1 /* |
|
2 LUFA Library |
|
3 Copyright (C) Dean Camera, 2010. |
|
4 |
|
5 dean [at] fourwalledcubicle [dot] com |
|
6 www.fourwalledcubicle.com |
|
7 */ |
|
8 |
|
9 /* |
|
10 Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) |
|
11 |
|
12 Permission to use, copy, modify, distribute, and sell this |
|
13 software and its documentation for any purpose is hereby granted |
|
14 without fee, provided that the above copyright notice appear in |
|
15 all copies and that both that the copyright notice and this |
|
16 permission notice and warranty disclaimer appear in supporting |
|
17 documentation, and that the name of the author not be used in |
|
18 advertising or publicity pertaining to distribution of the |
|
19 software without specific, written prior permission. |
|
20 |
|
21 The author disclaim all warranties with regard to this |
|
22 software, including all implied warranties of merchantability |
|
23 and fitness. In no event shall the author be liable for any |
|
24 special, indirect or consequential damages or any damages |
|
25 whatsoever resulting from loss of use, data or profits, whether |
|
26 in an action of contract, negligence or other tortious action, |
|
27 arising out of or in connection with the use or performance of |
|
28 this software. |
|
29 */ |
|
30 |
|
31 /** \file |
|
32 * |
|
33 * Main source file for the DFU class bootloader. This file contains the complete bootloader logic. |
|
34 */ |
|
35 |
|
36 #define INCLUDE_FROM_BOOTLOADER_C |
|
37 #include "Arduino-usbdfu.h" |
|
38 |
|
39 /** Flag to indicate if the bootloader should be running, or should exit and allow the application code to run |
|
40 * via a soft reset. When cleared, the bootloader will abort, the USB interface will shut down and the application |
|
41 * jumped to via an indirect jump to location 0x0000 (or other location specified by the host). |
|
42 */ |
|
43 bool RunBootloader = true; |
|
44 |
|
45 /** Flag to indicate if the bootloader is waiting to exit. When the host requests the bootloader to exit and |
|
46 * jump to the application address it specifies, it sends two sequential commands which must be properly |
|
47 * acknowledged. Upon reception of the first the RunBootloader flag is cleared and the WaitForExit flag is set, |
|
48 * causing the bootloader to wait for the final exit command before shutting down. |
|
49 */ |
|
50 bool WaitForExit = false; |
|
51 |
|
52 /** Current DFU state machine state, one of the values in the DFU_State_t enum. */ |
|
53 uint8_t DFU_State = dfuIDLE; |
|
54 |
|
55 /** Status code of the last executed DFU command. This is set to one of the values in the DFU_Status_t enum after |
|
56 * each operation, and returned to the host when a Get Status DFU request is issued. |
|
57 */ |
|
58 uint8_t DFU_Status = OK; |
|
59 |
|
60 /** Data containing the DFU command sent from the host. */ |
|
61 DFU_Command_t SentCommand; |
|
62 |
|
63 /** Response to the last issued Read Data DFU command. Unlike other DFU commands, the read command |
|
64 * requires a single byte response from the bootloader containing the read data when the next DFU_UPLOAD command |
|
65 * is issued by the host. |
|
66 */ |
|
67 uint8_t ResponseByte; |
|
68 |
|
69 /** Pointer to the start of the user application. By default this is 0x0000 (the reset vector), however the host |
|
70 * may specify an alternate address when issuing the application soft-start command. |
|
71 */ |
|
72 AppPtr_t AppStartPtr = (AppPtr_t)0x0000; |
|
73 |
|
74 /** 64-bit flash page number. This is concatenated with the current 16-bit address on USB AVRs containing more than |
|
75 * 64KB of flash memory. |
|
76 */ |
|
77 uint8_t Flash64KBPage = 0; |
|
78 |
|
79 /** Memory start address, indicating the current address in the memory being addressed (either FLASH or EEPROM |
|
80 * depending on the issued command from the host). |
|
81 */ |
|
82 uint16_t StartAddr = 0x0000; |
|
83 |
|
84 /** Memory end address, indicating the end address to read to/write from in the memory being addressed (either FLASH |
|
85 * of EEPROM depending on the issued command from the host). |
|
86 */ |
|
87 uint16_t EndAddr = 0x0000; |
|
88 |
|
89 |
|
90 /** Pulse generation counters to keep track of the number of milliseconds remaining for each pulse type */ |
|
91 volatile struct |
|
92 { |
|
93 uint8_t TxLEDPulse; /**< Milliseconds remaining for data Tx LED pulse */ |
|
94 uint8_t RxLEDPulse; /**< Milliseconds remaining for data Rx LED pulse */ |
|
95 uint8_t PingPongLEDPulse; /**< Milliseconds remaining for enumeration Tx/Rx ping-pong LED pulse */ |
|
96 } PulseMSRemaining; |
|
97 |
|
98 /** Main program entry point. This routine configures the hardware required by the bootloader, then continuously |
|
99 * runs the bootloader processing routine until instructed to soft-exit, or hard-reset via the watchdog to start |
|
100 * the loaded application code. |
|
101 */ |
|
102 int main(void) |
|
103 { |
|
104 /* Configure hardware required by the bootloader */ |
|
105 SetupHardware(); |
|
106 |
|
107 /* Enable global interrupts so that the USB stack can function */ |
|
108 sei(); |
|
109 |
|
110 /* Run the USB management task while the bootloader is supposed to be running */ |
|
111 while (RunBootloader || WaitForExit) |
|
112 USB_USBTask(); |
|
113 |
|
114 /* Reset configured hardware back to their original states for the user application */ |
|
115 ResetHardware(); |
|
116 |
|
117 /* Start the user application */ |
|
118 AppStartPtr(); |
|
119 } |
|
120 |
|
121 /** Configures all hardware required for the bootloader. */ |
|
122 void SetupHardware(void) |
|
123 { |
|
124 /* Disable watchdog if enabled by bootloader/fuses */ |
|
125 MCUSR &= ~(1 << WDRF); |
|
126 wdt_disable(); |
|
127 |
|
128 /* Disable clock division */ |
|
129 // clock_prescale_set(clock_div_1); |
|
130 |
|
131 /* Relocate the interrupt vector table to the bootloader section */ |
|
132 MCUCR = (1 << IVCE); |
|
133 MCUCR = (1 << IVSEL); |
|
134 |
|
135 LEDs_Init(); |
|
136 |
|
137 /* Initialize the USB subsystem */ |
|
138 USB_Init(); |
|
139 } |
|
140 |
|
141 /** Resets all configured hardware required for the bootloader back to their original states. */ |
|
142 void ResetHardware(void) |
|
143 { |
|
144 /* Shut down the USB subsystem */ |
|
145 USB_ShutDown(); |
|
146 |
|
147 /* Relocate the interrupt vector table back to the application section */ |
|
148 MCUCR = (1 << IVCE); |
|
149 MCUCR = 0; |
|
150 } |
|
151 |
|
152 /** Event handler for the USB_UnhandledControlRequest event. This is used to catch standard and class specific |
|
153 * control requests that are not handled internally by the USB library (including the DFU commands, which are |
|
154 * all issued via the control endpoint), so that they can be handled appropriately for the application. |
|
155 */ |
|
156 void EVENT_USB_Device_UnhandledControlRequest(void) |
|
157 { |
|
158 /* Get the size of the command and data from the wLength value */ |
|
159 SentCommand.DataSize = USB_ControlRequest.wLength; |
|
160 |
|
161 /* Turn off TX LED(s) once the TX pulse period has elapsed */ |
|
162 if (PulseMSRemaining.TxLEDPulse && !(--PulseMSRemaining.TxLEDPulse)) |
|
163 LEDs_TurnOffLEDs(LEDMASK_TX); |
|
164 |
|
165 /* Turn off RX LED(s) once the RX pulse period has elapsed */ |
|
166 if (PulseMSRemaining.RxLEDPulse && !(--PulseMSRemaining.RxLEDPulse)) |
|
167 LEDs_TurnOffLEDs(LEDMASK_RX); |
|
168 |
|
169 switch (USB_ControlRequest.bRequest) |
|
170 { |
|
171 case DFU_DNLOAD: |
|
172 LEDs_TurnOnLEDs(LEDMASK_RX); |
|
173 PulseMSRemaining.RxLEDPulse = TX_RX_LED_PULSE_MS; |
|
174 |
|
175 Endpoint_ClearSETUP(); |
|
176 |
|
177 /* Check if bootloader is waiting to terminate */ |
|
178 if (WaitForExit) |
|
179 { |
|
180 /* Bootloader is terminating - process last received command */ |
|
181 ProcessBootloaderCommand(); |
|
182 |
|
183 /* Turn off TX/RX status LEDs so that they're not left on when application starts */ |
|
184 LEDs_TurnOffLEDs(LEDMASK_TX); |
|
185 LEDs_TurnOffLEDs(LEDMASK_RX); |
|
186 |
|
187 /* Indicate that the last command has now been processed - free to exit bootloader */ |
|
188 WaitForExit = false; |
|
189 } |
|
190 |
|
191 /* If the request has a data stage, load it into the command struct */ |
|
192 if (SentCommand.DataSize) |
|
193 { |
|
194 while (!(Endpoint_IsOUTReceived())) |
|
195 { |
|
196 if (USB_DeviceState == DEVICE_STATE_Unattached) |
|
197 return; |
|
198 } |
|
199 |
|
200 /* First byte of the data stage is the DNLOAD request's command */ |
|
201 SentCommand.Command = Endpoint_Read_Byte(); |
|
202 |
|
203 /* One byte of the data stage is the command, so subtract it from the total data bytes */ |
|
204 SentCommand.DataSize--; |
|
205 |
|
206 /* Load in the rest of the data stage as command parameters */ |
|
207 for (uint8_t DataByte = 0; (DataByte < sizeof(SentCommand.Data)) && |
|
208 Endpoint_BytesInEndpoint(); DataByte++) |
|
209 { |
|
210 SentCommand.Data[DataByte] = Endpoint_Read_Byte(); |
|
211 SentCommand.DataSize--; |
|
212 } |
|
213 |
|
214 /* Process the command */ |
|
215 ProcessBootloaderCommand(); |
|
216 } |
|
217 |
|
218 /* Check if currently downloading firmware */ |
|
219 if (DFU_State == dfuDNLOAD_IDLE) |
|
220 { |
|
221 if (!(SentCommand.DataSize)) |
|
222 { |
|
223 DFU_State = dfuIDLE; |
|
224 } |
|
225 else |
|
226 { |
|
227 /* Throw away the filler bytes before the start of the firmware */ |
|
228 DiscardFillerBytes(DFU_FILLER_BYTES_SIZE); |
|
229 |
|
230 /* Throw away the packet alignment filler bytes before the start of the firmware */ |
|
231 DiscardFillerBytes(StartAddr % FIXED_CONTROL_ENDPOINT_SIZE); |
|
232 |
|
233 /* Calculate the number of bytes remaining to be written */ |
|
234 uint16_t BytesRemaining = ((EndAddr - StartAddr) + 1); |
|
235 |
|
236 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00)) // Write flash |
|
237 { |
|
238 /* Calculate the number of words to be written from the number of bytes to be written */ |
|
239 uint16_t WordsRemaining = (BytesRemaining >> 1); |
|
240 |
|
241 union |
|
242 { |
|
243 uint16_t Words[2]; |
|
244 uint32_t Long; |
|
245 } CurrFlashAddress = {.Words = {StartAddr, Flash64KBPage}}; |
|
246 |
|
247 uint32_t CurrFlashPageStartAddress = CurrFlashAddress.Long; |
|
248 uint8_t WordsInFlashPage = 0; |
|
249 |
|
250 while (WordsRemaining--) |
|
251 { |
|
252 /* Check if endpoint is empty - if so clear it and wait until ready for next packet */ |
|
253 if (!(Endpoint_BytesInEndpoint())) |
|
254 { |
|
255 Endpoint_ClearOUT(); |
|
256 |
|
257 while (!(Endpoint_IsOUTReceived())) |
|
258 { |
|
259 if (USB_DeviceState == DEVICE_STATE_Unattached) |
|
260 return; |
|
261 } |
|
262 } |
|
263 |
|
264 /* Write the next word into the current flash page */ |
|
265 boot_page_fill(CurrFlashAddress.Long, Endpoint_Read_Word_LE()); |
|
266 |
|
267 /* Adjust counters */ |
|
268 WordsInFlashPage += 1; |
|
269 CurrFlashAddress.Long += 2; |
|
270 |
|
271 /* See if an entire page has been written to the flash page buffer */ |
|
272 if ((WordsInFlashPage == (SPM_PAGESIZE >> 1)) || !(WordsRemaining)) |
|
273 { |
|
274 /* Commit the flash page to memory */ |
|
275 boot_page_write(CurrFlashPageStartAddress); |
|
276 boot_spm_busy_wait(); |
|
277 |
|
278 /* Check if programming incomplete */ |
|
279 if (WordsRemaining) |
|
280 { |
|
281 CurrFlashPageStartAddress = CurrFlashAddress.Long; |
|
282 WordsInFlashPage = 0; |
|
283 |
|
284 /* Erase next page's temp buffer */ |
|
285 boot_page_erase(CurrFlashAddress.Long); |
|
286 boot_spm_busy_wait(); |
|
287 } |
|
288 } |
|
289 } |
|
290 |
|
291 /* Once programming complete, start address equals the end address */ |
|
292 StartAddr = EndAddr; |
|
293 |
|
294 /* Re-enable the RWW section of flash */ |
|
295 boot_rww_enable(); |
|
296 } |
|
297 else // Write EEPROM |
|
298 { |
|
299 while (BytesRemaining--) |
|
300 { |
|
301 /* Check if endpoint is empty - if so clear it and wait until ready for next packet */ |
|
302 if (!(Endpoint_BytesInEndpoint())) |
|
303 { |
|
304 Endpoint_ClearOUT(); |
|
305 |
|
306 while (!(Endpoint_IsOUTReceived())) |
|
307 { |
|
308 if (USB_DeviceState == DEVICE_STATE_Unattached) |
|
309 return; |
|
310 } |
|
311 } |
|
312 |
|
313 /* Read the byte from the USB interface and write to to the EEPROM */ |
|
314 eeprom_write_byte((uint8_t*)StartAddr, Endpoint_Read_Byte()); |
|
315 |
|
316 /* Adjust counters */ |
|
317 StartAddr++; |
|
318 } |
|
319 } |
|
320 |
|
321 /* Throw away the currently unused DFU file suffix */ |
|
322 DiscardFillerBytes(DFU_FILE_SUFFIX_SIZE); |
|
323 } |
|
324 } |
|
325 |
|
326 Endpoint_ClearOUT(); |
|
327 |
|
328 Endpoint_ClearStatusStage(); |
|
329 |
|
330 break; |
|
331 case DFU_UPLOAD: |
|
332 Endpoint_ClearSETUP(); |
|
333 |
|
334 LEDs_TurnOnLEDs(LEDMASK_TX); |
|
335 PulseMSRemaining.TxLEDPulse = TX_RX_LED_PULSE_MS; |
|
336 |
|
337 while (!(Endpoint_IsINReady())) |
|
338 { |
|
339 if (USB_DeviceState == DEVICE_STATE_Unattached) |
|
340 return; |
|
341 } |
|
342 |
|
343 if (DFU_State != dfuUPLOAD_IDLE) |
|
344 { |
|
345 if ((DFU_State == dfuERROR) && IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01)) // Blank Check |
|
346 { |
|
347 /* Blank checking is performed in the DFU_DNLOAD request - if we get here we've told the host |
|
348 that the memory isn't blank, and the host is requesting the first non-blank address */ |
|
349 Endpoint_Write_Word_LE(StartAddr); |
|
350 } |
|
351 else |
|
352 { |
|
353 /* Idle state upload - send response to last issued command */ |
|
354 Endpoint_Write_Byte(ResponseByte); |
|
355 } |
|
356 } |
|
357 else |
|
358 { |
|
359 /* Determine the number of bytes remaining in the current block */ |
|
360 uint16_t BytesRemaining = ((EndAddr - StartAddr) + 1); |
|
361 |
|
362 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00)) // Read FLASH |
|
363 { |
|
364 /* Calculate the number of words to be written from the number of bytes to be written */ |
|
365 uint16_t WordsRemaining = (BytesRemaining >> 1); |
|
366 |
|
367 union |
|
368 { |
|
369 uint16_t Words[2]; |
|
370 uint32_t Long; |
|
371 } CurrFlashAddress = {.Words = {StartAddr, Flash64KBPage}}; |
|
372 |
|
373 while (WordsRemaining--) |
|
374 { |
|
375 /* Check if endpoint is full - if so clear it and wait until ready for next packet */ |
|
376 if (Endpoint_BytesInEndpoint() == FIXED_CONTROL_ENDPOINT_SIZE) |
|
377 { |
|
378 Endpoint_ClearIN(); |
|
379 |
|
380 while (!(Endpoint_IsINReady())) |
|
381 { |
|
382 if (USB_DeviceState == DEVICE_STATE_Unattached) |
|
383 return; |
|
384 } |
|
385 } |
|
386 |
|
387 /* Read the flash word and send it via USB to the host */ |
|
388 #if (FLASHEND > 0xFFFF) |
|
389 Endpoint_Write_Word_LE(pgm_read_word_far(CurrFlashAddress.Long)); |
|
390 #else |
|
391 Endpoint_Write_Word_LE(pgm_read_word(CurrFlashAddress.Long)); |
|
392 #endif |
|
393 |
|
394 /* Adjust counters */ |
|
395 CurrFlashAddress.Long += 2; |
|
396 } |
|
397 |
|
398 /* Once reading is complete, start address equals the end address */ |
|
399 StartAddr = EndAddr; |
|
400 } |
|
401 else if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x02)) // Read EEPROM |
|
402 { |
|
403 while (BytesRemaining--) |
|
404 { |
|
405 /* Check if endpoint is full - if so clear it and wait until ready for next packet */ |
|
406 if (Endpoint_BytesInEndpoint() == FIXED_CONTROL_ENDPOINT_SIZE) |
|
407 { |
|
408 Endpoint_ClearIN(); |
|
409 |
|
410 while (!(Endpoint_IsINReady())) |
|
411 { |
|
412 if (USB_DeviceState == DEVICE_STATE_Unattached) |
|
413 return; |
|
414 } |
|
415 } |
|
416 |
|
417 /* Read the EEPROM byte and send it via USB to the host */ |
|
418 Endpoint_Write_Byte(eeprom_read_byte((uint8_t*)StartAddr)); |
|
419 |
|
420 /* Adjust counters */ |
|
421 StartAddr++; |
|
422 } |
|
423 } |
|
424 |
|
425 /* Return to idle state */ |
|
426 DFU_State = dfuIDLE; |
|
427 } |
|
428 |
|
429 Endpoint_ClearIN(); |
|
430 |
|
431 Endpoint_ClearStatusStage(); |
|
432 break; |
|
433 case DFU_GETSTATUS: |
|
434 Endpoint_ClearSETUP(); |
|
435 |
|
436 /* Write 8-bit status value */ |
|
437 Endpoint_Write_Byte(DFU_Status); |
|
438 |
|
439 /* Write 24-bit poll timeout value */ |
|
440 Endpoint_Write_Byte(0); |
|
441 Endpoint_Write_Word_LE(0); |
|
442 |
|
443 /* Write 8-bit state value */ |
|
444 Endpoint_Write_Byte(DFU_State); |
|
445 |
|
446 /* Write 8-bit state string ID number */ |
|
447 Endpoint_Write_Byte(0); |
|
448 |
|
449 Endpoint_ClearIN(); |
|
450 |
|
451 Endpoint_ClearStatusStage(); |
|
452 break; |
|
453 case DFU_CLRSTATUS: |
|
454 Endpoint_ClearSETUP(); |
|
455 |
|
456 /* Reset the status value variable to the default OK status */ |
|
457 DFU_Status = OK; |
|
458 |
|
459 Endpoint_ClearStatusStage(); |
|
460 break; |
|
461 case DFU_GETSTATE: |
|
462 Endpoint_ClearSETUP(); |
|
463 |
|
464 /* Write the current device state to the endpoint */ |
|
465 Endpoint_Write_Byte(DFU_State); |
|
466 |
|
467 Endpoint_ClearIN(); |
|
468 |
|
469 Endpoint_ClearStatusStage(); |
|
470 break; |
|
471 case DFU_ABORT: |
|
472 Endpoint_ClearSETUP(); |
|
473 |
|
474 /* Turn off TX/RX status LEDs so that they're not left on when application starts */ |
|
475 LEDs_TurnOffLEDs(LEDMASK_TX); |
|
476 LEDs_TurnOffLEDs(LEDMASK_RX); |
|
477 |
|
478 /* Reset the current state variable to the default idle state */ |
|
479 DFU_State = dfuIDLE; |
|
480 |
|
481 Endpoint_ClearStatusStage(); |
|
482 break; |
|
483 } |
|
484 } |
|
485 |
|
486 /** Routine to discard the specified number of bytes from the control endpoint stream. This is used to |
|
487 * discard unused bytes in the stream from the host, including the memory program block suffix. |
|
488 * |
|
489 * \param[in] NumberOfBytes Number of bytes to discard from the host from the control endpoint |
|
490 */ |
|
491 static void DiscardFillerBytes(uint8_t NumberOfBytes) |
|
492 { |
|
493 while (NumberOfBytes--) |
|
494 { |
|
495 if (!(Endpoint_BytesInEndpoint())) |
|
496 { |
|
497 Endpoint_ClearOUT(); |
|
498 |
|
499 /* Wait until next data packet received */ |
|
500 while (!(Endpoint_IsOUTReceived())) |
|
501 { |
|
502 if (USB_DeviceState == DEVICE_STATE_Unattached) |
|
503 return; |
|
504 } |
|
505 } |
|
506 else |
|
507 { |
|
508 Endpoint_Discard_Byte(); |
|
509 } |
|
510 } |
|
511 } |
|
512 |
|
513 /** Routine to process an issued command from the host, via a DFU_DNLOAD request wrapper. This routine ensures |
|
514 * that the command is allowed based on the current secure mode flag value, and passes the command off to the |
|
515 * appropriate handler function. |
|
516 */ |
|
517 static void ProcessBootloaderCommand(void) |
|
518 { |
|
519 /* Check if device is in secure mode */ |
|
520 // if (IsSecure) |
|
521 // { |
|
522 // /* Don't process command unless it is a READ or chip erase command */ |
|
523 // if (!(((SentCommand.Command == COMMAND_WRITE) && |
|
524 // IS_TWOBYTE_COMMAND(SentCommand.Data, 0x00, 0xFF)) || |
|
525 // (SentCommand.Command == COMMAND_READ))) |
|
526 // { |
|
527 // /* Set the state and status variables to indicate the error */ |
|
528 // DFU_State = dfuERROR; |
|
529 // DFU_Status = errWRITE; |
|
530 // |
|
531 // /* Stall command */ |
|
532 // Endpoint_StallTransaction(); |
|
533 // |
|
534 // /* Don't process the command */ |
|
535 // return; |
|
536 // } |
|
537 // } |
|
538 |
|
539 /* Dispatch the required command processing routine based on the command type */ |
|
540 switch (SentCommand.Command) |
|
541 { |
|
542 case COMMAND_PROG_START: |
|
543 ProcessMemProgCommand(); |
|
544 break; |
|
545 case COMMAND_DISP_DATA: |
|
546 ProcessMemReadCommand(); |
|
547 break; |
|
548 case COMMAND_WRITE: |
|
549 ProcessWriteCommand(); |
|
550 break; |
|
551 case COMMAND_READ: |
|
552 ProcessReadCommand(); |
|
553 break; |
|
554 case COMMAND_CHANGE_BASE_ADDR: |
|
555 if (IS_TWOBYTE_COMMAND(SentCommand.Data, 0x03, 0x00)) // Set 64KB flash page command |
|
556 Flash64KBPage = SentCommand.Data[2]; |
|
557 break; |
|
558 } |
|
559 } |
|
560 |
|
561 /** Routine to concatenate the given pair of 16-bit memory start and end addresses from the host, and store them |
|
562 * in the StartAddr and EndAddr global variables. |
|
563 */ |
|
564 static void LoadStartEndAddresses(void) |
|
565 { |
|
566 union |
|
567 { |
|
568 uint8_t Bytes[2]; |
|
569 uint16_t Word; |
|
570 } Address[2] = {{.Bytes = {SentCommand.Data[2], SentCommand.Data[1]}}, |
|
571 {.Bytes = {SentCommand.Data[4], SentCommand.Data[3]}}}; |
|
572 |
|
573 /* Load in the start and ending read addresses from the sent data packet */ |
|
574 StartAddr = Address[0].Word; |
|
575 EndAddr = Address[1].Word; |
|
576 } |
|
577 |
|
578 /** Handler for a Memory Program command issued by the host. This routine handles the preparations needed |
|
579 * to write subsequent data from the host into the specified memory. |
|
580 */ |
|
581 static void ProcessMemProgCommand(void) |
|
582 { |
|
583 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00) || // Write FLASH command |
|
584 IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01)) // Write EEPROM command |
|
585 { |
|
586 /* Load in the start and ending read addresses */ |
|
587 LoadStartEndAddresses(); |
|
588 |
|
589 /* If FLASH is being written to, we need to pre-erase the first page to write to */ |
|
590 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00)) |
|
591 { |
|
592 union |
|
593 { |
|
594 uint16_t Words[2]; |
|
595 uint32_t Long; |
|
596 } CurrFlashAddress = {.Words = {StartAddr, Flash64KBPage}}; |
|
597 |
|
598 /* Erase the current page's temp buffer */ |
|
599 boot_page_erase(CurrFlashAddress.Long); |
|
600 boot_spm_busy_wait(); |
|
601 } |
|
602 |
|
603 /* Set the state so that the next DNLOAD requests reads in the firmware */ |
|
604 DFU_State = dfuDNLOAD_IDLE; |
|
605 } |
|
606 } |
|
607 |
|
608 /** Handler for a Memory Read command issued by the host. This routine handles the preparations needed |
|
609 * to read subsequent data from the specified memory out to the host, as well as implementing the memory |
|
610 * blank check command. |
|
611 */ |
|
612 static void ProcessMemReadCommand(void) |
|
613 { |
|
614 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00) || // Read FLASH command |
|
615 IS_ONEBYTE_COMMAND(SentCommand.Data, 0x02)) // Read EEPROM command |
|
616 { |
|
617 /* Load in the start and ending read addresses */ |
|
618 LoadStartEndAddresses(); |
|
619 |
|
620 /* Set the state so that the next UPLOAD requests read out the firmware */ |
|
621 DFU_State = dfuUPLOAD_IDLE; |
|
622 } |
|
623 else if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01)) // Blank check FLASH command |
|
624 { |
|
625 uint32_t CurrFlashAddress = 0; |
|
626 |
|
627 while (CurrFlashAddress < BOOT_START_ADDR) |
|
628 { |
|
629 /* Check if the current byte is not blank */ |
|
630 #if (FLASHEND > 0xFFFF) |
|
631 if (pgm_read_byte_far(CurrFlashAddress) != 0xFF) |
|
632 #else |
|
633 if (pgm_read_byte(CurrFlashAddress) != 0xFF) |
|
634 #endif |
|
635 { |
|
636 /* Save the location of the first non-blank byte for response back to the host */ |
|
637 Flash64KBPage = (CurrFlashAddress >> 16); |
|
638 StartAddr = CurrFlashAddress; |
|
639 |
|
640 /* Set state and status variables to the appropriate error values */ |
|
641 DFU_State = dfuERROR; |
|
642 DFU_Status = errCHECK_ERASED; |
|
643 |
|
644 break; |
|
645 } |
|
646 |
|
647 CurrFlashAddress++; |
|
648 } |
|
649 } |
|
650 } |
|
651 |
|
652 /** Handler for a Data Write command issued by the host. This routine handles non-programming commands such as |
|
653 * bootloader exit (both via software jumps and hardware watchdog resets) and flash memory erasure. |
|
654 */ |
|
655 static void ProcessWriteCommand(void) |
|
656 { |
|
657 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x03)) // Start application |
|
658 { |
|
659 /* Indicate that the bootloader is terminating */ |
|
660 WaitForExit = true; |
|
661 |
|
662 /* Check if data supplied for the Start Program command - no data executes the program */ |
|
663 if (SentCommand.DataSize) |
|
664 { |
|
665 if (SentCommand.Data[1] == 0x01) // Start via jump |
|
666 { |
|
667 union |
|
668 { |
|
669 uint8_t Bytes[2]; |
|
670 AppPtr_t FuncPtr; |
|
671 } Address = {.Bytes = {SentCommand.Data[4], SentCommand.Data[3]}}; |
|
672 |
|
673 /* Load in the jump address into the application start address pointer */ |
|
674 AppStartPtr = Address.FuncPtr; |
|
675 } |
|
676 } |
|
677 else |
|
678 { |
|
679 if (SentCommand.Data[1] == 0x00) // Start via watchdog |
|
680 { |
|
681 /* Start the watchdog to reset the AVR once the communications are finalized */ |
|
682 wdt_enable(WDTO_250MS); |
|
683 } |
|
684 else // Start via jump |
|
685 { |
|
686 /* Set the flag to terminate the bootloader at next opportunity */ |
|
687 RunBootloader = false; |
|
688 } |
|
689 } |
|
690 } |
|
691 else if (IS_TWOBYTE_COMMAND(SentCommand.Data, 0x00, 0xFF)) // Erase flash |
|
692 { |
|
693 uint32_t CurrFlashAddress = 0; |
|
694 |
|
695 /* Clear the application section of flash */ |
|
696 while (CurrFlashAddress < BOOT_START_ADDR) |
|
697 { |
|
698 boot_page_erase(CurrFlashAddress); |
|
699 boot_spm_busy_wait(); |
|
700 boot_page_write(CurrFlashAddress); |
|
701 boot_spm_busy_wait(); |
|
702 |
|
703 CurrFlashAddress += SPM_PAGESIZE; |
|
704 } |
|
705 |
|
706 /* Re-enable the RWW section of flash as writing to the flash locks it out */ |
|
707 boot_rww_enable(); |
|
708 |
|
709 /* Memory has been erased, reset the security bit so that programming/reading is allowed */ |
|
710 // IsSecure = false; |
|
711 } |
|
712 } |
|
713 |
|
714 /** Handler for a Data Read command issued by the host. This routine handles bootloader information retrieval |
|
715 * commands such as device signature and bootloader version retrieval. |
|
716 */ |
|
717 static void ProcessReadCommand(void) |
|
718 { |
|
719 const uint8_t BootloaderInfo[3] = {BOOTLOADER_VERSION, BOOTLOADER_ID_BYTE1, BOOTLOADER_ID_BYTE2}; |
|
720 const uint8_t SignatureInfo[3] = {AVR_SIGNATURE_1, AVR_SIGNATURE_2, AVR_SIGNATURE_3}; |
|
721 |
|
722 uint8_t DataIndexToRead = SentCommand.Data[1]; |
|
723 |
|
724 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00)) // Read bootloader info |
|
725 ResponseByte = BootloaderInfo[DataIndexToRead]; |
|
726 else if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01)) // Read signature byte |
|
727 ResponseByte = SignatureInfo[DataIndexToRead - 0x30]; |
|
728 } |