/*Copyright (c) 2012, Sierra Wireless, Inc
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
•Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer.
•Redistributions in binary form must reproduce the above copyright notice, this
 list of conditions and the following disclaimer in the documentation and/or
 other materials provided with the distribution.
•Neither the name of Sierra Wireless Inc, Richmond, Canada nor the names of its
 contributors may be used to endorse or promote products derived from this
 software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABIL ITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/* ************************************************************************* */
/*
   File:		gprs.c
   Version:		v1.0 (20091211)
   Author:		SPT
*/
/* ************************************************************************* */

/* ************************************************************************* */
#include "adl_global.h"
#include "wip.h"
#include "wireless_internet_gateway.h"
/* ************************************************************************* */
char gl_gprs_apn [32] = {0};
char gl_gprs_un  [32] = {0};
char gl_gprs_pw  [32] = {0};

wip_bearer_t gl_gprs_br = {0};
wip_in_addr_t gl_gprs_ip_addr = {0};
char gl_gprs_ip_str [16] = {0};
wip_in_addr_t gl_dns1_ip_addr = {0};
wip_in_addr_t gl_dns2_ip_addr = {0};

u8 gl_gprs_state = WIP_BEV_CLOSED;
bool gl_gprs_params_new = FALSE;
bool gl_restart_gprs = FALSE;
u8 gl_nw_cap = NW_CAP_UNKNOWN;
extern bool gl_reply_post_pending;
bool gl_gsm_avail = FALSE;
/* ************************************************************************* */
void
fn_restart_eth ( u8 lo_id, void* lo_ctx )
{
	// Restart with DNS settings.
	// At times, the DNS setting fails (random), Google Public DNS is therefore set as default DNS.
	if ( gl_reply_post_pending == FALSE )
		fn_restart_ethernet ();
	else
		adl_tmrSubscribe ( FALSE, 10, ADL_TMR_TYPE_100MS, fn_restart_eth );
}
/* ************************************************************************* */
void
fn_gprs_br_cb (
			   wip_bearer_t lo_br,
			   s8 lo_ev,
			   void* lo_ctx )
{
	switch ( lo_ev )
	{
		case WIP_BEV_IP_CONNECTED:
			gl_gprs_state = WIP_BEV_IP_CONNECTED;
			gl_nw_cap = NW_CAP_GPRS;
			TRACE (( 1, "[GPRS] WIP_BEV_IP_CONNECTED" ));
			{
				char lo_buf [256] = {0};
				char lo_dns1_ip_str [16], lo_dns2_ip_str [16] = {0};

				wip_bearerGetOpts ( lo_br,
									WIP_BOPT_IP_ADDR, &gl_gprs_ip_addr,
									WIP_BOPT_IP_DNS1, &gl_dns1_ip_addr,
									WIP_BOPT_IP_DNS2, &gl_dns2_ip_addr,
									WIP_BOPT_END );

				wip_inet_ntoa ( gl_gprs_ip_addr, &gl_gprs_ip_str, 16 );
				wip_inet_ntoa ( gl_dns1_ip_addr, lo_dns1_ip_str, 16 );
				wip_inet_ntoa ( gl_dns2_ip_addr, lo_dns2_ip_str, 16 );
				sprintf ( &lo_buf, "[GPRS] GPRS IP %s DNS1 IP %s DNS2 IP %s", gl_gprs_ip_str, lo_dns1_ip_str, lo_dns2_ip_str );
				TRACE (( 1, lo_buf ));
			}
			adl_tmrSubscribe ( FALSE, 1, ADL_TMR_TYPE_100MS, fn_restart_eth );
			break;
		case WIP_BEV_IP_DISCONNECTED:
			gl_gprs_state = WIP_BEV_IP_DISCONNECTED;

			if ( gl_gsm_avail == TRUE )
				gl_nw_cap = NW_CAP_GSM;
			else
				gl_nw_cap = NW_CAP_UNKNOWN;
			
			TRACE (( 1, "[GPRS] WIP_BEV_IP_DISCONNECTED" ));
			wip_bearerStop ( lo_br );
			break;
		case WIP_BEV_CONN_FAILED:
			gl_gprs_state = WIP_BEV_CONN_FAILED;

			if ( gl_gsm_avail == TRUE )
				gl_nw_cap = NW_CAP_GSM;
			else
				gl_nw_cap = NW_CAP_UNKNOWN;
			
			TRACE (( 1, "[GPRS] WIP_BEV_CONN_FAILED" ));			
			break;
		case WIP_BEV_STOPPED:
			gl_gprs_state = WIP_BEV_STOPPED;
			TRACE (( 1, "[GPRS] WIP_BEV_STOPPED" ));
			wip_bearerClose ( lo_br );
			gl_gprs_state = WIP_BEV_CLOSED;

			if ( gl_restart_gprs == TRUE )
			{
				gl_restart_gprs = FALSE;
				fn_start_gprs ();
			}
			break;
		default:
			break;
	}
}
/* ************************************************************************* */
void
fn_come_back_to_start_gprs ( u8 lo_id, void* lo_ctx )
{
	fn_start_gprs ();
}
/* ************************************************************************* */
void
fn_start_gprs ( void )
{
	s32 lo_ret = 0;

	if ( gl_nw_cap == NW_CAP_UNKNOWN )
	{
		TRACE (( 1, "[GSM] No network, try after 5 seconds" ));
		adl_tmrSubscribe ( FALSE, 50, ADL_TMR_TYPE_100MS, fn_come_back_to_start_gprs );
		return;
	}
	wip_bearerOpen ( &gl_gprs_br, "GPRS", fn_gprs_br_cb, NULL );
	wip_bearerSetOpts ( gl_gprs_br,
			            WIP_BOPT_GPRS_APN,        gl_gprs_apn,
	                    WIP_BOPT_LOGIN,           gl_gprs_un,
	                    WIP_BOPT_PASSWORD,        gl_gprs_pw,
	                    WIP_BOPT_GPRS_HEADERCOMP, TRUE,
					    WIP_BOPT_DEBUG_PKT,       TRUE,
						// GPRS is public interface
						WIP_BOPT_EXTNAT,          TRUE,
	                    WIP_BOPT_END );

	lo_ret = wip_bearerStart( gl_gprs_br );

	if ( lo_ret == WIP_BERR_OK_INPROGRESS )
		TRACE (( 1, "[GPRS] WIP_BERR_OK_INPROGRESS" ));
	else
	{
		TRACE (( 1, "[GPRS] %d <- wip_bearerStart()", lo_ret ));
		gl_gprs_state = GPRS_ERROR;
		adl_tmrSubscribe ( FALSE, 1, ADL_TMR_TYPE_100MS, fn_restart_eth );
	}
}
/* ************************************************************************* */
bool
fn_gprs_params_avail ( void )
{
	if ( strlen (gl_gprs_apn) > 0 )
		return TRUE;
	else
		return FALSE;
}
/* ************************************************************************* */
void
fn_close_gprs ( void )
{
	if ( (gl_gprs_state == WIP_BEV_IP_CONNECTED) || (gl_gprs_state == WIP_BEV_IP_DISCONNECTED) )
		wip_bearerStop ( gl_gprs_br );
	else if ( gl_gprs_state == WIP_BEV_STOPPED )
		wip_bearerClose ( gl_gprs_br );
	else if ( (gl_gprs_state == WIP_BEV_CLOSED) || (gl_gprs_state == WIP_BEV_CONN_FAILED) )
		fn_start_gprs ();		
}
/* ************************************************************************* */
void
fn_at_gprs_cb ( adl_atCmdPreParser_t* lo_params )
{
	char lo_buf [128] = {0};
	char lo_gprs_apn [32] = {0};
	char lo_gprs_un  [32] = {0};
	char lo_gprs_pw  [32] = {0};

	strcpy ( lo_gprs_apn, ADL_GET_PARAM ( lo_params, 0 ) );
	strcpy ( lo_gprs_un,  ADL_GET_PARAM ( lo_params, 1 ) );
	strcpy ( lo_gprs_pw,  ADL_GET_PARAM ( lo_params, 2 ) );

	if ( strcmp ( lo_gprs_apn, gl_gprs_apn ) != 0 )
	{
		gl_gprs_params_new = TRUE;
		strcpy ( gl_gprs_apn, lo_gprs_apn );
		fn_store_params ( "GPRS", APN, strlen(gl_gprs_apn), gl_gprs_apn );
	}
	if ( strcmp ( lo_gprs_un, gl_gprs_un ) != 0 )
	{
		gl_gprs_params_new = TRUE;
		strcpy ( gl_gprs_un, lo_gprs_un );
		fn_store_params ( "GPRS", UNAME, strlen(gl_gprs_un), gl_gprs_un );
	}
	if ( strcmp ( lo_gprs_pw, gl_gprs_pw ) != 0 )
	{
		gl_gprs_params_new = TRUE;
		strcpy ( gl_gprs_pw, lo_gprs_pw );
		fn_store_params ( "GPRS", PWD, strlen(gl_gprs_pw), gl_gprs_pw );
	}

	sprintf ( lo_buf, "[GPRS] APN %s USERNAME %s PASSWORD %s", gl_gprs_apn, gl_gprs_un, gl_gprs_pw );
	TRACE (( 1, lo_buf ));

	adl_atSendStdResponsePort ( ADL_AT_RSP, lo_params->Port, ADL_STR_OK );

	if ( gl_gprs_state == WIP_BEV_CLOSED )
		fn_start_gprs ();
	else if ( gl_gprs_params_new == TRUE )
	{
		TRACE (( 1, "[GPRS] Parameters new, restarting GPRS" ));
		gl_restart_gprs = TRUE;
		fn_close_gprs ();
	}
}
/* ************************************************************************* */
// GSM/GPRS/EDGE
bool
fn_nw_cap_cb (
		      adl_atUnsolicited_t* lo_rsp )
{
	ascii lo_buf[4] = {0};
	s32 lo_nw_cap = -1;
	s32 lo_nw_d_cs_mcs = -1;
	s32 lo_nw_u_cs_mcs = -1;
	u8 lo_f = 0;

	if ( (gl_gprs_state !=  WIP_BEV_IP_CONNECTED) )
	{
		if ( gl_gsm_avail == TRUE )
			gl_nw_cap = NW_CAP_GSM;
		else
			gl_nw_cap = NW_CAP_UNKNOWN;

		return;
	}
	
	lo_nw_cap = (s32) wm_atoi ( wm_strGetParameterString ( lo_buf, lo_rsp->StrData, 0 ) );
	
	if ( (lo_nw_cap == 1) || (lo_nw_cap == 2) )
	{
		for ( lo_f = 2; lo_f < 9; lo_f++ )
		{
			lo_nw_u_cs_mcs = -1;
			memset ( lo_buf, 0x0, 4 );
			
			wm_strGetParameterString ( lo_buf, lo_rsp->StrData, lo_f );
			
			if ( strlen(lo_buf) > 0 )
				lo_nw_u_cs_mcs = (s32) wm_atoi ( lo_buf );

			if ( (lo_nw_u_cs_mcs >= 0 ) && (lo_nw_u_cs_mcs <= 3 ) )
			{
				TRACE (( 1, "[GSM] NW CAP U GPRS" ));
				break;
			}
			else if ( (lo_nw_u_cs_mcs >= 4 ) && (lo_nw_u_cs_mcs <= 12 ) )
			{
				TRACE (( 1, "[GSM] NW CAP U EDGE" ));
				break;
			}
		}
		for ( lo_f = 9; lo_f < 17; lo_f++ )
		{
			lo_nw_d_cs_mcs = -1;
			memset ( lo_buf, 0x0, 4 );
			
			wm_strGetParameterString ( lo_buf, lo_rsp->StrData, lo_f );
			
			if ( strlen(lo_buf) > 0 )
				lo_nw_d_cs_mcs = (s32) wm_atoi ( lo_buf );

			if ( (lo_nw_d_cs_mcs >= 0 ) && (lo_nw_d_cs_mcs <= 3 ) )
			{
				TRACE (( 1, "[GSM] NW CAP D GPRS" ));
				gl_nw_cap = NW_CAP_GPRS;
				break;
			}
			else if ( (lo_nw_d_cs_mcs >= 4 ) && (lo_nw_d_cs_mcs <= 12 ) )
			{
				TRACE (( 1, "[GSM] NW CAP D EDGE" ));
				gl_nw_cap = NW_CAP_EDGE;
				break;
			}			
		}
		if ( (lo_nw_u_cs_mcs == -1) && (lo_nw_d_cs_mcs == -1) && (gl_gprs_state !=  WIP_BEV_IP_CONNECTED) )
		{
			if ( gl_gsm_avail == TRUE )
				gl_nw_cap = NW_CAP_GSM;
			else
				gl_nw_cap = NW_CAP_UNKNOWN;
		}
	}
	
	return FALSE;
}
/* ************************************************************************* */
bool
fn_nw_reg_cb (
		   adl_atUnsolicited_t* lo_rsp )
{
	ascii lo_buf[4] = {0};
	u32 lo_reg_state = wm_atoi ( wm_strGetParameterString ( lo_buf, lo_rsp->StrData, 0 ) );

	if ( (lo_reg_state == 1) || (lo_reg_state == 5) ) // Home or Roaming
	{
		gl_nw_cap = NW_CAP_GSM;
		gl_gsm_avail = TRUE;
		TRACE (( 1, "[GSM] REGISTERED" ));
		adl_atCmdCreate     ( "AT+WGPRS=9,1,1", FALSE, NULL, NULL );
		if ( fn_gprs_params_avail () == TRUE )
			fn_start_gprs ();
	}
	else if ( gl_nw_cap == NW_CAP_GSM )
	{
		gl_nw_cap = NW_CAP_UNKNOWN;
		gl_gsm_avail = FALSE;
		TRACE (( 1, "[GSM] DEREGISTERED" ));
	}
	return TRUE;
}
/* ************************************************************************* */
void fn_make_settings ( u8 lo_id, void* lo_ctx )
{
	adl_atCmdCreate     ( "AT+CREG=1;&W", FALSE, NULL, NULL );
}
/* ************************************************************************* */
void
fn_init_gprs ( void )
{
	adl_atUnSoSubscribe ( "+CREG:", fn_nw_reg_cb );
	adl_atUnSoSubscribe ( "+WGPRSIND:", fn_nw_cap_cb );
	adl_tmrSubscribe ( FALSE, 50, ADL_TMR_TYPE_100MS, fn_make_settings );
	adl_atCmdSubscribe  ( "AT+GPRS", fn_at_gprs_cb, ( ADL_CMD_TYPE_PARA | 0x33 ) );		

	// At times, the DNS setting fails (random), Google Public DNS is therefore set as default DNS.
	wip_inet_aton ( GOOGLE_PUBLIC_DNS1, &gl_dns1_ip_addr );
	wip_inet_aton ( GOOGLE_PUBLIC_DNS2, &gl_dns2_ip_addr );	
}
/* ************************************************************************* */
