Update copyright year to 2010.
[pub/USBasp.git] / Bootloaders / DFU / BootloaderDFU.c
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 "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.
42 */
43 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 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 bool WaitForExit = false;
57
58 /** Current DFU state machine state, one of the values in the DFU_State_t enum. */
59 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 uint8_t DFU_Status = OK;
65
66 /** Data containing the DFU command sent from the host. */
67 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 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 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 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 uint16_t StartAddr = 0x0000;
89
90 /** Memory end address, indicating the end address to read to/write from in the memory being addressed (either FLASH
91 * of EEPROM depending on the issued command from the host).
92 */
93 uint16_t EndAddr = 0x0000;
94
95
96 /** Main program entry point. This routine configures the hardware required by the bootloader, then continuously
97 * runs the bootloader processing routine until instructed to soft-exit, or hard-reset via the watchdog to start
98 * the loaded application code.
99 */
100 int main(void)
101 {
102 /* Configure hardware required by the bootloader */
103 SetupHardware();
104
105 /* Run the USB management task while the bootloader is supposed to be running */
106 while (RunBootloader || WaitForExit)
107 USB_USBTask();
108
109 /* Reset configured hardware back to their original states for the user application */
110 ResetHardware();
111
112 /* Start the user application */
113 AppStartPtr();
114 }
115
116 /** Configures all hardware required for the bootloader. */
117 void SetupHardware(void)
118 {
119 /* Disable watchdog if enabled by bootloader/fuses */
120 MCUSR &= ~(1 << WDRF);
121 wdt_disable();
122
123 /* Disable clock division */
124 clock_prescale_set(clock_div_1);
125
126 /* Relocate the interrupt vector table to the bootloader section */
127 MCUCR = (1 << IVCE);
128 MCUCR = (1 << IVSEL);
129
130 /* Initialize the USB subsystem */
131 USB_Init();
132 }
133
134 /** Resets all configured hardware required for the bootloader back to their original states. */
135 void ResetHardware(void)
136 {
137 /* Shut down the USB subsystem */
138 USB_ShutDown();
139
140 /* Relocate the interrupt vector table back to the application section */
141 MCUCR = (1 << IVCE);
142 MCUCR = 0;
143 }
144
145 /** Event handler for the USB_UnhandledControlRequest event. This is used to catch standard and class specific
146 * control requests that are not handled internally by the USB library (including the DFU commands, which are
147 * all issued via the control endpoint), so that they can be handled appropriately for the application.
148 */
149 void EVENT_USB_Device_UnhandledControlRequest(void)
150 {
151 /* Get the size of the command and data from the wLength value */
152 SentCommand.DataSize = USB_ControlRequest.wLength;
153
154 switch (USB_ControlRequest.bRequest)
155 {
156 case DFU_DNLOAD:
157 Endpoint_ClearSETUP();
158
159 /* Check if bootloader is waiting to terminate */
160 if (WaitForExit)
161 {
162 /* Bootloader is terminating - process last received command */
163 ProcessBootloaderCommand();
164
165 /* Indicate that the last command has now been processed - free to exit bootloader */
166 WaitForExit = false;
167 }
168
169 /* If the request has a data stage, load it into the command struct */
170 if (SentCommand.DataSize)
171 {
172 while (!(Endpoint_IsOUTReceived()))
173 {
174 if (USB_DeviceState == DEVICE_STATE_Unattached)
175 return;
176 }
177
178 /* First byte of the data stage is the DNLOAD request's command */
179 SentCommand.Command = Endpoint_Read_Byte();
180
181 /* One byte of the data stage is the command, so subtract it from the total data bytes */
182 SentCommand.DataSize--;
183
184 /* Load in the rest of the data stage as command parameters */
185 for (uint8_t DataByte = 0; (DataByte < sizeof(SentCommand.Data)) &&
186 Endpoint_BytesInEndpoint(); DataByte++)
187 {
188 SentCommand.Data[DataByte] = Endpoint_Read_Byte();
189 SentCommand.DataSize--;
190 }
191
192 /* Process the command */
193 ProcessBootloaderCommand();
194 }
195
196 /* Check if currently downloading firmware */
197 if (DFU_State == dfuDNLOAD_IDLE)
198 {
199 if (!(SentCommand.DataSize))
200 {
201 DFU_State = dfuIDLE;
202 }
203 else
204 {
205 /* Throw away the filler bytes before the start of the firmware */
206 DiscardFillerBytes(DFU_FILLER_BYTES_SIZE);
207
208 /* Throw away the page alignment filler bytes before the start of the firmware */
209 DiscardFillerBytes(StartAddr % SPM_PAGESIZE);
210
211 /* Calculate the number of bytes remaining to be written */
212 uint16_t BytesRemaining = ((EndAddr - StartAddr) + 1);
213
214 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00)) // Write flash
215 {
216 /* Calculate the number of words to be written from the number of bytes to be written */
217 uint16_t WordsRemaining = (BytesRemaining >> 1);
218
219 union
220 {
221 uint16_t Words[2];
222 uint32_t Long;
223 } CurrFlashAddress = {.Words = {StartAddr, Flash64KBPage}};
224
225 uint32_t CurrFlashPageStartAddress = CurrFlashAddress.Long;
226 uint8_t WordsInFlashPage = 0;
227
228 while (WordsRemaining--)
229 {
230 /* Check if endpoint is empty - if so clear it and wait until ready for next packet */
231 if (!(Endpoint_BytesInEndpoint()))
232 {
233 Endpoint_ClearOUT();
234
235 while (!(Endpoint_IsOUTReceived()))
236 {
237 if (USB_DeviceState == DEVICE_STATE_Unattached)
238 return;
239 }
240 }
241
242 /* Write the next word into the current flash page */
243 boot_page_fill(CurrFlashAddress.Long, Endpoint_Read_Word_LE());
244
245 /* Adjust counters */
246 WordsInFlashPage += 1;
247 CurrFlashAddress.Long += 2;
248
249 /* See if an entire page has been written to the flash page buffer */
250 if ((WordsInFlashPage == (SPM_PAGESIZE >> 1)) || !(WordsRemaining))
251 {
252 /* Commit the flash page to memory */
253 boot_page_write(CurrFlashPageStartAddress);
254 boot_spm_busy_wait();
255
256 /* Check if programming incomplete */
257 if (WordsRemaining)
258 {
259 CurrFlashPageStartAddress = CurrFlashAddress.Long;
260 WordsInFlashPage = 0;
261
262 /* Erase next page's temp buffer */
263 boot_page_erase(CurrFlashAddress.Long);
264 boot_spm_busy_wait();
265 }
266 }
267 }
268
269 /* Once programming complete, start address equals the end address */
270 StartAddr = EndAddr;
271
272 /* Re-enable the RWW section of flash */
273 boot_rww_enable();
274 }
275 else // Write EEPROM
276 {
277 while (BytesRemaining--)
278 {
279 /* Check if endpoint is empty - if so clear it and wait until ready for next packet */
280 if (!(Endpoint_BytesInEndpoint()))
281 {
282 Endpoint_ClearOUT();
283
284 while (!(Endpoint_IsOUTReceived()))
285 {
286 if (USB_DeviceState == DEVICE_STATE_Unattached)
287 return;
288 }
289 }
290
291 /* Read the byte from the USB interface and write to to the EEPROM */
292 eeprom_write_byte((uint8_t*)StartAddr, Endpoint_Read_Byte());
293
294 /* Adjust counters */
295 StartAddr++;
296 }
297 }
298
299 /* Throw away the currently unused DFU file suffix */
300 DiscardFillerBytes(DFU_FILE_SUFFIX_SIZE);
301 }
302 }
303
304 Endpoint_ClearOUT();
305
306 Endpoint_ClearStatusStage();
307
308 break;
309 case DFU_UPLOAD:
310 Endpoint_ClearSETUP();
311
312 while (!(Endpoint_IsINReady()))
313 {
314 if (USB_DeviceState == DEVICE_STATE_Unattached)
315 return;
316 }
317
318 if (DFU_State != dfuUPLOAD_IDLE)
319 {
320 if ((DFU_State == dfuERROR) && IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01)) // Blank Check
321 {
322 /* Blank checking is performed in the DFU_DNLOAD request - if we get here we've told the host
323 that the memory isn't blank, and the host is requesting the first non-blank address */
324 Endpoint_Write_Word_LE(StartAddr);
325 }
326 else
327 {
328 /* Idle state upload - send response to last issued command */
329 Endpoint_Write_Byte(ResponseByte);
330 }
331 }
332 else
333 {
334 /* Determine the number of bytes remaining in the current block */
335 uint16_t BytesRemaining = ((EndAddr - StartAddr) + 1);
336
337 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00)) // Read FLASH
338 {
339 /* Calculate the number of words to be written from the number of bytes to be written */
340 uint16_t WordsRemaining = (BytesRemaining >> 1);
341
342 union
343 {
344 uint16_t Words[2];
345 uint32_t Long;
346 } CurrFlashAddress = {.Words = {StartAddr, Flash64KBPage}};
347
348 while (WordsRemaining--)
349 {
350 /* Check if endpoint is full - if so clear it and wait until ready for next packet */
351 if (Endpoint_BytesInEndpoint() == FIXED_CONTROL_ENDPOINT_SIZE)
352 {
353 Endpoint_ClearIN();
354
355 while (!(Endpoint_IsINReady()))
356 {
357 if (USB_DeviceState == DEVICE_STATE_Unattached)
358 return;
359 }
360 }
361
362 /* Read the flash word and send it via USB to the host */
363 #if (FLASHEND > 0xFFFF)
364 Endpoint_Write_Word_LE(pgm_read_word_far(CurrFlashAddress.Long));
365 #else
366 Endpoint_Write_Word_LE(pgm_read_word(CurrFlashAddress.Long));
367 #endif
368
369 /* Adjust counters */
370 CurrFlashAddress.Long += 2;
371 }
372
373 /* Once reading is complete, start address equals the end address */
374 StartAddr = EndAddr;
375 }
376 else if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x02)) // Read EEPROM
377 {
378 while (BytesRemaining--)
379 {
380 /* Check if endpoint is full - if so clear it and wait until ready for next packet */
381 if (Endpoint_BytesInEndpoint() == FIXED_CONTROL_ENDPOINT_SIZE)
382 {
383 Endpoint_ClearIN();
384
385 while (!(Endpoint_IsINReady()))
386 {
387 if (USB_DeviceState == DEVICE_STATE_Unattached)
388 return;
389 }
390 }
391
392 /* Read the EEPROM byte and send it via USB to the host */
393 Endpoint_Write_Byte(eeprom_read_byte((uint8_t*)StartAddr));
394
395 /* Adjust counters */
396 StartAddr++;
397 }
398 }
399
400 /* Return to idle state */
401 DFU_State = dfuIDLE;
402 }
403
404 Endpoint_ClearIN();
405
406 Endpoint_ClearStatusStage();
407 break;
408 case DFU_GETSTATUS:
409 Endpoint_ClearSETUP();
410
411 /* Write 8-bit status value */
412 Endpoint_Write_Byte(DFU_Status);
413
414 /* Write 24-bit poll timeout value */
415 Endpoint_Write_Byte(0);
416 Endpoint_Write_Word_LE(0);
417
418 /* Write 8-bit state value */
419 Endpoint_Write_Byte(DFU_State);
420
421 /* Write 8-bit state string ID number */
422 Endpoint_Write_Byte(0);
423
424 Endpoint_ClearIN();
425
426 Endpoint_ClearStatusStage();
427 break;
428 case DFU_CLRSTATUS:
429 Endpoint_ClearSETUP();
430
431 /* Reset the status value variable to the default OK status */
432 DFU_Status = OK;
433
434 Endpoint_ClearStatusStage();
435 break;
436 case DFU_GETSTATE:
437 Endpoint_ClearSETUP();
438
439 /* Write the current device state to the endpoint */
440 Endpoint_Write_Byte(DFU_State);
441
442 Endpoint_ClearIN();
443
444 Endpoint_ClearStatusStage();
445 break;
446 case DFU_ABORT:
447 Endpoint_ClearSETUP();
448
449 /* Reset the current state variable to the default idle state */
450 DFU_State = dfuIDLE;
451
452 Endpoint_ClearStatusStage();
453 break;
454 }
455 }
456
457 /** Routine to discard the specified number of bytes from the control endpoint stream. This is used to
458 * discard unused bytes in the stream from the host, including the memory program block suffix.
459 *
460 * \param[in] NumberOfBytes Number of bytes to discard from the host from the control endpoint
461 */
462 static void DiscardFillerBytes(uint8_t NumberOfBytes)
463 {
464 while (NumberOfBytes--)
465 {
466 if (!(Endpoint_BytesInEndpoint()))
467 {
468 Endpoint_ClearOUT();
469
470 /* Wait until next data packet received */
471 while (!(Endpoint_IsOUTReceived()))
472 {
473 if (USB_DeviceState == DEVICE_STATE_Unattached)
474 return;
475 }
476 }
477 else
478 {
479 Endpoint_Discard_Byte();
480 }
481 }
482 }
483
484 /** Routine to process an issued command from the host, via a DFU_DNLOAD request wrapper. This routine ensures
485 * that the command is allowed based on the current secure mode flag value, and passes the command off to the
486 * appropriate handler function.
487 */
488 static void ProcessBootloaderCommand(void)
489 {
490 /* Check if device is in secure mode */
491 if (IsSecure)
492 {
493 /* Don't process command unless it is a READ or chip erase command */
494 if (!(((SentCommand.Command == COMMAND_WRITE) &&
495 IS_TWOBYTE_COMMAND(SentCommand.Data, 0x00, 0xFF)) ||
496 (SentCommand.Command == COMMAND_READ)))
497 {
498 /* Set the state and status variables to indicate the error */
499 DFU_State = dfuERROR;
500 DFU_Status = errWRITE;
501
502 /* Stall command */
503 Endpoint_StallTransaction();
504
505 /* Don't process the command */
506 return;
507 }
508 }
509
510 /* Dispatch the required command processing routine based on the command type */
511 switch (SentCommand.Command)
512 {
513 case COMMAND_PROG_START:
514 ProcessMemProgCommand();
515 break;
516 case COMMAND_DISP_DATA:
517 ProcessMemReadCommand();
518 break;
519 case COMMAND_WRITE:
520 ProcessWriteCommand();
521 break;
522 case COMMAND_READ:
523 ProcessReadCommand();
524 break;
525 case COMMAND_CHANGE_BASE_ADDR:
526 if (IS_TWOBYTE_COMMAND(SentCommand.Data, 0x03, 0x00)) // Set 64KB flash page command
527 Flash64KBPage = SentCommand.Data[2];
528
529 break;
530 }
531 }
532
533 /** Routine to concatenate the given pair of 16-bit memory start and end addresses from the host, and store them
534 * in the StartAddr and EndAddr global variables.
535 */
536 static void LoadStartEndAddresses(void)
537 {
538 union
539 {
540 uint8_t Bytes[2];
541 uint16_t Word;
542 } Address[2] = {{.Bytes = {SentCommand.Data[2], SentCommand.Data[1]}},
543 {.Bytes = {SentCommand.Data[4], SentCommand.Data[3]}}};
544
545 /* Load in the start and ending read addresses from the sent data packet */
546 StartAddr = Address[0].Word;
547 EndAddr = Address[1].Word;
548 }
549
550 /** Handler for a Memory Program command issued by the host. This routine handles the preparations needed
551 * to write subsequent data from the host into the specified memory.
552 */
553 static void ProcessMemProgCommand(void)
554 {
555 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00) || // Write FLASH command
556 IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01)) // Write EEPROM command
557 {
558 /* Load in the start and ending read addresses */
559 LoadStartEndAddresses();
560
561 /* If FLASH is being written to, we need to pre-erase the first page to write to */
562 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00))
563 {
564 union
565 {
566 uint16_t Words[2];
567 uint32_t Long;
568 } CurrFlashAddress = {.Words = {StartAddr, Flash64KBPage}};
569
570 /* Erase the current page's temp buffer */
571 boot_page_erase(CurrFlashAddress.Long);
572 boot_spm_busy_wait();
573 }
574
575 /* Set the state so that the next DNLOAD requests reads in the firmware */
576 DFU_State = dfuDNLOAD_IDLE;
577 }
578 }
579
580 /** Handler for a Memory Read command issued by the host. This routine handles the preparations needed
581 * to read subsequent data from the specified memory out to the host, as well as implementing the memory
582 * blank check command.
583 */
584 static void ProcessMemReadCommand(void)
585 {
586 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00) || // Read FLASH command
587 IS_ONEBYTE_COMMAND(SentCommand.Data, 0x02)) // Read EEPROM command
588 {
589 /* Load in the start and ending read addresses */
590 LoadStartEndAddresses();
591
592 /* Set the state so that the next UPLOAD requests read out the firmware */
593 DFU_State = dfuUPLOAD_IDLE;
594 }
595 else if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01)) // Blank check FLASH command
596 {
597 uint32_t CurrFlashAddress = 0;
598
599 while (CurrFlashAddress < BOOT_START_ADDR)
600 {
601 /* Check if the current byte is not blank */
602 #if (FLASHEND > 0xFFFF)
603 if (pgm_read_byte_far(CurrFlashAddress) != 0xFF)
604 #else
605 if (pgm_read_byte(CurrFlashAddress) != 0xFF)
606 #endif
607 {
608 /* Save the location of the first non-blank byte for response back to the host */
609 Flash64KBPage = (CurrFlashAddress >> 16);
610 StartAddr = CurrFlashAddress;
611
612 /* Set state and status variables to the appropriate error values */
613 DFU_State = dfuERROR;
614 DFU_Status = errCHECK_ERASED;
615
616 break;
617 }
618
619 CurrFlashAddress++;
620 }
621 }
622 }
623
624 /** Handler for a Data Write command issued by the host. This routine handles non-programming commands such as
625 * bootloader exit (both via software jumps and hardware watchdog resets) and flash memory erasure.
626 */
627 static void ProcessWriteCommand(void)
628 {
629 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x03)) // Start application
630 {
631 /* Indicate that the bootloader is terminating */
632 WaitForExit = true;
633
634 /* Check if empty request data array - an empty request after a filled request retains the
635 previous valid request data, but initializes the reset */
636 if (!(SentCommand.DataSize))
637 {
638 if (SentCommand.Data[1] == 0x00) // Start via watchdog
639 {
640 /* Start the watchdog to reset the AVR once the communications are finalized */
641 wdt_enable(WDTO_250MS);
642 }
643 else // Start via jump
644 {
645 /* Load in the jump address into the application start address pointer */
646 union
647 {
648 uint8_t Bytes[2];
649 AppPtr_t FuncPtr;
650 } Address = {.Bytes = {SentCommand.Data[4], SentCommand.Data[3]}};
651
652 AppStartPtr = Address.FuncPtr;
653
654 /* Set the flag to terminate the bootloader at next opportunity */
655 RunBootloader = false;
656 }
657 }
658 }
659 else if (IS_TWOBYTE_COMMAND(SentCommand.Data, 0x00, 0xFF)) // Erase flash
660 {
661 uint32_t CurrFlashAddress = 0;
662
663 /* Clear the application section of flash */
664 while (CurrFlashAddress < BOOT_START_ADDR)
665 {
666 boot_page_erase(CurrFlashAddress);
667 boot_spm_busy_wait();
668 boot_page_write(CurrFlashAddress);
669 boot_spm_busy_wait();
670
671 CurrFlashAddress += SPM_PAGESIZE;
672 }
673
674 /* Re-enable the RWW section of flash as writing to the flash locks it out */
675 boot_rww_enable();
676
677 /* Memory has been erased, reset the security bit so that programming/reading is allowed */
678 IsSecure = false;
679 }
680 }
681
682 /** Handler for a Data Read command issued by the host. This routine handles bootloader information retrieval
683 * commands such as device signature and bootloader version retrieval.
684 */
685 static void ProcessReadCommand(void)
686 {
687 const uint8_t BootloaderInfo[3] = {BOOTLOADER_VERSION, BOOTLOADER_ID_BYTE1, BOOTLOADER_ID_BYTE2};
688 const uint8_t SignatureInfo[3] = {AVR_SIGNATURE_1, AVR_SIGNATURE_2, AVR_SIGNATURE_3};
689
690 uint8_t DataIndexToRead = SentCommand.Data[1];
691
692 if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00)) // Read bootloader info
693 ResponseByte = BootloaderInfo[DataIndexToRead];
694 else if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01)) // Read signature byte
695 ResponseByte = SignatureInfo[DataIndexToRead - 0x30];
696 }