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