Statistics
| Branch: | Revision:

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
}