/* This case tests the basic connection of PPPoE server and client.  */
 
#include   "nx_pppoe_client.h"
#include   "nx_pppoe_server.h"
#include   "tx_api.h"
#include   "nx_api.h" 
#include   "nx_ppp.h"

extern void test_control_return(UINT);

#if !defined(NX_DISABLE_IPV4) && defined(NX_PPP_PPPOE_ENABLE) && defined(NX_PPPOE_SERVER_INITIALIZE_DRIVER_ENABLE) && defined(NX_PPPOE_CLIENT_INITIALIZE_DRIVER_ENABLE) && (NX_PHYSICAL_HEADER >= 24) && !defined(NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE)

/* Defined NX_PPP_PPPOE_ENABLE if use PPP, since PPP module has been modified to match PPPoE moduler under this definition.  */
                
/* If the driver is not initialized in other module, define NX_PPPOE_SERVER_INITIALIZE_DRIVER_ENABLE to initialize the driver in PPPoE module .  
   In this demo, the driver has been initialized in IP module.  */

/* NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE: 
   If defined, enables the feature that controls the PPPoE session. 
   PPPoE server does not automatically response to the request until application call specific API.  */

/* Define the block size.  */
#define     NX_PACKET_POOL_SIZE     ((1536 + sizeof(NX_PACKET)) * 12)
#define     DEMO_STACK_SIZE         2048
#define     PPPOE_THREAD_SIZE       2048

/* Define the ThreadX and NetX object control blocks...  */
static TX_THREAD               thread_server;
static TX_THREAD               thread_client;

/* Define the packet pool and IP instance for normal IP instnace.  */
static NX_PACKET_POOL          pool_server;
static NX_IP                   ip_server;
static NX_PACKET_POOL          pool_client;
static NX_IP                   ip_client;
static CHAR                    pool_buffer_server[NX_PACKET_POOL_SIZE];
static CHAR                    pool_buffer_client[NX_PACKET_POOL_SIZE];
                
/* Define the PPP Server instance.  */
static NX_PPP                  ppp_server;
static NX_PPP                  ppp_client;

/* Define the PPPoE Server instance.  */
static NX_PPPOE_SERVER         pppoe_server;
static NX_PPPOE_CLIENT         pppoe_client;
                         
/* Define the counters.  */
static CHAR                    *pointer;
static ULONG                   error_counter; 

/* Define thread prototypes.  */
static void    thread_server_entry(ULONG thread_input);
static void    thread_client_entry(ULONG thread_input);

/***** Substitute your PPP driver entry function here *********/
extern void    _nx_ppp_driver(NX_IP_DRIVER *driver_req_ptr);

/***** Substitute your Ethernet driver entry function here *********/ 
extern void    _nx_ram_network_driver(NX_IP_DRIVER *driver_req_ptr);

/* Define the callback functions.  */ 
static void    pppoe_server_packet_receive(UINT interfaceHandle, ULONG length, UCHAR *data, UINT packet_id);

/* Define the porting layer function for PPP.  
   Functions to be provided by PPP for calling by the PPPoE Stack.  */
static void    ppp_server_packet_send(NX_PACKET *packet_ptr);

/* Define the porting layer function for PPP.  
   Functions to be provided by PPP for calling by the PPPoE Stack.  */
static void    ppp_client_packet_send(NX_PACKET *packet_ptr);
static void    pppoe_client_packet_receive(NX_PACKET *packet_ptr);

#define SERVER_ADDRESS IP_ADDRESS(192, 168, 10, 43)
#define CLIENT_ADDRESS IP_ADDRESS(192, 168, 10, 44)

#define SERVICE_NAME_1 "test_service_1"
#define SERVICE_NAME_2 "test_service_2"

static UINT generate_login(CHAR *name, CHAR *password)
{

    /* Make a name and password, called "myname" and "mypassword".  */
    name[0] = 'm';
    name[1] = 'y';
    name[2] = 'n';
    name[3] = 'a';
    name[4] = 'm';
    name[5] = 'e';
    name[6] = (CHAR) 0;
    
    password[0] = 'm';
    password[1] = 'y';
    password[2] = 'p';
    password[3] = 'a';
    password[4] = 's';
    password[5] = 's';
    password[6] = 'w';
    password[7] = 'o';
    password[8] = 'r';
    password[9] = 'd';
    password[10] = (CHAR) 0;

    return(NX_SUCCESS);
}

static UINT verify_login(CHAR *name, CHAR *password)
{

if ((name[0] == 'm') &&
    (name[1] == 'y') &&
    (name[2] == 'n') &&
    (name[3] == 'a') &&
    (name[4] == 'm') &&
    (name[5] == 'e') &&
    (name[6] == (CHAR) 0) &&
    (password[0] == 'm') &&
    (password[1] == 'y') &&
    (password[2] == 'p') &&
    (password[3] == 'a') &&
    (password[4] == 's') &&
    (password[5] == 's') &&
    (password[6] == 'w') &&
    (password[7] == 'o') &&
    (password[8] == 'r') &&
    (password[9] == 'd') &&
    (password[10] == (CHAR) 0))
        return(NX_SUCCESS);
   else
        return(NX_PPP_ERROR);
}

/* Define what the initial system looks like.  */

#ifdef CTEST
VOID test_application_define(void *first_unused_memory)
#else
void    netx_pppoe_basic_test_application_define(void *first_unused_memory)
#endif
{

UINT    status;
                              
    /* Setup the working pointer.  */
    pointer =  (CHAR *) first_unused_memory;

    /* Initialize the NetX system.  */
    nx_system_initialize();

    /* Create a packet pool for normal IP instance.  */
    status = nx_packet_pool_create(&pool_server, "Server Packet Pool", 
                                   (1536 + sizeof(NX_PACKET)), 
                                   pool_buffer_server, NX_PACKET_POOL_SIZE); 
                 
    /* Check for error.  */
    if (status)
        error_counter++;

    /* Create an normal IP instance.  */
    status = nx_ip_create(&ip_server, "Server IP Instance", IP_ADDRESS(0, 0, 0, 0), 0xFFFFFF00UL, &pool_server, nx_ppp_driver,
                          pointer, 2048, 1);
    pointer = pointer + 2048;

    /* Check for error.  */
    if (status)
        error_counter++;

    /* Create the PPP instance.  */
    status = nx_ppp_create(&ppp_server, "PPP Server Instance", &ip_server, pointer, 2048, 1, &pool_server, NX_NULL, NX_NULL);
    pointer = pointer + 2048;

    /* Check for PPP create error.   */
    if (status)
        error_counter++;

    /* Set the PPP packet send function.  */
    status = nx_ppp_packet_send_set(&ppp_server, ppp_server_packet_send);

    /* Check for PPP packet send function set error.   */
    if (status)
        error_counter++;

    /* Define IP address. This PPP instance is effectively the server since it has both IP addresses. */
    status = nx_ppp_ip_address_assign(&ppp_server, SERVER_ADDRESS, CLIENT_ADDRESS);
    
    /* Check for PPP IP address assign error.   */
    if (status)
        error_counter++;
        
    /* Setup PAP, this PPP instance is effectively the server since it will verify the name and password.  */
    status = nx_ppp_pap_enable(&ppp_server, NX_NULL, verify_login);

    /* Check for PPP PAP enable error.  */
    if (status)
        error_counter++;

    /* Enable ARP and supply ARP cache memory for Normal IP Instance.  */
    status = nx_arp_enable(&ip_server, (void *) pointer, 1024);
    pointer = pointer + 1024; 

    /* Check for ARP enable errors.  */
    if (status)
        error_counter++;

    /* Enable ICMP */
    status = nx_icmp_enable(&ip_server);
    if(status)
        error_counter++;

    /* Create the main thread.  */
    tx_thread_create(&thread_server, "thread server", thread_server_entry, 0,  
                     pointer, DEMO_STACK_SIZE, 
                     5, 5, TX_NO_TIME_SLICE, TX_AUTO_START);
    pointer =  pointer + DEMO_STACK_SIZE;

    /* Create a packet pool for normal IP instance.  */
    status = nx_packet_pool_create(&pool_client, "Client Packet Pool", 
                                   (1536 + sizeof(NX_PACKET)), 
                                   pool_buffer_client, NX_PACKET_POOL_SIZE); 
                 
    /* Check for error.  */
    if (status)
        error_counter++;

    /* Create an normal IP instance.  */
    status = nx_ip_create(&ip_client, "Client IP Instance", IP_ADDRESS(0, 0, 0, 0), 0xFFFFFF00UL, &pool_client, nx_ppp_driver,
                          pointer, 2048, 1);
    pointer = pointer + 2048;

    /* Check for error.  */
    if (status)
        error_counter++;

    /* Create the PPP instance.  */
    status = nx_ppp_create(&ppp_client, "PPP Client Instance", &ip_client, pointer, 2048, 1, &pool_client, NX_NULL, NX_NULL);
    pointer = pointer + 2048;

    /* Check for PPP create error.   */
    if (status)
        error_counter++;

    /* Set the PPP packet send function.  */
    status = nx_ppp_packet_send_set(&ppp_client, ppp_client_packet_send);

    /* Check for PPP packet send function set error.   */
    if (status)
        error_counter++;

    /* Define IP address. This PPP instance is effectively the client since it doesn't have any IP addresses. */
    status = nx_ppp_ip_address_assign(&ppp_client, IP_ADDRESS(0, 0, 0, 0), IP_ADDRESS(0, 0, 0, 0));
    
    /* Check for PPP IP address assign error.   */
    if (status)
        error_counter++;
        
    /* Setup PAP, this PPP instance is effectively the since it generates the name and password for the peer..  */
    status = nx_ppp_pap_enable(&ppp_client, generate_login, NX_NULL);

    /* Check for PPP PAP enable error.  */
    if (status)
        error_counter++;

    /* Enable ARP and supply ARP cache memory for Normal IP Instance.  */
    status = nx_arp_enable(&ip_client, (void *) pointer, 1024);
    pointer = pointer + 1024; 

    /* Check for ARP enable errors.  */
    if (status)
        error_counter++;

    /* Enable ICMP */
    status = nx_icmp_enable(&ip_client);
    if(status)
        error_counter++;

    /* Create the main thread.  */
    tx_thread_create(&thread_client, "thread client", thread_client_entry, 0,  
                     pointer, DEMO_STACK_SIZE, 
                     6, 6, TX_NO_TIME_SLICE, TX_DONT_START);
    pointer =  pointer + DEMO_STACK_SIZE;

}

static void    thread_client_entry(ULONG thread_input)
{
UINT    status;
ULONG   ip_status;
NX_PACKET *recv_pkt;
ULONG   ip_address;
ULONG   network_mask;

    /* Create the PPPoE instance.  */
    status = nx_pppoe_client_create(&pppoe_client, (UCHAR *)"PPPoE Client", &ip_client, 0, &pool_client, pointer, PPPOE_THREAD_SIZE, 4, _nx_ram_network_driver, pppoe_client_packet_receive);
    pointer = pointer + PPPOE_THREAD_SIZE;
    if (status)
    {
        error_counter++;
    }

    /* Set service name.  */
    nx_pppoe_client_service_name_set(&pppoe_client, SERVICE_NAME_1);

    /* Establish PPPoE Client sessione.  */
    status = nx_pppoe_client_session_connect(&pppoe_client, NX_WAIT_FOREVER);
    if (status)
    {
        error_counter++;
    }

    /* Wait for the link to come up.  */
    status = nx_ip_interface_status_check(&ip_client, 0, NX_IP_ADDRESS_RESOLVED, &ip_status, NX_WAIT_FOREVER);
    if (status)
    {
        error_counter++;
    }

    /* Check client IP address.  */
    status = nx_ip_address_get(&ip_client, &ip_address, &network_mask);
    if (status || ip_address != CLIENT_ADDRESS)
    {
        error_counter++;
    }

    /* Ping test.  */
    status = nx_icmp_ping(&ip_client, SERVER_ADDRESS, "abcd", 4, &recv_pkt, NX_WAIT_FOREVER);
    if (status)
    {
        error_counter++;
    }
    else
    {
        nx_packet_release(recv_pkt);
    }

    if(error_counter)
    {
        printf("ERROR!\n");
        test_control_return(1);
    }
    else
    {
        printf("SUCCESS!\n");
        test_control_return(0);
    }
}

/* PPPoE Client receive function.  */
static void    pppoe_client_packet_receive(NX_PACKET *packet_ptr)
{

    /* Call PPP Client to receive the PPP data fame.  */
    nx_ppp_packet_receive(&ppp_client, packet_ptr);
}

/* PPP Client send function.  */
static void    ppp_client_packet_send(NX_PACKET *packet_ptr)
{

    /* Directly Call PPPoE send function to send out the data through PPPoE module.  */
    nx_pppoe_client_session_packet_send(&pppoe_client, packet_ptr);
}

/* Define the server threads.  */

static void    thread_server_entry(ULONG thread_input)
{
UINT    status;
ULONG   ip_status;
ULONG   ip_address;
ULONG   network_mask;
UCHAR   *service_names[2];
UINT    service_names_count = 2;

    /* Print out test information banner.  */
    printf("NetX Test:   PPPoE Basic Test..........................................");

    /* Create the PPPoE instance.  */
    status =  nx_pppoe_server_create(&pppoe_server, (UCHAR *)"PPPoE Server",  &ip_server, 0, _nx_ram_network_driver,  &pool_server, pointer, PPPOE_THREAD_SIZE, 4);
    pointer = pointer + PPPOE_THREAD_SIZE;
    if (status)
    {
        error_counter++;
    }

    /* Set the callback notify function.  */
    status = nx_pppoe_server_callback_notify_set(&pppoe_server, NX_NULL, NX_NULL, NX_NULL, NX_NULL, pppoe_server_packet_receive, NX_NULL);
    if (status)
    {
        error_counter++;
    }

    /* Set service name.  */
    service_names[0] = SERVICE_NAME_1;
    service_names[1] = SERVICE_NAME_2;
    nx_pppoe_server_service_name_set(&pppoe_server, service_names, service_names_count);
    
    /* Enable PPPoE Server.  */
    status = nx_pppoe_server_enable(&pppoe_server);
    if (status)
    {
        error_counter++;
    }

    tx_thread_resume(&thread_client);

    /* Wait for the link to come up.  */
    status = nx_ip_interface_status_check(&ip_server, 0, NX_IP_ADDRESS_RESOLVED, &ip_status, NX_WAIT_FOREVER);
    if (status)
    {
        error_counter++;
    }

    /* Check server IP address.  */
    status = nx_ip_address_get(&ip_server, &ip_address, &network_mask);
    if (status || ip_address != SERVER_ADDRESS)
    {
        error_counter++;
    }
}

static void    pppoe_server_packet_receive(UINT interfaceHandle, ULONG length, UCHAR *data, UINT packet_id)
{

NX_PACKET   *packet_ptr;

    /* Get the notify that receive the PPPoE Session data.  */

    /* Call PPP Server to receive the PPP data fame.  */
    packet_ptr = (NX_PACKET *)(packet_id);
    nx_ppp_packet_receive(&ppp_server, packet_ptr);
}
  
/* PPP Server send function.  */
static void    ppp_server_packet_send(NX_PACKET *packet_ptr)
{      

/* For test, the session should be the first session, so set interfaceHandle as 0.  */
UINT    interfaceHandle = 0;  

    /* Directly Call PPPoE send function to send out the data through PPPoE module.  */
    nx_pppoe_server_session_packet_send(&pppoe_server, interfaceHandle, packet_ptr);
}

#else

#ifdef CTEST
VOID test_application_define(void *first_unused_memory)
#else
void    netx_pppoe_basic_test_application_define(void *first_unused_memory)
#endif
{

    /* Print out test information banner.  */
    printf("NetX Test:   PPPoE Basic Test..........................................N/A\n"); 

    test_control_return(3);  
}      
#endif