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
parent fe60d15a20
commit 98e7813e77
8 changed files with 454 additions and 4 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;
PDEBUG(DISDN, DEBUG_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
PDEBUG(DISDN, DEBUG_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 */
PDEBUG(DISDN, DEBUG_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(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
PDEBUG(DISDN, DEBUG_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(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 */
PDEBUG(DISDN, DEBUG_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(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(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++;
PDEBUG(DISDN, DEBUG_DEBUG, "Sending next AOC-D unit information, total_units=%d\n", call->metering_total_units);
timer_start(&call->aocd_unit_timer, (double)call->metering_unit_period_decisecs / (double)10);
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
*/
@ -353,6 +454,10 @@ void setup_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
osmo_cc_call_t *cc_call = osmo_cc_call_new(&call->isdn_ep->cc_ep);
call->cc_callref = cc_call->callref;
PDEBUG(DDSS1, DEBUG_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);
@ -766,6 +871,14 @@ 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)
{
timer_stop(&call->aocd_unit_timer);
timer_exit(&call->aocd_unit_timer);
call->aocd_unit_timer_started = 0;
}
}
/* CC-DISCONNECT INDICATION of child instance */
@ -1981,6 +2094,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 +2158,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 +2181,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,7 +2238,38 @@ 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)
{
PDEBUG(DISDN, DEBUG_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) {
PDEBUG(DISDN, DEBUG_DEBUG, "Scheduling AOC-D unit information every %d deciseconds.\n", call->metering_unit_period_decisecs);
call->aocd_unit_timer_started = 1;
timer_init(&call->aocd_unit_timer, aocd_timer_cb, call);
timer_start(&call->aocd_unit_timer, (double)call->metering_unit_period_decisecs / (double)10);
}
}
/* send message to ISDN */
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_CONNECT, call->l3_pid, l3m);
@ -2337,6 +2497,14 @@ 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)
{
timer_stop(&call->aocd_unit_timer);
timer_exit(&call->aocd_unit_timer);
call->aocd_unit_timer_started = 0;
}
new_state(call, ISDN_STATE_OUT_DISCONNECT);
/* send message to ISDN */

View File

@ -684,7 +684,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;
@ -720,6 +720,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) {

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 timer 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

@ -45,6 +45,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;
@ -124,6 +126,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");
@ -153,6 +159,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)
{
@ -171,6 +179,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);
@ -238,6 +248,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;
@ -378,7 +394,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) {
PDEBUG(DISDN, DEBUG_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(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(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:

View File

@ -95,6 +95,7 @@ static const char *osmo_cc_ie_name[OSMO_CC_IE_NUM] = {
[OSMO_CC_IE_PROGRESS] = "IE_PROGRESS",
[OSMO_CC_IE_NOTIFY] = "IE_NOTIFY",
[OSMO_CC_IE_DISPLAY] = "IE_DISPLAY",
[OSMO_CC_IE_METERING] = "IE_METERING",
[OSMO_CC_IE_CAUSE] = "IE_CAUSE",
[OSMO_CC_IE_BEARER] = "IE_BEARER",
[OSMO_CC_IE_SDP] = "IE_SDP",
@ -440,7 +441,7 @@ void osmo_cc_debug_ie(osmo_cc_msg_t *msg, int level)
int rc;
int ie_repeat[256];
uint8_t type, plan, present, screen, coding, capability, mode, progress, reason, duration_ms, pause_ms, dtmf_mode, location, notify, isdn_cause, socket_cause;
uint16_t sip_cause;
uint16_t sip_cause, metering_connect_units, metering_unit_time_decisecs;
uint32_t unique;
char string[65536];
int i;
@ -573,6 +574,12 @@ void osmo_cc_debug_ie(osmo_cc_msg_t *msg, int level)
break;
LOGP(DCC, level, " %s info='%s'\n", osmo_cc_ie_value2name(ie->type), string);
break;
case OSMO_CC_IE_METERING:
rc = osmo_cc_get_ie_metering(msg, ie_repeat[ie->type], &metering_connect_units, &metering_unit_time_decisecs);
if (rc < 0)
break;
LOGP(DCC, level, " %s connect_units=%d unit_time_decisecs=%d (1/10 sec)\n", osmo_cc_ie_value2name(ie->type), metering_connect_units, metering_unit_time_decisecs);
break;
case OSMO_CC_IE_SDP:
rc = osmo_cc_get_ie_sdp(msg, ie_repeat[ie->type], string, sizeof(string));
if (rc < 0)
@ -1222,6 +1229,32 @@ int osmo_cc_get_ie_display(osmo_cc_msg_t *msg, int ie_repeat, char *text, size_t
return rc;
}
/* helper to encode METERING information */
void osmo_cc_add_ie_metering(osmo_cc_msg_t *msg, uint16_t connect_units, uint16_t unit_period_decisecs)
{
struct osmo_cc_ie_metering *ie_metering;
ie_metering = osmo_cc_add_ie(msg, OSMO_CC_IE_METERING, sizeof(*ie_metering));
ie_metering->connect_units = connect_units;
ie_metering->unit_period_decisecs = unit_period_decisecs;
}
/* helper to decode METERING information */
int osmo_cc_get_ie_metering(osmo_cc_msg_t *msg, int ie_repeat, uint16_t *connect_units, uint16_t *unit_period_decisecs)
{
struct osmo_cc_ie_metering *ie_metering;
int rc;
rc = osmo_cc_get_ie_data(msg, OSMO_CC_IE_METERING, ie_repeat, sizeof(*ie_metering), (const void **)&ie_metering);
if (rc < 0)
return rc;
*connect_units = ie_metering->connect_units;
*unit_period_decisecs = ie_metering->unit_period_decisecs;
return rc;
}
/* helper to encode SDP */
void osmo_cc_add_ie_sdp(osmo_cc_msg_t *msg, const char *sdp)
{

View File

@ -69,6 +69,7 @@ enum osmo_cc_ie_type {
OSMO_CC_IE_PROGRESS = 0x32,
OSMO_CC_IE_NOTIFY = 0x33,
OSMO_CC_IE_DISPLAY = 0x34,
OSMO_CC_IE_METERING = 0x35,
OSMO_CC_IE_CAUSE = 0x41,
OSMO_CC_IE_BEARER = 0x51,
OSMO_CC_IE_SDP = 0x52,
@ -440,6 +441,11 @@ struct osmo_cc_ie_display {
char text[0];
} __attribute__((packed));
struct osmo_cc_ie_metering {
uint16_t connect_units;
uint16_t unit_period_decisecs;
} __attribute__((packed));
struct osmo_cc_ie_sdp {
char sdp[0];
} __attribute__((packed));
@ -503,6 +509,8 @@ void osmo_cc_add_ie_cause(osmo_cc_msg_t *msg, uint8_t location, uint8_t isdn_cau
int osmo_cc_get_ie_cause(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *location, uint8_t *isdn_cause, uint16_t *sip_cause, uint8_t *socket_cause);
void osmo_cc_add_ie_display(osmo_cc_msg_t *msg, const char *text);
int osmo_cc_get_ie_display(osmo_cc_msg_t *msg, int ie_repeat, char *text, size_t text_size);
void osmo_cc_add_ie_metering(osmo_cc_msg_t *msg, uint16_t connect_units, uint16_t unit_period_decisecs);
int osmo_cc_get_ie_metering(osmo_cc_msg_t *msg, int ie_repeat, uint16_t *connect_units, uint16_t *unit_period_decisecs);
void osmo_cc_add_ie_sdp(osmo_cc_msg_t *msg, const char *sdp);
int osmo_cc_get_ie_sdp(osmo_cc_msg_t *msg, int ie_repeat, char *sdp, size_t sdp_size);
void osmo_cc_add_ie_socket_address(osmo_cc_msg_t *msg, const char *address);