/*****************************************************************************
			Eurecom OpenAirInterface 3
			Copyright(c) 2012 Eurecom

Source		esm_main.c

Version		0.1

Date		2012/12/04

Product		NAS stack

Subsystem	EPS Session Management

Author		Frederic Maurel

Description	Defines the EPS Session Management procedure call manager,
		the main entry point for elementary ESM processing.

*****************************************************************************/

#include "esm_main.h"
#include "commonDef.h"
#include "nas_log.h"

#include "esmData.h"
#include "esm_pt.h"
#include "esm_ebr.h"

/****************************************************************************/
/****************  E X T E R N A L    D E F I N I T I O N S  ****************/
/****************************************************************************/

/****************************************************************************/
/*******************  L O C A L    D E F I N I T I O N S  *******************/
/****************************************************************************/

/****************************************************************************/
/******************  E X P O R T E D    F U N C T I O N S  ******************/
/****************************************************************************/

#ifdef NAS_UE
/****************************************************************************
 **                                                                        **
 ** Name:	 esm_main_initialize()                                     **
 **                                                                        **
 ** Description: Initializes ESM internal data                             **
 **                                                                        **
 ** Inputs:	 cb:		The user notification callback             **
 **		 Others:	None                                       **
 **                                                                        **
 ** Outputs:	 None                                                      **
 **		 Return:	None                                       **
 **		 Others:	_esm_data                                  **
 **                                                                        **
 ***************************************************************************/
void esm_main_initialize(esm_indication_callback_t cb)
{
    LOG_FUNC_IN;

    /* Total number of active EPS bearer contexts */
    _esm_data.n_ebrs = 0;
    /* List of active PDN connections */
    _esm_data.n_pdns = 0;
    for (int i = 0; i < ESM_DATA_PDN_MAX + 1; i++) {
	_esm_data.pdn[i].pid = -1;
	_esm_data.pdn[i].is_active = FALSE;
	_esm_data.pdn[i].data = NULL;
    }
    /* Emergency bearer services indicator */
    _esm_data.emergency = FALSE;

    /* Initialize the procedure transaction identity manager */
    esm_pt_initialize();

    /* Initialize the EPS bearer context manager */
    esm_ebr_initialize(cb);

    LOG_FUNC_OUT;
}
#endif
#ifdef NAS_MME
/****************************************************************************
 **                                                                        **
 ** Name:	 esm_main_initialize()                                     **
 **                                                                        **
 ** Description: Initializes ESM internal data                             **
 **                                                                        **
 ** Inputs:	 None                                                      **
 **		 Others:	None                                       **
 **                                                                        **
 ** Outputs:	 None                                                      **
 **		 Return:	None                                       **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
void esm_main_initialize(void)
{
    int i;

    LOG_FUNC_IN;

    /* Retreive MME supported configuration data */
    if (mme_api_get_esm_config(&_esm_data.conf) != RETURNok) {
	LOG_TRACE(ERROR, "ESM-MAIN  - Failed to get MME configuration data");
    }

    /* Initialize ESM contexts */
    for (i = 0; i < ESM_DATA_NB_UE_MAX; i++) {
	_esm_data.ctx[i] = NULL;
    }

    /* Initialize the EPS bearer context manager */
    esm_ebr_initialize();

    LOG_FUNC_OUT;
}
#endif

/****************************************************************************
 **                                                                        **
 ** Name:	 esm_main_cleanup()                                        **
 **                                                                        **
 ** Description: Performs the EPS Session Management clean up procedure    **
 **                                                                        **
 ** Inputs:	 None                                                      **
 ** 	 	 Others:	None                                       **
 **                                                                        **
 ** Outputs:	 None                                                      **
 ** 		 Return:	None                                       **
 ** 	 	 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
void esm_main_cleanup(void)
{
    LOG_FUNC_IN;

#ifdef NAS_UE
    /* De-activate EPS bearers and clean up PDN connections */
    for (int pid = 0; pid < ESM_DATA_PDN_MAX; pid++) {
	if (_esm_data.pdn[pid].data) {
	    esm_pdn_t* pdn = _esm_data.pdn[pid].data;
	    if (pdn->apn.length > 0) {
		free(pdn->apn.value);
	    }
	    /* Release EPS bearer contexts */
	    for (int bid = 0; bid < pdn->n_bearers; bid++) {
		if (pdn->bearer[bid]) {
		    LOG_TRACE(WARNING, "ESM-MAIN  - Release EPS bearer "
			      "context (ebi=%d)", pdn->bearer[bid]->ebi);
		    /* Delete the TFT */
		    for (int i = 0; i < pdn->bearer[bid]->tft.n_pkfs; i++) {
			if (pdn->bearer[bid]->tft.pkf[i]) {
			    free(pdn->bearer[bid]->tft.pkf[i]);
			}
		    }
		    free(pdn->bearer[bid]);
		}
	    }
	    /* Release the PDN connection */
	    free(_esm_data.pdn[pid].data);
	}
    }
#endif

    LOG_FUNC_OUT;
}

#ifdef NAS_UE
/****************************************************************************
 **                                                                        **
 ** Name:	 esm_main_get_nb_pdn_max()                                 **
 **                                                                        **
 ** Description: Get the maximum number of PDN connections that may be in  **
 **		 a defined state at the same time                          **
 **                                                                        **
 ** Inputs:	 None                                                      **
 **		 Others:	_esm_data                                  **
 **                                                                        **
 ** Outputs:	 None                                                      **
 **		 Return:	The maximum number of PDN connections that **
 **				may be defined                             **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
int esm_main_get_nb_pdns_max(void)
{
    LOG_FUNC_IN;

    LOG_FUNC_RETURN (ESM_DATA_PDN_MAX);
}

/****************************************************************************
 **                                                                        **
 ** Name:	 esm_main_get_nb_pdns()                                    **
 **                                                                        **
 ** Description: Get the number of active PDN connections                  **
 **                                                                        **
 ** Inputs:	 None                                                      **
 **		 Others:	_esm_data                                  **
 **                                                                        **
 ** Outputs:	 None                                                      **
 **		 Return:	The number of active PDN connections       **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
int esm_main_get_nb_pdns(void)
{
    LOG_FUNC_IN;

    LOG_FUNC_RETURN (_esm_data.n_pdns);
}

/****************************************************************************
 **                                                                        **
 ** Name:	 esm_main_has_emergency()                                  **
 **                                                                        **
 ** Description: Check whether a PDN connection for emergency bearer ser-  **
 **		 vices is established                                      **
 **                                                                        **
 ** Inputs:	 None                                                      **
 **		 Others:	_esm_data                                  **
 **                                                                        **
 ** Outputs:	 None                                                      **
 **		 Return:	TRUE if a PDN connection for emergency     **
 **				bearer services is established             **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
int esm_main_has_emergency(void)
{
    LOG_FUNC_IN;

    LOG_FUNC_RETURN (_esm_data.emergency);
}

/****************************************************************************
 **                                                                        **
 ** Name:	 esm_main_get_pdn_status()                                 **
 **                                                                        **
 ** Description: Get the status of the specified PDN connection            **
 **                                                                        **
 ** Inputs:	 cid:		PDN connection identifier                  **
 **		 Others:	_esm_data                                  **
 **                                                                        **
 ** Outputs:	 state:		TRUE if the current state of the PDN con-  **
 **				nection is ACTIVE; FALSE otherwise.        **
 **		 Return:	TRUE if the specified PDN connection has a **
 **				PDN context defined; FALSE if no any PDN   **
 **				context has been defined for the specified **
 **				connection.                                **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
int esm_main_get_pdn_status(int cid, int* state)
{
    LOG_FUNC_IN;

    unsigned int pid = cid - 1;

    if (pid >= ESM_DATA_PDN_MAX) {
	return (FALSE);
    }
    else if (pid != _esm_data.pdn[pid].pid) {
	LOG_TRACE(WARNING, "ESM-MAIN  - PDN connection %d is not defined", cid);
	return (FALSE);
    }
    else if (_esm_data.pdn[pid].data == NULL) {
	LOG_TRACE(ERROR, "ESM-MAIN  - PDN connection %d has not been allocated",
		  cid);
	return (FALSE);
    }

    if (_esm_data.pdn[pid].data->bearer[0] != NULL) {
	/* The status of a PDN connection is the status of the default EPS bearer
	 * that has been assigned to this PDN connection at activation time */
	int ebi = _esm_data.pdn[pid].data->bearer[0]->ebi;
	*state = (esm_ebr_get_status(ebi) == ESM_EBR_ACTIVE);
    }
    /* The PDN connection has not been activated yet */
    LOG_FUNC_RETURN (TRUE);
}

/****************************************************************************
 **                                                                        **
 ** Name:	 esm_main_get_pdn()                                        **
 **                                                                        **
 ** Description: Get parameters defined for the specified PDN connection   **
 **                                                                        **
 ** Inputs:	 cid:		PDN connection identifier                  **
 **		 Others:	_esm_data                                  **
 **                                                                        **
 ** Outputs:	 type:		PDN connection type (IPv4, IPv6, IPv4v6)   **
 **		 apn:		Access Point logical Name in used          **
 **		 is_emergency:	Emergency bearer services indicator        **
 **		 is_active:	Active PDN connection indicator            **
 **		 Return:	RETURNok, RETURNerror                      **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
int esm_main_get_pdn(int cid, int* type, const char** apn,
		     int* is_emergency, int* is_active)
{
    LOG_FUNC_IN;

    unsigned int pid = cid - 1;

    if (pid >= ESM_DATA_PDN_MAX) {
	return (RETURNerror);
    }
    else if (pid != _esm_data.pdn[pid].pid) {
	LOG_TRACE(WARNING, "ESM-MAIN  - PDN connection %d is not defined", cid);
	return (RETURNerror);
    }
    else if (_esm_data.pdn[pid].data == NULL) {
	LOG_TRACE(ERROR, "ESM-MAIN  - PDN connection %d has not been allocated",
		  cid);
	return (RETURNerror);
    }

    /* Get the PDN type */
    *type = _esm_data.pdn[pid].data->type;
    /* Get the Access Point Name */
    if (_esm_data.pdn[pid].data->apn.length > 0) {
	*apn = (char*)(_esm_data.pdn[pid].data->apn.value);
    } else {
	*apn = NULL;
    }
    /* Get the emergency bearer services indicator */
    *is_emergency = _esm_data.pdn[pid].data->is_emergency;
    /* Get the active PDN connection indicator */
    *is_active = _esm_data.pdn[pid].is_active;

    LOG_FUNC_RETURN (RETURNok);
}

/****************************************************************************
 **                                                                        **
 ** Name:	 esm_main_get_pdn_addr()                                   **
 **                                                                        **
 ** Description: Get IP address(es) assigned to the specified PDN connec-  **
 **		 tion                                                      **
 **                                                                        **
 ** Inputs:	 cid:		PDN connection identifier                  **
 **		 Others:	_esm_data                                  **
 **                                                                        **
 ** Outputs:	 ipv4adddr:	IPv4 address                               **
 **		 ipv6adddr:	IPv6 address                               **
 **		 Return:	RETURNok, RETURNerror                      **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
int esm_main_get_pdn_addr(int cid, const char** ipv4addr, const char** ipv6addr)
{
    LOG_FUNC_IN;

    unsigned int pid = cid - 1;

    if (pid >= ESM_DATA_PDN_MAX) {
	return (RETURNerror);
    }
    else if (pid != _esm_data.pdn[pid].pid) {
	LOG_TRACE(WARNING, "ESM-MAIN  - PDN connection %d is not defined", cid);
	return (RETURNerror);
    }
    else if (_esm_data.pdn[pid].data == NULL) {
	LOG_TRACE(ERROR, "ESM-MAIN  - PDN connection %d has not been allocated",
		  cid);
	return (RETURNerror);
    }
    else if (!_esm_data.pdn[pid].is_active) {
	/* No any IP address has been assigned to this PDN connection */
	return (RETURNok);
    }

    if (_esm_data.pdn[pid].data->type == NET_PDN_TYPE_IPV4) {
	/* Get IPv4 address */
	*ipv4addr = _esm_data.pdn[pid].data->ip_addr;
    }
    else if (_esm_data.pdn[pid].data->type == NET_PDN_TYPE_IPV6) {
	/* Get IPv6 address */
	*ipv6addr = _esm_data.pdn[pid].data->ip_addr;
    }
    else {
	/* IPv4v6 dual-stack terminal */
	*ipv4addr = _esm_data.pdn[pid].data->ip_addr;
	*ipv6addr = _esm_data.pdn[pid].data->ip_addr+ESM_DATA_IPV4_ADDRESS_SIZE;
    }

    LOG_FUNC_RETURN (RETURNok);
}

#endif // NAS_UE

/****************************************************************************/
/*********************  L O C A L    F U N C T I O N S  *********************/
/****************************************************************************/

