AOC-D and AOC-S facility generation from CC metering information

If metering details via IE_METERING are received from osmo-cc, AOC-S and/or AOC-D facility IEs are attached to PROCEEDING/ALERTING/CONNECT messages, and AOC-D facility messages are generated during the call. This feature can be enabled by setting 'aocs' and/or 'aocd' within the mISDN endpoint configuration.
This commit is contained in:
Dennis Grunert 2024-01-09 13:28:05 +01:00 committed by Andreas Eversberg
parent da94632e6e
commit ea6f1bec84
6 changed files with 419 additions and 3 deletions

View File

@ -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 */

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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: