/* Name: main.c
 * Project: PowerSwitch based on AVR USB driver from Christian Starkjohann
 * Author: Edsard Boelen
 * Creation Date: 2009-05-01
 * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
 */

#include <avr/io.h>

#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <avr/wdt.h>

#include "usbdrv.h"
#include "oddebug.h"
#include <util/delay.h>


/*
tips:
  _BV(3) is convert bit nr to byte value. -> 0x80
  program with:
  avrdude -p m88 -c usbasp -U flash:w:main.hex:i -U hfuse:w:0xdf:m -U lfuse:w:0xf7:m
*/

#define EEPROM_LOCATION (void *)37
#define DEBOUNCE_TIME 5


/* This is the ATmega88 version of the routine. Change for others. */
static void outputByte(uchar b)
{

    PORTC = (PORTC & ~0x7f) | (b & 0x7f);  // use portc c 0...6
	//PORTC = (PORTC & ~0x3f) | (b & 0x3f);  // use portc c 0...5
    //PORTD = (PORTD & ~0x30) | ((b << 4) & 0x30);

}

static void tdelay(void)
{
	_delay_ms(DEBOUNCE_TIME);

	usbPoll();
	_delay_ms(DEBOUNCE_TIME);
	usbPoll();
	_delay_ms(DEBOUNCE_TIME);

	usbPoll();
	_delay_ms(DEBOUNCE_TIME);
	usbPoll();
	_delay_ms(DEBOUNCE_TIME);

	usbPoll();
	_delay_ms(DEBOUNCE_TIME);
	usbPoll();
	_delay_ms(DEBOUNCE_TIME);

	usbPoll();
}

static void toggleOutput(int outnr)
{
	uchar   status = eeprom_read_byte(EEPROM_LOCATION);

	status ^= _BV(outnr); 
	eeprom_write_byte(EEPROM_LOCATION, status);

	outputByte(status);
	tdelay();
	tdelay();
	tdelay();

	tdelay();
	tdelay();
	tdelay();
}

static void checkButton(uchar port, int pin, int out)
{

	if (bit_is_clear(port, pin))
    {
		tdelay();
		if (bit_is_clear(port, pin))
		{

			toggleOutput(out);
		}
	}
}

/* buttons are located on pind, except for pd2 which is the usb interrupt*/
static void checkButtons(void)
{

    checkButton(PIND,0,0);
	checkButton(PIND,1,1);

	/* skip PD2 as that is the usb interrupt */
	checkButton(PIND,3,2);  
	checkButton(PIND,4,3);

	checkButton(PIND,6,4);
	checkButton(PIND,7,5);
	
}


static void computeOutputStatus(void)
{
uchar   status = eeprom_read_byte(EEPROM_LOCATION);

    outputByte(status);
}



USB_PUBLIC uchar usbFunctionSetup(uchar data[8])
{

usbRequest_t    *rq = (void *)data;
uchar           status = eeprom_read_byte(EEPROM_LOCATION);

static uchar    replyBuf[2];

    usbMsgPtr = replyBuf;
    if(rq->bRequest == 0){  /* ECHO */

        replyBuf[0] = rq->wValue.bytes[0];
        replyBuf[1] = rq->wValue.bytes[1];

        return 2;
    }
    if(rq->bRequest == 1){  /* GET_STATUS -> result = 2 bytes */

        replyBuf[0] = status;
        replyBuf[1] = 0;

        return 2;
    }
    if(rq->bRequest == 2 || rq->bRequest == 3){ /* SWITCH_ON or SWITCH_OFF, index = bit number */

        uchar bit = rq->wIndex.bytes[0] & 7;

        uchar mask = 1 << bit;
        /*uchar needChange, isOn = status & mask; */
        if(rq->bRequest == 2){  /* SWITCH_ON */

            status |= mask;
            eeprom_write_byte(EEPROM_LOCATION, status);
        }else{              /* SWITCH_OFF */

            status &= ~mask;
            eeprom_write_byte(EEPROM_LOCATION, status);
        }
    }
    computeOutputStatus();

    return 0;
}

/* allow some inter-device compatibility */
#if !defined TCCR0 && defined TCCR0B
#define TCCR0   TCCR0B
#endif
#if !defined TIFR && defined TIFR0
#define TIFR    TIFR0
#endif

int main(void)            
{

	uchar   i;
    DDRD = 0;   /* port D are all inputs */
	DDRC = 0xf3; /* port C are all outputs */

	DDRB = 0;  /* port B all inputs */
    PORTD = 0;

    PORTB = 0;          /* no pullups on USB pins */
	PORTC = 0;   /*  portc is sourcing */
	/* DDRC  = 0xff;     data direction is output */
/* We fake an USB disconnect by pulling D+ and D- to 0 during reset. This is
 * necessary if we had a watchdog reset or brownout reset to notify the host
 * that it should re-enumerate the device. Otherwise the host's and device's
 * concept of the device-ID would be out of sync.
 */
      /*DDRB = ~USBMASK;   set all pins as outputs except USB */

    computeOutputStatus();  /* set output status before we do the delay */
    usbDeviceDisconnect();  /* enforce re-enumeration, do this while interrupts are disabled! */
    i = 0;

    while(--i){         /* fake USB disconnect for > 500 ms */
        _delay_ms(2);
    }
    usbDeviceConnect();

    usbInit();
    sei();			/* enable interrupt */
    for(;;){    /* main event loop */
        usbPoll();

		checkButtons();
    }
    return 0;
}