Files
svc.ems/plat/isup/src/isup_transit.c
2024-09-27 15:39:34 +08:00

403 lines
8.4 KiB
C

/*
*
* Copyright (C) 2008 LGC Wireless, Inc
* Written by: Xinyu Yan <xyyan@lgcwireless.com>
*
*-
*/
#include "./include/isup_debug.h"
#include "./include/isup_public.h"
#include "./include/isup_if.h"
#include "./include/isup_struct.h"
#include "./include/isup_def.h"
#include "./include/isup_const.h"
#define MAX_ISUP_VAR 10
#define MAX_ISUP_OPT 50
struct param_unit {
u8 len;
u8 *ptr;
};
struct isup_gen_fmt {
u8 var_num;
u8 opt_num;
struct param_unit fix;
struct param_unit var[MAX_ISUP_VAR];
struct param_unit opt[MAX_ISUP_OPT];
};
#define MAX_ISUP_MSGTYPE 100 //not sure, just an assumption
#define LEN_OF_LV(ptr) (*ptr + 1)
#define LEN_OF_TLV(ptr) (*(ptr+1) + 2)
struct isup_fmt_desc {
u8 exist;
u8 fixed_len; /* fixed len start from message type(include) */
u8 var_num;
};
const static struct isup_fmt_desc fmt_desc_itu[MAX_ISUP_MSGTYPE] = {
{0},
{1, 6, 1}, //Initial address(1)
{1, 1, 1}, //Subsequent address(2)
{1,3,0},//Information request (national use)(3)
{1,3,0},//Information (national use)(4)
{1,2,0},//Continuity(5)
{1,3,0},//Address complete(6)
{1,3,0},//Connect(7)
{1,1,0},//Forward transfer(8)
{1,1,0},//Answer(9)
{0},//10
{0},//11
{1,1,1},//Release(12)
{1,2,0},//Suspend(13)
{1,2,0},//Resume(14)
{0},//15
{1,1,0},//Release complete(16)
{0},
{0},
{0},
{0},//Unblocking(20)
{0},
{0},//Unblocking acknowledgement(22)
{0},
{0},
{0},//25
{0},
{0},
{0},
{0},
{0},//30
{1,2,0},//Facility request(31)
{1,2,0},//Facility accepted(32)
{1,2,1},//Facility reject(33)
{0},
{0},//35
{0},//Loop Back Acknowledgement(36)
{0},
{0},
{0},
{0},//Pass-Along(40)
{0},
{0},
{0},
{1,2,0},//Call progress(44)
{1,1,1},//User-to-user information(45)
{0},//Unequipped CIC (national use)(46)
{1,1,1},//Confusion(47)
{0},//Overload(48)
{0},//Charge information (national use)(49)
{1,1,0},//Network resource management(50)
{1,1,0},//Facility (51)
{1,1,0},//User Part Test(52)
{1,1,0},//User Part Available(53)
{1,1,0},//Identification request(54)
{1,1,0},//Identification response(55)
{1,1,0},//Segmentation(56)
{0},
{0},
{0},
{0},//60
{0},
{0},
{0},
{1,1,0},//Loop Prevention(64)
{1,1,0},//Application transport message(65)
{1,1,0},//Pre-release information message (66)
};
const static struct isup_fmt_desc fmt_desc_ansi[MAX_ISUP_MSGTYPE] = {
{0},
{1, 5, 2}, //Initial address(1)
{1, 1, 1}, //Subsequent address(2)
{1,3,0},//Information request (national use)(3)
{1,3,0},//Information (national use)(4)
{1,2,0},//Continuity(5)
{1,3,0},//Address complete(6)
{1,3,0},//Connect(7)
{1,1,0},//Forward transfer(8)
{1,1,0},//Answer(9)
{0},//10
{0},//11
{1,1,1},//Release(12)
{1,2,0},//Suspend(13)
{1,2,0},//Resume(14)
{0},//15
{1,1,0},//Release complete(16)
{0},
{0},
{0},
{0},//Unblocking(20)
{0},
{0},//Unblocking acknowledgement(22)
{0},
{0},
{0},//25
{0},
{0},
{0},
{0},
{0},//30
{1,2,0},//Facility request(31)
{1,2,0},//Facility accepted(32)
{1,2,1},//Facility reject(33)
{0},
{0},//35
{0},//Loop Back Acknowledgement(36)
{0},
{0},
{0},
{0},//Pass-Along(40)
{0},
{0},
{0},
{1,2,0},//Call progress(44)
{1,1,1},//User-to-user information(45)
{0},//Unequipped CIC (national use)(46)
{1,1,1},//Confusion(47)
{0},//Overload(48)
{0},//Charge information (national use)(49)
{1,1,0},//Network resource management(50)
{1,1,0},//Facility (51)
{1,1,0},//User Part Test(52)
{1,1,0},//User Part Available(53)
{1,1,0},//Identification request(54)
{1,1,0},//Identification response(55)
{1,1,0},//Segmentation(56)
{0},
{0},
{0},
{0},//60
{0},
{0},
{0},
{1,1,0},//Loop Prevention(64)
{1,1,0},//Application transport message(65)
{1,1,0},//Pre-release information message (66)
};
int raw_to_fmt(struct isup_gen_fmt *fmt, u8 *raw_head, u16 raw_len, u8 variant)
{
int i;
const struct isup_fmt_desc *desc;
struct param_unit *opt;
u8 msg_type;
u8 *raw = raw_head;
msg_type = *raw;
if(msg_type >= MAX_ISUP_MSGTYPE)
return 0;
if(variant == VARIANT_ANSI)
desc = &fmt_desc_ansi[msg_type];
else
desc = &fmt_desc_itu[msg_type];
if(desc->exist == 0)
return 0;
memset(fmt, 0, sizeof(struct isup_gen_fmt));
// M Fixed //
fmt->fix.len = desc->fixed_len;
fmt->fix.ptr = raw;
raw += fmt->fix.len;
// M Variable //
fmt->var_num = desc->var_num;
raw += *raw;
if(fmt->var_num != 0)
{
for(i = 0; i < fmt->var_num; i++)
{
if(raw + LEN_OF_LV(raw) - raw_head > raw_len)
return -1;
fmt->var[i].len = LEN_OF_LV(raw);
fmt->var[i].ptr = raw;
raw += fmt->var[i].len;
}
}
// Option //
while(*raw != 0)
{
if(raw + LEN_OF_TLV(raw) - raw_head > raw_len)
return -1;
opt = &fmt->opt[fmt->opt_num++];
opt->len = LEN_OF_TLV(raw);
opt->ptr = raw;
raw += opt->len;
}
return 1;
}
int param_need_merge(u8 param_name)
{
switch(param_name)
{
case ME_REDIRGNUM:
case ME_REDIRINFO:
case ME_ORIGCALDNUM:
case ME_LOCNMB:
case ME_LASDIVLINID:
return 1;
}
return 0;
}
int param_already_exist(u8 *index, struct isup_gen_fmt *org, u8 param_name)
{
int i;
for(i = 0; i < org->opt_num; i++)
{
if(*(org->opt[i].ptr) == param_name)
{
*index = i;
return 1;
}
}
return 0;
}
static u8 bw_call_ind[4];
void merge_cpg_acm(struct isup_gen_fmt *dst, struct isup_gen_fmt *cpg, struct isup_gen_fmt *acm)
{
int i;
u8 param_name;
u8 index;
memcpy(dst, cpg, sizeof(struct isup_gen_fmt));
/* backward call ind, from fix part to opt */
bw_call_ind[0] = ME_BACKCALLIND;
bw_call_ind[1] = 2;
memcpy(bw_call_ind+2, acm->fix.ptr+1, 2);
dst->opt[dst->opt_num].len = 4;
dst->opt[dst->opt_num].ptr = bw_call_ind;
dst->opt_num++;
for(i = 0; i < acm->opt_num; i++)
{
param_name = *(acm->opt[i].ptr);
/* discard echo control infomation */
if(param_name == ME_ECHOCNTRL)
continue;
if(param_already_exist(&index, dst, param_name) == 1)
memcpy(&dst->opt[index], &acm->opt[i], sizeof(struct param_unit));
else
memcpy(&dst->opt[dst->opt_num++], &acm->opt[i], sizeof(struct param_unit));
}
}
void merge_fmt(struct isup_gen_fmt *dst, struct isup_gen_fmt *org, struct isup_gen_fmt *add)
{
int i;
u8 param_name;
u8 index;
memcpy(dst, org, sizeof(struct isup_gen_fmt));
if(*(add->fix.ptr) == M_CPG)
memcpy(&dst->fix, &add->fix, sizeof(struct param_unit));
if(*(add->fix.ptr) == M_IAM)
{
for(i = 0; i < add->var_num; i++)
{
memcpy(&dst->var[i], &add->var[i], sizeof(struct param_unit));
}
}
else
{
for(i = 0; i < org->var_num; i++)
{
memcpy(&dst->var[i], &org->var[i], sizeof(struct param_unit));
}
}
for(i = 0; i < add->opt_num; i++)
{
param_name = *(add->opt[i].ptr);
if(param_need_merge(param_name) == 0)
continue;
if(param_already_exist(&index, dst, param_name) == 1)
memcpy(&dst->opt[index], &add->opt[i], sizeof(struct param_unit));
else
memcpy(&dst->opt[dst->opt_num++], &add->opt[i], sizeof(struct param_unit));
}
}
int fmt_to_raw(u8 *raw_head, struct isup_gen_fmt *fmt)
{
u8 *raw = raw_head;
int i;
u8 pre_pointer;
u8 pre_varlen;
// M Fixed //
memcpy(raw, fmt->fix.ptr, fmt->fix.len);
raw += fmt->fix.len;
/* Set initial condition, note that if VAR not exist, it applys to OPT directly */
pre_varlen = fmt->var_num + 1;
pre_pointer = 1;
// M Variable //
if(fmt->var_num != 0)
{
for(i = 0; i < fmt->var_num; i++)
{
*raw = pre_pointer + pre_varlen - 1;
memcpy(raw+*raw, fmt->var[i].ptr, fmt->var[i].len);
pre_varlen = fmt->var[i].len;
pre_pointer = *(raw++);
}
// /* "pointer of optional part" is set to 0 first */
// *raw = 0;
}
// Option //
if(fmt->opt_num != 0)
{
/* Set "pointer of optional part" */
*raw = pre_pointer + pre_varlen - 1;
raw += *raw;
for(i = 0; i < fmt->opt_num; i++)
{
memcpy(raw, fmt->opt[i].ptr, fmt->opt[i].len);
raw += fmt->opt[i].len;
}
// /* Set "end of optional parameter" */
// *raw = 0;
}
/* Last octet set to 0. It may be "pointer of optional part" or "end of optional parameter" */
*(raw++) = 0;
if(fmt->opt_num == 0)
return raw-raw_head+pre_pointer + pre_varlen - 1 -1;
else
return raw-raw_head;
}
int isup_transit_msg(u8 *raw_dst, u8 *raw_add, u8 len_add, u8 *raw_org, u8 len_org, u8 variant)
{
struct isup_gen_fmt fmt_org;
struct isup_gen_fmt fmt_add;
struct isup_gen_fmt fmt_dst;
if(raw_to_fmt(&fmt_org, raw_org, len_org, variant)==0)
return 0;
if(raw_to_fmt(&fmt_add, raw_add, len_add, variant)==0)
return 0;
if((*(fmt_org.fix.ptr) == M_ACM) && (*(fmt_add.fix.ptr) == M_CPG))
merge_cpg_acm(&fmt_dst, &fmt_add, &fmt_org);
else
merge_fmt(&fmt_dst, &fmt_org, &fmt_add);
return fmt_to_raw(raw_dst, &fmt_dst);
}