
/*------------------------------------------------------------------------------------------------+

	Program: 		WRT54GL_add_on.c
		
	Description:	Add on electronics to be incorporated into the housing of a Linksys WRG54GL
					wireless router. Together with the DD-WRT special firmware for the router,
					the devices becomes a WLAN client that is able to switch on the connected PC,
					triggered by a SSH command entered by a remote user.
					
					Communication between the AVR and the Broadcom CPU in the WRT54GL is 
					established via the 2nd serial interface of the router (which is only available
					internally and does not connect to the outside world).
					
					As the AVR has many unused inputs (including 6x 10bit ADC inputs), much more
					applications are left open to be added later. Also there is much space left on
					the add-on PCB inside the router to add more components.

	Author:			Herbert Dingfelder, DL5NEG
	
	Plattform:		Atmel AVR ATmega8
	
	Clock-Rate:		3.686 MHz from external Crystal
	
	RS232 setting:	9k6 8N1 (default setting of the free serial interface in the WRT54GL)
							
	History:
		
	16.12.2008		- Start of development, based on NSLU2 interface software (version 2.0)
	
	17.12.2008		- Serial communication established between WRT and AVR
					- Commands ATPCB, ATPCOFF and HELP implemented and tested successfully
					
					-> Version 1.0 released
	
		
	Pin-Mapping on ATmega8:

		Pin 	Port	additional 		Usage in Application
	------------------------------------------------------------------------------------------------
		1		PC6		Reset		
		2		PD0     RXD				RS232 RX
		3		PD1     TXD				RS232 RX
		4		PD2						output to red LED (active low)	
		5		PD3						
		6		PD4						
		7		VCC				
		8		GND			
		9		PB6		XTAL1			XTAL				
		10		PB7		XTAL2			XTAL
		11		PD5
		12		PD6
		13		PD7
		14		PB0						output to optical coupler to PC power switch (active high)						
		15		PB1						
		16		PB2							
		17		PB3		MOSI			
		18		PB4		MISO				
		19		PB5		SCK					
		20		AVCC						
		21		AREF
		22		GND
		23		PC0
		24		PC1
		25		PC2
		26		PC3
		27		PC4
		28		PC5						
		
														
-------------------------------------------------------------------------------------------------*/


#include <avr/io.h>			// The io.h already includes the io-avr.h and the iomacros.h !!!
							// Also the correct address to token (e.g. TIFR = $36) include
							// file is automatically loaded 
							// (the device must be specified in the makefile!)

#include <avr/pgmspace.h> 	// For storing strings in the flash memory

#include <avr/interrupt.h>	// For using interupts, e.g. for incoming RS232 data handling

#include <string.h>			// For dealing with strings (arrays of characters)

#include <stdio.h>



// ----- Compiler Switches -----

//#define DEBUG_MODE									// Compiler switch for debugging


// ----- Defines for better code readability -----

	#define CR					13						// ASCII(13) = CR
	#define LF					10						// ASCII(10) = LF
	
	#define UART_BUF_LENGTH		128						// max. 128 characters for command line entrys

	#define LED_PORT 			PORTD					// red LED is connected to PD2
	#define LED_DIR				DDRD
	#define LED_RED				_BV(2)

	#define PC_PORT 			PORTB					// PC power switch is connected to PB0
	#define PC_DIR				DDRB
	#define PC_PWR_SW			_BV(0)


// ----- Global variables -----

	char uart_buffer[UART_BUF_LENGTH];
	unsigned char uart_pos;
	

// ----- Prototypes for functions -----

	void pause(unsigned int n);
	void reset_handler(void);
	void send_ser(unsigned char sendbyte);
	void send_ser_string(char *send_string);
	void send_help_text(void);
	

// =================================== Main program starts here ===================================
int main(void) 
{

	reset_handler();									// Initialize all necessary parameters

	
	LED_PORT &= ~LED_RED;								// switch on LED for 3 seconds 
	pause(3000);
	LED_PORT |=  LED_RED;								// switch off LED
	
								
	send_help_text();									// Notivy the host that the AVR is working 
														// and show available commands
		
				
	for(;;);											// Endless loop - From now on all actions are triggered
														// via Interupts from incomming commands via RS232
	
	
}

//=================================== Subroutines from here on ====================================

//---------------------------------------- reset handler ------------------------------------------
void reset_handler(void)
{

	//------------------- configure GPIOs and set to their default settings -----------------------
		
	LED_DIR  |=  LED_RED;								// configure LED GPIO as output
	LED_PORT |=  LED_RED;								// switch off LEDs and switch outputs (output is active low)
	
	PC_DIR   |=  PC_PWR_SW;								// configure PC power switch GPIO as output
	PC_PORT  &= ~PC_PWR_SW;								// deactive the optical coupler that connects to the PC power switch
														// (output is active high)

	
	//--------------------------- configure UART (RS232-Interfache) -------------------------------
	
	UBRRH = 0;
	UBRRL = 23;											// UART Baudrate 23 => 9k6 at 3.686 MHz ref clock
									
	UCSRB = _BV(RXEN) | _BV(TXEN) | _BV(RXCIE); 		// UART RX and TX on and RX interupt on
														// (The TX output pin is automatically set as 
														// output by enabling the UART TX)									
			
	//----------------------------- Enable Interupts in general -----------------------------------
	
	sei();
			
}


//-------------------------------------------- pause ----------------------------------------------
// waits for n milliseconds (pause for 1ms to 65sec)
void pause(unsigned int n)
{

	unsigned int t;

	TCCR0 = 0x03; 										// 8bit counter on, prescaler factor 64 
	
	for( t=0 ; t<n ; t++ )								// the loop lasts 1ms -> perform the loop n times
	{

		TIFR = _BV(TOV0);								// reset the overflow flag for the 8bit counter 	
 		 	
		TCNT0 = 198;									// preset the counter to 198 -> 8bit overflow after 1ms
				
		while( !( TIFR & _BV(TOV0) ) );					// wait in loop until overflow in timer0
														// (i.e. wait for TOV0 Bit is set in TIFR) 
	}
		
}


//------------------------------- UART receiver complete interrupt --------------------------------
SIGNAL(SIG_UART_RECV)
{
	
	unsigned char uart_char='\0';
		
	uart_char=UDR;										// read received character from UART input buffer

	
	if( uart_char >= 'a' && uart_char <='z' )			// convert lower case characters to upper case, so
		uart_char -= ( 'a' - 'A' );						// the input is not case-sensitive any more
		
	
	if( uart_pos< (UART_BUF_LENGTH-1) && uart_char>=32)	// take over the new character into the buffer
	{													// if the buffer is not full and no ctrl character
		uart_buffer[uart_pos] = uart_char;
		uart_pos++;
	}
	
	if( uart_char==LF )									// if the received character was a LF (ASCII 10) which is
	{													// the final termination of a string which is sent from
														// the WRT54GL with the "echo Text > /dev/tts/1" command
														// (The the Text a CR + LF is always added automatically)
		
							 
		// ----------------------------- check for PC boot command --------------------------------		
		
		if( strncmp( uart_buffer, "ATPCB", 5 ) == 0 )	// if the atpcb (= PC boot) command was received
		{
			
			send_ser_string("PC boot command received! \r\n");	// trace output for debugging
			
			PC_PORT  |=  PC_PWR_SW;						// activate the optical coupler that connects to the PC power switch
			LED_PORT &= ~LED_RED;						// switch on LED 
			
			pause(1000);								// wait one second (long enough to switch on the PC,
														// short enough to avoid a forced-off reaction from the PC)
			
			PC_PORT  &= ~PC_PWR_SW;						// deactivate the optical coupler that connects to the PC power switch
			LED_PORT |=  LED_RED;						// switch off LED
					
			
		}
						
						
		// ----------------------------- check for PC forced-off command --------------------------
		// (This is a emergency command. If the PC does not react any more, it can be forced off by holding
		// the power switch via the optical coupler for more than 6 seconds. All PCs do a power off then.)
			
		if( strncmp( uart_buffer, "ATPCOFF", 7 ) == 0 )	// if the atpcoff (= PC forced off) command was received
		{
			
			send_ser_string("PC forced off command received! \r\n");	// trace output for debugging
			
			PC_PORT  |=  PC_PWR_SW;						// activate the optical coupler that connects to the PC power switch
			LED_PORT &= ~LED_RED;						// switch on LED 
			
			pause(10000);								// wait 10 seconds (long enough force the PC off to switch on the PC,
														// short enough to avoid a forced-off reaction from the PC)
			
			PC_PORT  &= ~PC_PWR_SW;						// deactivate the optical coupler that connects to the PC power switch
			LED_PORT |=  LED_RED;						// switch off LED
					
			
		}
						
						
		// ------------------------------------ check for help command ----------------------------
		// (This is a emergency command. If the PC does not react any more, it can be forced off by holding
		// the power switch via the optical coupler for more than 6 seconds. All PCs do a power off then.)
			
		if( strncmp( uart_buffer, "HELP", 4 ) == 0 )	// if the help command was received
		{
			
			send_help_text();
			
			LED_PORT &= ~LED_RED;						// switch on LED 
			pause(200);									// keep it on for a short flash
			LED_PORT |=  LED_RED;						// switch off LED
					
			
		}
										
						
		// ----------------------------------------------------------------------------------------
		// after a LF we start with a new (empty) buffer at the first postion
		memset( uart_buffer, ' ', UART_BUF_LENGTH );	// overwrite the uart buffer with all spaces
		uart_pos = 0;									// reset the postion pointer for the uart buffer
		
	}
		
}


//------------------------------ send one character via RS232 -------------------------------------
// Waits until the TX buffer in the UART is free, then transmits the character
void send_ser(unsigned char sendbyte)
{

	while( !(UCSRA & _BV(UDRE) ) );						// wait for UART data register empty
		
	UDR = sendbyte;										// send the character via UART
	
}



// ------------------------------------ send string to RS232 --------------------------------------
void send_ser_string(char *send_string)
{
	
	char send_char;
	
	while( (send_char = *send_string)!='\0' )
	{
		send_ser(send_char);
		send_string++;
	}

}

// --------------------------------- send help text via serial port -------------------------------
void send_help_text(void)
{

	send_ser_string("\r\n\nAVR Mega8 as WRT54GL add-on is alive...\r\n");		 
	send_ser_string("(c) 2008 by Herbert Dingfelder, DL5NEG\r\n\n");		 
	send_ser_string("Available commands:\r\n");		 
	send_ser_string("ATPCB   Starts the connected PC\r\n");
	send_ser_string("ATPCOFF Forces the PC hard off\r\n");		 
	send_ser_string("HELP    Shows this help text\r\n\n");		 	
	
}

