//*****************************************************************************
//
// enet_lwip.c - Sample WebServer Application using lwIP.
//
// Copyright (c) 2009-2010 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
// 
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
// 
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
// 
// This is part of revision 5821 of the EK-LM3S9B92 Firmware Package.
//
//*****************************************************************************

#include "inc/hw_ethernet.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_nvic.h"
#include "inc/hw_types.h"
#include "driverlib/ethernet.h"
#include "driverlib/flash.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "utils/locator.h"
#include "utils/lwiplib.h"
#include "utils/uartstdio.h"
#include "httpserver_raw/httpd.h"

//*****************************************************************************
//
//! \addtogroup example_list
//! <h1>Ethernet with lwIP (enet_lwip)</h1>
//!
//! This example application demonstrates the operation of the Stellaris
//! Ethernet controller using the lwIP TCP/IP Stack.  DHCP is used to obtain
//! an Ethernet address.  If DHCP times out without obtaining an address,
//! AutoIP will be used to obtain a link-local address.  The address that is
//! selected will be shown on the UART.
//!
//! UART0, connected to the FTDI virtual COM port and running at 115,200,
//! 8-N-1, is used to display messages from this application.
//!
//! For additional details on lwIP, refer to the lwIP web page at:
//! http://savannah.nongnu.org/projects/lwip/
//
//*****************************************************************************

//*****************************************************************************
//
// Defines for setting up the system clock.
//
//*****************************************************************************
#define SYSTICKHZ               100
#define SYSTICKMS               (1000 / SYSTICKHZ)

//*****************************************************************************
//
// A twirling line used to indicate that DHCP/AutoIP address acquisition is in
// progress.
//
//*****************************************************************************
static char g_pcTwirl[4] = { '\\', '|', '/', '-' };

//*****************************************************************************
//
// The index into the twirling line array of the next line orientation to be
// printed.
//
//*****************************************************************************
static unsigned long g_ulTwirlPos = 0;

//*****************************************************************************
//
// The most recently assigned IP address.  This is used to detect when the IP
// address has changed (due to DHCP/AutoIP) so that the new address can be
// printed.
//
//*****************************************************************************
static unsigned long g_ulLastIPAddr = 0;

//*****************************************************************************
//
// The error routine that is called if the driver library encounters an error.
//
//*****************************************************************************
#ifdef DEBUG
void
__error__(char *pcFilename, unsigned long ulLine)
{
}
#endif

//*****************************************************************************
//
// Required by lwIP library to support any host-related timer functions.
//
//*****************************************************************************
void
lwIPHostTimerHandler(void)
{
    unsigned long ulIPAddress;

    //
    // Get the local IP address.
    //
    ulIPAddress = lwIPLocalIPAddrGet();

    //
    // See if an IP address has been assigned.
    //
    if(ulIPAddress == 0)
    {
        //
        // Draw a spinning line to indicate that the IP address is being
        // discoverd.
        //
        UARTprintf("\b%c", g_pcTwirl[g_ulTwirlPos]);

        //
        // Update the index into the twirl.
        //
        g_ulTwirlPos = (g_ulTwirlPos + 1) & 3;
    }

    //
    // Check if IP address has changed, and display if it has.
    //
    else if(ulIPAddress != g_ulLastIPAddr)
    {
        //
        // Display the new IP address.
        //
        UARTprintf("\rIP: %d.%d.%d.%d       \n", ulIPAddress & 0xff,
                   (ulIPAddress >> 8) & 0xff, (ulIPAddress >> 16) & 0xff,
                   (ulIPAddress >> 24) & 0xff);

        //
        // Save the new IP address.
        //
        g_ulLastIPAddr = ulIPAddress;

        //
        // Display the new network mask.
        //
        ulIPAddress = lwIPLocalNetMaskGet();
        UARTprintf("Netmask: %d.%d.%d.%d\n", ulIPAddress & 0xff,
                   (ulIPAddress >> 8) & 0xff, (ulIPAddress >> 16) & 0xff,
                   (ulIPAddress >> 24) & 0xff);

        //
        // Display the new gateway address.
        //
        ulIPAddress = lwIPLocalGWAddrGet();
        UARTprintf("Gateway: %d.%d.%d.%d\n", ulIPAddress & 0xff,
                   (ulIPAddress >> 8) & 0xff, (ulIPAddress >> 16) & 0xff,
                   (ulIPAddress >> 24) & 0xff);
    }
}

//*****************************************************************************
//
// The interrupt handler for the SysTick interrupt.
//
//*****************************************************************************
void
SysTickIntHandler(void)
{
    //
    // Call the lwIP timer handler.
    //
    lwIPTimer(SYSTICKMS);
}

//*****************************************************************************
//
//! Configures pin(s) for use by the Ethernet peripheral as MII signals.
//!
//! \param ulPort is the base address of the GPIO port.
//! \param ucPins is the bit-packed representation of the pin(s).
//!
//! The Ethernet peripheral on some parts provides a set of MII signals that
//! are used to connect to an external PHY.  This function provides a typical
//! configuration for the pins.
//!
//! The pin(s) are specified using a bit-packed byte, where each bit that is
//! set identifies the pin to be accessed, and where bit 0 of the byte
//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on.
//!
//! \note This cannot be used to turn any pin into an Ethernet MII pin; it only
//! configures an Ethernet MII pin for proper operation.
//!
//! \return None.
//
//*****************************************************************************
void
GPIOPinTypeEthernetMII(unsigned long ulPort, unsigned char ucPins)
{
    //
    // Make the pin(s) be peripheral controlled.
    //
    GPIODirModeSet(ulPort, ucPins, GPIO_DIR_MODE_HW);

    //
    // Set the pad(s) for standard push-pull operation.
    //
    GPIOPadConfigSet(ulPort, ucPins, GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_STD);
}

//*****************************************************************************
//
// This example demonstrates the use of the Ethernet Controller.
//
//*****************************************************************************
int
main(void)
{
    unsigned long ulUser0, ulUser1;
    unsigned char pucMACArray[8];

    //
    // Set the clocking to run directly from the crystal.
    //
    ROM_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
                       SYSCTL_XTAL_16MHZ);

    //
    // Initialize the UART.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    UARTStdioInit(0);
    UARTprintf("\033[2JEthernet with lwIP\n");

    //
    // Enable and Reset the Ethernet Controller.
    //
#define SYSCTL_PERIPH_MAC       0x20101000
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_MAC);
    ROM_SysCtlPeripheralReset(SYSCTL_PERIPH_MAC);

    //
    // Configure IO Mux Stuff for external PHY support
    //
    //  "PHYRSTn"       => "PE6"
    //  
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_7);
    GPIOPinWrite(GPIO_PORTE_BASE, GPIO_PIN_7, 0);
    GPIOPinWrite(GPIO_PORTE_BASE, GPIO_PIN_7, GPIO_PIN_7);

    //  
    //  "RXDV"          => "PD0"
    //  "TXER"          => "PD1"
    //  "TXD3"          => "PD4"
    //  "TXD2"          => "PD5"
    //  "TXD1"          => "PD6"
    //  "TXD0"          => "PD7"
    //
#define GPIO_PD0_RXDV           0x00030007
#define GPIO_PD1_TXER           0x00030407
#define GPIO_PD4_TXD3           0x00031004
#define GPIO_PD5_TXD2           0x00031404
#define GPIO_PD6_TXD1           0x00031804
#define GPIO_PD7_TXD0           0x00031c04
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
    GPIOPinConfigure(GPIO_PD0_RXDV);
    GPIOPinConfigure(GPIO_PD1_TXER);
    GPIOPinConfigure(GPIO_PD4_TXD3);
    GPIOPinConfigure(GPIO_PD5_TXD2);
    GPIOPinConfigure(GPIO_PD6_TXD1);
    GPIOPinConfigure(GPIO_PD7_TXD0);
    GPIOPinTypeEthernetMII(GPIO_PORTD_BASE,
                           (GPIO_PIN_7 | GPIO_PIN_6 | GPIO_PIN_5 | GPIO_PIN_4 |
                            GPIO_PIN_1 | GPIO_PIN_0));

    //
    //  "RXD0"          => "PE4"
    //
#define GPIO_PE4_RXD0           0x00041007
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    GPIOPinConfigure(GPIO_PE4_RXD0);
    GPIOPinTypeEthernetMII(GPIO_PORTE_BASE, GPIO_PIN_4);

    //
    //  "RXCK"          => "PF0"
    //  "RXER"          => "PF1"
    //  "MDC"           => "PF3"
    //  "MDIO"          => "PF4"
    //  "RXD3"          => "PF5"
    //  "RXD2"          => "PF6"
    //  "RXD1"          => "PF7"
    //
#define GPIO_PF0_RXCK           0x00050004
#define GPIO_PF1_RXER           0x00050404
#define GPIO_PF3_MDC            0x00050c03
#define GPIO_PF4_MDIO           0x00051003
#define GPIO_PF5_RXD3           0x00051403
#define GPIO_PF6_RXD2           0x00051803
#define GPIO_PF7_RXD1           0x00051c03
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    GPIOPinConfigure(GPIO_PF0_RXCK);
    GPIOPinConfigure(GPIO_PF1_RXER);
    GPIOPinConfigure(GPIO_PF3_MDC);
    GPIOPinConfigure(GPIO_PF4_MDIO);
    GPIOPinConfigure(GPIO_PF5_RXD3);
    GPIOPinConfigure(GPIO_PF6_RXD2);
    GPIOPinConfigure(GPIO_PF7_RXD1);
    GPIOPinTypeEthernetMII(GPIO_PORTF_BASE,
                           (GPIO_PIN_7 | GPIO_PIN_6 | GPIO_PIN_5 | GPIO_PIN_4 |
                            GPIO_PIN_3 | GPIO_PIN_1 | GPIO_PIN_0));

    //
    //  "COL"           => "PG2"
    //  "CRS"           => "PG3"
    //  "TXEN"          => "PG5"
    //  "TXCK"          => "PG6"
    //
#define GPIO_PG2_COL            0x00060803
#define GPIO_PG3_CRS            0x00060c03
#define GPIO_PG5_TXEN           0x00061403
#define GPIO_PG6_TXCK           0x00061803
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);
    GPIOPinConfigure(GPIO_PG2_COL);
    GPIOPinConfigure(GPIO_PG3_CRS);
    GPIOPinConfigure(GPIO_PG5_TXEN);
    GPIOPinConfigure(GPIO_PG6_TXCK);
    GPIOPinTypeEthernetMII(GPIO_PORTG_BASE,
                           (GPIO_PIN_6 | GPIO_PIN_5 | GPIO_PIN_3 |
                            GPIO_PIN_2));

    //
    // Configure SysTick for a periodic interrupt.
    //
    ROM_SysTickPeriodSet(ROM_SysCtlClockGet() / SYSTICKHZ);
    ROM_SysTickEnable();
    ROM_SysTickIntEnable();

    //
    // Enable processor interrupts.
    //
    ROM_IntMasterEnable();

    //
    // Read the MAC address from the user registers.
    //
    ROM_FlashUserGet(&ulUser0, &ulUser1);
    if((ulUser0 == 0xffffffff) || (ulUser1 == 0xffffffff))
    {
        //
        // We should never get here.  This is an error if the MAC address has
        // not been programmed into the device.  Exit the program.
        //
        UARTprintf("MAC Address Not Programmed!\n");
        while(1)
        {
        }
    }

    //
    // Convert the 24/24 split MAC address from NV ram into a 32/16 split MAC
    // address needed to program the hardware registers, then program the MAC
    // address into the Ethernet Controller registers.
    //
    pucMACArray[0] = ((ulUser0 >>  0) & 0xff);
    pucMACArray[1] = ((ulUser0 >>  8) & 0xff);
    pucMACArray[2] = ((ulUser0 >> 16) & 0xff);
    pucMACArray[3] = ((ulUser1 >>  0) & 0xff);
    pucMACArray[4] = ((ulUser1 >>  8) & 0xff);
    pucMACArray[5] = ((ulUser1 >> 16) & 0xff);

    //
    // Initialze the lwIP library, using DHCP.
    //
    lwIPInit(pucMACArray, 0, 0, 0, IPADDR_USE_DHCP);

    //
    // Setup the device locator service.
    //
    LocatorInit();
    LocatorMACAddrSet(pucMACArray);
    LocatorAppTitleSet("EK-LM3S9B92 enet_lwip");

    //
    // Indicate that DHCP has started.
    //
    UARTprintf("Waiting for IP... ");

    //
    // Initialize a sample httpd server.
    //
    httpd_init();

    //
    // Loop forever.  All the work is done in interrupt handlers.
    //
    while(1)
    {
    }
}
