]> git.donarmstrong.com Git - qmk_firmware.git/blobdiff - tmk_core/common/avr/bootloader.c
Usbasploader bootloader option addition (#6304)
[qmk_firmware.git] / tmk_core / common / avr / bootloader.c
index ad547b9853c9cbed234c225d49214c831782761b..29036f7c5a87ef518763402a2863cae47c2886e9 100644 (file)
@@ -1,22 +1,23 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include <avr/io.h>
+#include <avr/eeprom.h>
 #include <avr/interrupt.h>
 #include <avr/wdt.h>
 #include <util/delay.h>
 #include "bootloader.h"
+#include <avr/boot.h>
 
 #ifdef PROTOCOL_LUFA
 #include <LUFA/Drivers/USB/USB.h>
 #endif
 
 
-/* Bootloader Size in *bytes*
+/** \brief Bootloader Size in *bytes*
  *
  * AVR Boot section size are defined by setting BOOTSZ fuse in fact. Consult with your MCU datasheet.
  * Note that 'Word'(2 bytes) size and address are used in datasheet while TMK uses 'Byte'.
  *
- *
  * Size of Bootloaders in bytes:
  *   Atmel DFU loader(ATmega32U4)   4096
  *   Atmel DFU loader(AT90USB128)   8192
  *   Teensy   halfKay(ATmega32U4)   512
  *   Teensy++ halfKay(AT90USB128)   1024
  *
- *
  * AVR Boot section is located at the end of Flash memory like the followings.
  *
- *
  * byte     Atmel/LUFA(ATMega32u4)          byte     Atmel(AT90SUB128)
  * 0x0000   +---------------+               0x00000  +---------------+
  *          |               |                        |               |
  *          |  Bootloader   | 512B                   |  Bootloader   | 1KB
  * 0x7FFF   +---------------+               0x1FFFF  +---------------+
  */
-#ifndef BOOTLOADER_SIZE
-#warning To use bootloader_jump() you need to define BOOTLOADER_SIZE in config.h.
-#define BOOTLOADER_SIZE     4096
+#define FLASH_SIZE (FLASHEND + 1L)
+
+#if !defined(BOOTLOADER_SIZE)
+    uint16_t bootloader_start;
 #endif
 
-#define FLASH_SIZE          (FLASHEND + 1L)
-#define BOOTLOADER_START    (FLASH_SIZE - BOOTLOADER_SIZE)
+#define BOOT_SIZE_256  0b110
+#define BOOT_SIZE_512  0b100
+#define BOOT_SIZE_1024 0b010
+#define BOOT_SIZE_2048 0b000
 
+//compatibility between ATMega8 and ATMega88
+#if !defined (MCUCSR)
+    #if defined (MCUSR)
+        #define MCUCSR MCUSR
+    #endif
+#endif
 
-/*
- * Entering the Bootloader via Software
+/** \brief Entering the Bootloader via Software
+ *
  * http://www.fourwalledcubicle.com/files/LUFA/Doc/120730/html/_page__software_bootloader_start.html
  */
 #define BOOTLOADER_RESET_KEY 0xB007B007
-uint32_t reset_key  __attribute__ ((section (".noinit")));
+uint32_t reset_key  __attribute__ ((section (".noinit,\"aw\",@nobits;")));
 
-/* initialize MCU status by watchdog reset */
+/** \brief initialize MCU status by watchdog reset
+ *
+ * FIXME: needs doc
+ */
 void bootloader_jump(void) {
-    #ifndef CATERINA_BOOTLOADER
 
-        #ifdef PROTOCOL_LUFA
-            USB_Disable();
-            cli();
-            _delay_ms(2000);
-        #endif
+    #if !defined(BOOTLOADER_SIZE)
+        uint8_t high_fuse = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS);
+
+        if (high_fuse & BOOT_SIZE_256) {
+            bootloader_start = (FLASH_SIZE - 512) >> 1;
+        } else if (high_fuse & BOOT_SIZE_512) {
+            bootloader_start = (FLASH_SIZE - 1024) >> 1;
+        } else if (high_fuse & BOOT_SIZE_1024) {
+            bootloader_start = (FLASH_SIZE - 2048) >> 1;
+        } else {
+            bootloader_start = (FLASH_SIZE - 4096) >> 1;
+        }
+    #endif
 
-        #ifdef PROTOCOL_PJRC
-            cli();
-            UDCON = 1;
-            USBCON = (1<<FRZCLK);
-            UCSR1B = 0;
-            _delay_ms(5);
+    // Something like this might work, but it compiled larger than the block above
+    // bootloader_start = FLASH_SIZE - (256 << (~high_fuse & 0b110 >> 1));
+
+
+    #if defined(BOOTLOADER_HALFKAY)
+        //  http://www.pjrc.com/teensy/jump_to_bootloader.html
+        cli();
+        // disable watchdog, if enabled (it's not)
+        // disable all peripherals
+        // a shutdown call might make sense here
+        UDCON = 1;
+        USBCON = (1<<FRZCLK);  // disable USB
+        UCSR1B = 0;
+        _delay_ms(5);
+        #if defined(__AVR_AT90USB162__)                // Teensy 1.0
+            EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0;
+            TIMSK0 = 0; TIMSK1 = 0; UCSR1B = 0;
+            DDRB = 0; DDRC = 0; DDRD = 0;
+            PORTB = 0; PORTC = 0; PORTD = 0;
+            asm volatile("jmp 0x3E00");
+        #elif defined(__AVR_ATmega32U4__)              // Teensy 2.0
+            EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0;
+            TIMSK0 = 0; TIMSK1 = 0; TIMSK3 = 0; TIMSK4 = 0; UCSR1B = 0; TWCR = 0;
+            DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0; TWCR = 0;
+            PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0;
+            asm volatile("jmp 0x7E00");
+        #elif defined(__AVR_AT90USB646__)              // Teensy++ 1.0
+            EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0;
+            TIMSK0 = 0; TIMSK1 = 0; TIMSK2 = 0; TIMSK3 = 0; UCSR1B = 0; TWCR = 0;
+            DDRA = 0; DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0;
+            PORTA = 0; PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0;
+            asm volatile("jmp 0xFC00");
+        #elif defined(__AVR_AT90USB1286__)             // Teensy++ 2.0
+            EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0;
+            TIMSK0 = 0; TIMSK1 = 0; TIMSK2 = 0; TIMSK3 = 0; UCSR1B = 0; TWCR = 0;
+            DDRA = 0; DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0;
+            PORTA = 0; PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0;
+            asm volatile("jmp 0x1FC00");
         #endif
 
-        // watchdog reset
-        reset_key = BOOTLOADER_RESET_KEY;
-        wdt_enable(WDTO_250MS);
-        for (;;);
-
-    #else
+    #elif defined(BOOTLOADER_CATERINA)
         // this block may be optional
         // TODO: figure it out
 
@@ -111,95 +156,84 @@ void bootloader_jump(void) {
 
         while(1) {} // wait for watchdog timer to trigger
 
+    #elif defined(BOOTLOADER_USBASP)
+        // Taken with permission of Stephan Baerwolf from https://github.com/tinyusbboard/API/blob/master/apipage.c
+        wdt_enable(WDTO_15MS);
+        wdt_reset();
+        asm volatile (
+            "cli                    \n\t"
+            "ldi    r29 ,       %[ramendhi] \n\t"
+            "ldi    r28 ,       %[ramendlo] \n\t"
+            #if (FLASHEND>131071)
+                "ldi    r18 ,       %[bootaddrhi]   \n\t"
+                "st     Y+,         r18     \n\t"
+            #endif
+            "ldi    r18 ,       %[bootaddrme]   \n\t"
+            "st     Y+,     r18     \n\t"
+            "ldi    r18 ,       %[bootaddrlo]   \n\t"
+            "st     Y+,     r18     \n\t"
+            "out    %[mcucsrio],    __zero_reg__    \n\t"
+            "bootloader_startup_loop%=:         \n\t"
+            "rjmp bootloader_startup_loop%=     \n\t"
+            : 
+            : [mcucsrio]    "I" (_SFR_IO_ADDR(MCUCSR)),
+            #if (FLASHEND>131071)
+                [ramendhi]    "M" (((RAMEND - 2) >> 8) & 0xff),
+                [ramendlo]    "M" (((RAMEND - 2) >> 0) & 0xff),
+                [bootaddrhi]  "M" ((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >>16) & 0xff),
+            #else
+                [ramendhi]    "M" (((RAMEND - 1) >> 8) & 0xff),
+                [ramendlo]    "M" (((RAMEND - 1) >> 0) & 0xff),
+            #endif
+            [bootaddrme]  "M" ((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 8) & 0xff),
+            [bootaddrlo]  "M" ((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 0) & 0xff)
+        );
+
+    #else // Assume remaining boards are DFU, even if the flag isn't set
+
+        #if !(defined(__AVR_ATmega32A__) || defined(__AVR_ATmega328P__)) // no USB - maybe BOOTLOADER_BOOTLOADHID instead though?
+            UDCON = 1;
+            USBCON = (1<<FRZCLK);  // disable USB
+            UCSR1B = 0;
+            _delay_ms(5); // 5 seems to work fine
+        #endif
+
+        #ifdef BOOTLOADER_BOOTLOADHID
+            // force bootloadHID to stay in bootloader mode, so that it waits
+            // for a new firmware to be flashed
+            eeprom_write_byte((uint8_t *)1, 0x00);
+        #endif
+
+        // watchdog reset
+        reset_key = BOOTLOADER_RESET_KEY;
+        wdt_enable(WDTO_250MS);
+        for (;;);
     #endif
+
 }
 
 /* this runs before main() */
 void bootloader_jump_after_watchdog_reset(void) __attribute__ ((used, naked, section (".init3")));
 void bootloader_jump_after_watchdog_reset(void)
 {
-    if ((MCUSR & (1<<WDRF)) && reset_key == BOOTLOADER_RESET_KEY) {
-        reset_key = 0;
-
-        // My custom USBasploader requires this to come up.
-        MCUSR = 0;
-
-        // Seems like Teensy halfkay loader requires clearing WDRF and disabling watchdog.
-        MCUSR &= ~(1<<WDRF);
-        wdt_disable();
-
-        // This is compled into 'icall', address should be in word unit, not byte.
-        ((void (*)(void))(BOOTLOADER_START/2))();
-    }
-}
-
+    #ifndef BOOTLOADER_HALFKAY
+        if ((MCUCSR & (1<<WDRF)) && reset_key == BOOTLOADER_RESET_KEY) {
+            reset_key = 0;
 
-#if 0
-/* Jumping To The Bootloader
- * http://www.pjrc.com/teensy/jump_to_bootloader.html
- *
- * This method doen't work when using LUFA. idk why.
- * - needs to initialize more regisers or interrupt setting?
- */
-void bootloader_jump(void) {
-#ifdef PROTOCOL_LUFA
-    USB_Disable();
-    cli();
-    _delay_ms(2000);
-#endif
+            // My custom USBasploader requires this to come up.
+            MCUCSR = 0;
 
-#ifdef PROTOCOL_PJRC
-    cli();
-    UDCON = 1;
-    USBCON = (1<<FRZCLK);
-    UCSR1B = 0;
-    _delay_ms(5);
-#endif
+            // Seems like Teensy halfkay loader requires clearing WDRF and disabling watchdog.
+            MCUCSR &= ~(1<<WDRF);
+            wdt_disable();
 
-    /*
-     * Initialize
-     */
-#if defined(__AVR_AT90USB162__)
-    EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0;
-    TIMSK0 = 0; TIMSK1 = 0; UCSR1B = 0;
-    DDRB = 0; DDRC = 0; DDRD = 0;
-    PORTB = 0; PORTC = 0; PORTD = 0;
-#elif defined(__AVR_ATmega32U4__)
-    EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0;
-    TIMSK0 = 0; TIMSK1 = 0; TIMSK3 = 0; TIMSK4 = 0; UCSR1B = 0; TWCR = 0;
-    DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0; TWCR = 0;
-    PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0;
-#elif defined(__AVR_AT90USB646__)
-    EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0;
-    TIMSK0 = 0; TIMSK1 = 0; TIMSK2 = 0; TIMSK3 = 0; UCSR1B = 0; TWCR = 0;
-    DDRA = 0; DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0;
-    PORTA = 0; PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0;
-#elif defined(__AVR_AT90USB1286__)
-    EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0;
-    TIMSK0 = 0; TIMSK1 = 0; TIMSK2 = 0; TIMSK3 = 0; UCSR1B = 0; TWCR = 0;
-    DDRA = 0; DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0;
-    PORTA = 0; PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0;
-#endif
-
-    /*
-     * USBaspLoader
-     */
-#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328P__)
-    // This makes custom USBasploader come up.
-    MCUSR = 0;
-
-    // initialize ports
-    PORTB = 0; PORTC= 0; PORTD = 0;
-    DDRB = 0; DDRC= 0; DDRD = 0;
-
-    // disable interrupts
-    EIMSK = 0; EECR = 0; SPCR = 0;
-    ACSR = 0; SPMCSR = 0; WDTCSR = 0; PCICR = 0;
-    TIMSK0 = 0; TIMSK1 = 0; TIMSK2 = 0;
-    ADCSRA = 0; TWCR = 0; UCSR0B = 0;
-#endif
 
-    // This is compled into 'icall', address should be in word unit, not byte.
-    ((void (*)(void))(BOOTLOADER_START/2))();
+            // This is compled into 'icall', address should be in word unit, not byte.
+            #ifdef BOOTLOADER_SIZE
+                ((void (*)(void))( (FLASH_SIZE - BOOTLOADER_SIZE) >> 1))();
+            #else
+                asm("ijmp" :: "z" (bootloader_start));
+            #endif
+        }
+    #endif
 }
-#endif