/* * * Copyright (C) 2008 LGC Wireless, Inc * Written by: Xinyu Yan * *- */ #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); }