diff --git a/src/isdn/dss1.c b/src/isdn/dss1.c index 51517e5..1272d4c 100644 --- a/src/isdn/dss1.c +++ b/src/isdn/dss1.c @@ -184,6 +184,107 @@ static void split_3pty(call_t *call) } } +/* Generate AOC-S facility IE from metering information */ +#define AOCS_CURRENCY_AMOUNT_PER_UNIT 5 +static void generate_aocs_ie(call_t *call, struct l3_msg *l3m) +{ + uint8_t fac_ie[256]; + struct asn1_parm fac; + + if (call->isdn_ep->ntmode && call->isdn_ep->aocs && call->metering_info_received) { + memset(&fac, 0, sizeof(fac)); + fac.Valid = 1; + fac.comp = CompInvoke; + fac.u.inv.invokeId = 2; /* doesn't matter since no response is expected */ + fac.u.inv.operationValue = Fac_AOCSCurrency; + + LOGP(DISDN, LOGL_DEBUG, "Sending AOC-S information from metering data: connect_units=%d unit_period_decisecs=%d\n", call->metering_connect_units, call->metering_unit_period_decisecs); + + if(call->metering_connect_units == 0) { // Free call + LOGP(DISDN, LOGL_DEBUG, "AOC-S currencyInfoList: BasicComm FreeOfCharge\n"); + fac.u.inv.o.AOCcuril.currencyInfoCount = 1; + fac.u.inv.o.AOCcuril.currencyInfo[0].chargedItem = 0x00; // basic comm + fac.u.inv.o.AOCcuril.currencyInfo[0].currencyType = 0x84; // free + } + else if(call->metering_unit_period_decisecs == 0) { // setup/connect FlatRate + /* note some payphones (e.g. BluePhone) only interpret 'basic comm' charged item, therefore we don't use 'call setup' charged item */ + LOGP(DISDN, LOGL_DEBUG, "AOC-S currencyInfoList: BasicComm FlatRate\n"); + fac.u.inv.o.AOCcuril.currencyInfoCount = 1; + fac.u.inv.o.AOCcuril.currencyInfo[0].chargedItem = 0x00; // basic comm + fac.u.inv.o.AOCcuril.currencyInfo[0].currencyType = 0xA2; // FlatRate + strncpy((char *)fac.u.inv.o.AOCcuril.currencyInfo[0].FlatRateCurrency.currency, "EUR", 10); // Currency value + fac.u.inv.o.AOCcuril.currencyInfo[0].FlatRateCurrency.currencyAmount = AOCS_CURRENCY_AMOUNT_PER_UNIT * call->metering_connect_units; + fac.u.inv.o.AOCcuril.currencyInfo[0].FlatRateCurrency.multiplier = 1; // 1/100 EUR + } + else if(call->metering_connect_units == 1) { // Normal call + LOGP(DISDN, LOGL_DEBUG, "AOC-S currencyInfoList: BasicComm DurationCurrency\n"); + fac.u.inv.o.AOCcuril.currencyInfoCount = 1; + fac.u.inv.o.AOCcuril.currencyInfo[0].chargedItem = 0x00; // basic comm + fac.u.inv.o.AOCcuril.currencyInfo[0].currencyType = 0xA1; // DurationCurrency + strncpy((char *)fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.currency, "EUR", 10); // Currency value + fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.currencyAmount = AOCS_CURRENCY_AMOUNT_PER_UNIT; + fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.multiplier = 1; // 1/100 EUR + fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.typeOfCharging = 0x00; // Contin. Charging + fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.durLengthTimeUnit = call->metering_unit_period_decisecs * 10; // Unit duration + fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.durLengthTimeScale = 0x00; // 1/100 s + fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.granLengthTimeUnit = 1; // Granularity 1 + fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.granLengthTimeScale = 2; // 1/1 s + } + else { // Special call with setup/connect FlatRate + /* note some payphones (e.g. BluePhone) only interpret 'basic comm' charged item */ + LOGP(DISDN, LOGL_DEBUG, "AOC-S currencyInfoList: BasicComm DurationCurrency / CallSetup FlatRate\n"); + fac.u.inv.o.AOCcuril.currencyInfoCount = 2; + fac.u.inv.o.AOCcuril.currencyInfo[0].chargedItem = 0x00; // basic comm + fac.u.inv.o.AOCcuril.currencyInfo[0].currencyType = 0xA1; // DurationCurrency + strncpy((char *)fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.currency, "EUR", 10); // Currency value + fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.currencyAmount = AOCS_CURRENCY_AMOUNT_PER_UNIT; + fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.multiplier = 1; // 1/100 EUR + fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.typeOfCharging = 0x00; // Contin. Charging + fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.durLengthTimeUnit = call->metering_unit_period_decisecs * 10; // Unit duration + fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.durLengthTimeScale = 0x00; // 1/100 s + fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.granLengthTimeUnit = 1; // Granularity 1 + fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.granLengthTimeScale = 2; // 1/1 s + fac.u.inv.o.AOCcuril.currencyInfo[1].chargedItem = 0x02; // call setup + fac.u.inv.o.AOCcuril.currencyInfo[1].currencyType = 0xA2; // FlatRate + strncpy((char *)fac.u.inv.o.AOCcuril.currencyInfo[1].FlatRateCurrency.currency, "EUR", 10); // Currency value + fac.u.inv.o.AOCcuril.currencyInfo[1].FlatRateCurrency.currencyAmount = AOCS_CURRENCY_AMOUNT_PER_UNIT * call->metering_connect_units; + fac.u.inv.o.AOCcuril.currencyInfo[1].FlatRateCurrency.multiplier = 1; // 1/100 EUR + } + + encodeFac(fac_ie, &fac); + enc_ie_facility(l3m, fac_ie + 2, fac_ie[1]); + } +} + +/* AOC-D timer callback */ +static void aocd_timer_cb(void *data) +{ + uint8_t fac_ie[256]; + struct asn1_parm fac; + struct l3_msg *l3m; + call_t *call = data; + + call->metering_total_units++; + + LOGP(DISDN, LOGL_DEBUG, "Sending next AOC-D unit information, total_units=%d\n", call->metering_total_units); + + osmo_timer_schedule(&call->aocd_unit_timer, call->metering_unit_period_decisecs / 10, (call->metering_unit_period_decisecs % 10) * 100000); + + memset(&fac, 0, sizeof(fac)); + fac.Valid = 1; + fac.comp = CompInvoke; + fac.u.inv.invokeId = 2; /* doesn't matter since no response is expected */ + fac.u.inv.operationValue = Fac_AOCDChargingUnit; + fac.u.inv.o.AOCchu.recordedUnits = call->metering_total_units; + + encodeFac(fac_ie, &fac); + + // sending facility + l3m = create_l3msg(); + enc_ie_facility(l3m, fac_ie + 2, fac_ie[1]); + call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_FACILITY, call->l3_pid, l3m); +} + /* * handles all indications from ISDN stack */ @@ -354,6 +455,10 @@ void setup_ind(call_t *call, uint32_t pid, struct l3_msg *l3m) call->cc_callref = cc_call->callref; LOGP(DDSS1, LOGL_DEBUG, " -> new callref assigned (callref = %d)\n", call->cc_callref); + /* reset AOC information */ + call->metering_info_received = 0; + call->aocd_unit_timer_started = 0; + new_state(call, ISDN_STATE_IN_SETUP); /* send message to osmo-cc */ @@ -766,6 +871,13 @@ void disconnect_ind(call_t *call, uint32_t pid, struct l3_msg *l3m) /* send message to osmo-cc */ osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg); + + /* stop AOC-D timer */ + if(call->aocd_unit_timer_started) + { + osmo_timer_del(&call->aocd_unit_timer); + call->aocd_unit_timer_started = 0; + } } /* CC-DISCONNECT INDICATION of child instance */ @@ -1981,6 +2093,13 @@ void proc_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg, int with_ies) if (rc >= 0 && call->isdn_ep->ntmode) enc_ie_redirection(l3m, type, plan, 1, present, redir); + /* Metering handling and AOC-S generation */ + rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period_decisecs)); + if(rc >= 0) { + call->metering_info_received = 1; + generate_aocs_ie(call, l3m); + } + skip_ies: new_state(call, ISDN_STATE_IN_PROCEEDING); @@ -2038,6 +2157,13 @@ void alert_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg) if (rc >= 0 && call->isdn_ep->ntmode) enc_ie_redirection(l3m, type, plan, 1, present, redir); + /* Metering handling and AOC-S generation */ + rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period_decisecs)); + if(rc >= 0) { + call->metering_info_received = 1; + generate_aocs_ie(call, l3m); + } + new_state(call, ISDN_STATE_IN_ALERTING); /* send message to ISDN */ @@ -2054,6 +2180,8 @@ void setup_rsp(call_t *call, uint32_t pid, osmo_cc_msg_t *msg) char connected[33]; char display[128]; int rc; + uint8_t fac_ie[256]; + struct asn1_parm fac; /* NT-MODE in setup state we must send PROCEEDING first */ if (!call->proceeding_sent && call->isdn_ep->ntmode) { @@ -2109,8 +2237,39 @@ void setup_rsp(call_t *call, uint32_t pid, osmo_cc_msg_t *msg) enc_ie_connected_pn(l3m, type, plan, 1, present, screen, connected); } + /* Metering handling and AOC-S generation */ + rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period_decisecs)); + if(rc >= 0) { + call->metering_info_received = 1; + generate_aocs_ie(call, l3m); + } + new_state(call, ISDN_STATE_IN_CONNECTING); + /* AOC-D handling on connect */ + if(call->isdn_ep->ntmode && call->isdn_ep->aocd && call->metering_info_received && call->metering_connect_units > 0) + { + LOGP(DISDN, LOGL_DEBUG, "Attaching AOC-D connect facility, units: %d\n", call->metering_connect_units); + call->metering_total_units = call->metering_connect_units; + + memset(&fac, 0, sizeof(fac)); + fac.Valid = 1; + fac.comp = CompInvoke; + fac.u.inv.invokeId = 2; /* doesn't matter since no response is expected */ + fac.u.inv.operationValue = Fac_AOCDChargingUnit; + + fac.u.inv.o.AOCchu.recordedUnits = call->metering_total_units; + encodeFac(fac_ie, &fac); + enc_ie_facility(l3m, fac_ie + 2, fac_ie[1]); + + if(call->metering_unit_period_decisecs > 0) { + LOGP(DISDN, LOGL_DEBUG, "Scheduling AOC-D unit information every %d deciseconds.\n", call->metering_unit_period_decisecs); + call->aocd_unit_timer_started = 1; + osmo_timer_setup(&call->aocd_unit_timer, aocd_timer_cb, call); + osmo_timer_schedule(&call->aocd_unit_timer, call->metering_unit_period_decisecs / 10, (call->metering_unit_period_decisecs % 10) * 100000); + } + } + /* send message to ISDN */ call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_CONNECT, call->l3_pid, l3m); @@ -2209,6 +2368,12 @@ void progress_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg) /* progress information */ rc = process_progress(call, msg, l3m, call->state, 0); + /* Metering handling and AOC-S generation */ + rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period_decisecs)); + if(rc >= 0) { + call->metering_info_received = 1; + generate_aocs_ie(call, l3m); + } /* send message to ISDN */ if (rc) call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_PROGRESS, call->l3_pid, l3m); @@ -2337,6 +2502,13 @@ void disc_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg) enc_ie_display(l3m, display); } + /* stop AOC-D timer */ + if(call->aocd_unit_timer_started) + { + osmo_timer_del(&call->aocd_unit_timer); + call->aocd_unit_timer_started = 0; + } + new_state(call, ISDN_STATE_OUT_DISCONNECT); /* send message to ISDN */ diff --git a/src/isdn/isdn.c b/src/isdn/isdn.c index 7be94e1..45646bf 100644 --- a/src/isdn/isdn.c +++ b/src/isdn/isdn.c @@ -690,7 +690,7 @@ void isdn_destroy(isdn_t *isdn_ep) static void clock_timeout(void *data); /* initialization and configuration to isdn interface instance */ -int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int local_tones, int serving_location) +int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int local_tones, int serving_location, int aocd, int aocs) { int rc; void *mui; @@ -726,6 +726,8 @@ int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const cha isdn_ep->tx_delay = tx_delay; isdn_ep->local_tones = local_tones; isdn_ep->serving_location = serving_location; + isdn_ep->aocd = aocd; + isdn_ep->aocs = aocs; /* channel selection list */ if (channel_out) { @@ -850,6 +852,9 @@ void call_destroy(call_t *call) osmo_cc_free_session(call->cc_session); free((char *)call->sdp); + /* remove metering timer, if still active */ + osmo_timer_del(&call->aocd_unit_timer); + /* detach */ call_p = &call->isdn_ep->call_list; while (*call_p) { diff --git a/src/isdn/isdn.h b/src/isdn/isdn.h index 7f92298..801d0df 100644 --- a/src/isdn/isdn.h +++ b/src/isdn/isdn.h @@ -89,6 +89,9 @@ typedef struct isdn { const char *timeouts; int tx_delay; int local_tones; + /* metering/AOC-D/AOC-S */ + int aocd; + int aocs; /* osmo-cc */ struct osmo_cc_endpoint cc_ep; @@ -180,6 +183,13 @@ typedef struct call_list { int conference_3pty; /* if call is the active call in a 3pty conference */ int park_len; uint8_t park_callid[8]; + /* metering/AOC-D/AOC-S */ + struct osmo_timer_list aocd_unit_timer; + int aocd_unit_timer_started; + int metering_info_received; + uint16_t metering_connect_units; + uint16_t metering_unit_period_decisecs; + uint16_t metering_total_units; /* bridge states */ int can_bridge; /* last state sent to the server */ @@ -204,7 +214,7 @@ int open_bchannel_out(call_t *call, unsigned int cmd, int channel, int exclusive /* isdn instance */ isdn_t *isdn_create(void); void isdn_destroy(isdn_t *isdn_ep); -int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int local_tones, int serving_location); +int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int local_tones, int serving_location, int aocd, int aocs); int isdn_open(isdn_t *isdn_ep); void isdn_close(isdn_t *isdn_ep); void isdn_add_msn(isdn_t *isdn_ep, const char *msn); diff --git a/src/isdn/main.c b/src/isdn/main.c index ed9a59a..1e966cd 100644 --- a/src/isdn/main.c +++ b/src/isdn/main.c @@ -46,6 +46,8 @@ static int ntmode = 0; static int ptp = 0; static int layer1hold = 0; static int layer2hold = 0; +static int aocd = 0; +static int aocs = 0; static const char *channel_out = NULL; static const char *channel_in = NULL; static const char *timeouts = NULL; @@ -125,6 +127,10 @@ static void print_help() printf(" Give a delay in milliseconds. This is required for modem/fax. Audio\n"); printf(" toward ISDN interface is buffered with the given delay.\n"); printf(" This feature alters dejittering strategy.\n"); + printf(" --aocd\n"); + printf(" Send AOC-D charging information\n"); + printf(" --aocs\n"); + printf(" Send AOC-S charging information\n"); printf(" -T --local-tones german | oldgerman | american\n"); printf(" Send locally generated tones, if not provided by remote interface.\n"); printf(" -D --debug-misdn\n"); @@ -154,6 +160,8 @@ static void print_help() #define OPT_SERVING 265 #define OPT_PCM_SLOTS 266 #define OPT_BR_ONLY 267 +#define OPT_AOCD 268 +#define OPT_AOCS 269 static void add_options(void) { @@ -172,6 +180,8 @@ static void add_options(void) option_add(OPT_TIMEOUTS, "timeouts", 1); option_add(OPT_TX_DELAY, "tx-delay", 1); option_add('T', "local-tones", 1); + option_add(OPT_AOCD, "aocd", 0); + option_add(OPT_AOCS, "aocs", 0); option_add('D', "debug-misdn", 0); option_add(OPT_SERVING, "serving-location", 1); option_add('B', "bridging", 1); @@ -237,6 +247,12 @@ static int handle_options(int short_option, int argi, char **argv) case OPT_TX_DELAY: tx_delay = atoi(argv[argi]); break; + case OPT_AOCD: + aocd = 1; + break; + case OPT_AOCS: + aocs = 1; + break; case 'T': if (!strcasecmp(argv[argi], "american")) local_tones = TONES_TYPE_AMERICAN; @@ -376,7 +392,7 @@ int main(int argc, char *argv[]) isdn_tone_generate_ulaw_samples(); /* init instance */ - rc = isdn_initialize(isdn_ep, (misdn_user) ? &ph_drv.ph_socket : NULL, law, portname, ntmode, ptp, layer1hold, layer2hold, channel_out, channel_in, timeouts, tx_delay, local_tones, serving_location); + rc = isdn_initialize(isdn_ep, (misdn_user) ? &ph_drv.ph_socket : NULL, law, portname, ntmode, ptp, layer1hold, layer2hold, channel_out, channel_in, timeouts, tx_delay, local_tones, serving_location, aocd, aocs); if (rc) { LOGP(DISDN, LOGL_ERROR, "mISDN initializing failed!\n"); goto error; diff --git a/src/libmisdnuser/include/mISDN/suppserv.h b/src/libmisdnuser/include/mISDN/suppserv.h index ebf3794..d03bc91 100644 --- a/src/libmisdnuser/include/mISDN/suppserv.h +++ b/src/libmisdnuser/include/mISDN/suppserv.h @@ -309,6 +309,36 @@ extern "C" { __u32 billingId; struct ChargingAssociation chargeAssoc; }; + + struct FacAOCSDurationCurrency { + __u8 currency[10+1]; + __u16 currencyAmount; + __u8 multiplier; + __u8 typeOfCharging; + __u16 durLengthTimeUnit; + __u8 durLengthTimeScale; + __u8 granLengthTimeUnit; + __u8 granLengthTimeScale; + }; + + struct FacAOCSFlatRateCurrency { + __u8 currency[10+1]; + __u16 currencyAmount; + __u8 multiplier; + }; + + struct FacAOCSCurrencyInfo { + __u8 chargedItem; + __u8 currencyType; + struct FacAOCSDurationCurrency durationCurrency; + struct FacAOCSFlatRateCurrency FlatRateCurrency; + }; + + struct FacAOCSCurrencyInfoList { + __u8 currencyInfoCount; + struct FacAOCSCurrencyInfo currencyInfo[4]; + }; + struct Q931_BearerCapability { __u8 Length; @@ -1105,6 +1135,7 @@ extern "C" { union { struct FacAOCChargingUnit AOCchu; struct FacAOCCurrency AOCcur; + struct FacAOCSCurrencyInfoList AOCcuril; struct FacStatusRequest StatusRequest; diff --git a/src/libmisdnuser/suppserv/fac.c b/src/libmisdnuser/suppserv/fac.c index 9e682f4..dd55857 100644 --- a/src/libmisdnuser/suppserv/fac.c +++ b/src/libmisdnuser/suppserv/fac.c @@ -191,6 +191,185 @@ __u8 *encodeComponentInvoke_Head_Long_u8(__u8 * Dest, int InvokeID, enum Operati return p; } /* end encodeComponentInvoke_Head_Long_u8() */ +static int encodeAOCSChargingUnitOperation(__u8 * dest, const struct asn1_parm *pc, const struct FacAOCSCurrencyInfoList *currinfolist) +{ + /* Manually encoded ASN.1 */ + int i = 0, fac_len_index, supp_len_index, cil_len_index, ci, ci_len_index, ci_len, c_len_index, c_len, cval_len; + int result = -1; + + __u8 *p = dest; + + if ((p != NULL)) { + p[i++] = IE_FACILITY; // IE identifier + fac_len_index = i; + p[i++] = 0x00; // length -- not known yet + p[i++] = SUPPLEMENTARY_SERVICE; // remote operations protocol + p[i++] = 0xa1; // invoke component + supp_len_index = i; + p[i++] = 0x00; // length -- not known yet + p[i++] = 0x02; // Invoke ID + p[i++] = 0x01; // InvokeId Length + p[i++] = pc->u.inv.invokeId; + p[i++] = 0x02; // Operation Tag + p[i++] = 0x01; // Tag Length + p[i++] = Fac_AOCSCurrency; // Operation Value 31 AOCS Currency + p[i++] = 0x30; // AOCS Currency Info List + cil_len_index = i; + p[i++] = 0x00; // Length -- not known yet + + for(ci = 0; ci < currinfolist->currencyInfoCount; ci++) { + /* iterate through available currencyInfos within currencyInfoList */ + ci_len = 0; // CurrencyInfoLength + + p[i++] = 0x30; // APDU AOCS Currency Info + ci_len_index = i; + p[i++] = 0x00; // Length -- not known yet + p[i++] = 0x0A; // ChargedItem + ci_len++; + p[i++] = 0x01; // Length + ci_len++; + p[i++] = currinfolist->currencyInfo[ci].chargedItem; + ci_len++; + p[i++] = currinfolist->currencyInfo[ci].currencyType; + ci_len++; + if(currinfolist->currencyInfo[ci].currencyType == 0xA1) { // DurationCurrency + c_len = 0; // CurrencyLength + c_len_index = i; + p[i++] = 0x00; // Length -- not known yet + ci_len++; + p[i++] = 0x81; // CurrencySpec + ci_len++; c_len++; + cval_len = strlen((char *)currinfolist->currencyInfo[ci].durationCurrency.currency); + if(cval_len > 10) + cval_len = 10; + p[i++] = cval_len; + ci_len++; c_len++; + memcpy(p + i, currinfolist->currencyInfo[ci].durationCurrency.currency, cval_len); + i += cval_len; ci_len += cval_len; c_len += cval_len; + p[i++] = 0xA2; // dAmount + ci_len++; c_len++; + p[i++] = 0x06; // Length + ci_len++; c_len++; + p[i++] = 0x81; // CurrencyAmount + ci_len++; c_len++; + p[i++] = 0x01; // Length + ci_len++; c_len++; + p[i++] = currinfolist->currencyInfo[ci].durationCurrency.currencyAmount; + ci_len++; c_len++; + p[i++] = 0x82; // Multiplier + ci_len++; c_len++; + p[i++] = 0x01; // Length + ci_len++; c_len++; + p[i++] = currinfolist->currencyInfo[ci].durationCurrency.multiplier; + ci_len++; c_len++; + p[i++] = 0x83; // ChargeType + ci_len++; c_len++; + p[i++] = 0x01; // Length + ci_len++; c_len++; + p[i++] = currinfolist->currencyInfo[ci].durationCurrency.typeOfCharging; + ci_len++; c_len++; + p[i++] = 0xA4; // dTime + ci_len++; c_len++; + p[i++] = 0x07; // Length + ci_len++; c_len++; + p[i++] = 0x81; // durationLengthOfTimeUnit + ci_len++; c_len++; + if(currinfolist->currencyInfo[ci].durationCurrency.durLengthTimeUnit > 0xff) { + p[i++] = 0x02; // Length + ci_len++; c_len++; + p[i++] = ((currinfolist->currencyInfo[ci].durationCurrency.durLengthTimeUnit) >> 8) & 0xff; + ci_len++; c_len++; + } + else { + p[i++] = 0x01; // Length + ci_len++; c_len++; + } + p[i++] = ((currinfolist->currencyInfo[ci].durationCurrency.durLengthTimeUnit) >> 0) & 0xff; + ci_len++; c_len++; + p[i++] = 0x82; // durationScaleOfTimeUnit + ci_len++; c_len++; + p[i++] = 0x01; // Length + ci_len++; c_len++; + p[i++] = currinfolist->currencyInfo[ci].durationCurrency.durLengthTimeScale; + ci_len++; c_len++; + p[i++] = 0xA5; // dGranularity + ci_len++; c_len++; + p[i++] = 0x06; // Length + ci_len++; c_len++; + p[i++] = 0x81; // granuLengthOfTimeUnit + ci_len++; c_len++; + p[i++] = 0x01; // Length + ci_len++; c_len++; + p[i++] = currinfolist->currencyInfo[ci].durationCurrency.granLengthTimeUnit; + ci_len++; c_len++; + p[i++] = 0x82; // granuScaleOfTimeUnit + ci_len++; c_len++; + p[i++] = 0x01; // Length + ci_len++; c_len++; + p[i++] = currinfolist->currencyInfo[ci].durationCurrency.granLengthTimeScale; + ci_len++; c_len++; + p[c_len_index] = c_len; + } + else if(currinfolist->currencyInfo[ci].currencyType == 0xA2) { // FlatRateCurrency + c_len = 0; // CurrencyLength + c_len_index = i; + p[i++] = 0x00; // Length -- not known yet + ci_len++; + p[i++] = 0x81; // CurrencySpec + ci_len++; c_len++; + cval_len = strlen((char *)currinfolist->currencyInfo[ci].FlatRateCurrency.currency); + if(cval_len > 10) + cval_len = 10; + p[i++] = cval_len; + ci_len++; c_len++; + memcpy(p + i, currinfolist->currencyInfo[ci].FlatRateCurrency.currency, cval_len); + i += cval_len; ci_len += cval_len; c_len += cval_len; + p[i++] = 0xA2; // dAmount + ci_len++; c_len++; + p[i++] = 0x06; // Length + ci_len++; c_len++; + p[i++] = 0x81; // CurrencyAmount + ci_len++; c_len++; + if(currinfolist->currencyInfo[ci].FlatRateCurrency.currencyAmount > 0xff) { + p[i++] = 0x02; // Length + ci_len++; c_len++; + p[i++] = ((currinfolist->currencyInfo[ci].FlatRateCurrency.currencyAmount) >> 8) & 0xff; + ci_len++; c_len++; + } + else { + p[i++] = 0x01; // Length + ci_len++; c_len++; + } + p[i++] = ((currinfolist->currencyInfo[ci].FlatRateCurrency.currencyAmount) >> 0) & 0xff; + ci_len++; c_len++; + p[i++] = 0x82; // Multiplier + ci_len++; c_len++; + p[i++] = 0x01; // Length + ci_len++; c_len++; + p[i++] = currinfolist->currencyInfo[ci].FlatRateCurrency.multiplier; + ci_len++; c_len++; + p[c_len_index] = c_len; + } + else if(currinfolist->currencyInfo[ci].currencyType == 0x84) { // FreeOfCharge + p[i++] = 0x00; + ci_len++; + } + else { + result = 0; + return(result); + } + p[ci_len_index] = ci_len; + } + + p[fac_len_index] = i - 2; + p[supp_len_index] = p[fac_len_index] - 3; + p[cil_len_index] = p[supp_len_index] - 8; + + result = p[fac_len_index] + 2; // Total Length of IE + } + return result; +} + static int encodeAOCDChargingUnitOperation(__u8 * dest, const struct asn1_parm *pc, int numberOfUnits) { /* Manually encoded ASN.1 */ @@ -567,7 +746,10 @@ static int encodeFacInvoke(__u8 * dest, const struct asn1_parm *pc) /* AOC support */ case Fac_ChargingRequest: + break; case Fac_AOCSCurrency: + len = encodeAOCSChargingUnitOperation(dest, pc, &inv->o.AOCcuril); + break; case Fac_AOCSSpecialArr: case Fac_AOCDCurrency: case Fac_AOCECurrency: