/**
 * \file bootloader/bootloaderconfig.h
 * \brief This file (together with some settings in Makefile) configures the
 * boot loader according to the hardware.
 *
 * This file contains (besides the hardware configuration normally found in
 * usbconfig.h) two functions or macros: bootLoaderInit() and
 * bootLoaderCondition(). Whether you implement them as macros or as static
 * inline functions is up to you, decide based on code size and convenience.
 *
 * bootLoaderInit() is called as one of the first actions after reset. It should
 * be a minimum initialization of the hardware so that the boot loader condition
 * can be read. This will usually consist of activating a pull-up resistor for an
 * external jumper which selects boot loader mode.
 *
 * bootLoaderCondition() is called immediately after initialization and in each
 * main loop iteration. If it returns TRUE, the boot loader will be active. If it
 * returns FALSE, the boot loader jumps to address 0 (the loaded application)
 * immediately.
 *
 * For compatibility with Thomas Fischl's avrusbboot, we also support the macro
 * names BOOTLOADER_INIT and BOOTLOADER_CONDITION for this functionality. If
 * these macros are defined, the boot loader usees them.
 *
 * \author Ronald Schaten <ronald@schatenseite.de>
 * \version $Id$
 *
 * License: GNU GPL v2 (see License.txt)
 */

#ifndef __bootloaderconfig_h_included__
#define __bootloaderconfig_h_included__

/* ---------------------------- Hardware Config ---------------------------- */

/** This is the port where the USB bus is connected. When you configure it to
 * "B", the registers PORTB, PINB and DDRB will be used.
 */
#define USB_CFG_IOPORTNAME      D
/** This is the bit number in USB_CFG_IOPORT where the USB D- line is connected.
 * This may be any bit in the port.
 */
#define USB_CFG_DMINUS_BIT      0
/** This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected.
 * This may be any bit in the port. Please note that D+ must also be connected
 * to interrupt pin INT0!
 */
#define USB_CFG_DPLUS_BIT       2
/** Clock rate of the AVR in MHz. Legal values are 12000, 16000 or 16500.
 * The 16.5 MHz version of the code requires no crystal, it tolerates +/- 1%
 * deviation from the nominal frequency. All other rates require a precision
 * of 2000 ppm and thus a crystal!
 * Default if not specified: 12 MHz
 */
#define USB_CFG_CLOCK_KHZ       (F_CPU/1000)

/* ----------------------- Optional Hardware Config ------------------------ */

/* If you connect the 1.5k pullup resistor from D- to a port pin instead of
 * V+, you can connect and disconnect the device from firmware by calling
 * the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h).
 * This constant defines the port on which the pullup resistor is connected.
 */
/* #define USB_CFG_PULLUP_IOPORTNAME   D */
/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined
 * above) where the 1.5k pullup resistor is connected. See description
 * above for details.
 */
/* #define USB_CFG_PULLUP_BIT          4 */

/* ------------------------------------------------------------------------- */
/* ---------------------- feature / code size options ---------------------- */
/* ------------------------------------------------------------------------- */

/** If HAVE_EEPROM_PAGED_ACCESS is defined to 1, page mode access to EEPROM is
 * compiled in. Whether page mode or byte mode access is used by AVRDUDE
 * depends on the target device. Page mode is only used if the device supports
 * it, e.g. for the ATMega88, 168 etc. You can save quite a bit of memory by
 * disabling page mode EEPROM access. Costs ~ 138 bytes.
 */
#define HAVE_EEPROM_PAGED_ACCESS    1
/** If HAVE_EEPROM_BYTE_ACCESS is defined to 1, byte mode access to EEPROM is
 * compiled in. Byte mode is only used if the device (as identified by its
 * signature) does not support page mode for EEPROM. It is required for
 * accessing the EEPROM on the ATMega8. Costs ~54 bytes.
 */
#define HAVE_EEPROM_BYTE_ACCESS     1
/** If this macro is defined to 1, the boot loader will exit shortly after the
 * programmer closes the connection to the device. Costs ~36 bytes.
 */
#define BOOTLOADER_CAN_EXIT         1
/** This macro defines the signature bytes returned by the emulated USBasp to
 * the programmer software. They should match the actual device at least in
 * memory size and features. If you don't define this, values for ATMega8,
 * ATMega88, ATMega168 and ATMega328 are guessed correctly.
 */
#define SIGNATURE_BYTES             0x1e, 0x95, 0x02, 0     /* ATMega32 */

/* The following block guesses feature options so that the resulting code
 * should fit into 2k bytes boot block with the given device and clock rate.
 * Activate by passing "-DUSE_AUTOCONFIG=1" to the compiler.
 * This requires gcc 3.4.6 for small enough code size!
 */
#if USE_AUTOCONFIG
#   undef HAVE_EEPROM_PAGED_ACCESS
#   define HAVE_EEPROM_PAGED_ACCESS     (USB_CFG_CLOCK_KHZ >= 16000)
#   undef HAVE_EEPROM_BYTE_ACCESS
#   define HAVE_EEPROM_BYTE_ACCESS      1
#   undef BOOTLOADER_CAN_EXIT
#   define BOOTLOADER_CAN_EXIT          1
#   undef SIGNATURE_BYTES
#endif /* USE_AUTOCONFIG */

/* ------------------------------------------------------------------------- */

#ifndef __ASSEMBLER__   /* assembler cannot parse function definitions */

#ifdef MODELSUNTYPE5
#   define SRCLOCKON    PORTC |= (1 << PC5)
#   define SRCLOCKOFF   PORTC &= ~(1 << PC5)
#   define SRDATAON     PORTC |= (1 << PC6)
#   define SRDATAOFF    PORTC &= ~(1 << PC6)
#   define SRSTROBEON   PORTC |= (1 << PC7)
#   define SRSTROBEOFF  PORTC &= ~(1 << PC7)
#   define KEYROW 16
#endif
uint8_t ledbrightness = 0;  ///< brightness level of the leds, between 0 and 127
uint8_t ledcounter = 0;     ///< needed for PWM operation
int8_t leddirection = 1;    ///< indicates if leds fade higher or lower

/**
 * Prepare IO-ports for detection of bootloader-condition, which happens in
 * bootLoaderCondition().
 */
#ifdef MODELIBMMODELM
static inline void bootLoaderInit(void) {
    // switch on leds
    DDRD  |= (1 << PIND4) | (1 << PIND5) | (1 << PIND6);
    PORTD &= ~((1 << PIND4) | (1 << PIND5) | (1 << PIND6));
    // choose matrix position for hotkey. we use KEY_D, so we set row 4
    // and later look for column 5
    DDRA  = (1 << DDA4);
    PORTA = ~(1 << PINA4);
    DDRB &= ~(1 << PB5);
    PORTB |= (1 << PB5);
    DDRC  = 0x00;
    PORTC = 0xff;
}
#endif
#ifdef MODELMAYHEM
static inline void bootLoaderInit(void) {
    // switch on leds
    DDRD  |= (1 << PIND1) | (1 << PIND3) | (1 << PIND6) | (1 << PIND7);
    PORTD &= ~((1 << PIND1) | (1 << PIND3) | (1 << PIND6) | (1 << PIND7));
    // choose matrix position for hotkey. we use KEY_D, so we set row 4
    // and later look for column 5
    DDRA  = (1 << DDA4);
    PORTA = ~(1 << PINA4);
    DDRB &= ~(1 << PB5);
    PORTB |= (1 << PB5);
    DDRC  = 0x00;
    PORTC = 0xff;
}
#endif
#ifdef MODELSUNTYPE5
static inline void bootLoaderInit(void) {
    // configure ports
    DDRA = 0x00;
    PORTA = 0xff;
    DDRB = (1 << PB4) |(1 << PB5) | (1 << PB6) | (1 << PB7);
    PORTB = (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3);
    DDRC = (1 << PC5) | (1 << PC6) | (1 << PC7);
    PORTC = (1 << PC0) | (1 << PC1) | (1 << PC2) | (1 << PC3) | (1 << PC4);
    DDRD &= ~((1 << PD4) | (1 << PD5) | (1 << PD6) | (1 << PD7));
    PORTD |= (1 << PD4) | (1 << PD5) | (1 << PD6) | (1 << PD7);

    // switch on leds
    PORTB &= ~((1 << PB4) | (1 << PB5) | (1 << PB6) | (1 << PB7));

    // choose matrix position for hotkey. we use KEY_D, so we set all
    // rows to 1 except for row 16 (KEYROW) and later look for column 12
    SRDATAON;
    SRSTROBEOFF;
    uint8_t i = 0;
    for (i = 0; i < (21 - KEYROW); i++) {
        SRCLOCKON; SRCLOCKOFF;
    }
    SRDATAOFF;
    SRCLOCKON; SRCLOCKOFF;
    SRDATAON;
    for (i = 0; i < KEYROW; i++) {
        SRCLOCKON; SRCLOCKOFF;
    }
    SRSTROBEON; SRSTROBEOFF;
}
#endif
#ifdef MODELIBMHOST
static inline void bootLoaderInit(void) {
    // choose matrix position for hotkey. we use KEY_D, so we set row 13
    DDRA  = 0x00;
    PORTA = 0xff;
    DDRC  = (1 << DDC2);
    PORTC = ~(1 << PINC2);
    DDRD  &= ~((1 << PIND4) | (1 << PIND5) | (1 << PIND6) | (1 << PIND7));
    PORTD |= ((1 << PIND4) | (1 << PIND5) | (1 << PIND6) | (1 << PIND7));
    // and later look for column 6
    DDRB  &= ~(1 << PB6);
    PORTB |= (1 << PB6);
}
#endif

/**
 * Clean up after boot loader action. In this case: switch off all LEDs.
 */
#ifdef MODELIBMMODELM
static inline void bootLoaderExit(void) {
    // switch off leds
    PORTD |= (1 << PIND4) | (1 << PIND5) | (1 << PIND6);
}
#endif
#ifdef MODELMAYHEM
static inline void bootLoaderExit(void) {
    // switch off leds
    PORTD |= (1 << PIND1) | (1 << PIND3) | (1 << PIND6) | (1 << PIND7);
}
#endif
#ifdef MODELSUNTYPE5
static inline void bootLoaderExit(void) {
    // switch off leds
    PORTB |= (1 << PB4) | (1 << PB5) | (1 << PB6) | (1 << PB7);
}
#endif
#ifdef MODELIBMHOST
static inline void bootLoaderExit(void) {
    // do nothing
}
#endif

/**
 * Check if conditions for boot loader are met. This function is called in an
 * endless loop, so we use our spare time to display a nice running light on
 * the LEDs.
 * \return 1 if bootloader should be active, 0 otherwise
 */
#ifdef MODELIBMMODELM
static inline uint8_t bootLoaderCondition() {
    // look for pin 5
    if (!(PINB & (1 << PINB5))) {
        // boot loader active, fade leds
        ledcounter++;
        if (ledcounter < ledbrightness) {
            // switch on leds
            PORTD &= ~((1 << PIND4) | (1 << PIND5) | (1 << PIND6));
        } else {
            // switch off leds
            PORTD |= (1 << PIND4) | (1 << PIND5) | (1 << PIND6);
        }
        if (ledcounter == 255) {
            ledcounter = 0;
            ledbrightness += leddirection;
            if (ledbrightness == 255) {
                leddirection = -leddirection;
                ledbrightness += leddirection;
            }
        }
        return 1;
    } else {
        // no boot loader
        return 0;
    }
}
#endif
#ifdef MODELMAYHEM
static inline uint8_t bootLoaderCondition() {
    // look for pin 5
    if (!(PINB & (1 << PINB5))) {
        // boot loader active, fade leds
        ledcounter++;
        if (ledcounter < ledbrightness) {
            // switch on leds
            PORTD &= ~((1 << PIND1) | (1 << PIND3) | (1 << PIND6) | (1 << PIND7));
        } else {
            // switch off leds
            PORTD |= (1 << PIND1) | (1 << PIND3) | (1 << PIND6) | (1 << PIND7);
        }
        if (ledcounter == 255) {
            ledcounter = 0;
            ledbrightness += leddirection;
            if (ledbrightness == 255) {
                leddirection = -leddirection;
                ledbrightness += leddirection;
            }
        }
        return 1;
    } else {
        // no boot loader
        return 0;
    }
}
#endif
#ifdef MODELSUNTYPE5
static inline uint8_t bootLoaderCondition() {
    // look for pin 12, KEY_D
    if (!(PINC & (1 << PINC4))) {
        // boot loader active, fade leds
        ledcounter++;
        if (ledcounter < ledbrightness) {
            // switch on leds
            PORTB &= ~((1 << PINB4) | (1 << PINB5) | (1 << PINB6) | (1 << PINB7));
        } else {
            // switch off leds
            PORTB |= (1 << PINB4) | (1 << PINB5) | (1 << PINB6) | (1 << PINB7);
        }
        if (ledcounter == 255) {
            ledcounter = 0;
            ledbrightness += leddirection;
            if (ledbrightness == 255) {
                leddirection = -leddirection;
                ledbrightness += leddirection;
            }
        }
        return 1;
    } else {
        // no boot loader
        return 0;
    }
}
#endif
#ifdef MODELIBMHOST
static inline uint8_t bootLoaderCondition() {
    // look for pin 6
    if (!(PINB & (1 << PINB6))) {
        // boot loader active
        return 1;
    } else {
        // no boot loader
        return 0;
    }
}
#endif

#endif /* __ASSEMBLER__ */

/* ------------------------------------------------------------------------- */

#endif /* __bootloader_h_included__ */
