/***********************************************************
Copyright (C), LGC Wireless.
File Name:		isdn_rm.c
Description:	ISDN Resource Management
Version:		v9.0.0
Author:			Gengxin Chen
Create Date:	2008-2-19
History:
	<author>	<date>		<version>		<desc>
	
************************************************************/
#include "./include/isdn_const.h"
#include "../../m2ua/src/include/m2ua_const.h"
#include "./include/isdn_rm.h"
#include "./include/isdn_debug.h"
#include "./include/isdn_ext.h"
#include "./include/isdn_inc.h"

extern int isdn_receive_msg();
extern int isdn_redirect_receive();
extern void isdn_send_heartbeat(int link_id);
extern void isdn_cpc_timer(u32 pid);
extern void maintenance_timer(u32 pid);
extern void isdn_restart_timer(u32 pid);
extern void service_dc_proc(u32 pid);
extern void isdn_restart_ctrl(u32 pid);
extern int trans_callref_sd_rest_req(u32 pid, u8 g_pri_cmd);

/*************************************************************
Function:		timer_init
Description:	initialize the timers of isdn
Input:		interval_ms:time cycle
Return:		NULL
*************************************************************/
static void timer_init(u32 interval_ms)
{
	isdn_timer_var.t301 = ISDN_T301 * 1000 / interval_ms;
	isdn_timer_var.t302 = ISDN_T302 * 1000 / interval_ms;
	isdn_timer_var.t303 = ISDN_T303 * 1000 / interval_ms;
	isdn_timer_var.t304 = ISDN_T304 * 1000 / interval_ms;
	isdn_timer_var.t305 = ISDN_T305 * 1000 / interval_ms;
	isdn_timer_var.t306 = ISDN_T306 * 1000 / interval_ms;
	isdn_timer_var.t307 = ISDN_T307 * 1000 / interval_ms;
	isdn_timer_var.t308 = ISDN_T308 * 1000 / interval_ms;
	isdn_timer_var.t309 = ISDN_T309 * 1000 / interval_ms;
	isdn_timer_var.t310 = ISDN_T310 * 1000 / interval_ms;
	isdn_timer_var.t312 = ISDN_T312 * 1000 / interval_ms;
	isdn_timer_var.t313 = ISDN_T313 * 1000 / interval_ms;
	isdn_timer_var.t316 = ISDN_T316 * 1000 / interval_ms;
	isdn_timer_var.t317 = ISDN_T317 * 1000 / interval_ms;
	isdn_timer_var.t321 = ISDN_T321 * 1000 / interval_ms;
	isdn_timer_var.t322 = ISDN_T322 * 1000 / interval_ms;
	isdn_timer_var.t1s = ISDN_T1 * 1000 / interval_ms + 20;
	isdn_timer_var.t3M1 = ISDN_T3M1 * 1000 / interval_ms;
}

/*************************************************************
Function:		isdn_init
Description:	initialize the parameters of isdn
Input:		systemID:plat id, interval_ms:time cycle
Return:		ISDN_CM_OK:success
*************************************************************/
int isdn_init(u8 systemID, u32 interval_ms)
{
	u32 stack_size;
	if(interval_ms == 0)
		interval_ms = 10;
	printf("Start ISDN module init...\n");
	memset(&isdn_rs, 0, sizeof(ISDN_Resource_Pond));
	memset(&isdn_timer_var, 1, sizeof(IsdnTimerStr));
	memset(&isdn_rv_msg, 0, sizeof(IsdnMsgStr));
	memset(&isdn_sd_msg, 0, sizeof(IsdnMsgStr));
	memset(&iua_rv_msg, 0, sizeof(IuaMsgStr));
	memset(&iua_sd_msg, 0, sizeof(IuaMsgStr));
	timer_init(interval_ms);
	isdn_rs.attrib.systemID = systemID;
	isdn_rs.attrib.isdn_remote_ip = 0;
	isdn_rs.attrib.isdn_run_mode = 0;
	stack_size = sizeof(ISDN_Resource_Pond) + sizeof(IsdnTimerStr);
	isdn_debug_init();
	printf("ISDN module init completed, stack size: %ld bytes\n", stack_size);

	return ISDN_CM_OK;
}

/*************************************************************
Function:		isdn_sd_link
Description:	isdn send link command to iua
Input:		iua_id:link number, opercode:link command parameter
Return:		ISDN_CM_OK:success
*************************************************************/
int isdn_sd_link(u8 link_id, u8 opercode)
{
	IuaMsgStr isdn_lk;
	isdn_lk.msg_class = IUA_MGMT_CMD;
	isdn_lk.e1_no = link_id;
	isdn_lk.msg_length = htons(0x0003);
	isdn_lk.msgcontent.lkmsg.opercode = opercode;

	put_xua_msg((xua_up_msg_t*)&isdn_lk.msg_class);

	return ISDN_CM_OK;
}

/*************************************************************
Function:		isdn_link_ctrl
Description:	isdn link control
Input:		NULL
Return:		NULL
*************************************************************/
void isdn_link_ctrl()
{
	int i, link_id;
	const pal_circuit_struct *circuit_ptr = NULL;
	const pal_cg_struct *cg_ptr = NULL;

	for(i = 0; i < ISDN_MAX_CIRCUIT; i++)	//for timer  //setup link
	{
		circuit_ptr = pal_circuit_ptr(i);
		if(circuit_ptr == NULL)
			continue;
		
		if(circuit_ptr->enable == 0)
			continue;
		if((isdn_rs.attrib.isdn_run_mode == ISDN_DUAL_MODE) && 
			(circuit_ptr->attrib.plat_id != isdn_rs.attrib.systemID))
			continue;

		cg_ptr = pal_cg_ptr(circuit_ptr->cg_id);
		if(cg_ptr == NULL)
			continue;
		if(cg_ptr->attrib.protocol != PROTO_ISDN)
			continue;
		if(!(cg_ptr->attrib.variant == VARIANT_ITU || cg_ptr->attrib.variant == VARIANT_ANSI || cg_ptr->attrib.variant == VARIANT_QSIG) || 
			(cg_ptr->attrib.variant == VARIANT_QSIG && (cg_ptr->attrib.nfas.flag || circuit_ptr->attrib.user_network_if == NETWORK_SIDE)))
			continue;
		
		link_id = circuit_ptr->attrib.link_id;
		if(cg_ptr->enable)
		{
			if(isdn_rs.link_pond[link_id].lk3_status == LK_OOS)	
			{
				if(cg_ptr->attrib.nfas.flag)	//nfas
				{
					if((cg_ptr->attrib.nfas.prim_link != link_id) && 
						(cg_ptr->attrib.nfas.backup_link != link_id))
						continue;
				}
				
				isdn_sd_link(link_id, xUA_ENABLE_xUA_LINK);	//enable
				isdn_sd_link(link_id, M2UA_MTP3_CMD_ACTIVE_LK);	//active
			}
		}
		else		//modify by 080821
		{
			if(isdn_rs.link_pond[link_id].lk3_status == LK_IS)
			{
				if(cg_ptr->attrib.nfas.flag)
				{
					if((cg_ptr->attrib.nfas.prim_link != link_id) && 
						(cg_ptr->attrib.nfas.backup_link != link_id))
						continue;
				}
				isdn_sd_link(link_id, M2UA_MTP3_CMD_DEACTIVE_LK);
				isdn_sd_link(link_id, xUA_STOP_xUA_LINK);
			}
		}
	}

	for(i = 0; i < ISDN_MAX_CIRCUIT; i++)		//release link
	{
		circuit_ptr = pal_circuit_ptr(i);
		if(circuit_ptr == NULL)
			continue;
		
/*		if(circuit_ptr->enable == 0)
		{
			continue;
		}*/
		if(((isdn_rs.attrib.isdn_run_mode == ISDN_SINGLE_MODE) || 
			(isdn_rs.attrib.isdn_run_mode == ISDN_DUAL_MODE && 
			circuit_ptr->attrib.plat_id == isdn_rs.attrib.systemID)) && 
			(circuit_ptr->enable == 1))	//modify by 080821
			continue;

		cg_ptr = pal_cg_ptr(circuit_ptr->cg_id);
		if(cg_ptr == NULL)
			continue;
		if(cg_ptr->attrib.protocol != PROTO_ISDN)
			continue;
		if(!(cg_ptr->attrib.variant == VARIANT_ITU || cg_ptr->attrib.variant == VARIANT_ANSI || cg_ptr->attrib.variant == VARIANT_QSIG) || 
			(cg_ptr->attrib.variant == VARIANT_QSIG && (cg_ptr->attrib.nfas.flag || circuit_ptr->attrib.user_network_if == NETWORK_SIDE)))
			continue;
		
		link_id = circuit_ptr->attrib.link_id;
		if(isdn_rs.link_pond[link_id].lk3_status == LK_IS)
		{
			if(cg_ptr->attrib.nfas.flag)
			{
				if((cg_ptr->attrib.nfas.prim_link != link_id) && 
					(cg_ptr->attrib.nfas.backup_link != link_id))
					continue;
			}
			isdn_sd_link(link_id, M2UA_MTP3_CMD_DEACTIVE_LK);
			isdn_sd_link(link_id, xUA_STOP_xUA_LINK);
		}
	}
}

void isdn_heartbeat_timer(int link_id)
{
	if(isdn_rs.link_pond[link_id].plat_heartbeat_time > 0)
	{
		isdn_rs.link_pond[link_id].plat_heartbeat_time--;
		if(isdn_rs.link_pond[link_id].plat_heartbeat_time == 0)
		{
			isdn_rs.link_pond[link_id].dc_status = DS_OOS;
			isdn_log_err(0, "link %d heartbeat timeout: another plat doesnot send link state!\r\n",link_id);
		}
	}
}

/*************************************************************
Function:		isdn_timer
Description:	isdn task schedule, include isdn link control, isdn receive msg from iua and control the timer
Input:		NULL
Return:		NULL
*************************************************************/
void isdn_timer()
{
	u16 i, j, chnl_j, cg_id;
	u32 offset, pid;
	int circuit_cic, link_id, ds_0, ds_1;
	static int link_10s = 0;
	const pal_circuit_struct *circuit_ptr = NULL;
	const pal_cg_struct *cg_ptr = NULL;

	link_10s++;
	if(link_10s % 600 == 0)	// 6s
	{
		isdn_link_ctrl();	//setup link
		link_10s = 0;
	}
	
	for(i = 0; i < 64; i++)
	{
		if(isdn_receive_msg() == 0)
			break;	//no message in lower layer
		if(isdn_redirect_receive() == 0)
			break;	//no message from another plat
	}

	for(i = 0; i < ISDN_MAX_CIRCUIT; i++)	//for timer  
	{
		circuit_ptr = pal_circuit_ptr(i);
		if(circuit_ptr == NULL)
			continue;
		
		if(circuit_ptr->enable == 0)
			continue;
		if((isdn_rs.attrib.isdn_run_mode == ISDN_DUAL_MODE) && 
			(circuit_ptr->attrib.plat_id != isdn_rs.attrib.systemID))
			continue;

		cg_id = circuit_ptr->cg_id;
		cg_ptr = pal_cg_ptr(cg_id);
		if(cg_ptr == NULL)
			continue;
		if(cg_ptr->attrib.protocol != PROTO_ISDN)
			continue;
		if(!(cg_ptr->attrib.variant == VARIANT_ITU || cg_ptr->attrib.variant == VARIANT_ANSI || cg_ptr->attrib.variant == VARIANT_QSIG) || 
			(cg_ptr->attrib.variant == VARIANT_QSIG && (cg_ptr->attrib.nfas.flag || circuit_ptr->attrib.user_network_if == NETWORK_SIDE)))
			continue;

		link_id = circuit_ptr->attrib.link_id;
		isdn_rs.link_pond[link_id].link_heartbeat_time++;
		if(isdn_rs.link_pond[link_id].link_heartbeat_time % LINK_MAX_TIME == 0)
		{
			isdn_rs.link_pond[link_id].lk3_status = isdn_rs.link_pond[link_id].lk2_status;
			isdn_rs.link_pond[link_id].lk2_status = LK_OOS;
			isdn_rs.link_pond[link_id].link_heartbeat_time = 0;
		}
		
		circuit_cic = circuit_ptr->attrib.cic_range;
		offset = ISDN_CIRCUIT_CIC * i;		//ISDN_CIRCUIT_CIC
		if(circuit_cic == 24)	//T1
		{
			if((trans_callref_sd_rest_req(offset+24, ISDN_RESTART_CNF)) == circuit_cic)
			{
				isdn_rs.port_pond[offset+24].g_pri_cmd = ISDN_RESTART_CNF;
				isdn_restart_ctrl(offset+24);	//restart control
			}
//			offset++;
		}
		else
		{
			if((trans_callref_sd_rest_req(offset, ISDN_RESTART_CNF)) == circuit_cic)
			{
				isdn_rs.port_pond[offset].g_pri_cmd = ISDN_RESTART_CNF;
				isdn_restart_ctrl(offset);	//restart control
			}
		}
		for(j = 0; j < circuit_cic; j++)
		{
			chnl_j = j;
			if(circuit_cic == 24)	//T1
				chnl_j = j + 1;
			pid = offset + chnl_j;
			if(cg_ptr->attrib.nfas.flag)		//NFAS
			{
				ds_0 = cg_ptr->attrib.nfas.prim_link;
				ds_1 = cg_ptr->attrib.nfas.backup_link;
				if(circuit_ptr->attrib.d_chnl == chnl_j)		//d-channel
				{
					if(ds_0 == link_id)
					{
						if(isdn_rs.link_pond[link_id].lk3_status == LK_IS)
						{
							if((isdn_rs.link_pond[ds_0].dc_status == DS_MOOS) && 
								(isdn_rs.link_pond[ds_1].dc_status != DS_IS))
								isdn_rs.port_pond[pid].g_pri_cmd = ISDN_SERVICE_REQ;
						}
						else
							isdn_rs.link_pond[link_id].dc_status = DS_OOS;
						service_dc_proc(pid);
						
						if((link_10s % 100 == 0) && (isdn_rs.attrib.isdn_run_mode == ISDN_DUAL_MODE))	//send heartbeat per 1 second
							isdn_send_heartbeat(link_id);
						if(isdn_rs.link_pond[link_id].plat_heartbeat_time > 0)
							isdn_rs.link_pond[link_id].plat_heartbeat_time = 0;
					}
					else if(ds_1 == link_id)
					{
						if(isdn_rs.link_pond[link_id].lk3_status == LK_IS)
						{
							if((isdn_rs.link_pond[ds_1].dc_status == DS_MOOS) && 
								((isdn_rs.link_pond[ds_0].dc_status == DS_OOS) || 
								(isdn_rs.link_pond[ds_0].dc_status == DS_STBY)))
								isdn_rs.port_pond[pid].g_pri_cmd = ISDN_SERVICE_REQ;
						}
						else
							isdn_rs.link_pond[link_id].dc_status = DS_OOS;
						service_dc_proc(pid);
						
						if((link_10s % 100 == 0) && (isdn_rs.attrib.isdn_run_mode == ISDN_DUAL_MODE))	//send heartbeat per 1 second
							isdn_send_heartbeat(link_id);
						if(isdn_rs.link_pond[link_id].plat_heartbeat_time > 0)
							isdn_rs.link_pond[link_id].plat_heartbeat_time = 0;
					}
				}
			}
			else		//associated signaling
			{
				if(isdn_rs.link_pond[link_id].lk3_status == LK_IS)
				{
					if(circuit_ptr->attrib.d_chnl == chnl_j)	//d-channel
						isdn_rs.link_pond[link_id].dc_status = DS_IS;
				}
				else
				{
					if(circuit_ptr->attrib.d_chnl == chnl_j)	//d-channel
						isdn_rs.link_pond[link_id].dc_status = DS_OOS;
				}
			}
			
			if(isdn_rs.port_pond[pid].enable)
				isdn_cpc_timer(pid);
			isdn_restart_timer(pid);
			maintenance_timer(pid);
		}
		if(isdn_rs.attrib.isdn_run_mode == ISDN_DUAL_MODE)
			isdn_heartbeat_timer(i);
	}
	isdn_debug_timer();

}

/*************************************************************
Function:		isdn_assign_port
Description:	isdn assign the port id
Input:		e1no:link number
Return:		port id:success
			0:failed
*************************************************************/
u32 isdn_assign_port(u16 circuit_id)
{
	u8 i, chnl;
	u32 e1nox32 = 0;
	u32 portnum = 0;
	
	chnl = 32;
	e1nox32 = circuit_id;
	e1nox32 = (e1nox32 << 5);
	
	for(i = 0; i < 31; i++)
	{
		chnl = (chnl - i) & 0x1F;
		if (((chnl & 0x1F) != 0) && (chnl != 16))
		{
			portnum = e1nox32 + chnl;
			if (isdn_rs.port_pond[portnum].enable == 0)
			{
				if(isdn_rs.port_pond[portnum].cic_state.maintenance_state != IN_SERVICE)
				{
					isdn_log_err(0, "this cic is not in service in assign port!\r\n");
					break;
				}
				isdn_rs.port_pond[portnum].enable = 1;
				return portnum;
			}
		}
	}
	
	return 0;
}


/*************************************************************
Function:		find_port_by_callref
Description:	find port id by the call referrence and link id
Input:		cref:call referrence, circuit_id:circuit id
Return:		port id:success
			0:failed
*************************************************************/
u32 find_port_by_callref(u16 cref, u16 circuit_id)
{
	int i, circuit_cic, d_chnl=16;
	u32 portnum = 0;
	const pal_circuit_struct *circuit_ptr = pal_circuit_ptr(circuit_id);
	if(circuit_ptr == NULL)
		return ISDN_CM_FAILED;

	circuit_cic = circuit_ptr->attrib.cic_range;
	if(circuit_cic == 24)	//T1
	{
		d_chnl = 24;
	}
	portnum = (circuit_id * ISDN_CIRCUIT_CIC) + 1;

	for (i = 1; i < circuit_cic; i++, portnum++)
	{
		if (i != d_chnl)
		{
			if(isdn_rs.port_pond[portnum].call_ref == cref)
				return portnum;
		}
	}
//	isdn_log_err(0, "find port by callref err!call_ref=%d, circuit_id=%d\r\n",cref, circuit_id);
	
	return 0;
}

/*************************************************************
Function:		find_port_by_cic
Description:	find port id by the cic of one link
Input:		cic:cic, circuit_id:circuit id
Return:		port id:success
*************************************************************/
u32 find_port_by_cic(u32 cic, u16 circuit_id)
{
	u32 head_cic;
	const pal_circuit_struct *circuit_ptr = pal_circuit_ptr(circuit_id);
	if(circuit_ptr == NULL)
		return ISDN_CM_FAILED;
	head_cic = circuit_ptr->attrib.head_cic;
	if(circuit_ptr->attrib.cic_range == 24)
		head_cic--;
	
	return ISDN_CIRCUIT_CIC * circuit_id + (cic - head_cic);
}

int find_circuit_by_interfaceId(int cg_id, u8 interface_id)
{
	int i;
	const pal_circuit_struct *circuit_ptr = NULL;

	for(i = 0; i < ISDN_MAX_CIRCUIT; i++)
	{
		circuit_ptr = pal_circuit_ptr(i);
		if(circuit_ptr == NULL)
			continue;
		
		if(circuit_ptr->enable == 0)
			continue;
		if((isdn_rs.attrib.isdn_run_mode == ISDN_DUAL_MODE) && 
			(circuit_ptr->attrib.plat_id != isdn_rs.attrib.systemID))
			continue;
		if(circuit_ptr->cg_id != cg_id)
			continue;
		
		if((circuit_ptr->attrib.cic_range != 0) && (circuit_ptr->attrib.head_cic / circuit_ptr->attrib.cic_range) == interface_id)
			return i;
	}
	isdn_log_err(0, "find circuit by interface id err!cg_id=%d, interface_id=%d\r\n",cg_id, interface_id);

	return -1;
}

//d-channel status
int check_dc_status(u8 link_id)
{
	if(isdn_rs.link_pond[link_id].dc_status == DS_IS)
		return ISDN_CM_OK;
	else
		return ISDN_CM_FAILED;
}

int isdn_set_run_mode (u8 run_mode, u32 alter_ip)
{
	isdn_trace_func(0,"APP => function trace: %s\r\n", __FUNCTION__);
	isdn_rs.attrib.isdn_run_mode = run_mode % 2;
	isdn_rs.attrib.isdn_remote_ip = alter_ip;
	return ISDN_CM_OK;
}

