f29af3b515d224ba239b2d3200c0e6091377b2eb
[pub/lufa.git] / Bootloaders / DFU / BootloaderDFU.c
1 /*
2 LUFA Library
3 Copyright (C) Dean Camera, 2021.
4
5 dean [at] fourwalledcubicle [dot] com
6 www.lufa-lib.org
7 */
8
9 /*
10 Copyright 2021 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 disclaims 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 "BootloaderDFU.h"
38
39 /** Flag to indicate if the bootloader is currently running in secure mode, disallowing memory operations
40 * other than erase. This is initially set to the value set by SECURE_MODE, and cleared by the bootloader
41 * once a memory erase has completed in a bootloader session.
42 */
43 static bool IsSecure = SECURE_MODE;
44
45 /** Flag to indicate if the bootloader should be running, or should exit and allow the application code to run
46 * via a soft reset. When cleared, the bootloader will abort, the USB interface will shut down and the application
47 * jumped to via an indirect jump to location 0x0000 (or other location specified by the host).
48 */
49 static bool RunBootloader = true;
50
51 /** Flag to indicate if the bootloader is waiting to exit. When the host requests the bootloader to exit and
52 * jump to the application address it specifies, it sends two sequential commands which must be properly
53 * acknowledged. Upon reception of the first the RunBootloader flag is cleared and the WaitForExit flag is set,
54 * causing the bootloader to wait for the final exit command before shutting down.
55 */
56 static bool WaitForExit = false;
57
58 /** Current DFU state machine state, one of the values in the DFU_State_t enum. */
59 static uint8_t DFU_State = dfuIDLE;
60
61 /** Status code of the last executed DFU command. This is set to one of the values in the DFU_Status_t enum after
62 * each operation, and returned to the host when a Get Status DFU request is issued.
63 */
64 static uint8_t DFU_Status = OK;
65
66 /** Data containing the DFU command sent from the host. */
67 static DFU_Command_t SentCommand;
68
69 /** Response to the last issued Read Data DFU command. Unlike other DFU commands, the read command
70 * requires a single byte response from the bootloader containing the read data when the next DFU_UPLOAD command
71 * is issued by the host.
72 */
73 static uint8_t ResponseByte;
74
75 /** Pointer to the start of the user application. By default this is 0x0000 (the reset vector), however the host
76 * may specify an alternate address when issuing the application soft-start command.
77 */
78 static AppPtr_t AppStartPtr = (AppPtr_t)0x0000;
79
80 /** 64-bit flash page number. This is concatenated with the current 16-bit address on USB AVRs containing more than
81 * 64KB of flash memory.
82 */
83 static uint8_t Flash64KBPage = 0;
84
85 /** Memory start address, indicating the current address in the memory being addressed (either FLASH or EEPROM
86 * depending on the issued command from the host).
87 */
88 static uint16_t StartAddr = 0x0000;
89
90 /** Memory end address, indicating the end address to read from/write to in the memory being addressed (either FLASH
91 * of EEPROM depending on the issued command from the host).
92 */
93 static uint16_t EndAddr = 0x0000;
94
95 /** Magic lock for forced application start. If the HWBE fuse is programmed and BOOTRST is unprogrammed, the bootloader
96 * will start if the /HWB line of the AVR is held low and the system is reset. However, if the /HWB line is still held
97 * low when the application attempts to start via a watchdog reset, the bootloader will re-start. If set to the value
98 * \ref MAGIC_BOOT_KEY the special init function \ref Application_Jump_Check() will force the application to start.
99 */
100 uint16_t MagicBootKey ATTR_NO_INIT;
101
102
103 /** Special startup routine to check if the bootloader was started via a watchdog reset, and if the magic application
104 * start key has been loaded into \ref MagicBootKey. If the bootloader started via the watchdog and the key is valid,
105 * this will force the user application to start via a software jump.
106 */
107 void Application_Jump_Check(void)
108 {
109 bool JumpToApplication = false;
110
111 #if (BOARD == BOARD_LEONARDO)
112 /* Enable pull-up on the IO13 pin so we can use it to select the mode */
113 PORTC |= (1 << 7);
114 Delay_MS(10);
115
116 /* If IO13 is not jumpered to ground, start the user application instead */
117 JumpToApplication = ((PINC & (1 << 7)) != 0);
118
119 /* Disable pull-up after the check has completed */
120 PORTC &= ~(1 << 7);
121 #elif ((BOARD == BOARD_XPLAIN) || (BOARD == BOARD_XPLAIN_REV1))
122 /* Disable JTAG debugging */
123 JTAG_DISABLE();
124
125 /* Enable pull-up on the JTAG TCK pin so we can use it to select the mode */
126 PORTF |= (1 << 4);
127 Delay_MS(10);
128
129 /* If the TCK pin is not jumpered to ground, start the user application instead */
130 JumpToApplication = ((PINF & (1 << 4)) != 0);
131
132 /* Re-enable JTAG debugging */
133 JTAG_ENABLE();
134 #elif ((BOARD == BOARD_PROMICRO) || (BOARD == BOARD_MICRO))
135 /* Pro-Micro and Arduino Micro board use power-on reset, but no external reset. Both boards have
136 * the hardware bootloader pin HWBE enabled. Unfortunately only the external reset allows together
137 * with an enabled HWBE that the CPU start at the bootloader address independent of the FUSE_BOOTRST.
138 * That means the power-on reset will start just controlled by the FUSE_BOOTRST the bootloader or
139 * direct in the application and cannot be overridden by HWBE signal. Therfore FUSE_BOOTRST shall
140 * be enabled, otherwise the bootloader will not be reached for these boards.
141 * The bootloader checks FUSE_HWBE as *unprogammed* instead of FUSE_BOOTRST as programmed on other
142 * board variants to decide fast application start, without waiting the dedicted bootloader timeout
143 * in case of a USB, watchdog, brown-out or JTAG reset. If the watchdog reset was initiated from
144 * the bootloader marked with the MAGIC_BOOT_KEY this reset flag is reset. All other reset flags
145 * are left untouched to allow the application code checking the reset signals, especially in case
146 * of application fast start.
147 * The bootloader is entered always for external reset and power-on reset. But the bootloader is
148 * anyway exited after that dedicted timeout, if a reset-vector to the application is programmed.
149 * Once a DFU program interacts this the bootloader during this dedicted timeout, the timer stops
150 * and the application needs to be started by DFU bootloader command manually or using a reset.
151 */
152
153 /* Check if the device's forced Bootloader via Hardware Bootenable is unprogrammed */
154 if (BootloaderAPI_ReadFuse(GET_EXTENDED_FUSE_BITS) & ~FUSE_HWBE)
155 {
156 /* If the reset source was not an external or power-on reset jump to the application */
157 if (!(MCUSR & ((1 << EXTRF) || (1 << PORF))))
158 JumpToApplication = true;
159 }
160 /* If the reset source was the bootloader and the key is correct, clear it and jump to the application;
161 * this can happen in the HWBE fuse is set, and the HBE pin is low during the watchdog reset */
162 if ((MCUSR & (1 << WDRF)) && (MagicBootKey == MAGIC_BOOT_KEY))
163 {
164 JumpToApplication = true;
165
166 /* Clear reset source */
167 MCUSR &= ~(1 << WDRF);
168 }
169 #else
170 /* Check if the device's BOOTRST fuse is set */
171 if (!(BootloaderAPI_ReadFuse(GET_HIGH_FUSE_BITS) & ~FUSE_BOOTRST))
172 {
173 /* If the reset source was not an external reset or the key is correct, clear it and jump to the application */
174 if (!(MCUSR & (1 << EXTRF)) || (MagicBootKey == MAGIC_BOOT_KEY))
175 JumpToApplication = true;
176
177 /* Clear reset source */
178 MCUSR &= ~(1 << EXTRF);
179 }
180 else
181 {
182 /* If the reset source was the bootloader and the key is correct, clear it and jump to the application;
183 * this can happen in the HWBE fuse is set, and the HBE pin is low during the watchdog reset */
184 if ((MCUSR & (1 << WDRF)) && (MagicBootKey == MAGIC_BOOT_KEY))
185 JumpToApplication = true;
186
187 /* Clear reset source */
188 MCUSR &= ~(1 << WDRF);
189 }
190 #endif
191
192 /* Clear the boot key in any case */
193 MagicBootKey = 0;
194
195 /* Don't run the user application if the reset vector is blank (no app loaded) */
196 bool ApplicationValid = (pgm_read_word_near(0) != 0xFFFF);
197
198 /* If a request has been made to jump to the user application, honor it */
199 if (JumpToApplication && ApplicationValid)
200 {
201 /* Turn off the watchdog */
202 wdt_disable();
203
204 // cppcheck-suppress constStatement
205 ((void (*)(void))0x0000)();
206 }
207 }
208
209
210 static volatile bool stayinbootloader;
211
212 /** Main program entry point. This routine configures the hardware required by the bootloader, then continuously
213 * runs the bootloader processing routine until instructed to soft-exit, or hard-reset via the watchdog to start
214 * the loaded application code.
215 */
216 int main(void)
217 {
218 /* Configure hardware required by the bootloader */
219 SetupHardware();
220
221 /* Turn on first LED on the board to indicate that the bootloader has started */
222 LEDs_SetAllLEDs(LEDS_LED1);
223
224 /* Enable global interrupts so that the USB stack can function */
225 GlobalInterruptEnable();
226
227 /* Run the USB management task while the bootloader is supposed to be running */
228 stayinbootloader = false;
229
230 uint16_t i = 0;
231 while (RunBootloader || WaitForExit)
232 {
233 USB_USBTask();
234
235 if (!stayinbootloader)
236 {
237 Delay_MS(1);
238 if (i++ > 5000)
239 {
240 break;
241 }
242 }
243 else
244 {
245 i = 0;
246 }
247 }
248
249 /* Wait a short time to end all USB transactions and then disconnect */
250 _delay_us(1000);
251
252 /* Reset configured hardware back to their original states for the user application */
253 ResetHardware();
254
255 /* Start the user application */
256 AppStartPtr();
257 }
258
259 /** Configures all hardware required for the bootloader. */
260 static void SetupHardware(void)
261 {
262 /* Disable watchdog if enabled by bootloader/fuses */
263 MCUSR &= ~(1 << WDRF);
264 wdt_disable();
265
266 /* Disable clock division */
267 clock_prescale_set(clock_div_1);
268
269 /* Relocate the interrupt vector table to the bootloader section */
270 MCUCR = (1 << IVCE);
271 MCUCR = (1 << IVSEL);
272
273 /* Initialize the USB and other board hardware drivers */
274 USB_Init();
275 LEDs_Init();
276
277 /* Bootloader active LED toggle timer initialization */
278 TIMSK1 = (1 << TOIE1);
279 TCCR1B = ((1 << CS11) | (1 << CS10));
280 }
281
282 /** Resets all configured hardware required for the bootloader back to their original states. */
283 static void ResetHardware(void)
284 {
285 /* Shut down the USB and other board hardware drivers */
286 USB_Disable();
287 LEDs_Disable();
288
289 /* Disable Bootloader active LED toggle timer */
290 TIMSK1 = 0;
291 TCCR1B = 0;
292
293 /* Relocate the interrupt vector table back to the application section */
294 MCUCR = (1 << IVCE);
295 MCUCR = 0;
296 }
297
298 /** ISR to periodically toggle the LEDs on the board to indicate that the bootloader is active. */
299 ISR(TIMER1_OVF_vect, ISR_BLOCK)
300 {
301 LEDs_ToggleLEDs(LEDS_LED1 | LEDS_LED2);
302 }
303
304 /** Event handler for the USB_ControlRequest event. This is used to catch and process control requests sent to
305 * the device from the USB host before passing along unhandled control requests to the library for processing
306 * internally.
307 */
308 void EVENT_USB_Device_ControlRequest(void)
309 {
310 /* Ignore any requests that aren't directed to the DFU interface */
311 if ((USB_ControlRequest.bmRequestType & (CONTROL_REQTYPE_TYPE | CONTROL_REQTYPE_RECIPIENT)) !=
312 (REQTYPE_CLASS | REQREC_INTERFACE))
313 {
314 return;
315 }
316
317 stayinbootloader = true;
318
319 /* Activity - toggle indicator LEDs */
320 LEDs_ToggleLEDs(LEDS_LED1 | LEDS_LED2);
321
322 /* Get the size of the command and data from the wLength value */
323 SentCommand.DataSize = USB_ControlRequest.wLength;
324
325 switch (USB_ControlRequest.bRequest)
326 {
327 case DFU_REQ_DNLOAD:
328 Endpoint_ClearSETUP();
329
330 /* Check if bootloader is waiting to terminate */
331 if (WaitForExit)
332 {
333 /* Bootloader is terminating - process last received command */
334 ProcessBootloaderCommand();
335
336 /* Indicate that the last command has now been processed - free to exit bootloader */
337 WaitForExit = false;
338 }
339
340 /* If the request has a data stage, load it into the command struct */
341 if (SentCommand.DataSize)
342 {
343 while (!(Endpoint_IsOUTReceived()))
344 {
345 if (USB_DeviceState == DEVICE_STATE_Unattached)
346 return;
347 }
348
349 /* First byte of the data stage is the DNLOAD request's command */
350 SentCommand.Command = Endpoint_Read_8();
351
352 /* One byte of the data stage is the command, so subtract it from the total data bytes */
353 SentCommand.DataSize--;
354
355 /* Load in the rest of the data stage as command parameters */
356 for (uint8_t DataByte = 0; (DataByte < sizeof(SentCommand.Data)) &&
357 Endpoint_BytesInEndpoint(); DataByte++)
358 {
359 SentCommand.Data[DataByte] = Endpoint_Read_8();
360 SentCommand.DataSize--;
361 }
362
363 /* Process the command */
364 ProcessBootloaderCommand();
365 }
366
367 /* Check if currently downloading firmware */
368 if (DFU_State == dfuDNLOAD_IDLE)
369 {
370 if (!(SentCommand.DataSize))
371 {
372 DFU_State = dfuIDLE;
373 }
374 else
375 {
376 /* Throw away the filler bytes before the start of the firmware */
377 DiscardFillerBytes(DFU_FILLER_BYTES_SIZE);
378
379 /* Throw away the packet alignment filler bytes before the start of the firmware */
380 DiscardFillerBytes(StartAddr % FIXED_CONTROL_ENDPOINT_SIZE);
381
382 /* Calculate the number of bytes remaining to be written */
383 uint16_t BytesRemaining = ((EndAddr - StartAddr) + 1);
384
385 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00)) // Write flash
386 {
387 /* Calculate the number of words to be written from the number of bytes to be written */
388 uint16_t WordsRemaining = (BytesRemaining >> 1);
389
390 union
391 {
392 uint16_t Words[2];
393 uint32_t Long;
394 } CurrFlashAddress = {.Words = {StartAddr, Flash64KBPage}};
395
396 uint32_t CurrFlashPageStartAddress = CurrFlashAddress.Long;
397 uint8_t WordsInFlashPage = 0;
398
399 while (WordsRemaining--)
400 {
401 /* Check if endpoint is empty - if so clear it and wait until ready for next packet */
402 if (!(Endpoint_BytesInEndpoint()))
403 {
404 Endpoint_ClearOUT();
405
406 while (!(Endpoint_IsOUTReceived()))
407 {
408 if (USB_DeviceState == DEVICE_STATE_Unattached)
409 return;
410 }
411 }
412
413 /* Write the next word into the current flash page */
414 BootloaderAPI_FillWord(CurrFlashAddress.Long, Endpoint_Read_16_LE());
415
416 /* Adjust counters */
417 WordsInFlashPage += 1;
418 CurrFlashAddress.Long += 2;
419
420 /* See if an entire page has been written to the flash page buffer */
421 if ((WordsInFlashPage == (SPM_PAGESIZE >> 1)) || !(WordsRemaining))
422 {
423 /* Commit the flash page to memory */
424 BootloaderAPI_WritePage(CurrFlashPageStartAddress);
425
426 /* Check if programming incomplete */
427 if (WordsRemaining)
428 {
429 CurrFlashPageStartAddress = CurrFlashAddress.Long;
430 WordsInFlashPage = 0;
431
432 /* Erase next page's temp buffer */
433 BootloaderAPI_ErasePage(CurrFlashAddress.Long);
434 }
435 }
436 }
437
438 /* Once programming complete, start address equals the end address */
439 StartAddr = EndAddr;
440 }
441 else // Write EEPROM
442 {
443 while (BytesRemaining--)
444 {
445 /* Check if endpoint is empty - if so clear it and wait until ready for next packet */
446 if (!(Endpoint_BytesInEndpoint()))
447 {
448 Endpoint_ClearOUT();
449
450 while (!(Endpoint_IsOUTReceived()))
451 {
452 if (USB_DeviceState == DEVICE_STATE_Unattached)
453 return;
454 }
455 }
456
457 /* Read the byte from the USB interface and write to to the EEPROM */
458 eeprom_update_byte((uint8_t*)StartAddr, Endpoint_Read_8());
459
460 /* Adjust counters */
461 StartAddr++;
462 }
463 }
464
465 /* Throw away the currently unused DFU file suffix */
466 DiscardFillerBytes(DFU_FILE_SUFFIX_SIZE);
467 }
468 }
469
470 Endpoint_ClearOUT();
471
472 Endpoint_ClearStatusStage();
473
474 break;
475 case DFU_REQ_UPLOAD:
476 Endpoint_ClearSETUP();
477
478 while (!(Endpoint_IsINReady()))
479 {
480 if (USB_DeviceState == DEVICE_STATE_Unattached)
481 return;
482 }
483
484 if (DFU_State != dfuUPLOAD_IDLE)
485 {
486 if ((DFU_State == dfuERROR) && IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01)) // Blank Check
487 {
488 /* Blank checking is performed in the DFU_DNLOAD request - if we get here we've told the host
489 that the memory isn't blank, and the host is requesting the first non-blank address */
490 Endpoint_Write_16_LE(StartAddr);
491 }
492 else
493 {
494 /* Idle state upload - send response to last issued command */
495 Endpoint_Write_8(ResponseByte);
496 }
497 }
498 else
499 {
500 /* Determine the number of bytes remaining in the current block */
501 uint16_t BytesRemaining = ((EndAddr - StartAddr) + 1);
502
503 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00)) // Read FLASH
504 {
505 /* Calculate the number of words to be written from the number of bytes to be written */
506 uint16_t WordsRemaining = (BytesRemaining >> 1);
507
508 union
509 {
510 uint16_t Words[2];
511 uint32_t Long;
512 } CurrFlashAddress = {.Words = {StartAddr, Flash64KBPage}};
513
514 while (WordsRemaining--)
515 {
516 /* Check if endpoint is full - if so clear it and wait until ready for next packet */
517 if (Endpoint_BytesInEndpoint() == FIXED_CONTROL_ENDPOINT_SIZE)
518 {
519 Endpoint_ClearIN();
520
521 while (!(Endpoint_IsINReady()))
522 {
523 if (USB_DeviceState == DEVICE_STATE_Unattached)
524 return;
525 }
526 }
527
528 /* Read the flash word and send it via USB to the host */
529 #if (FLASHEND > 0xFFFF)
530 Endpoint_Write_16_LE(pgm_read_word_far(CurrFlashAddress.Long));
531 #else
532 Endpoint_Write_16_LE(pgm_read_word(CurrFlashAddress.Long));
533 #endif
534
535 /* Adjust counters */
536 CurrFlashAddress.Long += 2;
537 }
538
539 /* Once reading is complete, start address equals the end address */
540 StartAddr = EndAddr;
541 }
542 else if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x02)) // Read EEPROM
543 {
544 while (BytesRemaining--)
545 {
546 /* Check if endpoint is full - if so clear it and wait until ready for next packet */
547 if (Endpoint_BytesInEndpoint() == FIXED_CONTROL_ENDPOINT_SIZE)
548 {
549 Endpoint_ClearIN();
550
551 while (!(Endpoint_IsINReady()))
552 {
553 if (USB_DeviceState == DEVICE_STATE_Unattached)
554 return;
555 }
556 }
557
558 /* Read the EEPROM byte and send it via USB to the host */
559 Endpoint_Write_8(eeprom_read_byte((uint8_t*)StartAddr));
560
561 /* Adjust counters */
562 StartAddr++;
563 }
564 }
565
566 /* Return to idle state */
567 DFU_State = dfuIDLE;
568 }
569
570 Endpoint_ClearIN();
571
572 Endpoint_ClearStatusStage();
573 break;
574 case DFU_REQ_GETSTATUS:
575 Endpoint_ClearSETUP();
576
577 while (!(Endpoint_IsINReady()))
578 {
579 if (USB_DeviceState == DEVICE_STATE_Unattached)
580 return;
581 }
582
583 /* Write 8-bit status value */
584 Endpoint_Write_8(DFU_Status);
585
586 /* Write 24-bit poll timeout value */
587 Endpoint_Write_8(0);
588 Endpoint_Write_16_LE(0);
589
590 /* Write 8-bit state value */
591 Endpoint_Write_8(DFU_State);
592
593 /* Write 8-bit state string ID number */
594 Endpoint_Write_8(0);
595
596 Endpoint_ClearIN();
597
598 Endpoint_ClearStatusStage();
599 break;
600 case DFU_REQ_CLRSTATUS:
601 Endpoint_ClearSETUP();
602
603 /* Reset the status value variable to the default OK status */
604 DFU_Status = OK;
605
606 Endpoint_ClearStatusStage();
607 break;
608 case DFU_REQ_GETSTATE:
609 Endpoint_ClearSETUP();
610
611 while (!(Endpoint_IsINReady()))
612 {
613 if (USB_DeviceState == DEVICE_STATE_Unattached)
614 return;
615 }
616
617 /* Write the current device state to the endpoint */
618 Endpoint_Write_8(DFU_State);
619
620 Endpoint_ClearIN();
621
622 Endpoint_ClearStatusStage();
623 break;
624 case DFU_REQ_ABORT:
625 Endpoint_ClearSETUP();
626
627 /* Reset the current state variable to the default idle state */
628 DFU_State = dfuIDLE;
629
630 Endpoint_ClearStatusStage();
631 break;
632 }
633 }
634
635 /** Routine to discard the specified number of bytes from the control endpoint stream. This is used to
636 * discard unused bytes in the stream from the host, including the memory program block suffix.
637 *
638 * \param[in] NumberOfBytes Number of bytes to discard from the host from the control endpoint
639 */
640 static void DiscardFillerBytes(uint8_t NumberOfBytes)
641 {
642 while (NumberOfBytes--)
643 {
644 if (!(Endpoint_BytesInEndpoint()))
645 {
646 Endpoint_ClearOUT();
647
648 /* Wait until next data packet received */
649 while (!(Endpoint_IsOUTReceived()))
650 {
651 if (USB_DeviceState == DEVICE_STATE_Unattached)
652 return;
653 }
654 }
655 else
656 {
657 Endpoint_Discard_8();
658 }
659 }
660 }
661
662 /** Routine to process an issued command from the host, via a DFU_DNLOAD request wrapper. This routine ensures
663 * that the command is allowed based on the current secure mode flag value, and passes the command off to the
664 * appropriate handler function.
665 */
666 static void ProcessBootloaderCommand(void)
667 {
668 /* Check if device is in secure mode */
669 if (IsSecure)
670 {
671 /* Don't process command unless it is a READ or chip erase command */
672 if (!(((SentCommand.Command == COMMAND_WRITE) &&
673 IS_TWOBYTE_COMMAND(SentCommand.Data, 0x00, 0xFF)) ||
674 (SentCommand.Command == COMMAND_READ)))
675 {
676 /* Set the state and status variables to indicate the error */
677 DFU_State = dfuERROR;
678 DFU_Status = errWRITE;
679
680 /* Stall command */
681 Endpoint_StallTransaction();
682
683 /* Don't process the command */
684 return;
685 }
686 }
687
688 /* Dispatch the required command processing routine based on the command type */
689 switch (SentCommand.Command)
690 {
691 case COMMAND_PROG_START:
692 ProcessMemProgCommand();
693 break;
694 case COMMAND_DISP_DATA:
695 ProcessMemReadCommand();
696 break;
697 case COMMAND_WRITE:
698 ProcessWriteCommand();
699 break;
700 case COMMAND_READ:
701 ProcessReadCommand();
702 break;
703 case COMMAND_CHANGE_BASE_ADDR:
704 if (IS_TWOBYTE_COMMAND(SentCommand.Data, 0x03, 0x00)) // Set 64KB flash page command
705 Flash64KBPage = SentCommand.Data[2];
706
707 break;
708 }
709 }
710
711 /** Routine to concatenate the given pair of 16-bit memory start and end addresses from the host, and store them
712 * in the StartAddr and EndAddr global variables.
713 */
714 static void LoadStartEndAddresses(void)
715 {
716 union
717 {
718 uint8_t Bytes[2];
719 uint16_t Word;
720 } Address[2] = {{.Bytes = {SentCommand.Data[2], SentCommand.Data[1]}},
721 {.Bytes = {SentCommand.Data[4], SentCommand.Data[3]}}};
722
723 /* Load in the start and ending read addresses from the sent data packet */
724 StartAddr = Address[0].Word;
725 EndAddr = Address[1].Word;
726 }
727
728 /** Handler for a Memory Program command issued by the host. This routine handles the preparations needed
729 * to write subsequent data from the host into the specified memory.
730 */
731 static void ProcessMemProgCommand(void)
732 {
733 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00) || // Write FLASH command
734 IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01)) // Write EEPROM command
735 {
736 /* Load in the start and ending read addresses */
737 LoadStartEndAddresses();
738
739 /* If FLASH is being written to, we need to pre-erase the first page to write to */
740 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00))
741 {
742 union
743 {
744 uint16_t Words[2];
745 uint32_t Long;
746 } CurrFlashAddress = {.Words = {StartAddr, Flash64KBPage}};
747
748 /* Erase the current page's temp buffer */
749 BootloaderAPI_ErasePage(CurrFlashAddress.Long);
750 }
751
752 /* Set the state so that the next DNLOAD requests reads in the firmware */
753 DFU_State = dfuDNLOAD_IDLE;
754 }
755 }
756
757 /** Handler for a Memory Read command issued by the host. This routine handles the preparations needed
758 * to read subsequent data from the specified memory out to the host, as well as implementing the memory
759 * blank check command.
760 */
761 static void ProcessMemReadCommand(void)
762 {
763 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00) || // Read FLASH command
764 IS_ONEBYTE_COMMAND(SentCommand.Data, 0x02)) // Read EEPROM command
765 {
766 /* Load in the start and ending read addresses */
767 LoadStartEndAddresses();
768
769 /* Set the state so that the next UPLOAD requests read out the firmware */
770 DFU_State = dfuUPLOAD_IDLE;
771 }
772 else if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01)) // Blank check FLASH command
773 {
774 uint32_t CurrFlashAddress = 0;
775
776 while (CurrFlashAddress < (uint32_t)BOOT_START_ADDR)
777 {
778 /* Check if the current byte is not blank */
779 #if (FLASHEND > 0xFFFF)
780 if (pgm_read_byte_far(CurrFlashAddress) != 0xFF)
781 #else
782 if (pgm_read_byte(CurrFlashAddress) != 0xFF)
783 #endif
784 {
785 /* Save the location of the first non-blank byte for response back to the host */
786 Flash64KBPage = (CurrFlashAddress >> 16);
787 StartAddr = CurrFlashAddress;
788
789 /* Set state and status variables to the appropriate error values */
790 DFU_State = dfuERROR;
791 DFU_Status = errCHECK_ERASED;
792
793 break;
794 }
795
796 CurrFlashAddress++;
797 }
798 }
799 }
800
801 /** Handler for a Data Write command issued by the host. This routine handles non-programming commands such as
802 * bootloader exit (both via software jumps and hardware watchdog resets) and flash memory erasure.
803 */
804 static void ProcessWriteCommand(void)
805 {
806 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x03)) // Start application
807 {
808 /* Indicate that the bootloader is terminating */
809 WaitForExit = true;
810
811 /* Check if data supplied for the Start Program command - no data executes the program */
812 if (SentCommand.DataSize)
813 {
814 if (SentCommand.Data[1] == 0x01) // Start via jump
815 {
816 union
817 {
818 uint8_t Bytes[2];
819 AppPtr_t FuncPtr;
820 } Address = {.Bytes = {SentCommand.Data[4], SentCommand.Data[3]}};
821
822 /* Load in the jump address into the application start address pointer */
823 AppStartPtr = Address.FuncPtr;
824 }
825 }
826 else
827 {
828 if (SentCommand.Data[1] == 0x00) // Start via watchdog
829 {
830 /* Unlock the forced application start mode of the bootloader if it is restarted */
831 MagicBootKey = MAGIC_BOOT_KEY;
832
833 /* Start the watchdog to reset the AVR once the communications are finalized */
834 wdt_enable(WDTO_250MS);
835 }
836 else // Start via jump
837 {
838 /* Set the flag to terminate the bootloader at next opportunity if a valid application has been loaded */
839 if (pgm_read_word_near(0) == 0xFFFF)
840 RunBootloader = false;
841 }
842 }
843 }
844 else if (IS_TWOBYTE_COMMAND(SentCommand.Data, 0x00, 0xFF)) // Erase flash
845 {
846 /* Clear the application section of flash */
847 for (uint32_t CurrFlashAddress = 0; CurrFlashAddress < (uint32_t)BOOT_START_ADDR; CurrFlashAddress += SPM_PAGESIZE)
848 BootloaderAPI_ErasePage(CurrFlashAddress);
849
850 /* Memory has been erased, reset the security bit so that programming/reading is allowed */
851 IsSecure = false;
852 }
853 }
854
855 /** Handler for a Data Read command issued by the host. This routine handles bootloader information retrieval
856 * commands such as device signature and bootloader version retrieval.
857 */
858 static void ProcessReadCommand(void)
859 {
860 const uint8_t BootloaderInfo[3] = {BOOTLOADER_VERSION, BOOTLOADER_ID_BYTE1, BOOTLOADER_ID_BYTE2};
861 const uint8_t SignatureInfo[4] = {0x58, AVR_SIGNATURE_1, AVR_SIGNATURE_2, AVR_SIGNATURE_3};
862
863 uint8_t DataIndexToRead = SentCommand.Data[1];
864 bool ReadAddressInvalid = false;
865
866 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00)) // Read bootloader info
867 {
868 if (DataIndexToRead < 3)
869 ResponseByte = BootloaderInfo[DataIndexToRead];
870 else
871 ReadAddressInvalid = true;
872 }
873 else if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01)) // Read signature byte
874 {
875 switch (DataIndexToRead)
876 {
877 case 0x30:
878 ResponseByte = SignatureInfo[0];
879 break;
880 case 0x31:
881 ResponseByte = SignatureInfo[1];
882 break;
883 case 0x60:
884 ResponseByte = SignatureInfo[2];
885 break;
886 case 0x61:
887 ResponseByte = SignatureInfo[3];
888 break;
889 default:
890 ReadAddressInvalid = true;
891 break;
892 }
893 }
894
895 if (ReadAddressInvalid)
896 {
897 /* Set the state and status variables to indicate the error */
898 DFU_State = dfuERROR;
899 DFU_Status = errADDRESS;
900 }
901 }