/********************************************************************
Copyright ?2007 LGC Wireless, Inc. All rights reserved
File Name: 		aifg_m.c
Description:	FSM of AIFG module
Version:		v9.0.0
Author:			Roy Jiang
Create Date:	2007-3-12

History:
2007-3-12		v9.0.0	Create
*********************************************************************/
//#include "aifg_mpp.h"
#include "./include/aifg_var_ext.h"
#include "./include/aifg.h"

aifg_sap sap[AIFG_MAX_SAP];
aifg_port aifg_pcb[AIFG_MAX_PORT];
aifg_circuit_group cg[AIFG_MAX_CG];
aifg_link_info link_info[SCCP_MAX_LINK];
aifg_dual_server dual_server;
aifg_event_code	aifg_event;
aifg_port *pOpenPort_h = NULL;	//List head of opened port
aifg_port *pPagPort_h = NULL;	//List head of paging port
aifg_port *pPagPort_t = NULL;	//List tail of paging port

static int paging_port_count = 0;	//for protection

extern inline void aifg_add_paging_port(aifg_port *p);
extern inline void aifg_del_paging_port(aifg_port *p);
extern void aifg_csta_init();
extern void aifg_csta_proc();

enum REL_CAUSE{
	REL_CAUSE_CALL_CONTROL	= 0x09,
	REL_CAUSE_HO_SUCCESS	= 0x0B,
	REL_CAUSE_TIMER_EXPIRE	= 0x66,
	REL_CAUSE_PROTOCOL_ERR	= 0x6F,
	REL_CAUSE_TEMP_FAILURE	= 0x29,
};

/*
Purpose:Init AIFG module.
Input:	None
Output:	None
*/
void aifg_init()
{
	int memsize;
	
	memset(&sap, 0, sizeof(sap)); 
	memset(aifg_pcb, 0, sizeof(aifg_port)*AIFG_MAX_PORT);
	memset(link_info, 0, sizeof(aifg_link_info)*SCCP_MAX_LINK);

	memsize = (sizeof(sap)+sizeof(aifg_port)*AIFG_MAX_PORT+sizeof(aifg_raw_msg)+sizeof(aifg_msg_pre_decoded)
		+ sizeof(dual_server) + sizeof(aifg_event) + sizeof(cg) + sizeof(aifg_link_info)*SCCP_MAX_LINK
		+ sizeof(SP_UiPriPara))/1024;

	printf("AIFG memory usage: %d KB\n", memsize);

	aifg_set_running_mode(AIFG_RUN_MODE_SINGLE);

	aifg_event = AIFG_SUCCEED;

	aifg_mpp_init();
	aifg_debug_init();
	aifg_csta_init();
	//for dual server test
	aifg_set_running_mode(AIFG_RUN_MODE_DUAL);

	printf("AIFG init completed!\n");
}

/*
Purpose:10ms routing of AIFG module.
Input:	None
Output:	None
*/
void aifg_proc()
{
	int i;
	aifg_port *pPort;

	for (i = 0; i < MSG_PROC_EACH_TIME; i++){
		if(AIFG_PROC_FINISH == aifg_mpp_proc())
			break;
	}
	
	//process opened port
	i = 0;
	pPort = pOpenPort_h;
	while(pPort != NULL){
		aifg_port *pTmpPort = pPort;
		pPort = pPort->pNextOpenPort;

		(pTmpPort->timer > 0 ) ? pTmpPort->timer-- : 0;
		aifg_port_proc(pTmpPort, NULL, NULL);

		if(++i > AIFG_MAX_PORT)
			break;
	}

	//---------for protection---------------
	if(i > AIFG_MAX_PORT)
	{
		//memory corrupted, reset all ports
		pOpenPort_h = NULL;
		for(i = 0; i < AIFG_MAX_PORT; i++)
			aifg_port_release(&aifg_pcb[i]);
	}
	//---------protection end---------------
	aifg_csta_proc();
	aifg_debug_timer();
}

//FSM---------------------------------------------------------------------------------------
inline void aifg_send_release_ind(aifg_sap_type sapid, int app_port, int aif_port, int cause)
{
	rv_msg.id = AIFG_MSG_ID_RELEASE;
	rv_msg.app_port = app_port;
	rv_msg.aif_port = aif_port;
	rv_msg.cause = cause;
	sap[sapid].callback_func(AIFG_IND_RV_MSG, &rv_msg);
	aifg_pcb[aif_port].app_rel_flag = 1;
}

inline void aifg_reset_timer(int port, int value)
{
	aifg_pcb[port].timer = value;
}

inline int aifg_get_cause_value(aifg_ie_ptr *cause)
{
	aifg_ie tmp_ie;
	if(cause->ptr != NULL){
		aifg_ie_decode(cause, &tmp_ie);
		if (tmp_ie.iei == AIFG_IE_ID_BM_CAUSE)
			return tmp_ie.param.bm_cause.value;
		else
			return tmp_ie.param.dt_cause.cause_value;
	}
	else
		return 0x1F;		//unspecified
}

enum RESET_STATE{
	RESET_INIT,
	RESET_WAIT_RESET_COMPLETE,
};
int reset_fsm(aifg_port *p, aifg_msg_t *upper_msg, aifg_msg_pre_decoded *peer_msg)
{
	switch(p->ti_stat){
		case RESET_INIT:
			if(AIFG_SAP_MSC == p->sapid)
				p->timer = AIFG_TIMER_T2;
			else
				p->timer = AIFG_TIMER_T13;				
			p->ti_stat = RESET_WAIT_RESET_COMPLETE;
			break;		
		case RESET_WAIT_RESET_COMPLETE:
			//Send RESET_ACK
			if(0 == p->timer){
				sd_msg.id = AIFG_MSG_ID_RESET_ACK;
				aifg_send_udt(p->cgid, &sd_msg, NULL);
				return AIFG_PROC_FINISH;
			}
			break;		
		default:
			assert(0);
			goto ERR_PROC;
			break;
	}

	return AIFG_PROC_CONTINUE;

ERR_PROC:
	//release the port
	return AIFG_ERROR;
}


enum PAGING_STATE{
	PAGING_INIT,
	PAGING_WAIT_RESP,
	PAGING_REPAGING,
};
int paging_fsm(aifg_port *p, aifg_msg_t *upper_msg, aifg_msg_pre_decoded *peer_msg)
{
	switch(p->ti_stat){
	case PAGING_INIT:
		{
			//remember imsi or tmsi
			if (upper_msg->msg.paging.tmsi.flag == 1){
				//TMSI, because the TMSI in paging is BCD coding and single byte coding in paging response
				//so here BCD coded TMSI will be translated to single byte coding
				int i = 0, j = 0;
				while (i < 4){
					p->imsi[j++] = upper_msg->msg.paging.tmsi.value[i] & 0x0F;
					p->imsi[j++] = (upper_msg->msg.paging.tmsi.value[i++] & 0xF0) >> 4;
				}
				p->imsi_len = j;
			}
			else{
				p->imsi_len = upper_msg->msg.paging.imsi.imsi_len;
				memcpy(p->imsi, upper_msg->msg.paging.imsi.imsi, p->imsi_len);
			}
			aifg_send_udt(p->cgid, upper_msg, &p->sd_buffer);

			//add the port paging port list
//			aifg_add_paging_port(p);
			p->ti_stat = PAGING_WAIT_RESP;
			p->timer = AIFG_TIMER_T3113;
		}
		break;
	case PAGING_WAIT_RESP:
		{
			if((peer_msg != NULL && AIFG_MSG_ID_PAGING_RESP == peer_msg->msg.completeL3Info.l3msg.id)){			
				peer_msg->app_port = p->upper_port;
				sap[p->sapid].callback_func(AIFG_IND_RV_MSG, peer_msg);
				return AIFG_PROC_FINISH;
			}
			else if(p->timer <= 0){
				aifg_debug_print(p-aifg_pcb, AIFG_ERR_PAGING_NOT_RESP, NULL, p-aifg_pcb);
				goto ERR_PROC;
			}
			else if(0 == (p->timer % AIFG_TIMER_REPAGE))
				p->ti_stat = PAGING_REPAGING;
		}			
		break;		
	case PAGING_REPAGING:
		{
			//Re-paging
			PostSp(&p->sd_buffer);
			p->ti_stat = PAGING_WAIT_RESP;
		}			
		break;		
	default:
		assert(0);
		goto ERR_PROC;
		break;
	}
	return AIFG_PROC_CONTINUE;

ERR_PROC:
	//remove this port from paging list
	p->app_rel_flag = 1;	//no need to release app
//	aifg_del_paging_port(p);
	return AIFG_ERROR;
}

enum LU_STATE{
	LU_WAIT_RESPONSE,
	LU_CLEAR,
	LU_WAIT_CLEAR_CMP,
};
int lu_fsm(aifg_port *p, aifg_msg_t *upper_msg, aifg_msg_pre_decoded *peer_msg)
{
	if (peer_msg != NULL && peer_msg->id == AIFG_MSG_ID_CLR_REQ){
		p->rel_cause = aifg_get_cause_value(&peer_msg->msg.clearReq.cause);
		aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, p->rel_cause);
		p->ti_stat = LU_CLEAR;
		p->timer = 0;
	}

	switch(p->ti_stat)
	{
	case LU_WAIT_RESPONSE:
		{
			if(upper_msg != NULL){
				if(upper_msg->id == AIFG_MSG_ID_RELEASE){
					p->ti_stat = LU_CLEAR;
					p->app_rel_flag = 1;
				}
				else{
					aifg_send_dt1(p->link_id, upper_msg, p->ti);
				}
			}
			else if(peer_msg != NULL){
				peer_msg->app_port = p->upper_port;
				sap[p->sapid].callback_func(AIFG_IND_RV_MSG, peer_msg);
			}
		}
		break;
	case LU_CLEAR:
		{
			sd_msg.aif_port = p - aifg_pcb;	//for debug print
			sd_msg.app_port = p->upper_port;//for debug print
			sd_msg.pd = AIFG_PD_BSSMAP_DEDICATED;
			sd_msg.id = AIFG_MSG_ID_CLR_CMD;
			sd_msg.msg.clearCmd.cause.flag = 1;
			sd_msg.msg.clearCmd.cause.ext_value.flag = 0;
			sd_msg.msg.clearCmd.cause.value = REL_CAUSE_CALL_CONTROL;
			sd_msg.msg.clearCmd.l3HeaderInfo.flag = 0;
			aifg_send_dt1(p->link_id, &sd_msg, p->ti);

			p->ti_stat = LU_WAIT_CLEAR_CMP;
			p->timer = AIFG_TIMER_CLEAR_TIMEOUT;
		}
		break;
	case LU_WAIT_CLEAR_CMP:
		{
			if((peer_msg != NULL && peer_msg->id == AIFG_MSG_ID_CLR_CMP)
				|| p->timer <= 0){
					link_info[p->link_id].cleared = 1;
					return AIFG_PROC_FINISH;
			}			
		}
		break;
	default:
		assert(0);
		return AIFG_PROC_FINISH;
		break;
	}

	return AIFG_PROC_CONTINUE;
}

enum HO_STATE{
	HO_IDLE,
	HO_SETUP_NEW_LINK,
	HO_EXECUTE,
	HO_WAIT_COMPLETE,
	HO_CLEAR_OLD_LINK,
	HO_WAIT_OLD_CLEAR_CMP,
	HO_CLEAR_NEW_LINK,
	HO_WAIT_NEW_CLEAR_CMP,
};
enum HO_RETURN{
	HO_PROCECCED,
	HO_NOT_PROCECCED,
};
int ho_fsm(aifg_port *p, aifg_msg_t *upper_msg, aifg_msg_pre_decoded *peer_msg)
{
	int retVal = HO_PROCECCED;

	switch(p->ho_stat){
		case HO_IDLE:
			{
				if (peer_msg->id == AIFG_MSG_ID_HO_REQUIRED){
					peer_msg->app_port = p->upper_port;
					sap[p->sapid].callback_func(AIFG_IND_RV_MSG, peer_msg);
					p->app_ho_port = peer_msg->app_port;
					p->ho_stat = HO_SETUP_NEW_LINK;
					p->timer = AIFG_TIMER_TRANS_TIMEOUT;
				}
				else{
					assert(0);
					retVal = HO_NOT_PROCECCED;
					goto ERROR_PROC;
				}
			}
			break;
		case HO_SETUP_NEW_LINK:
			{
				if (upper_msg != NULL ){
					if (upper_msg->id == AIFG_MSG_ID_HO_REQUEST){
						p->new_link = aifg_send_cr(p->sapid, p->cgid, upper_msg);
						aifg_add_link(p->new_link, p->sapid);
						link_info[p->new_link].cur_port = p - aifg_pcb;
						p->ho_stat = HO_EXECUTE;
						p->timer = AIFG_TIMER_TRANS_TIMEOUT;
					}
					else if (upper_msg->id == AIFG_MSG_ID_HO_REQ_REJ){
						aifg_send_dt1(p->link_id, upper_msg, -1);
						p->ho_stat = 0;
					}
					else
						retVal = HO_NOT_PROCECCED;
				}
				else if(p->timer == 0)
					goto ERROR_PROC;
				else
					retVal = HO_NOT_PROCECCED;
			}
			break;
		case HO_EXECUTE:
			{
				if (upper_msg != NULL){
					switch(upper_msg->id)
					{
					case AIFG_MSG_ID_HO_CMD:
						aifg_send_dt1(p->link_id, upper_msg, -1);
						retVal = HO_PROCECCED;
						p->ho_stat = HO_WAIT_COMPLETE;
						p->timer = AIFG_TIMER_TRANS_TIMEOUT;
						break;
					default:
						retVal = HO_NOT_PROCECCED;
						break;
					}
				}
				else if(peer_msg != NULL){
					switch(peer_msg->id)
					{
					case AIFG_MSG_ID_HO_FAIL:
						//rollback to HO_SETUP_NEW_LINK
						p->ho_stat = HO_SETUP_NEW_LINK;
						//no break here
					case AIFG_MSG_ID_HO_REQ_ACK:
					case AIFG_MSG_ID_QUEUE_IND:
						peer_msg->app_port = p->app_ho_port;
						sap[p->sapid].callback_func(AIFG_IND_RV_MSG, peer_msg);
						break;
					default:
						retVal = HO_NOT_PROCECCED;
						break;
					}					
				}
				else if(p->timer == 0)
					goto ERROR_PROC;
			}
			break;
		case HO_WAIT_COMPLETE:
			{
				if(peer_msg != NULL){
					switch(peer_msg->id)
					{
					case AIFG_MSG_ID_HO_DETECT:
						peer_msg->app_port = p->app_ho_port;
						sap[p->sapid].callback_func(AIFG_IND_RV_MSG, peer_msg);
						break;
					case AIFG_MSG_ID_HO_CMP:
						p->ho_stat = HO_CLEAR_OLD_LINK;
						peer_msg->app_port = p->app_ho_port;
						sap[p->sapid].callback_func(AIFG_IND_RV_MSG, peer_msg);
						break;
					case AIFG_MSG_ID_HO_FAIL:
						p->ho_stat = HO_CLEAR_NEW_LINK;
						peer_msg->app_port = p->app_ho_port;
						sap[p->sapid].callback_func(AIFG_IND_RV_MSG, peer_msg);
						break;
					default:
						retVal = HO_NOT_PROCECCED;
						break;
					}
				}
				else if(p->timer == 0)
					goto ERROR_PROC;
				else
					retVal = HO_NOT_PROCECCED;
			}
			break;
		case HO_CLEAR_OLD_LINK:
			{
				sd_msg.aif_port = p - aifg_pcb;	//for debug print
				sd_msg.app_port = p->upper_port;//for debug print
				sd_msg.pd = AIFG_PD_BSSMAP_DEDICATED;
				sd_msg.id = AIFG_MSG_ID_CLR_CMD;
				sd_msg.msg.clearCmd.cause.flag = 1;
				sd_msg.msg.clearCmd.cause.ext_value.flag = 0;
				sd_msg.msg.clearCmd.cause.value = REL_CAUSE_HO_SUCCESS;
				sd_msg.msg.clearCmd.l3HeaderInfo.flag = 0;
				aifg_send_dt1(p->link_id, &sd_msg, p->ti);

				p->ho_stat = HO_WAIT_OLD_CLEAR_CMP;
				p->timer = AIFG_TIMER_CLEAR_TIMEOUT;
			}
			break;
		case HO_WAIT_OLD_CLEAR_CMP:
			{
				if((peer_msg != NULL && peer_msg->id == AIFG_MSG_ID_CLR_CMP)|| p->timer <= 0){
					int old_link = p->link_id;
					aifg_pcb[link_info[old_link].cur_port].link_id = p->new_link;
					if(link_info[old_link].ti_number > 0){
						//handover all transaction on the old link to the new link
						int i = 0;
						while(link_info[old_link].ti_number > 0 && i < AIFG_MAX_TI_PER_LINK){
							if(link_info[old_link].ti[TI_FOR_CC][i].en_flag == 1){
								aifg_pcb[link_info[old_link].ti[TI_FOR_CC][i].port].link_id = p->new_link;
								link_info[old_link].ti_number--;
							}
							if(link_info[old_link].ti[TI_FOR_SMS][i].en_flag == 1){
								aifg_pcb[link_info[old_link].ti[TI_FOR_SMS][i].port].link_id = p->new_link;
								link_info[old_link].ti_number--;
							}
							if(link_info[old_link].ti[TI_FOR_SS][i].en_flag == 1){
								aifg_pcb[link_info[old_link].ti[TI_FOR_SS][i].port].link_id = p->new_link;
								link_info[old_link].ti_number--;
							}
							i++;
						}
					}
					link_info[old_link].cleared = 1;
					aifg_send_rlsd(old_link);

					p->ho_stat = 0;	//Handover completed
					aifg_reset_timer(p-aifg_pcb, AIFG_TIMER_TRANS_TIMEOUT);
				}
			}
			break;
		case HO_CLEAR_NEW_LINK:
			{
				sd_msg.aif_port = p - aifg_pcb;	//for debug print
				sd_msg.app_port = p->upper_port;//for debug print
				sd_msg.pd = AIFG_PD_BSSMAP_DEDICATED;
				sd_msg.id = AIFG_MSG_ID_CLR_CMD;
				sd_msg.msg.clearCmd.cause.flag = 1;
				sd_msg.msg.clearCmd.cause.ext_value.flag = 0;
				sd_msg.msg.clearCmd.cause.value = REL_CAUSE_CALL_CONTROL;
				sd_msg.msg.clearCmd.l3HeaderInfo.flag = 0;
				aifg_send_dt1(p->new_link, &sd_msg, p->ti);

				p->ho_stat = HO_WAIT_NEW_CLEAR_CMP;
				p->timer = AIFG_TIMER_CLEAR_TIMEOUT;
			}
			break;
		case HO_WAIT_NEW_CLEAR_CMP:
			if((peer_msg != NULL && peer_msg->id == AIFG_MSG_ID_CLR_CMP)|| p->timer <= 0){
				link_info[p->new_link].cleared = 1;
				aifg_send_rlsd(p->new_link);
				p->new_link = -1;
				p->ho_stat = 0;	//Handover failed
			}
			else
				retVal = HO_NOT_PROCECCED;
			break;
		default:
			assert(0);
			goto ERROR_PROC;
			break;
	}
	return retVal;

ERROR_PROC:
	p->ho_stat = 0;
	return retVal;
}

enum CC_STATE{
	CC_WAIT_SETUP,
	CC_CONNECTING,
	CC_CONNECTED,
	CC_DISCONNECTING,
	CC_WAIT_RELEASE,
	CC_RELEASING,
	CC_WAIT_RELEASE_CMP,
	CC_RELEASE_CMP,
	CC_CLEARING,
	CC_WAIT_CLEAR_CMP,
	CC_HANDOVER,
};
int cc_fsm(aifg_port *p, aifg_msg_t *upper_msg, aifg_msg_pre_decoded *peer_msg)
{
	if (peer_msg != NULL && peer_msg->id == AIFG_MSG_ID_CLR_REQ){
		p->rel_cause = aifg_get_cause_value(&peer_msg->msg.clearReq.cause);
		aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, p->rel_cause);
		p->ti_stat = CC_CLEARING;
		p->timer = 0;
	}
	
	switch(p->ti_stat)
	{
	case CC_WAIT_SETUP:
		{
			if(upper_msg != NULL){
				if (upper_msg->pd == AIFG_PD_DTAP_CC && p->ti == -1){
					//assign a new ti for the transaction
					p->ti = aifg_get_ti(p->link_id, p-aifg_pcb, TI_FOR_CC);
					if(p->ti == -1){
						aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, REL_CAUSE_PROTOCOL_ERR);
						p->rel_cause = REL_CAUSE_PROTOCOL_ERR;
						p->ti_stat = CC_CLEARING;

						aifg_debug_print(p-aifg_pcb, AIFG_ERR_CAN_NOT_FIND_TI, NULL, p-aifg_pcb);
						#ifdef _AIFG_DEBUG_
							assert(0);
						#endif
					}
				}
				switch(upper_msg->id){
					case AIFG_MSG_ID_SETUP:
						p->ti_stat = CC_CONNECTING;
						aifg_send_dt1(p->link_id, upper_msg, p->ti);
						break;
					case AIFG_MSG_ID_RELEASE:
						p->rel_cause = upper_msg->msg.release.cause1.cause_value;
						p->ti_stat = CC_RELEASING;
						p->app_rel_flag = 1;
						break;
					default:
						aifg_send_dt1(p->link_id, upper_msg, p->ti);
						break;
				}
			}
			else if(peer_msg != NULL){
				if(peer_msg->pd == AIFG_PD_DTAP_CC && p->ti == -1){
					p->ti = peer_msg->ti;
					aifg_add_ti(p->link_id, p->ti, TI_FOR_CC, p-aifg_pcb);
				}
				switch(peer_msg->id){
					case AIFG_MSG_ID_DISCONNECT:
						p->ti_stat = CC_RELEASING;
						p->rel_cause = aifg_get_cause_value(&peer_msg->msg.disconnect.cause);
						aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, p->rel_cause);
						break;
					case AIFG_MSG_ID_RELEASE:
						p->ti_stat = CC_RELEASE_CMP;
						p->rel_cause = aifg_get_cause_value(&peer_msg->msg.release.cause1);
						aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, p->rel_cause);
						break;
					case AIFG_MSG_ID_RELEASE_CMP:
						p->ti_stat = CC_CLEARING;
						p->rel_cause = aifg_get_cause_value(&peer_msg->msg.releaseComp.cause);
						aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, p->rel_cause);
						break;
					case AIFG_MSG_ID_EMERGENCY_SETUP:
					case AIFG_MSG_ID_SETUP:
						p->ti_stat = CC_CONNECTING;
						//no break here
					default:
						peer_msg->app_port = p->upper_port;
						sap[p->sapid].callback_func(AIFG_IND_RV_MSG, peer_msg);
						break;
				}
			}
			else if(p->timer == 0){
				p->rel_cause = REL_CAUSE_TIMER_EXPIRE;	//protocol timeout
				aifg_debug_print(p-aifg_pcb, AIFG_ERR_PORT_TIMER_OUT, NULL, p-aifg_pcb);
				aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, p->rel_cause);
				p->ti_stat = CC_CLEARING;
			}

			if (p->ti_stat != CC_WAIT_SETUP && p->ti_stat!=CC_RELEASE_CMP){
				//status changed
				aifg_reset_timer(p-aifg_pcb, AIFG_TIMER_TRANS_TIMEOUT);
			}
		}
		break;
	case CC_CONNECTING:
		{
			if(upper_msg != NULL){
				switch(upper_msg->id){
					case AIFG_MSG_ID_CONNECT:
						p->ti_stat = CC_CONNECTED;
						aifg_send_dt1(p->link_id, upper_msg, p->ti);
						break;
					case AIFG_MSG_ID_RELEASE:
						p->rel_cause = upper_msg->msg.release.cause1.cause_value;
						if(upper_msg->msg.release.cause1.diag_len > 0){
							p->diag_len = upper_msg->msg.release.cause1.diag_len;
							memcpy(p->diagnostic, upper_msg->msg.release.cause1.diagnostic, p->diag_len);
						}
						else
							p->diag_len = 0;
						p->ti_stat = CC_DISCONNECTING;
						p->app_rel_flag = 1;
						break;
					default:
						aifg_send_dt1(p->link_id, upper_msg, p->ti);
						break;
				}
				aifg_reset_timer(p-aifg_pcb, AIFG_TIMER_TRANS_TIMEOUT);
			}
			else if(peer_msg != NULL){
				switch(peer_msg->id){
					case AIFG_MSG_ID_DISCONNECT:
						p->ti_stat = CC_RELEASING;
						p->rel_cause = aifg_get_cause_value(&peer_msg->msg.disconnect.cause);
						aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, p->rel_cause);
						break;
					case AIFG_MSG_ID_RELEASE:
						p->ti_stat = CC_RELEASE_CMP;
						p->rel_cause = aifg_get_cause_value(&peer_msg->msg.release.cause1);
						aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, p->rel_cause);
						break;
					case AIFG_MSG_ID_RELEASE_CMP:
						p->ti_stat = CC_CLEARING;
						p->rel_cause = aifg_get_cause_value(&peer_msg->msg.releaseComp.cause);
						aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, p->rel_cause);
						break;
					case AIFG_MSG_ID_CONNECT:
						p->ti_stat = CC_CONNECTED;
						//no break here
					default:
						peer_msg->app_port = p->upper_port;
						sap[p->sapid].callback_func(AIFG_IND_RV_MSG, peer_msg);
						break;
				}
				aifg_reset_timer(p-aifg_pcb, AIFG_TIMER_TRANS_TIMEOUT);
			}
			else if(p->timer == 0){
				p->rel_cause = REL_CAUSE_TIMER_EXPIRE;	//protocol timeout
				aifg_debug_print(p-aifg_pcb, AIFG_ERR_PORT_TIMER_OUT, NULL, p-aifg_pcb);
				aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, p->rel_cause);
				p->ti_stat = CC_RELEASE_CMP;
			}

			if (p->ti_stat!=CC_CONNECTING && p->ti_stat!=CC_RELEASE_CMP){
				//status changed
				aifg_reset_timer(p-aifg_pcb, AIFG_TIMER_TRANS_TIMEOUT);
			}
		}
		break;
	case CC_CONNECTED:
		{
			if(upper_msg != NULL){
				if(upper_msg->id == AIFG_MSG_ID_RELEASE){
					p->rel_cause = upper_msg->msg.release.cause1.cause_value;
					if(upper_msg->msg.release.cause1.diag_len > 0){
						p->diag_len = upper_msg->msg.release.cause1.diag_len;
						memcpy(p->diagnostic, upper_msg->msg.release.cause1.diagnostic, p->diag_len);
					}
					else
						p->diag_len = 0;
					p->ti_stat = CC_DISCONNECTING;	
					p->app_rel_flag = 1;
				}
				else
					aifg_send_dt1(p->link_id, upper_msg, p->ti);
			}
			else if(peer_msg != NULL){
				switch(peer_msg->id){
					case AIFG_MSG_ID_DISCONNECT:
						p->ti_stat = CC_RELEASING;
						p->rel_cause = aifg_get_cause_value(&peer_msg->msg.disconnect.cause);
						aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, p->rel_cause);
						break;
					case AIFG_MSG_ID_RELEASE:
						p->ti_stat = CC_RELEASE_CMP;
						p->rel_cause = aifg_get_cause_value(&peer_msg->msg.release.cause1);
						aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, p->rel_cause);
						break;
					case AIFG_MSG_ID_RELEASE_CMP:
						p->ti_stat = CC_CLEARING;
						p->rel_cause = aifg_get_cause_value(&peer_msg->msg.releaseComp.cause);
						aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, p->rel_cause);
						break;
					default:
						peer_msg->app_port = p->upper_port;
						sap[p->sapid].callback_func(AIFG_IND_RV_MSG, peer_msg);
						break;
				}
			}
		}
		break;
	case CC_DISCONNECTING:
		{
			memset(&sd_msg.msg.disconnect, 0 ,sizeof(sd_msg.msg.disconnect));
			sd_msg.aif_port = p - aifg_pcb;	//for debug print
			sd_msg.app_port = p->upper_port;//for debug print
			sd_msg.pd = AIFG_PD_DTAP_CC;
			sd_msg.id = AIFG_MSG_ID_DISCONNECT;
			sd_msg.msg.disconnect.cause.flag = 1;
			sd_msg.msg.disconnect.cause.coding_std = 3;
			sd_msg.msg.disconnect.cause.location = 1;
			sd_msg.msg.disconnect.cause.cause_value = p->rel_cause;
			if(p->diag_len > 0)
				memcpy(sd_msg.msg.disconnect.cause.diagnostic, p->diagnostic, p->diag_len);
			sd_msg.msg.disconnect.cause.diag_len = p->diag_len;
			aifg_send_dt1(p->link_id, &sd_msg, p->ti);
			p->ti_stat = CC_WAIT_RELEASE;
			aifg_reset_timer(p-aifg_pcb, AIFG_TIMER_RELEASE_TIMEOUT);
		}
		break;
	case CC_WAIT_RELEASE:
		{
			if(peer_msg != NULL && peer_msg->id == AIFG_MSG_ID_RELEASE){
				p->ti_stat = CC_RELEASE_CMP;
				p->timer = AIFG_TIMER_WAIT_RELEASE_CMP;
			}
			else if(p->timer == 0){
				p->ti_stat = CC_RELEASING;
				p->rel_cause = REL_CAUSE_TIMER_EXPIRE;
			}
		}
		break;
	case CC_RELEASE_CMP:
		{
			if (p->timer == 0){
				memset(&sd_msg.msg.releaseCmp, 0 ,sizeof(sd_msg.msg.releaseCmp));
				sd_msg.aif_port = p - aifg_pcb;	//for debug print
				sd_msg.app_port = p->upper_port;//for debug print
				sd_msg.pd = AIFG_PD_DTAP_CC;
				sd_msg.id = AIFG_MSG_ID_RELEASE_CMP;
				sd_msg.msg.releaseCmp.cause.flag = 1;
				sd_msg.msg.releaseCmp.cause.coding_std = 3;
				sd_msg.msg.releaseCmp.cause.location = 1;
				sd_msg.msg.releaseCmp.cause.cause_value = p->rel_cause;
				aifg_send_dt1(p->link_id, &sd_msg, p->ti);
				p->ti_stat = CC_CLEARING;
				aifg_reset_timer(p-aifg_pcb, AIFG_TIMER_WAIT_RELEASE_CMP);
			}			
		}
		break;
	case CC_RELEASING:
		{
			memset(&sd_msg.msg.release, 0 ,sizeof(sd_msg.msg.release));
			sd_msg.aif_port = p - aifg_pcb;	//for debug print
			sd_msg.app_port = p->upper_port;//for debug print
			sd_msg.pd = AIFG_PD_DTAP_CC;
			sd_msg.id = AIFG_MSG_ID_RELEASE;
			sd_msg.msg.release.cause1.flag = 1;
			sd_msg.msg.release.cause1.coding_std = 3;
			sd_msg.msg.release.cause1.location = 1;
			sd_msg.msg.release.cause1.cause_value = p->rel_cause;
			
			aifg_send_dt1(p->link_id, &sd_msg, p->ti);
			p->ti_stat = CC_WAIT_RELEASE_CMP;
			aifg_reset_timer(p-aifg_pcb, AIFG_TIMER_RELEASE_TIMEOUT);
		}
		break;
	case CC_WAIT_RELEASE_CMP:
		{
			if((peer_msg != NULL && peer_msg->id == AIFG_MSG_ID_RELEASE_CMP) || p->timer == 0){
				p->ti_stat = CC_CLEARING;
				aifg_reset_timer(p-aifg_pcb, 0);
			}
		}
		break;
	case CC_CLEARING:
		{
			if (p->timer <= 0){
				if(aifg_del_ti(p->link_id, p->ti, TI_FOR_CC) > 0)
					return AIFG_PROC_FINISH;
				else{
					//send clear command
					sd_msg.aif_port = p - aifg_pcb;	//for debug print
					sd_msg.app_port = p->upper_port;//for debug print
					sd_msg.pd = AIFG_PD_BSSMAP_DEDICATED;
					sd_msg.id = AIFG_MSG_ID_CLR_CMD;
					sd_msg.msg.clearCmd.cause.flag = 1;
					sd_msg.msg.clearCmd.cause.ext_value.flag = 0;
					sd_msg.msg.clearCmd.cause.value = REL_CAUSE_CALL_CONTROL;
					sd_msg.msg.clearCmd.l3HeaderInfo.flag = 0;
					aifg_send_dt1(p->link_id, &sd_msg, p->ti);

					p->ti_stat = CC_WAIT_CLEAR_CMP;
					p->timer = AIFG_TIMER_CLEAR_TIMEOUT;
				}
			}			
		}
		break;
	case CC_WAIT_CLEAR_CMP:
		{
			if((peer_msg != NULL && peer_msg->id == AIFG_MSG_ID_CLR_CMP)
				|| p->timer <= 0){
				link_info[p->link_id].cleared = 1;
				return AIFG_PROC_FINISH;
			}				
		}
		break;
	default:
		assert(0);
		return AIFG_PROC_FINISH;
		break;
	}

	return AIFG_PROC_CONTINUE;
}

enum SMS_STATE{
	SMS_RP_TRANS,
	SMS_RELEASING,
	SMS_CLEARING,
	SMS_WAIT_CLEAR_CMP,
};
int sms_fsm(aifg_port *p, aifg_msg_t *upper_msg, aifg_msg_pre_decoded *peer_msg)
{
	if (peer_msg != NULL && peer_msg->id == AIFG_MSG_ID_CLR_REQ){
		p->rel_cause = aifg_get_cause_value(&peer_msg->msg.clearReq.cause);
		aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, p->rel_cause);
		p->ti_stat = SMS_CLEARING;
		p->timer = 0;
	}

	switch(p->ti_stat){
	case SMS_RP_TRANS:
		{
			if (upper_msg != NULL){
				if(upper_msg->id == AIFG_MSG_ID_RELEASE){
					p->ti_stat = SMS_RELEASING;
					p->app_rel_flag = 1;
				}
				else{
					if (upper_msg->pd == AIFG_PD_DTAP_SMS && p->ti == -1){
						//assign a new ti for the transaction
						p->ti = aifg_get_ti(p->link_id, p-aifg_pcb, TI_FOR_SMS);
						if(p->ti == -1){
							aifg_debug_print(p-aifg_pcb, AIFG_ERR_CAN_NOT_FIND_TI, NULL, p-aifg_pcb);
							aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, REL_CAUSE_PROTOCOL_ERR);
							p->ti_stat = SMS_RELEASING;							
							
							#ifdef _AIFG_DEBUG_
								assert(0);
							#endif
						}
					}
					aifg_send_dt1(p->link_id, upper_msg, p->ti);
					aifg_reset_timer(p-aifg_pcb, AIFG_TIMER_TRANS_TIMEOUT);
				}
			}
			else if(peer_msg != NULL){
				if(peer_msg->pd == AIFG_PD_DTAP_SMS && p->ti == -1){
					p->ti = peer_msg->ti;
					aifg_add_ti(p->link_id, p->ti, TI_FOR_SMS, p-aifg_pcb);
				}
				peer_msg->app_port = p->upper_port;
				sap[p->sapid].callback_func(AIFG_IND_RV_MSG, peer_msg);
				aifg_reset_timer(p-aifg_pcb, AIFG_TIMER_TRANS_TIMEOUT);
			}
			else if(p->timer == 0){
				aifg_debug_print(p-aifg_pcb, AIFG_ERR_PORT_TIMER_OUT, NULL, p-aifg_pcb);
				aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, REL_CAUSE_TIMER_EXPIRE);
				p->ti_stat = SMS_RELEASING;
			}
		}
		break;
	case SMS_RELEASING:
		{
			if(aifg_del_ti(p->link_id, p->ti, TI_FOR_SMS) == 0
				&& link_info[p->link_id].cur_port == p-aifg_pcb){
				p->ti_stat = SMS_CLEARING;
				p->timer = AIFG_TIMER_WAIT_RELEASE_CMP;
			}
			else
				return AIFG_PROC_FINISH;
		}
		break;
	case SMS_CLEARING:
		{
			if (p->timer == 0){
				sd_msg.aif_port = p - aifg_pcb;	//for debug print
				sd_msg.app_port = p->upper_port;//for debug print
				sd_msg.pd = AIFG_PD_BSSMAP_DEDICATED;
				sd_msg.id = AIFG_MSG_ID_CLR_CMD;
				sd_msg.msg.clearCmd.cause.flag = 1;
				sd_msg.msg.clearCmd.cause.ext_value.flag = 0;
				sd_msg.msg.clearCmd.cause.value = REL_CAUSE_CALL_CONTROL;
				sd_msg.msg.clearCmd.l3HeaderInfo.flag = 0;
				aifg_send_dt1(p->link_id, &sd_msg, p->ti);

				p->ti_stat = SMS_WAIT_CLEAR_CMP;
				p->timer = AIFG_TIMER_CLEAR_TIMEOUT;
			}	
		}
		break;
	case SMS_WAIT_CLEAR_CMP:
		{
			if((peer_msg != NULL && peer_msg->id == AIFG_MSG_ID_CLR_CMP)
				|| p->timer <= 0){
				link_info[p->link_id].cleared = 1;
				return AIFG_PROC_FINISH;
			}
		}
		break;
	default:
		assert(0);
		return AIFG_PROC_FINISH;
		break;
	}

	return AIFG_PROC_CONTINUE;
}

enum SS_STATE{
	SS_REGISTERING,
	SS_CLEARING,
	SS_WAIT_CLEAR_CMP,
};
int ss_fsm(aifg_port *p, aifg_msg_t *upper_msg, aifg_msg_pre_decoded *peer_msg)
{
	if (peer_msg != NULL && peer_msg->id == AIFG_MSG_ID_CLR_REQ){
		p->rel_cause = aifg_get_cause_value(&peer_msg->msg.clearReq.cause);
		aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, p->rel_cause);

		//clear request, BSC may encounter error
		//send clear command without checking if there exist other transaction
		sd_msg.aif_port = p - aifg_pcb;	//for debug print
		sd_msg.app_port = p->upper_port;//for debug print
		sd_msg.pd = AIFG_PD_BSSMAP_DEDICATED;
		sd_msg.id = AIFG_MSG_ID_CLR_CMD;
		sd_msg.msg.clearCmd.cause.flag = 1;
		sd_msg.msg.clearCmd.cause.ext_value.flag = 0;
		sd_msg.msg.clearCmd.cause.value = REL_CAUSE_CALL_CONTROL;
		sd_msg.msg.clearCmd.l3HeaderInfo.flag = 0;
		aifg_send_dt1(p->link_id, &sd_msg, p->ti);

		p->ti_stat = SS_WAIT_CLEAR_CMP;
		p->timer = AIFG_TIMER_CLEAR_TIMEOUT;
	}
	
	switch(p->ti_stat){
	case SS_REGISTERING:
		{
			if(upper_msg != NULL){
				if(upper_msg->id == AIFG_MSG_ID_RELEASE_CMP_SS){
					p->ti_stat = SS_CLEARING;
					p->app_rel_flag = 1;
				}
				aifg_send_dt1(p->link_id, upper_msg, p->ti);
			}
			else if(peer_msg != NULL){
				if (peer_msg->pd == AIFG_PD_DTAP_SS && p->ti == -1){
					p->ti = peer_msg->ti;
					aifg_add_ti(p->link_id, p->ti, TI_FOR_SS, p-aifg_pcb);
				}
				peer_msg->app_port = p->upper_port;
				sap[p->sapid].callback_func(AIFG_IND_RV_MSG, peer_msg);
				aifg_reset_timer(p-aifg_pcb, AIFG_TIMER_TRANS_TIMEOUT);
			}
			else if(p->timer <= 0){
				aifg_debug_print(p-aifg_pcb, AIFG_ERR_PORT_TIMER_OUT, NULL, p-aifg_pcb);
				aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, REL_CAUSE_TIMER_EXPIRE);
				p->ti_stat = SS_CLEARING;			
			}
		}
		break;
	case SS_CLEARING:
		{
			if(aifg_del_ti(p->link_id, p->ti, TI_FOR_SS) > 0)
				return AIFG_PROC_FINISH;
			else{
				memset(&sd_msg.msg.clearCmd, 0 ,sizeof(sd_msg.msg.clearCmd));
				sd_msg.aif_port = p - aifg_pcb;	//for debug print
				sd_msg.app_port = p->upper_port;//for debug print
				sd_msg.pd = AIFG_PD_BSSMAP_DEDICATED;
				sd_msg.id = AIFG_MSG_ID_CLR_CMD;
				sd_msg.msg.clearCmd.cause.flag = 1;
				sd_msg.msg.clearCmd.cause.ext_value.flag = 0;
				sd_msg.msg.clearCmd.cause.value = REL_CAUSE_CALL_CONTROL;
				sd_msg.msg.clearCmd.l3HeaderInfo.flag = 0;
				aifg_send_dt1(p->link_id, &sd_msg, p->ti);

				p->ti_stat = SS_WAIT_CLEAR_CMP;
				p->timer = AIFG_TIMER_CLEAR_TIMEOUT;
			}
		}
		break;
	case SS_WAIT_CLEAR_CMP:
		{
			if((peer_msg != NULL && peer_msg->id == AIFG_MSG_ID_CLR_CMP)
				|| p->timer <= 0){
				link_info[p->link_id].cleared = 1;
				return AIFG_PROC_FINISH;
			}
		}
		break;	
	}
	
	return AIFG_PROC_CONTINUE;
}

void aifg_port_proc(aifg_port *pPort, aifg_msg_t *upper_msg, aifg_msg_pre_decoded *peer_msg)
{
	switch(pPort->stat){
		case AIFG_PORT_INIT:	//init
			{
				if(upper_msg != NULL){
					//the port is activate by upper app
					switch(upper_msg->id){
						case AIFG_MSG_ID_PAGING:
							assert(AIFG_SAP_MSC == pPort->sapid);	//only MSC can send this message
							pPort->stat = AIFG_PORT_PAGING;
							paging_fsm(pPort, upper_msg, peer_msg);
							break;
						case AIFG_MSG_ID_SETUP:
							pPort->ti_type = AIFG_TRANS_CC;
							pPort->ti_stat = 0;
							pPort->stat = AIFG_PORT_TRANSACTION;
							pPort->timer = AIFG_TIMER_TRANS_TIMEOUT;
							cc_fsm(pPort, upper_msg, peer_msg);
							break;
						case AIFG_MSG_ID_RP_DATA:
							pPort->ti_type = AIFG_TRANS_SMS;
							pPort->ti_stat = 0;
							pPort->stat = AIFG_PORT_TRANSACTION;
							pPort->timer = AIFG_TIMER_TRANS_TIMEOUT;
							sms_fsm(pPort, upper_msg, peer_msg);
							break;
						case AIFG_MSG_ID_L3_INFO:
							{
								int link_id;
								assert(AIFG_SAP_BSC == pPort->sapid);	//only BSC can send this message
								pPort->stat = AIFG_PORT_TRANSPARENT;
								link_id = aifg_send_cr(pPort->sapid, pPort->cgid, upper_msg);
								if(link_id < 0 || link_id > SCCP_MAX_LINK)
									goto ERROR_PROC;
								aifg_add_link(link_id, pPort->sapid);
								pPort->link_id = link_id;
								link_info[link_id].cur_port = pPort - aifg_pcb;
							}
							break;
						default:
							assert(0);
							goto ERROR_PROC;
							break;
					}
				}
				else if(peer_msg != NULL){
					//the port is activate by peer app
					switch(peer_msg->id){
						case AIFG_MSG_ID_RESET:
							peer_msg->app_port = -1;
							pPort->stat = AIFG_PORT_RESET;
							break;
						case AIFG_MSG_ID_L3_INFO:
							peer_msg->app_port = -1;
							pPort->stat = AIFG_PORT_TRANSACTION;
							pPort->timer = AIFG_TIMER_TRANS_TIMEOUT;
							pPort->ti = -1;
							pPort->ti_stat = 0;
							if(AIFG_MSG_ID_LU_REQUEST == peer_msg->msg.completeL3Info.l3msg.id)
								pPort->ti_type = AIFG_TRANS_LU;
							else if(AIFG_MSG_ID_IMSI_DETACH_IND == peer_msg->msg.completeL3Info.l3msg.id){
								pPort->ti_type = AIFG_TRANS_LU;
								pPort->ti_stat = LU_CLEAR;
							}
							else if(AIFG_MSG_ID_CM_REQUEST == peer_msg->msg.completeL3Info.l3msg.id){
								switch(peer_msg->msg.completeL3Info.l3msg.msg.cmRequest.type.ptr[0] & 0x0F){	//cm service type
									case 0x01:	//MO Call
										pPort->ti_type = AIFG_TRANS_CC;
										break;
									case 0x04:
										pPort->ti_type = AIFG_TRANS_SMS;
										break;
									case 0x08:
										pPort->ti_type = AIFG_TRANS_SS;
										break;
									default:
										pPort->ti_type = AIFG_TRANS_UNKNOW;
										break;
								}
							}
							else{
								assert(0);
							}
							break;
						case AIFG_MSG_ID_CM_REQUEST:	//the app_port is set to last opened port for this message
							pPort->stat = AIFG_PORT_TRANSACTION;
							pPort->timer = AIFG_TIMER_TRANS_TIMEOUT;
							pPort->ti = -1;
							pPort->ti_stat = 0;
							switch(peer_msg->msg.cmReq.type.ptr[0] & 0x0F){	//cm service type
								case 0x01:	//MO Call
									pPort->ti_type = AIFG_TRANS_CC;
									break;
								case 0x04:
									pPort->ti_type = AIFG_TRANS_SMS;
									break;
								case 0x08:
									pPort->ti_type = AIFG_TRANS_SS;
									break;
								default:
									pPort->ti_type = AIFG_TRANS_UNKNOW;
									break;
							}
							break;
						case AIFG_MSG_ID_HO_REQUEST:
							peer_msg->app_port = -1;
							pPort->stat = AIFG_PORT_TRANSPARENT;
							break;
						default:
							assert(0);
							//unexpected message
							goto ERROR_PROC;
							break;
					}
					rv_msg.trace_flag = 0;
					sap[pPort->sapid].callback_func(AIFG_IND_RV_MSG, peer_msg);
					pPort->upper_port = peer_msg->app_port;	//remember new assigned app port
					if (rv_msg.trace_flag == 1)
						aifg_pcb[rv_msg.aif_port].trace_flag = rv_msg.trace_flag;	//app may enable trace by setting rv_msg.trace_flag
				}
				else{
					assert(0);
					goto ERROR_PROC;
				}
			}
			break;
		case AIFG_PORT_TRANSACTION:
			{
				switch(pPort->ti_type){
					case AIFG_TRANS_LU:
						{
							int ret = lu_fsm(pPort, upper_msg, peer_msg);
							if(AIFG_PROC_FINISH == ret){
								pPort->stat = AIFG_PORT_FINISH;
							}
							else if(AIFG_ERROR == ret)
								goto ERROR_PROC;
						}
						break;
					case AIFG_TRANS_CC:
						{
							int ret = -1;

							/*
							*when in handover state, we will first try ho_fsm
							*if the ho_fsm return HO_PROCECCED, the process will end here
							*else, we will continue to cc_fsm
							*/
							if(pPort->ho_stat != 0 || (peer_msg != NULL && peer_msg->id == AIFG_MSG_ID_HO_REQUIRED)){
								//perform handover, the ho_stat will be set to 0 when the handover is completed
								if (HO_PROCECCED == ho_fsm(pPort, upper_msg, peer_msg))
									goto CC_FINISH;	/*BEWARE: switch-case may break here*/
							}

							ret = cc_fsm(pPort, upper_msg, peer_msg);
							if(AIFG_PROC_FINISH == ret)
								pPort->stat = AIFG_PORT_FINISH;
							else if(AIFG_ERROR == ret)
								goto ERROR_PROC;
						}
					CC_FINISH:
						break;
					case AIFG_TRANS_SS:
						{
							int ret = ss_fsm(pPort, upper_msg, peer_msg);
							if(AIFG_PROC_FINISH == ret)
								pPort->stat = AIFG_PORT_FINISH;
							else if(AIFG_ERROR == ret)
								goto ERROR_PROC;
						}
						break;
					case AIFG_TRANS_SMS:
						{
							int ret = sms_fsm(pPort, upper_msg, peer_msg);
							if(AIFG_PROC_FINISH == ret)
								pPort->stat = AIFG_PORT_FINISH;
							else if(AIFG_ERROR == ret)
								goto ERROR_PROC;
						}
						break;
					case AIFG_TRANS_UNKNOW:	//for paging response
						{
							aifg_pd tmp_pd;
							if(upper_msg != NULL)
								tmp_pd = upper_msg->pd;
							else if(peer_msg != NULL)
								tmp_pd = peer_msg->pd;
							else if(pPort->timer == 0)
								goto ERROR_PROC;
							else
								break;	//case AIFG_TRANS_UNKNOW may break here
							
							switch(tmp_pd)
							{
								case AIFG_PD_DTAP_CC:
									pPort->ti_type = AIFG_TRANS_CC;
									cc_fsm(pPort, upper_msg, peer_msg);
									break;
								case AIFG_PD_DTAP_SMS:
									pPort->ti_type = AIFG_TRANS_SMS;
									sms_fsm(pPort, upper_msg, peer_msg);
									break;
								case AIFG_PD_DTAP_SS:
									pPort->ti_type = AIFG_TRANS_SS;
									ss_fsm(pPort, upper_msg, peer_msg);
									break;
								default:
									if (upper_msg != NULL){
										aifg_send_dt1(pPort->link_id, upper_msg, pPort->ti);
									}
									else{
										peer_msg->app_port = pPort->upper_port;
										sap[pPort->sapid].callback_func(AIFG_IND_RV_MSG, peer_msg);
									}
									break;
							}
						}
						break;
					default:
						assert(0);
						break;
				}
			}
			break;
		case AIFG_PORT_TRANSPARENT:
			{
				//transparent transfer
				if(upper_msg != NULL){
					aifg_send_dt1(pPort->link_id, upper_msg, pPort->ti);
				}
				else if (peer_msg != NULL){
					peer_msg->app_port = pPort->upper_port;
					sap[pPort->sapid].callback_func(AIFG_IND_RV_MSG, peer_msg);
				}
			}
			break;
		case AIFG_PORT_PAGING:	//paging transaction
			{
				int ret =  paging_fsm(pPort, upper_msg, peer_msg);
				if(AIFG_PROC_FINISH == ret){
					//assert(NULL != p->sd_buffer);
					//free(p->sd_buffer);
					pPort->stat = AIFG_PORT_TRANSACTION;
					pPort->timer = AIFG_TIMER_TRANS_TIMEOUT;
					pPort->ti_type = AIFG_TRANS_UNKNOW;
					pPort->ti_stat = 0;
					aifg_reset_timer(pPort-aifg_pcb, AIFG_TIMER_TRANS_TIMEOUT);
				}
				else if(AIFG_ERROR == ret){
					//assert(NULL != p->sd_buffer);
					//free(p->sd_buffer);
					goto ERROR_PROC;
				}
			}
			break;
		case AIFG_PORT_RESET:	//reset transaction
			{
				int ret =  reset_fsm(pPort, upper_msg, peer_msg);
				if(AIFG_PROC_FINISH == ret)
					pPort->stat = AIFG_PORT_FINISH;
				else if(AIFG_ERROR == ret)
					goto ERROR_PROC;
			}
			break;
		case AIFG_PORT_FINISH:
			if(pPort->link_id != -1 
//				&& link_info[pPort->link_id].ti_number <= 0
				&& link_info[pPort->link_id].cleared == 1)
			{
				aifg_link_release(pPort->link_id);
			}
			else{
				if(pPort->link_id != -1 && link_info[pPort->link_id].cur_port == pPort - aifg_pcb)
					link_info[pPort->link_id].cur_port = -1;
				aifg_port_release(pPort);
			}
			break;
		default:
			assert(0);
			goto ERROR_PROC;
			break;
	}

	return;

ERROR_PROC:
	if(pPort->link_id != -1 
		&& link_info[pPort->link_id].ti_number <= 1)	//only one transaction or less
//		&& link_info[pPort->link_id].cleared == 1)
	{
		aifg_link_release(pPort->link_id);
	}
	else{
		if(link_info[pPort->link_id].cur_port == pPort - aifg_pcb)
			link_info[pPort->link_id].cur_port = -1;
		aifg_port_release(pPort);
	}
	return;
}

//end of FSM---------------------------------------------------------------------------------

/*
Purpose:assign a free port and init
Input:	int sapid: SAP ID
		int linkid: Link ID
Output:	int: Port number, -1 if no port available
*/
int start_pos = 0;	//start position
inline int aifg_port_assign(int sapid, int cgid, int linkid)
{
	int i = start_pos;
	int end_pos = start_pos - 1;
	if (end_pos < 0){
		end_pos = AIFG_MAX_PORT -1 ;
	}

	while(i != end_pos){
		if(aifg_pcb[i].stat == AIFG_PORT_IDLE){
			aifg_pcb[i].sapid = sapid;
			aifg_pcb[i].cgid = cgid;
			aifg_pcb[i].link_id = linkid;
			aifg_pcb[i].upper_port = -1; //not assigned yet
			aifg_pcb[i].stat = AIFG_PORT_INIT;
			aifg_pcb[i].timer = 0;
			aifg_pcb[i].pNextPort = aifg_pcb[i].pPrevPort = NULL;
			aifg_pcb[i].ti = -1;	//not assigned yet
			aifg_pcb[i].ti_stat = 0;
			aifg_pcb[i].ti_type = 0;
			//aifg_pcb[i].sd_buffer = NULL;

			//add this port to opened port list
			if(NULL == pOpenPort_h){
				//this is the first one
				pOpenPort_h = &aifg_pcb[i];
				aifg_pcb[i].pNextOpenPort = NULL;
				aifg_pcb[i].pPrevOpenPort = NULL;
			}
			else{
				//add to the head of the list
				aifg_pcb[i].pNextOpenPort = pOpenPort_h;
				aifg_pcb[i].pPrevOpenPort = NULL;
				pOpenPort_h->pPrevOpenPort = &aifg_pcb[i];
				pOpenPort_h = &aifg_pcb[i];
			}

			//set next time start position
			start_pos = i + 1;
			if(start_pos >= AIFG_MAX_PORT)
				start_pos = 0;

			return i;
		}
		if(++i >= AIFG_MAX_PORT)
			i = 0;	//restart from beginning
	}
	return -1;
}

/*
Purpose:release a opened port
Input:	int l_port: port number
Output:	None
*/
inline void aifg_port_release(aifg_port *p)
{
	if (p->stat == AIFG_PORT_IDLE)
		return;

	if (p->upper_port != -1 && p->app_rel_flag == 0){	//release application if not released
		aifg_send_release_ind(p->sapid, p->upper_port, p-aifg_pcb, REL_CAUSE_TEMP_FAILURE);
	}

	//delete this port from the opened port list
	if(NULL != p->pPrevOpenPort)
		p->pPrevOpenPort->pNextOpenPort = p->pNextOpenPort;
	else
		pOpenPort_h = p->pNextOpenPort;

	if(NULL != p->pNextOpenPort)
		p->pNextOpenPort->pPrevOpenPort = p->pPrevOpenPort;

	memset(p, 0, sizeof(*p));

	aifg_debug_print(p-aifg_pcb, AIFG_EVENT_PORT_RELEASE, NULL, p-aifg_pcb);

	return;
}

inline void aifg_link_release(int link_id)
{
	aifg_send_rlsd(link_id);
	aifg_del_link(link_id);
}

/*
Purpose: Get local port according to specific TI
Input:	int ti: ti_value
int link_id: search port on which link
Output: int: local port number, -1 if not found
*/
inline int aifg_ti2lport(int ti, int link_id, int pd)
{
	int ti_type;
	assert(ti >=0 && ti < AIFG_MAX_TI_PER_LINK);
	assert(link_id >=0 && link_id < SCCP_MAX_LINK);

	switch(pd)
	{
	case AIFG_PD_DTAP_CC:
		ti_type = TI_FOR_CC;
		break;
	case AIFG_PD_DTAP_SMS:
		ti_type = TI_FOR_SMS;
		break;
	case AIFG_PD_DTAP_SS:
		ti_type = TI_FOR_SS;
		break;
	default:
		assert(0);
	}

	if(1 == link_info[link_id].ti[ti_type][ti].en_flag)
		return link_info[link_id].ti[ti_type][ti].port;

	return -1;
}

/*
Purpose:get the paging port according to the IMSI
Input:	BYTE imsi_ptr: pointer to imsi buffer
Output:	local port number, -1 if not found
*/
inline int aifg_get_paging_port(int cgid, BYTE *imsi_ptr, int imsi_len)
{
	int retVal = -1;
	aifg_port *pPort = pOpenPort_h; //pPagPort_h;
	aifg_port *pTmpPort;
	int i = 0;

	while (pPort != NULL){
		pTmpPort = pPort->pNextOpenPort; //pPort->pNextPort;
		if(pPort->stat == AIFG_PORT_PAGING){
			if(memcmp(pPort->imsi, imsi_ptr, imsi_len) == 0){
				if (pPort->cgid == cgid){
					retVal = pPort-aifg_pcb;
	//				aifg_del_paging_port(pPort);
				}
				else{
					pPort->app_rel_flag = 1;
	//				aifg_del_paging_port(pPort);
					aifg_port_release(pPort);
				}
			}
		}
		pPort = pTmpPort;

		if(++i > AIFG_MAX_PORT){	//for protection
			break;
		}
	}

	return retVal;
}

inline void aifg_add_paging_port(aifg_port *p)
{
	p->pPrevPort = NULL;
	p->pNextPort = NULL;

	if (NULL == pPagPort_t){
		assert(pPagPort_h == NULL);	//pointer to head should also be NULL
		pPagPort_h = p;
		pPagPort_t = p;
	}
	else{
		p->pPrevPort = pPagPort_t;
		pPagPort_t->pNextPort = p;
		pPagPort_t = p;
	}
	paging_port_count++;
}

inline void aifg_del_paging_port(aifg_port *p)
{
	if(pPagPort_h == p)
		pPagPort_h = p->pNextPort;	//update list head
	if(pPagPort_t == p)
		pPagPort_t = p->pPrevPort;	//update list tail

	if (p->pPrevPort != NULL){
		p->pPrevPort->pNextPort = p->pNextPort;
	}
	if (p->pNextPort != NULL){
		p->pNextPort->pPrevPort = p->pPrevPort;
	}
	p->pNextPort = NULL;
	p->pPrevPort = NULL;
	paging_port_count--;
}
//Circuit group & link management functions-------------------------------------
inline int aifg_get_cg(int dpc, int ni)
{
	int i = 0;

	while(i < AIFG_MAX_CG){
		if(cg[i].enable == 1 && cg[i].dpc == dpc && cg[i].ni == ni)
			return i;
		i++;
	}	
	return -1;
}

/*
Purpose:	Get SCCP link info from its ID
Input:		int sccp_link_id: ID of the connection
Output:		aifg_link_info: SCCP link structure pointer
*/
inline aifg_link_info *aifg_get_link_info(int linkid)
{
	assert(linkid < SCCP_MAX_LINK);
	assert(link_info[linkid].en_flag == 1);
	return &link_info[linkid];
}

/*
Purpose:	Add a sccp link object to the link pool
Input:		int linkid:ID of the link, assigned by SCCP module
			int sapid: which sap manage this link
			int status: next message is CC or DT1
Output:		int: result
*/
inline int aifg_add_link(int linkid, int sapid)
{
	link_info[linkid].en_flag = 1;
	link_info[linkid].ti_number = 0;
	link_info[linkid].sapid = sapid;
	link_info[linkid].cur_port = -1;

	return AIFG_SUCCEED;
}

/*
Purpose:	Delete specific connection from pool
Input:		int sccp_link_id: ID of the connection
Output:		int: result
*/
inline int aifg_del_link(int link_id)
{
	int i = 0;
	assert(link_id < SCCP_MAX_LINK);

	if(link_id > SCCP_MAX_LINK)
		return AIFG_ERROR;

	//release ports belongs to this link, if any
	while(link_info[link_id].ti_number > 0){
		if (1 == link_info[link_id].ti[TI_FOR_CC][i].en_flag){
			link_info[link_id].ti_number--;
			aifg_port_release(&aifg_pcb[link_info[link_id].ti[TI_FOR_CC][i].port]);
		}
		if (1 == link_info[link_id].ti[TI_FOR_SMS][i].en_flag){
			link_info[link_id].ti_number--;
			aifg_port_release(&aifg_pcb[link_info[link_id].ti[TI_FOR_SMS][i].port]);
		}
		if (1 == link_info[link_id].ti[TI_FOR_SS][i].en_flag){
			link_info[link_id].ti_number--;
			aifg_port_release(&aifg_pcb[link_info[link_id].ti[TI_FOR_SS][i].port]);
		}

		if (++i > AIFG_MAX_TI_PER_LINK)
			break;
	}
	if(aifg_pcb[link_info[link_id].cur_port].ho_stat == 0)
		aifg_port_release(&aifg_pcb[link_info[link_id].cur_port]);
	
	memset(&link_info[link_id], 0, sizeof(aifg_link_info));

	return AIFG_SUCCEED;
}
//end of Circuit group & link management functions---------------------------------

//TI management function definition-----------------------------------------------------
/*
Purpose:	get a new free TI of a link and add it
Input:		int link_id: link ID
			int port:port number of upper layer
Output:		int: new assigned TI, -1 if not found
*/
inline int aifg_get_ti(int link_id, int port, int ti_type)
{
	int i;

	assert(link_id < SCCP_MAX_LINK);

	for(i=0; i < 7; i++){
		if (0 == link_info[link_id].ti[ti_type][i].en_flag){
			aifg_add_ti(link_id, i, ti_type, port);
			return i;
		}
	}
	return -1;
}

/*
Purpose:	Add a new TI for a existed SCCP link
Input:		int link_id: link ID of the TI
			int ti:	TI value
			int port: port number of upper layer
Output:		None
*/
inline void aifg_add_ti(int link_id, int ti, int ti_type, int port)
{
	assert(ti <= AIFG_MAX_TI_PER_LINK);
	assert(link_id < SCCP_MAX_LINK);
	assert(ti_type < 3);
	assert(link_info[link_id].ti[ti_type][ti].en_flag != 1);

	link_info[link_id].ti[ti_type][ti].en_flag = 1;
	link_info[link_id].ti[ti_type][ti].port = port;
	link_info[link_id].ti_number++;

	return;
}

/*
Purpose:	Delete a terminated TI
Input:		int link_id: link ID of the TI
			int ti:	TI value
			int ti_type: could be 1 of the 3: TI_FOR_CC, TI_FOR_SMS, TI_FOR_SS
Output:		int: remaining TI number
*/
inline int aifg_del_ti(int link_id, int ti, int ti_type)
{
	assert(ti <= AIFG_MAX_TI_PER_LINK);
	assert(link_id < SCCP_MAX_LINK);
	assert(ti_type < 3);

	if (ti >= 0 && link_info[link_id].ti[ti_type][ti].en_flag > 0){
		link_info[link_id].ti[ti_type][ti].en_flag = 0;
		link_info[link_id].ti_number--;
	}

	return link_info[link_id].ti_number;
}
//end of TI management function definition----------------------------------------------

