root / arduino-1.0 / hardware / arduino / firmwares / arduino-usbdfu / Arduino-usbdfu.c @ 58d82c77
History | View | Annotate | Download (23.4 KB)
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 |
} |