3      Copyright (C) Dean Camera, 2009. 
   5   dean [at] fourwalledcubicle [dot] com 
   6       www.fourwalledcubicle.com 
  10   Copyright 2009  Dean Camera (dean [at] fourwalledcubicle [dot] com) 
  12   Permission to use, copy, modify, and distribute this software 
  13   and its documentation for any purpose and without fee is hereby 
  14   granted, provided that the above copyright notice appear in all 
  15   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. 
  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 
  33  *  Main source file for the CDC class bootloader. This file contains the complete bootloader logic. 
  36 #define  INCLUDE_FROM_BOOTLOADERCDC_C 
  37 #include "BootloaderCDC.h" 
  40 /** Line coding options for the virtual serial port. Although the virtual serial port data is never 
  41  *  sent through a physical serial port, the line encoding data must still be read and preserved from 
  42  *  the host, or the host will detect a problem and fail to open the port. This structure contains the 
  43  *  current encoding options, including baud rate, character format, parity mode and total number of  
  44  *  bits in each data chunk. 
  46 CDC_Line_Coding_t LineCoding 
= { .BaudRateBPS 
= 9600, 
  47                                  .CharFormat  
= OneStopBit
, 
  48                                  .ParityType  
= Parity_None
, 
  51 /** Current address counter. This stores the current address of the FLASH or EEPROM as set by the host, 
  52  *  and is used when reading or writing to the AVRs memory (either FLASH or EEPROM depending on the issued 
  57 /** Flag to indicate if the bootloader should be running, or should exit and allow the application code to run 
  58  *  via a soft reset. When cleared, the bootloader will abort, the USB interface will shut down and the application 
  59  *  jumped to via an indirect jump to location 0x0000. 
  61 bool RunBootloader 
= true; 
  64 /** Main program entry point. This routine configures the hardware required by the bootloader, then continuously  
  65  *  runs the bootloader processing routine until instructed to soft-exit, or hard-reset via the watchdog to start 
  66  *  the loaded application code. 
  70         /* Setup hardware required for the bootloader */ 
  79         /* Reset all configured hardware to their default states for the user app */ 
  82         /* Start the user application */ 
  83         AppPtr_t AppStartPtr 
= (AppPtr_t
)0x0000; 
  87 /** Configures all hardware required for the bootloader. */ 
  88 void SetupHardware(void) 
  90         /* Disable watchdog if enabled by bootloader/fuses */ 
  91         MCUSR 
&= ~(1 << WDRF
); 
  94         /* Disable clock division */ 
  95         clock_prescale_set(clock_div_1
); 
  97         /* Relocate the interrupt vector table to the bootloader section */ 
 101         /* Initialize USB Subsystem */ 
 105 /** Resets all configured hardware required for the bootloader back to their original states. */ 
 106 void ResetHardware(void) 
 108         /* Shut down the USB subsystem */ 
 111         /* Relocate the interrupt vector table back to the application section */ 
 115         /* Re-enable RWW section */ 
 119 /** Event handler for the USB_ConfigurationChanged event. This configures the device's endpoints ready 
 120  *  to relay data to and from the attached USB host. 
 122 void EVENT_USB_Device_ConfigurationChanged(void) 
 124         /* Setup CDC Notification, Rx and Tx Endpoints */ 
 125         Endpoint_ConfigureEndpoint(CDC_NOTIFICATION_EPNUM
, EP_TYPE_INTERRUPT
, 
 126                                        ENDPOINT_DIR_IN
, CDC_NOTIFICATION_EPSIZE
, 
 127                                    ENDPOINT_BANK_SINGLE
); 
 129         Endpoint_ConfigureEndpoint(CDC_TX_EPNUM
, EP_TYPE_BULK
, 
 130                                        ENDPOINT_DIR_IN
, CDC_TXRX_EPSIZE
, 
 131                                    ENDPOINT_BANK_SINGLE
); 
 133         Endpoint_ConfigureEndpoint(CDC_RX_EPNUM
, EP_TYPE_BULK
, 
 134                                        ENDPOINT_DIR_OUT
, CDC_TXRX_EPSIZE
, 
 135                                    ENDPOINT_BANK_SINGLE
); 
 138 /** Event handler for the USB_UnhandledControlRequest event. This is used to catch standard and class specific 
 139  *  control requests that are not handled internally by the USB library, so that they can be handled appropriately 
 140  *  for the application. 
 142 void EVENT_USB_Device_UnhandledControlRequest(void) 
 144         uint8_t* LineCodingData 
= (uint8_t*)&LineCoding
; 
 146         /* Process CDC specific control requests */ 
 147         switch (USB_ControlRequest
.bRequest
) 
 149                 case REQ_GetLineEncoding
: 
 150                         if (USB_ControlRequest
.bmRequestType 
== (REQDIR_DEVICETOHOST 
| REQTYPE_CLASS 
| REQREC_INTERFACE
)) 
 152                                 Endpoint_ClearSETUP(); 
 154                                 for (uint8_t i 
= 0; i 
< sizeof(LineCoding
); i
++) 
 155                                   Endpoint_Write_Byte(*(LineCodingData
++));      
 159                                 Endpoint_ClearStatusStage(); 
 163                 case REQ_SetLineEncoding
: 
 164                         if (USB_ControlRequest
.bmRequestType 
== (REQDIR_HOSTTODEVICE 
| REQTYPE_CLASS 
| REQREC_INTERFACE
)) 
 166                                 Endpoint_ClearSETUP(); 
 168                                 while (!(Endpoint_IsOUTReceived())) 
 170                                         if (USB_DeviceState 
== DEVICE_STATE_Unattached
) 
 174                                 for (uint8_t i 
= 0; i 
< sizeof(LineCoding
); i
++) 
 175                                   *(LineCodingData
++) = Endpoint_Read_Byte(); 
 179                                 Endpoint_ClearStatusStage(); 
 183                 case REQ_SetControlLineState
: 
 184                         if (USB_ControlRequest
.bmRequestType 
== (REQDIR_HOSTTODEVICE 
| REQTYPE_CLASS 
| REQREC_INTERFACE
)) 
 186                                 Endpoint_ClearSETUP(); 
 188                                 Endpoint_ClearStatusStage(); 
 195 /** Reads or writes a block of EEPROM or FLASH memory to or from the appropriate CDC data endpoint, depending 
 196  *  on the AVR910 protocol command issued. 
 198  *  \param[in] Command  Single character AVR910 protocol command indicating what memory operation to perform 
 200 static void ReadWriteMemoryBlock(const uint8_t Command
) 
 205         bool     HighByte 
= false; 
 208         BlockSize  
= (FetchNextCommandByte() << 8); 
 209         BlockSize 
|=  FetchNextCommandByte(); 
 211         MemoryType 
=  FetchNextCommandByte(); 
 213         if ((MemoryType 
!= 'E') && (MemoryType 
!= 'F')) 
 215                 /* Send error byte back to the host */ 
 216                 WriteNextResponseByte('?'); 
 221         /* Check if command is to read memory */ 
 224                 /* Re-enable RWW section */ 
 229                         if (MemoryType 
== 'F') 
 231                                 /* Read the next FLASH byte from the current FLASH page */ 
 232                                 #if (FLASHEND > 0xFFFF) 
 233                                 WriteNextResponseByte(pgm_read_byte_far(CurrAddress 
| HighByte
)); 
 235                                 WriteNextResponseByte(pgm_read_byte(CurrAddress 
| HighByte
));                                    
 238                                 /* If both bytes in current word have been read, increment the address counter */ 
 242                                 HighByte 
= !HighByte
; 
 246                                 /* Read the next EEPROM byte into the endpoint */ 
 247                                 WriteNextResponseByte(eeprom_read_byte((uint8_t*)(uint16_t)(CurrAddress 
>> 1))); 
 249                                 /* Increment the address counter after use */ 
 256                 uint32_t PageStartAddress 
= CurrAddress
; 
 258                 if (MemoryType 
== 'F') 
 260                         boot_page_erase(PageStartAddress
); 
 261                         boot_spm_busy_wait(); 
 266                         if (MemoryType 
== 'F') 
 268                                 /* If both bytes in current word have been written, increment the address counter */ 
 271                                         /* Write the next FLASH word to the current FLASH page */ 
 272                                         boot_page_fill(CurrAddress
, ((FetchNextCommandByte() << 8) | LowByte
)); 
 274                                         /* Increment the address counter after use */ 
 281                                         LowByte 
= FetchNextCommandByte(); 
 288                                 /* Write the next EEPROM byte from the endpoint */ 
 289                                 eeprom_write_byte((uint8_t*)(uint16_t)(CurrAddress 
>> 1), FetchNextCommandByte());                                       
 291                                 /* Increment the address counter after use */ 
 296                 /* If in FLASH programming mode, commit the page after writing */ 
 297                 if (MemoryType 
== 'F') 
 299                         /* Commit the flash page to memory */ 
 300                         boot_page_write(PageStartAddress
); 
 302                         /* Wait until write operation has completed */ 
 303                         boot_spm_busy_wait(); 
 306                 /* Send response byte back to the host */ 
 307                 WriteNextResponseByte('\r');             
 311 /** Retrieves the next byte from the host in the CDC data OUT endpoint, and clears the endpoint bank if needed 
 312  *  to allow reception of the next data packet from the host. 
 314  *  \return Next received byte from the host in the CDC data OUT endpoint 
 316 static uint8_t FetchNextCommandByte(void) 
 318         /* Select the OUT endpoint so that the next data byte can be read */ 
 319         Endpoint_SelectEndpoint(CDC_RX_EPNUM
); 
 321         /* If OUT endpoint empty, clear it and wait for the next packet from the host */ 
 322         while (!(Endpoint_IsReadWriteAllowed())) 
 326                 while (!(Endpoint_IsOUTReceived())) 
 328                         if (USB_DeviceState 
== DEVICE_STATE_Unattached
) 
 333         /* Fetch the next byte from the OUT endpoint */ 
 334         return Endpoint_Read_Byte(); 
 337 /** Writes the next response byte to the CDC data IN endpoint, and sends the endpoint back if needed to free up the 
 338  *  bank when full ready for the next byte in the packet to the host. 
 340  *  \param[in] Response  Next response byte to send to the host 
 342 static void WriteNextResponseByte(const uint8_t Response
) 
 344         /* Select the IN endpoint so that the next data byte can be written */ 
 345         Endpoint_SelectEndpoint(CDC_TX_EPNUM
); 
 347         /* If IN endpoint full, clear it and wait util ready for the next packet to the host */ 
 348         if (!(Endpoint_IsReadWriteAllowed())) 
 352                 while (!(Endpoint_IsINReady())) 
 354                         if (USB_DeviceState 
== DEVICE_STATE_Unattached
) 
 359         /* Write the next byte to the OUT endpoint */ 
 360         Endpoint_Write_Byte(Response
); 
 363 /** Task to read in AVR910 commands from the CDC data OUT endpoint, process them, perform the required actions 
 364  *  and send the appropriate response back to the host. 
 368         /* Select the OUT endpoint */ 
 369         Endpoint_SelectEndpoint(CDC_RX_EPNUM
); 
 371         /* Check if endpoint has a command in it sent from the host */ 
 372         if (Endpoint_IsOUTReceived()) 
 374                 /* Read in the bootloader command (first byte sent from host) */ 
 375                 uint8_t Command 
= FetchNextCommandByte(); 
 377                 if ((Command 
== 'L') || (Command 
== 'P') || (Command 
== 'T') || (Command 
== 'E')) 
 380                           RunBootloader 
= false; 
 382                           FetchNextCommandByte(); 
 384                         /* Send confirmation byte back to the host */ 
 385                         WriteNextResponseByte('\r');                     
 387                 else if (Command 
== 't') 
 389                         /* Return ATMEGA128 part code - this is only to allow AVRProg to use the bootloader */ 
 390                         WriteNextResponseByte(0x44); 
 392                         WriteNextResponseByte(0x00); 
 394                 else if (Command 
== 'a') 
 396                         /* Indicate auto-address increment is supported */ 
 397                         WriteNextResponseByte('Y'); 
 399                 else if (Command 
== 'A') 
 401                         /* Set the current address to that given by the host */ 
 402                         CurrAddress   
= (FetchNextCommandByte() << 9); 
 403                         CurrAddress  
|= (FetchNextCommandByte() << 1); 
 405                         /* Send confirmation byte back to the host */ 
 406                         WriteNextResponseByte('\r'); 
 408                 else if (Command 
== 'p') 
 410                         /* Indicate serial programmer back to the host */ 
 411                         WriteNextResponseByte('S');               
 413                 else if (Command 
== 'S') 
 415                         /* Write the 7-byte software identifier to the endpoint */ 
 416                         for (uint8_t CurrByte 
= 0; CurrByte 
< 7; CurrByte
++) 
 417                           WriteNextResponseByte(SOFTWARE_IDENTIFIER
[CurrByte
]);          
 419                 else if (Command 
== 'V') 
 421                         WriteNextResponseByte('0' + BOOTLOADER_VERSION_MAJOR
); 
 422                         WriteNextResponseByte('0' + BOOTLOADER_VERSION_MINOR
); 
 424                 else if (Command 
== 's') 
 426                         WriteNextResponseByte(AVR_SIGNATURE_3
);          
 427                         WriteNextResponseByte(AVR_SIGNATURE_2
); 
 428                         WriteNextResponseByte(AVR_SIGNATURE_1
); 
 430                 else if (Command 
== 'b') 
 432                         WriteNextResponseByte('Y'); 
 434                         /* Send block size to the host */ 
 435                         WriteNextResponseByte(SPM_PAGESIZE 
>> 8); 
 436                         WriteNextResponseByte(SPM_PAGESIZE 
& 0xFF);              
 438                 else if (Command 
== 'e') 
 440                         /* Clear the application section of flash */ 
 441                         for (uint32_t CurrFlashAddress 
= 0; CurrFlashAddress 
< BOOT_START_ADDR
; CurrFlashAddress
++) 
 443                                 boot_page_erase(CurrFlashAddress
); 
 444                                 boot_spm_busy_wait(); 
 445                                 boot_page_write(CurrFlashAddress
); 
 446                                 boot_spm_busy_wait(); 
 448                                 CurrFlashAddress 
+= SPM_PAGESIZE
; 
 451                         /* Send confirmation byte back to the host */ 
 452                         WriteNextResponseByte('\r');             
 454                 else if (Command 
== 'l') 
 456                         /* Set the lock bits to those given by the host */ 
 457                         boot_lock_bits_set(FetchNextCommandByte()); 
 459                         /* Send confirmation byte back to the host */ 
 460                         WriteNextResponseByte('\r'); 
 462                 else if (Command 
== 'r') 
 464                         WriteNextResponseByte(boot_lock_fuse_bits_get(GET_LOCK_BITS
));           
 466                 else if (Command 
== 'F') 
 468                         WriteNextResponseByte(boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS
)); 
 470                 else if (Command 
== 'N') 
 472                         WriteNextResponseByte(boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS
));              
 474                 else if (Command 
== 'Q') 
 476                         WriteNextResponseByte(boot_lock_fuse_bits_get(GET_EXTENDED_FUSE_BITS
));          
 478                 else if (Command 
== 'C') 
 480                         /* Write the high byte to the current flash page */ 
 481                         boot_page_fill(CurrAddress
, FetchNextCommandByte()); 
 483                         /* Send confirmation byte back to the host */ 
 484                         WriteNextResponseByte('\r');             
 486                 else if (Command 
== 'c') 
 488                         /* Write the low byte to the current flash page */ 
 489                         boot_page_fill(CurrAddress 
| 1, FetchNextCommandByte()); 
 491                         /* Increment the address */ 
 494                         /* Send confirmation byte back to the host */ 
 495                         WriteNextResponseByte('\r');             
 497                 else if (Command 
== 'm') 
 499                         /* Commit the flash page to memory */ 
 500                         boot_page_write(CurrAddress
); 
 502                         /* Wait until write operation has completed */ 
 503                         boot_spm_busy_wait(); 
 505                         /* Send confirmation byte back to the host */ 
 506                         WriteNextResponseByte('\r');             
 508                 else if ((Command 
== 'B') || (Command 
== 'g')) 
 510                         /* Delegate the block write/read to a separate function for clarity */ 
 511                         ReadWriteMemoryBlock(Command
); 
 513                 else if (Command 
== 'R') 
 515                         #if (FLASHEND > 0xFFFF) 
 516                         uint16_t ProgramWord 
= pgm_read_word_far(CurrAddress
); 
 518                         uint16_t ProgramWord 
= pgm_read_word(CurrAddress
);                       
 521                         WriteNextResponseByte(ProgramWord 
>> 8); 
 522                         WriteNextResponseByte(ProgramWord 
& 0xFF); 
 524                 else if (Command 
== 'D') 
 526                         /* Read the byte from the endpoint and write it to the EEPROM */ 
 527                         eeprom_write_byte((uint8_t*)((uint16_t)(CurrAddress 
>> 1)), FetchNextCommandByte()); 
 529                         /* Increment the address after use */                    
 532                         /* Send confirmation byte back to the host */ 
 533                         WriteNextResponseByte('\r');             
 535                 else if (Command 
== 'd') 
 537                         /* Read the EEPROM byte and write it to the endpoint */ 
 538                         WriteNextResponseByte(eeprom_read_byte((uint8_t*)((uint16_t)(CurrAddress 
>> 1)))); 
 540                         /* Increment the address after use */ 
 543                 else if (Command 
== 27) 
 545                         /* Escape is sync, ignore */ 
 549                         /* Unknown command, return fail code */ 
 550                         WriteNextResponseByte('?'); 
 553                 /* Select the IN endpoint */ 
 554                 Endpoint_SelectEndpoint(CDC_TX_EPNUM
); 
 556                 /* Remember if the endpoint is completely full before clearing it */ 
 557                 bool IsEndpointFull 
= !(Endpoint_IsReadWriteAllowed()); 
 559                 /* Send the endpoint data to the host */ 
 562                 /* If a full endpoint's worth of data was sent, we need to send an empty packet afterwards to signal end of transfer */ 
 565                         while (!(Endpoint_IsINReady())) 
 567                                 if (USB_DeviceState 
== DEVICE_STATE_Unattached
) 
 574                 /* Wait until the data has been sent to the host */ 
 575                 while (!(Endpoint_IsINReady())) 
 577                         if (USB_DeviceState 
== DEVICE_STATE_Unattached
) 
 581                 /* Select the OUT endpoint */ 
 582                 Endpoint_SelectEndpoint(CDC_RX_EPNUM
); 
 584                 /* Acknowledge the command from the host */