ETSI Advice Of Charge (AOC) event reporting.
This feature passes ETSI AOC-S, AOC-D, and AOC-E message information to the upper layer (i.e. Asterisk) for processing. Relevant specification: EN 300 182 Consideration was made for the possible future addition of Q.SIG AOC support (ECMA-212) with the events passed to the upper layer. Review: https://reviewboard.asterisk.org/r/538/ git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@1737 2fbb986a-6c06-0410-b554-c9c1f0a7f128
This commit is contained in:
parent
382e7e5c86
commit
9bb285fdc1
2
Makefile
2
Makefile
@ -47,6 +47,7 @@ STATIC_OBJS= \
|
|||||||
q921.o \
|
q921.o \
|
||||||
prisched.o \
|
prisched.o \
|
||||||
q931.o \
|
q931.o \
|
||||||
|
pri_aoc.o \
|
||||||
pri_cc.o \
|
pri_cc.o \
|
||||||
pri_facility.o \
|
pri_facility.o \
|
||||||
asn1_primitive.o \
|
asn1_primitive.o \
|
||||||
@ -71,6 +72,7 @@ DYNAMIC_OBJS= \
|
|||||||
q921.lo \
|
q921.lo \
|
||||||
prisched.lo \
|
prisched.lo \
|
||||||
q931.lo \
|
q931.lo \
|
||||||
|
pri_aoc.lo \
|
||||||
pri_cc.lo \
|
pri_cc.lo \
|
||||||
pri_facility.lo \
|
pri_facility.lo \
|
||||||
asn1_primitive.lo \
|
asn1_primitive.lo \
|
||||||
|
241
libpri.h
241
libpri.h
@ -539,6 +539,11 @@ struct pri_rerouting_data {
|
|||||||
#define PRI_SUBCMD_CC_CANCEL 15 /*!< Unsolicited indication that CC is canceled */
|
#define PRI_SUBCMD_CC_CANCEL 15 /*!< Unsolicited indication that CC is canceled */
|
||||||
#define PRI_SUBCMD_CC_STOP_ALERTING 16 /*!< Indicate that someone else has responed to remote user free */
|
#define PRI_SUBCMD_CC_STOP_ALERTING 16 /*!< Indicate that someone else has responed to remote user free */
|
||||||
#define PRI_SUBCMD_TRANSFER_CALL 17 /*!< Request to transfer the specified calls together. */
|
#define PRI_SUBCMD_TRANSFER_CALL 17 /*!< Request to transfer the specified calls together. */
|
||||||
|
#define PRI_SUBCMD_AOC_S 18 /*!< Advice Of Charge Start information (Rate list) */
|
||||||
|
#define PRI_SUBCMD_AOC_D 19 /*!< Advice Of Charge During information */
|
||||||
|
#define PRI_SUBCMD_AOC_E 20 /*!< Advice Of Charge End information */
|
||||||
|
//#define PRI_SUBCMD_AOC_CHARGING_REQ 21 /*!< Advice Of Charge Request information */
|
||||||
|
//#define PRI_SUBCMD_AOC_CHARGING_REQ_RSP 22 /*!< Advice Of Charge Request Response information */
|
||||||
|
|
||||||
#if defined(STATUS_REQUEST_PLACE_HOLDER)
|
#if defined(STATUS_REQUEST_PLACE_HOLDER)
|
||||||
struct pri_subcmd_status_request {
|
struct pri_subcmd_status_request {
|
||||||
@ -648,6 +653,227 @@ struct pri_subcmd_transfer {
|
|||||||
int invoke_id;
|
int invoke_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*! \brief What is being charged. */
|
||||||
|
enum PRI_AOC_CHARGED_ITEM {
|
||||||
|
PRI_AOC_CHARGED_ITEM_NOT_AVAILABLE,
|
||||||
|
PRI_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT,
|
||||||
|
PRI_AOC_CHARGED_ITEM_BASIC_COMMUNICATION,
|
||||||
|
PRI_AOC_CHARGED_ITEM_CALL_ATTEMPT,
|
||||||
|
PRI_AOC_CHARGED_ITEM_CALL_SETUP,
|
||||||
|
PRI_AOC_CHARGED_ITEM_USER_USER_INFO,
|
||||||
|
PRI_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! \brief Rate method being used. */
|
||||||
|
enum PRI_AOC_RATE_TYPE {
|
||||||
|
PRI_AOC_RATE_TYPE_NOT_AVAILABLE,
|
||||||
|
PRI_AOC_RATE_TYPE_FREE,
|
||||||
|
PRI_AOC_RATE_TYPE_FREE_FROM_BEGINNING,
|
||||||
|
PRI_AOC_RATE_TYPE_DURATION,
|
||||||
|
PRI_AOC_RATE_TYPE_FLAT,
|
||||||
|
PRI_AOC_RATE_TYPE_VOLUME,
|
||||||
|
PRI_AOC_RATE_TYPE_SPECIAL_CODE,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PRI_AOC_TIME_SCALE {
|
||||||
|
PRI_AOC_TIME_SCALE_HUNDREDTH_SECOND,
|
||||||
|
PRI_AOC_TIME_SCALE_TENTH_SECOND,
|
||||||
|
PRI_AOC_TIME_SCALE_SECOND,
|
||||||
|
PRI_AOC_TIME_SCALE_TEN_SECOND,
|
||||||
|
PRI_AOC_TIME_SCALE_MINUTE,
|
||||||
|
PRI_AOC_TIME_SCALE_HOUR,
|
||||||
|
PRI_AOC_TIME_SCALE_DAY,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pri_aoc_time {
|
||||||
|
/*! LengthOfTimeUnit (Not valid if length is zero.) */
|
||||||
|
long length;
|
||||||
|
/*! \see enum PRI_AOC_TIME_SCALE */
|
||||||
|
int scale;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PRI_AOC_MULTIPLIER {
|
||||||
|
PRI_AOC_MULTIPLIER_THOUSANDTH,
|
||||||
|
PRI_AOC_MULTIPLIER_HUNDREDTH,
|
||||||
|
PRI_AOC_MULTIPLIER_TENTH,
|
||||||
|
PRI_AOC_MULTIPLIER_ONE,
|
||||||
|
PRI_AOC_MULTIPLIER_TEN,
|
||||||
|
PRI_AOC_MULTIPLIER_HUNDRED,
|
||||||
|
PRI_AOC_MULTIPLIER_THOUSAND,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pri_aoc_amount {
|
||||||
|
long cost;
|
||||||
|
/*! \see enum PRI_AOC_MULTIPLIER */
|
||||||
|
int multiplier;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pri_aoc_duration {
|
||||||
|
struct pri_aoc_amount amount;
|
||||||
|
struct pri_aoc_time time;
|
||||||
|
/*! Not present if the granularity time is zero. */
|
||||||
|
struct pri_aoc_time granularity;
|
||||||
|
/*!
|
||||||
|
* \brief Charging interval type
|
||||||
|
* \details
|
||||||
|
* continuousCharging(0),
|
||||||
|
* stepFunction(1)
|
||||||
|
*/
|
||||||
|
int charging_type;
|
||||||
|
/*! Name of currency involved. Null terminated. */
|
||||||
|
char currency[10 + 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pri_aoc_flat {
|
||||||
|
struct pri_aoc_amount amount;
|
||||||
|
/*! Name of currency involved. Null terminated. */
|
||||||
|
char currency[10 + 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PRI_AOC_VOLUME_UNIT {
|
||||||
|
PRI_AOC_VOLUME_UNIT_OCTET,
|
||||||
|
PRI_AOC_VOLUME_UNIT_SEGMENT,
|
||||||
|
PRI_AOC_VOLUME_UNIT_MESSAGE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pri_aoc_volume {
|
||||||
|
struct pri_aoc_amount amount;
|
||||||
|
/*! \see enum PRI_AOC_VOLUME_UNIT */
|
||||||
|
int unit;
|
||||||
|
/*! Name of currency involved. Null terminated. */
|
||||||
|
char currency[10 + 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pri_aoc_s_element {
|
||||||
|
/*!
|
||||||
|
* \brief What is being charged.
|
||||||
|
* \see enum PRI_AOC_CHARGED_ITEM
|
||||||
|
*/
|
||||||
|
int chargeable;
|
||||||
|
/*!
|
||||||
|
* \brief Rate method being used.
|
||||||
|
* \see enum PRI_AOC_RATE_TYPE
|
||||||
|
*/
|
||||||
|
int rate_type;
|
||||||
|
/*! \brief Charge rate being applied. */
|
||||||
|
union {
|
||||||
|
struct pri_aoc_duration duration;
|
||||||
|
struct pri_aoc_flat flat;
|
||||||
|
struct pri_aoc_volume volume;
|
||||||
|
int special;
|
||||||
|
} rate;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pri_subcmd_aoc_s {
|
||||||
|
/*!
|
||||||
|
* \brief Number of items in the rate list.
|
||||||
|
* \note If the list is empty then the charging information is not available.
|
||||||
|
*/
|
||||||
|
int num_items;
|
||||||
|
struct pri_aoc_s_element item[10];
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PRI_AOC_DE_CHARGE {
|
||||||
|
PRI_AOC_DE_CHARGE_NOT_AVAILABLE,
|
||||||
|
PRI_AOC_DE_CHARGE_FREE,
|
||||||
|
PRI_AOC_DE_CHARGE_CURRENCY,
|
||||||
|
PRI_AOC_DE_CHARGE_UNITS,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pri_aoc_recorded_currency {
|
||||||
|
struct pri_aoc_amount amount;
|
||||||
|
/*! Name of currency involved. Null terminated. */
|
||||||
|
char currency[10 + 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pri_aoc_units_element {
|
||||||
|
/*! Number of units recorded. -1 if not available. */
|
||||||
|
long number;
|
||||||
|
/*! Type of unit recorded. -1 if not available. */
|
||||||
|
int type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pri_aoc_recorded_units {
|
||||||
|
int num_items;
|
||||||
|
struct pri_aoc_units_element item[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PRI_AOC_D_BILLING_ID {
|
||||||
|
PRI_AOC_D_BILLING_ID_NOT_AVAILABLE,
|
||||||
|
PRI_AOC_D_BILLING_ID_NORMAL,
|
||||||
|
PRI_AOC_D_BILLING_ID_REVERSE,
|
||||||
|
PRI_AOC_D_BILLING_ID_CREDIT_CARD,
|
||||||
|
};
|
||||||
|
struct pri_subcmd_aoc_d {
|
||||||
|
/*!
|
||||||
|
* \brief What is being charged.
|
||||||
|
* \see enum PRI_AOC_DE_CHARGE
|
||||||
|
*/
|
||||||
|
int charge;
|
||||||
|
/*!
|
||||||
|
* \brief Billing accumulation
|
||||||
|
* \details
|
||||||
|
* subTotal(0),
|
||||||
|
* total(1)
|
||||||
|
*/
|
||||||
|
int billing_accumulation;
|
||||||
|
/*! \see enum PRI_AOC_D_BILLING_ID */
|
||||||
|
int billing_id;
|
||||||
|
union {
|
||||||
|
/*! Recorded currency */
|
||||||
|
struct pri_aoc_recorded_currency money;
|
||||||
|
/*! Recorded units list */
|
||||||
|
struct pri_aoc_recorded_units unit;
|
||||||
|
} recorded;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PRI_AOC_E_BILLING_ID {
|
||||||
|
PRI_AOC_E_BILLING_ID_NOT_AVAILABLE,
|
||||||
|
PRI_AOC_E_BILLING_ID_NORMAL,
|
||||||
|
PRI_AOC_E_BILLING_ID_REVERSE,
|
||||||
|
PRI_AOC_E_BILLING_ID_CREDIT_CARD,
|
||||||
|
PRI_AOC_E_BILLING_ID_CALL_FORWARDING_UNCONDITIONAL,
|
||||||
|
PRI_AOC_E_BILLING_ID_CALL_FORWARDING_BUSY,
|
||||||
|
PRI_AOC_E_BILLING_ID_CALL_FORWARDING_NO_REPLY,
|
||||||
|
PRI_AOC_E_BILLING_ID_CALL_DEFLECTION,
|
||||||
|
PRI_AOC_E_BILLING_ID_CALL_TRANSFER,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PRI_AOC_E_CHARGING_ASSOCIATION {
|
||||||
|
PRI_AOC_E_CHARGING_ASSOCIATION_NOT_AVAILABLE,
|
||||||
|
PRI_AOC_E_CHARGING_ASSOCIATION_NUMBER,
|
||||||
|
PRI_AOC_E_CHARGING_ASSOCIATION_ID,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pri_aoc_e_charging_association {
|
||||||
|
union {
|
||||||
|
/*! Charged number */
|
||||||
|
struct pri_party_number number;
|
||||||
|
/*! Charge identifier */
|
||||||
|
int id;
|
||||||
|
} charge;
|
||||||
|
/*! \see enum PRI_AOC_E_CHARGING_ASSOCIATION */
|
||||||
|
int charging_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pri_subcmd_aoc_e {
|
||||||
|
/*!
|
||||||
|
* \brief What is being charged.
|
||||||
|
* \see enum PRI_AOC_DE_CHARGE
|
||||||
|
*/
|
||||||
|
int charge;
|
||||||
|
/*! \see enum PRI_AOC_E_BILLING_ID */
|
||||||
|
int billing_id;
|
||||||
|
union {
|
||||||
|
/*! Recorded currency */
|
||||||
|
struct pri_aoc_recorded_currency money;
|
||||||
|
/*! Recorded units list */
|
||||||
|
struct pri_aoc_recorded_units unit;
|
||||||
|
} recorded;
|
||||||
|
/*! Charging association. */
|
||||||
|
struct pri_aoc_e_charging_association associated;
|
||||||
|
};
|
||||||
|
|
||||||
struct pri_subcommand {
|
struct pri_subcommand {
|
||||||
/*! PRI_SUBCMD_xxx defined values */
|
/*! PRI_SUBCMD_xxx defined values */
|
||||||
int cmd;
|
int cmd;
|
||||||
@ -673,6 +899,9 @@ struct pri_subcommand {
|
|||||||
struct pri_subcmd_cc_id cc_call;
|
struct pri_subcmd_cc_id cc_call;
|
||||||
struct pri_subcmd_cc_cancel cc_cancel;
|
struct pri_subcmd_cc_cancel cc_cancel;
|
||||||
struct pri_subcmd_transfer transfer;
|
struct pri_subcmd_transfer transfer;
|
||||||
|
struct pri_subcmd_aoc_s aoc_s;
|
||||||
|
struct pri_subcmd_aoc_d aoc_d;
|
||||||
|
struct pri_subcmd_aoc_e aoc_e;
|
||||||
} u;
|
} u;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -817,7 +1046,7 @@ typedef struct pri_event_hangup {
|
|||||||
int cause;
|
int cause;
|
||||||
int cref;
|
int cref;
|
||||||
q931_call *call; /* Opaque call pointer of call hanging up. */
|
q931_call *call; /* Opaque call pointer of call hanging up. */
|
||||||
long aoc_units; /* Advise of Charge number of charged units */
|
long aoc_units; /* Advice of Charge number of charged units */
|
||||||
char useruserinfo[260]; /* User->User info */
|
char useruserinfo[260]; /* User->User info */
|
||||||
struct pri_subcommands *subcmds;
|
struct pri_subcommands *subcmds;
|
||||||
/*!
|
/*!
|
||||||
@ -1378,6 +1607,16 @@ void pri_transfer_enable(struct pri *ctrl, int enable);
|
|||||||
*/
|
*/
|
||||||
int pri_transfer_rsp(struct pri *ctrl, q931_call *call, int invoke_id, int is_successful);
|
int pri_transfer_rsp(struct pri *ctrl, q931_call *call, int invoke_id, int is_successful);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Set the advice of charge events feature enable flag.
|
||||||
|
*
|
||||||
|
* \param ctrl D channel controller.
|
||||||
|
* \param enable TRUE to enable AOC events feature.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
void pri_aoc_events_enable(struct pri *ctrl, int enable);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Set the call hold feature enable flag.
|
* \brief Set the call hold feature enable flag.
|
||||||
*
|
*
|
||||||
|
678
pri_aoc.c
Normal file
678
pri_aoc.c
Normal file
@ -0,0 +1,678 @@
|
|||||||
|
/*
|
||||||
|
* libpri: An implementation of Primary Rate ISDN
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Digium, Inc.
|
||||||
|
*
|
||||||
|
* Richard Mudgett <rmudgett@digium.com>
|
||||||
|
*
|
||||||
|
* See http://www.asterisk.org for more information about
|
||||||
|
* the Asterisk project. Please do not directly contact
|
||||||
|
* any of the maintainers of this project for assistance;
|
||||||
|
* the project provides a web site, mailing lists and IRC
|
||||||
|
* channels for your use.
|
||||||
|
*
|
||||||
|
* This program is free software, distributed under the terms of
|
||||||
|
* the GNU General Public License Version 2 as published by the
|
||||||
|
* Free Software Foundation. See the LICENSE file included with
|
||||||
|
* this program for more details.
|
||||||
|
*
|
||||||
|
* In addition, when this program is distributed with Asterisk in
|
||||||
|
* any form that would qualify as a 'combined work' or as a
|
||||||
|
* 'derivative work' (but not mere aggregation), you can redistribute
|
||||||
|
* and/or modify the combination under the terms of the license
|
||||||
|
* provided with that copy of Asterisk, instead of the license
|
||||||
|
* terms granted here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \file
|
||||||
|
* \brief Advice Of Charge (AOC) facility support.
|
||||||
|
*
|
||||||
|
* \author Richard Mudgett <rmudgett@digium.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "compat.h"
|
||||||
|
#include "libpri.h"
|
||||||
|
#include "pri_internal.h"
|
||||||
|
#include "pri_facility.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Fill in the AOC subcmd amount from the ETSI amount.
|
||||||
|
*
|
||||||
|
* \param subcmd_amount AOC subcmd amount.
|
||||||
|
* \param etsi_amount AOC ETSI amount.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
static void aoc_etsi_subcmd_amount(struct pri_aoc_amount *subcmd_amount, const struct roseEtsiAOCAmount *etsi_amount)
|
||||||
|
{
|
||||||
|
subcmd_amount->cost = etsi_amount->currency;
|
||||||
|
subcmd_amount->multiplier = etsi_amount->multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Fill in the AOC subcmd time from the ETSI time.
|
||||||
|
*
|
||||||
|
* \param subcmd_time AOC subcmd time.
|
||||||
|
* \param etsi_time AOC ETSI time.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
static void aoc_etsi_subcmd_time(struct pri_aoc_time *subcmd_time, const struct roseEtsiAOCTime *etsi_time)
|
||||||
|
{
|
||||||
|
subcmd_time->length = etsi_time->length;
|
||||||
|
subcmd_time->scale = etsi_time->scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Fill in the AOC subcmd recorded currency from the ETSI recorded currency.
|
||||||
|
*
|
||||||
|
* \param subcmd_recorded AOC subcmd recorded currency.
|
||||||
|
* \param etsi_recorded AOC ETSI recorded currency.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
static void aoc_etsi_subcmd_recorded_currency(struct pri_aoc_recorded_currency *subcmd_recorded, const struct roseEtsiAOCRecordedCurrency *etsi_recorded)
|
||||||
|
{
|
||||||
|
aoc_etsi_subcmd_amount(&subcmd_recorded->amount, &etsi_recorded->amount);
|
||||||
|
libpri_copy_string(subcmd_recorded->currency, (char *) etsi_recorded->currency,
|
||||||
|
sizeof(subcmd_recorded->currency));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Fill in the AOC subcmd recorded units from the ETSI recorded units.
|
||||||
|
*
|
||||||
|
* \param subcmd_recorded AOC subcmd recorded units list.
|
||||||
|
* \param etsi_recorded AOC ETSI recorded units list.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
static void aoc_etsi_subcmd_recorded_units(struct pri_aoc_recorded_units *subcmd_recorded, const struct roseEtsiAOCRecordedUnitsList *etsi_recorded)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
/* Fill in the itemized list of recorded units. */
|
||||||
|
for (idx = 0; idx < etsi_recorded->num_records
|
||||||
|
&& idx < ARRAY_LEN(subcmd_recorded->item); ++idx) {
|
||||||
|
if (etsi_recorded->list[idx].not_available) {
|
||||||
|
subcmd_recorded->item[idx].number = -1;
|
||||||
|
} else {
|
||||||
|
subcmd_recorded->item[idx].number = etsi_recorded->list[idx].number_of_units;
|
||||||
|
}
|
||||||
|
if (etsi_recorded->list[idx].type_of_unit_present) {
|
||||||
|
subcmd_recorded->item[idx].type = etsi_recorded->list[idx].type_of_unit;
|
||||||
|
} else {
|
||||||
|
subcmd_recorded->item[idx].type = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subcmd_recorded->num_items = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Fill in the AOC-S subcmd currency info list of chargeable items.
|
||||||
|
*
|
||||||
|
* \param aoc_s AOC-S info list of chargeable items.
|
||||||
|
* \param info ETSI info list of chargeable items.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
static void aoc_etsi_subcmd_aoc_s_currency_info(struct pri_subcmd_aoc_s *aoc_s, const struct roseEtsiAOCSCurrencyInfoList *info)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
/* Fill in the itemized list of chargeable items. */
|
||||||
|
for (idx = 0; idx < info->num_records && idx < ARRAY_LEN(aoc_s->item); ++idx) {
|
||||||
|
/* What is being charged. */
|
||||||
|
switch (info->list[idx].charged_item) {
|
||||||
|
case 0:/* basicCommunication */
|
||||||
|
aoc_s->item[idx].chargeable = PRI_AOC_CHARGED_ITEM_BASIC_COMMUNICATION;
|
||||||
|
break;
|
||||||
|
case 1:/* callAttempt */
|
||||||
|
aoc_s->item[idx].chargeable = PRI_AOC_CHARGED_ITEM_CALL_ATTEMPT;
|
||||||
|
break;
|
||||||
|
case 2:/* callSetup */
|
||||||
|
aoc_s->item[idx].chargeable = PRI_AOC_CHARGED_ITEM_CALL_SETUP;
|
||||||
|
break;
|
||||||
|
case 3:/* userToUserInfo */
|
||||||
|
aoc_s->item[idx].chargeable = PRI_AOC_CHARGED_ITEM_USER_USER_INFO;
|
||||||
|
break;
|
||||||
|
case 4:/* operationOfSupplementaryServ */
|
||||||
|
aoc_s->item[idx].chargeable = PRI_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
aoc_s->item[idx].chargeable = PRI_AOC_CHARGED_ITEM_NOT_AVAILABLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rate method being used. */
|
||||||
|
switch (info->list[idx].currency_type) {
|
||||||
|
case 0:/* specialChargingCode */
|
||||||
|
aoc_s->item[idx].rate_type = PRI_AOC_RATE_TYPE_SPECIAL_CODE;
|
||||||
|
aoc_s->item[idx].rate.special = info->list[idx].u.special_charging_code;
|
||||||
|
break;
|
||||||
|
case 1:/* durationCurrency */
|
||||||
|
aoc_s->item[idx].rate_type = PRI_AOC_RATE_TYPE_DURATION;
|
||||||
|
aoc_etsi_subcmd_amount(&aoc_s->item[idx].rate.duration.amount,
|
||||||
|
&info->list[idx].u.duration.amount);
|
||||||
|
aoc_etsi_subcmd_time(&aoc_s->item[idx].rate.duration.time,
|
||||||
|
&info->list[idx].u.duration.time);
|
||||||
|
if (info->list[idx].u.duration.granularity_present) {
|
||||||
|
aoc_etsi_subcmd_time(&aoc_s->item[idx].rate.duration.granularity,
|
||||||
|
&info->list[idx].u.duration.granularity);
|
||||||
|
} else {
|
||||||
|
aoc_s->item[idx].rate.duration.granularity.length = 0;
|
||||||
|
aoc_s->item[idx].rate.duration.granularity.scale =
|
||||||
|
PRI_AOC_TIME_SCALE_HUNDREDTH_SECOND;
|
||||||
|
}
|
||||||
|
aoc_s->item[idx].rate.duration.charging_type =
|
||||||
|
info->list[idx].u.duration.charging_type;
|
||||||
|
libpri_copy_string(aoc_s->item[idx].rate.duration.currency,
|
||||||
|
(char *) info->list[idx].u.duration.currency,
|
||||||
|
sizeof(aoc_s->item[idx].rate.duration.currency));
|
||||||
|
break;
|
||||||
|
case 2:/* flatRateCurrency */
|
||||||
|
aoc_s->item[idx].rate_type = PRI_AOC_RATE_TYPE_FLAT;
|
||||||
|
aoc_etsi_subcmd_amount(&aoc_s->item[idx].rate.flat.amount,
|
||||||
|
&info->list[idx].u.flat_rate.amount);
|
||||||
|
libpri_copy_string(aoc_s->item[idx].rate.flat.currency,
|
||||||
|
(char *) info->list[idx].u.flat_rate.currency,
|
||||||
|
sizeof(aoc_s->item[idx].rate.flat.currency));
|
||||||
|
break;
|
||||||
|
case 3:/* volumeRateCurrency */
|
||||||
|
aoc_s->item[idx].rate_type = PRI_AOC_RATE_TYPE_VOLUME;
|
||||||
|
aoc_etsi_subcmd_amount(&aoc_s->item[idx].rate.volume.amount,
|
||||||
|
&info->list[idx].u.volume_rate.amount);
|
||||||
|
aoc_s->item[idx].rate.volume.unit = info->list[idx].u.volume_rate.unit;
|
||||||
|
libpri_copy_string(aoc_s->item[idx].rate.volume.currency,
|
||||||
|
(char *) info->list[idx].u.volume_rate.currency,
|
||||||
|
sizeof(aoc_s->item[idx].rate.volume.currency));
|
||||||
|
break;
|
||||||
|
case 4:/* freeOfCharge */
|
||||||
|
aoc_s->item[idx].rate_type = PRI_AOC_RATE_TYPE_FREE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case 5:/* currencyInfoNotAvailable */
|
||||||
|
aoc_s->item[idx].rate_type = PRI_AOC_RATE_TYPE_NOT_AVAILABLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aoc_s->num_items = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Handle the ETSI AOCSCurrency message.
|
||||||
|
*
|
||||||
|
* \param ctrl D channel controller for diagnostic messages or global options.
|
||||||
|
* \param invoke Decoded ROSE invoke message contents.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
void aoc_etsi_aoc_s_currency(struct pri *ctrl, const struct rose_msg_invoke *invoke)
|
||||||
|
{
|
||||||
|
struct pri_subcommand *subcmd;
|
||||||
|
|
||||||
|
if (!PRI_MASTER(ctrl)->aoc_support) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
subcmd = q931_alloc_subcommand(ctrl);
|
||||||
|
if (!subcmd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
subcmd->cmd = PRI_SUBCMD_AOC_S;
|
||||||
|
if (!invoke->args.etsi.AOCSCurrency.type) {
|
||||||
|
subcmd->u.aoc_s.num_items = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill in the itemized list of chargeable items. */
|
||||||
|
aoc_etsi_subcmd_aoc_s_currency_info(&subcmd->u.aoc_s,
|
||||||
|
&invoke->args.etsi.AOCSCurrency.currency_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Handle the ETSI AOCSSpecialArr message.
|
||||||
|
*
|
||||||
|
* \param ctrl D channel controller for diagnostic messages or global options.
|
||||||
|
* \param invoke Decoded ROSE invoke message contents.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
void aoc_etsi_aoc_s_special_arrangement(struct pri *ctrl, const struct rose_msg_invoke *invoke)
|
||||||
|
{
|
||||||
|
struct pri_subcommand *subcmd;
|
||||||
|
|
||||||
|
if (!PRI_MASTER(ctrl)->aoc_support) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
subcmd = q931_alloc_subcommand(ctrl);
|
||||||
|
if (!subcmd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
subcmd->cmd = PRI_SUBCMD_AOC_S;
|
||||||
|
if (!invoke->args.etsi.AOCSSpecialArr.type) {
|
||||||
|
subcmd->u.aoc_s.num_items = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
subcmd->u.aoc_s.num_items = 1;
|
||||||
|
subcmd->u.aoc_s.item[0].chargeable = PRI_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT;
|
||||||
|
subcmd->u.aoc_s.item[0].rate_type = PRI_AOC_RATE_TYPE_SPECIAL_CODE;
|
||||||
|
subcmd->u.aoc_s.item[0].rate.special =
|
||||||
|
invoke->args.etsi.AOCSSpecialArr.special_arrangement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Determine the AOC-D subcmd billing_id value.
|
||||||
|
*
|
||||||
|
* \param billing_id_present TRUE if billing_id valid.
|
||||||
|
* \param billing_id ETSI billing id from ROSE.
|
||||||
|
*
|
||||||
|
* \return enum PRI_AOC_D_BILLING_ID value
|
||||||
|
*/
|
||||||
|
static enum PRI_AOC_D_BILLING_ID aoc_etsi_subcmd_aoc_d_billing_id(int billing_id_present, int billing_id)
|
||||||
|
{
|
||||||
|
enum PRI_AOC_D_BILLING_ID value;
|
||||||
|
|
||||||
|
if (billing_id_present) {
|
||||||
|
switch (billing_id) {
|
||||||
|
case 0:/* normalCharging */
|
||||||
|
value = PRI_AOC_D_BILLING_ID_NORMAL;
|
||||||
|
break;
|
||||||
|
case 1:/* reverseCharging */
|
||||||
|
value = PRI_AOC_D_BILLING_ID_REVERSE;
|
||||||
|
break;
|
||||||
|
case 2:/* creditCardCharging */
|
||||||
|
value = PRI_AOC_D_BILLING_ID_CREDIT_CARD;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
value = PRI_AOC_D_BILLING_ID_NOT_AVAILABLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value = PRI_AOC_D_BILLING_ID_NOT_AVAILABLE;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Handle the ETSI AOCDCurrency message.
|
||||||
|
*
|
||||||
|
* \param ctrl D channel controller for diagnostic messages or global options.
|
||||||
|
* \param invoke Decoded ROSE invoke message contents.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
void aoc_etsi_aoc_d_currency(struct pri *ctrl, const struct rose_msg_invoke *invoke)
|
||||||
|
{
|
||||||
|
struct pri_subcommand *subcmd;
|
||||||
|
|
||||||
|
if (!PRI_MASTER(ctrl)->aoc_support) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
subcmd = q931_alloc_subcommand(ctrl);
|
||||||
|
if (!subcmd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
subcmd->cmd = PRI_SUBCMD_AOC_D;
|
||||||
|
switch (invoke->args.etsi.AOCDCurrency.type) {
|
||||||
|
default:
|
||||||
|
case 0:/* charge_not_available */
|
||||||
|
subcmd->u.aoc_d.charge = PRI_AOC_DE_CHARGE_NOT_AVAILABLE;
|
||||||
|
break;
|
||||||
|
case 1:/* free_of_charge */
|
||||||
|
subcmd->u.aoc_d.charge = PRI_AOC_DE_CHARGE_FREE;
|
||||||
|
break;
|
||||||
|
case 2:/* specific_currency */
|
||||||
|
subcmd->u.aoc_d.charge = PRI_AOC_DE_CHARGE_CURRENCY;
|
||||||
|
aoc_etsi_subcmd_recorded_currency(&subcmd->u.aoc_d.recorded.money,
|
||||||
|
&invoke->args.etsi.AOCDCurrency.specific.recorded);
|
||||||
|
subcmd->u.aoc_d.billing_accumulation =
|
||||||
|
invoke->args.etsi.AOCDCurrency.specific.type_of_charging_info;
|
||||||
|
subcmd->u.aoc_d.billing_id = aoc_etsi_subcmd_aoc_d_billing_id(
|
||||||
|
invoke->args.etsi.AOCDCurrency.specific.billing_id_present,
|
||||||
|
invoke->args.etsi.AOCDCurrency.specific.billing_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Handle the ETSI AOCDChargingUnit message.
|
||||||
|
*
|
||||||
|
* \param ctrl D channel controller for diagnostic messages or global options.
|
||||||
|
* \param invoke Decoded ROSE invoke message contents.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
void aoc_etsi_aoc_d_charging_unit(struct pri *ctrl, const struct rose_msg_invoke *invoke)
|
||||||
|
{
|
||||||
|
struct pri_subcommand *subcmd;
|
||||||
|
|
||||||
|
if (!PRI_MASTER(ctrl)->aoc_support) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
subcmd = q931_alloc_subcommand(ctrl);
|
||||||
|
if (!subcmd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
subcmd->cmd = PRI_SUBCMD_AOC_D;
|
||||||
|
switch (invoke->args.etsi.AOCDChargingUnit.type) {
|
||||||
|
default:
|
||||||
|
case 0:/* charge_not_available */
|
||||||
|
subcmd->u.aoc_d.charge = PRI_AOC_DE_CHARGE_NOT_AVAILABLE;
|
||||||
|
break;
|
||||||
|
case 1:/* free_of_charge */
|
||||||
|
subcmd->u.aoc_d.charge = PRI_AOC_DE_CHARGE_FREE;
|
||||||
|
break;
|
||||||
|
case 2:/* specific_charging_units */
|
||||||
|
subcmd->u.aoc_d.charge = PRI_AOC_DE_CHARGE_UNITS;
|
||||||
|
aoc_etsi_subcmd_recorded_units(&subcmd->u.aoc_d.recorded.unit,
|
||||||
|
&invoke->args.etsi.AOCDChargingUnit.specific.recorded);
|
||||||
|
subcmd->u.aoc_d.billing_accumulation =
|
||||||
|
invoke->args.etsi.AOCDChargingUnit.specific.type_of_charging_info;
|
||||||
|
subcmd->u.aoc_d.billing_id = aoc_etsi_subcmd_aoc_d_billing_id(
|
||||||
|
invoke->args.etsi.AOCDChargingUnit.specific.billing_id_present,
|
||||||
|
invoke->args.etsi.AOCDChargingUnit.specific.billing_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Encode the ETSI AOCEChargingUnit invoke message.
|
||||||
|
*
|
||||||
|
* \param ctrl D channel controller for diagnostic messages or global options.
|
||||||
|
* \param pos Starting position to encode the facility ie contents.
|
||||||
|
* \param end End of facility ie contents encoding data buffer.
|
||||||
|
* \param chargedunits Number of units charged to encode.
|
||||||
|
*
|
||||||
|
* \retval Start of the next ASN.1 component to encode on success.
|
||||||
|
* \retval NULL on error.
|
||||||
|
*/
|
||||||
|
static unsigned char *enc_etsi_aoce_charging_unit(struct pri *ctrl, unsigned char *pos,
|
||||||
|
unsigned char *end, long chargedunits)
|
||||||
|
{
|
||||||
|
struct rose_msg_invoke msg;
|
||||||
|
|
||||||
|
pos = facility_encode_header(ctrl, pos, end, NULL);
|
||||||
|
if (!pos) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&msg, 0, sizeof(msg));
|
||||||
|
msg.operation = ROSE_ETSI_AOCEChargingUnit;
|
||||||
|
msg.invoke_id = get_invokeid(ctrl);
|
||||||
|
msg.args.etsi.AOCEChargingUnit.type = 1; /* charging_unit */
|
||||||
|
if (chargedunits <= 0) {
|
||||||
|
msg.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 1;
|
||||||
|
} else {
|
||||||
|
msg.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.num_records = 1;
|
||||||
|
msg.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.list[0].
|
||||||
|
number_of_units = chargedunits;
|
||||||
|
}
|
||||||
|
pos = rose_encode_invoke(ctrl, pos, end, &msg);
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Send the ETSI AOCEChargingUnit invoke message.
|
||||||
|
*
|
||||||
|
* \param ctrl D channel controller for diagnostic messages or global options.
|
||||||
|
* \param call Call leg from which to encode AOC.
|
||||||
|
* \param chargedunits Number of units charged to encode.
|
||||||
|
*
|
||||||
|
* \retval 0 on success.
|
||||||
|
* \retval -1 on error.
|
||||||
|
*/
|
||||||
|
static int aoc_aoce_charging_unit_encode(struct pri *ctrl, q931_call *call,
|
||||||
|
long chargedunits)
|
||||||
|
{
|
||||||
|
unsigned char buffer[255];
|
||||||
|
unsigned char *end;
|
||||||
|
|
||||||
|
/* sample data: [ 91 a1 12 02 02 3a 78 02 01 24 30 09 30 07 a1 05 30 03 02 01 01 ] */
|
||||||
|
|
||||||
|
end =
|
||||||
|
enc_etsi_aoce_charging_unit(ctrl, buffer, buffer + sizeof(buffer), chargedunits);
|
||||||
|
if (!end) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remember that if we queue a facility IE for a facility message we
|
||||||
|
* have to explicitly send the facility message ourselves */
|
||||||
|
if (pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL)
|
||||||
|
|| q931_facility(call->pri, call)) {
|
||||||
|
pri_message(ctrl, "Could not schedule facility message for call %d\n", call->cr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Fill in the AOC-E subcmd charging association from the ETSI charging association.
|
||||||
|
*
|
||||||
|
* \param ctrl D channel controller for diagnostic messages or global options.
|
||||||
|
* \param subcmd_association AOC-E subcmd charging association.
|
||||||
|
* \param etsi_association AOC-E ETSI charging association.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
static void aoc_etsi_subcmd_aoc_e_charging_association(struct pri *ctrl, struct pri_aoc_e_charging_association *subcmd_association, const struct roseEtsiAOCChargingAssociation *etsi_association)
|
||||||
|
{
|
||||||
|
struct q931_party_number q931_number;
|
||||||
|
|
||||||
|
switch (etsi_association->type) {
|
||||||
|
case 0:/* charge_identifier */
|
||||||
|
subcmd_association->charging_type = PRI_AOC_E_CHARGING_ASSOCIATION_ID;
|
||||||
|
subcmd_association->charge.id = etsi_association->id;
|
||||||
|
break;
|
||||||
|
case 1:/* charged_number */
|
||||||
|
subcmd_association->charging_type = PRI_AOC_E_CHARGING_ASSOCIATION_NUMBER;
|
||||||
|
q931_party_number_init(&q931_number);
|
||||||
|
rose_copy_number_to_q931(ctrl, &q931_number, &etsi_association->number);
|
||||||
|
q931_party_number_copy_to_pri(&subcmd_association->charge.number, &q931_number);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
subcmd_association->charging_type = PRI_AOC_E_CHARGING_ASSOCIATION_NOT_AVAILABLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Determine the AOC-E subcmd billing_id value.
|
||||||
|
*
|
||||||
|
* \param billing_id_present TRUE if billing_id valid.
|
||||||
|
* \param billing_id ETSI billing id from ROSE.
|
||||||
|
*
|
||||||
|
* \return enum PRI_AOC_E_BILLING_ID value
|
||||||
|
*/
|
||||||
|
static enum PRI_AOC_E_BILLING_ID aoc_etsi_subcmd_aoc_e_billing_id(int billing_id_present, int billing_id)
|
||||||
|
{
|
||||||
|
enum PRI_AOC_E_BILLING_ID value;
|
||||||
|
|
||||||
|
if (billing_id_present) {
|
||||||
|
switch (billing_id) {
|
||||||
|
case 0:/* normalCharging */
|
||||||
|
value = PRI_AOC_E_BILLING_ID_NORMAL;
|
||||||
|
break;
|
||||||
|
case 1:/* reverseCharging */
|
||||||
|
value = PRI_AOC_E_BILLING_ID_REVERSE;
|
||||||
|
break;
|
||||||
|
case 2:/* creditCardCharging */
|
||||||
|
value = PRI_AOC_E_BILLING_ID_CREDIT_CARD;
|
||||||
|
break;
|
||||||
|
case 3:/* callForwardingUnconditional */
|
||||||
|
value = PRI_AOC_E_BILLING_ID_CALL_FORWARDING_UNCONDITIONAL;
|
||||||
|
break;
|
||||||
|
case 4:/* callForwardingBusy */
|
||||||
|
value = PRI_AOC_E_BILLING_ID_CALL_FORWARDING_BUSY;
|
||||||
|
break;
|
||||||
|
case 5:/* callForwardingNoReply */
|
||||||
|
value = PRI_AOC_E_BILLING_ID_CALL_FORWARDING_NO_REPLY;
|
||||||
|
break;
|
||||||
|
case 6:/* callDeflection */
|
||||||
|
value = PRI_AOC_E_BILLING_ID_CALL_DEFLECTION;
|
||||||
|
break;
|
||||||
|
case 7:/* callTransfer */
|
||||||
|
value = PRI_AOC_E_BILLING_ID_CALL_TRANSFER;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
value = PRI_AOC_E_BILLING_ID_NOT_AVAILABLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value = PRI_AOC_E_BILLING_ID_NOT_AVAILABLE;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Handle the ETSI AOCECurrency message.
|
||||||
|
*
|
||||||
|
* \param ctrl D channel controller for diagnostic messages or global options.
|
||||||
|
* \param call Q.931 call leg.
|
||||||
|
* \param invoke Decoded ROSE invoke message contents.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
void aoc_etsi_aoc_e_currency(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke)
|
||||||
|
{
|
||||||
|
struct pri_subcommand *subcmd;
|
||||||
|
|
||||||
|
if (!PRI_MASTER(ctrl)->aoc_support) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
subcmd = q931_alloc_subcommand(ctrl);
|
||||||
|
if (!subcmd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
subcmd->cmd = PRI_SUBCMD_AOC_E;
|
||||||
|
subcmd->u.aoc_e.associated.charging_type =
|
||||||
|
PRI_AOC_E_CHARGING_ASSOCIATION_NOT_AVAILABLE;
|
||||||
|
|
||||||
|
if (!invoke->args.etsi.AOCECurrency.type) {
|
||||||
|
subcmd->u.aoc_e.charge = PRI_AOC_DE_CHARGE_NOT_AVAILABLE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill in charging association if present. */
|
||||||
|
if (invoke->args.etsi.AOCECurrency.currency_info.charging_association_present) {
|
||||||
|
aoc_etsi_subcmd_aoc_e_charging_association(ctrl, &subcmd->u.aoc_e.associated,
|
||||||
|
&invoke->args.etsi.AOCECurrency.currency_info.charging_association);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call was free of charge. */
|
||||||
|
if (invoke->args.etsi.AOCECurrency.currency_info.free_of_charge) {
|
||||||
|
subcmd->u.aoc_e.charge = PRI_AOC_DE_CHARGE_FREE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill in currency cost of call. */
|
||||||
|
subcmd->u.aoc_e.charge = PRI_AOC_DE_CHARGE_CURRENCY;
|
||||||
|
aoc_etsi_subcmd_recorded_currency(&subcmd->u.aoc_e.recorded.money,
|
||||||
|
&invoke->args.etsi.AOCECurrency.currency_info.specific.recorded);
|
||||||
|
subcmd->u.aoc_e.billing_id = aoc_etsi_subcmd_aoc_e_billing_id(
|
||||||
|
invoke->args.etsi.AOCECurrency.currency_info.specific.billing_id_present,
|
||||||
|
invoke->args.etsi.AOCECurrency.currency_info.specific.billing_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Handle the ETSI AOCEChargingUnit message.
|
||||||
|
*
|
||||||
|
* \param ctrl D channel controller for diagnostic messages or global options.
|
||||||
|
* \param call Q.931 call leg.
|
||||||
|
* \param invoke Decoded ROSE invoke message contents.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
void aoc_etsi_aoc_e_charging_unit(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke)
|
||||||
|
{
|
||||||
|
struct pri_subcommand *subcmd;
|
||||||
|
unsigned idx;
|
||||||
|
|
||||||
|
/* Fill in legacy stuff. */
|
||||||
|
call->aoc_units = 0;
|
||||||
|
if (invoke->args.etsi.AOCEChargingUnit.type == 1
|
||||||
|
&& !invoke->args.etsi.AOCEChargingUnit.charging_unit.free_of_charge) {
|
||||||
|
for (idx =
|
||||||
|
invoke->args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.num_records;
|
||||||
|
idx--;) {
|
||||||
|
if (!invoke->args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.
|
||||||
|
list[idx].not_available) {
|
||||||
|
call->aoc_units +=
|
||||||
|
invoke->args.etsi.AOCEChargingUnit.charging_unit.specific.
|
||||||
|
recorded.list[idx].number_of_units;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* the following function is currently not used - just to make the compiler happy */
|
||||||
|
if (0) {
|
||||||
|
/* use this function to forward the aoc-e on a bridged channel */
|
||||||
|
aoc_aoce_charging_unit_encode(ctrl, call, call->aoc_units);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PRI_MASTER(ctrl)->aoc_support) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
subcmd = q931_alloc_subcommand(ctrl);
|
||||||
|
if (!subcmd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
subcmd->cmd = PRI_SUBCMD_AOC_E;
|
||||||
|
subcmd->u.aoc_e.associated.charging_type =
|
||||||
|
PRI_AOC_E_CHARGING_ASSOCIATION_NOT_AVAILABLE;
|
||||||
|
|
||||||
|
if (!invoke->args.etsi.AOCEChargingUnit.type) {
|
||||||
|
subcmd->u.aoc_e.charge = PRI_AOC_DE_CHARGE_NOT_AVAILABLE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill in charging association if present. */
|
||||||
|
if (invoke->args.etsi.AOCEChargingUnit.charging_unit.charging_association_present) {
|
||||||
|
aoc_etsi_subcmd_aoc_e_charging_association(ctrl, &subcmd->u.aoc_e.associated,
|
||||||
|
&invoke->args.etsi.AOCEChargingUnit.charging_unit.charging_association);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call was free of charge. */
|
||||||
|
if (invoke->args.etsi.AOCEChargingUnit.charging_unit.free_of_charge) {
|
||||||
|
subcmd->u.aoc_e.charge = PRI_AOC_DE_CHARGE_FREE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill in unit cost of call. */
|
||||||
|
subcmd->u.aoc_e.charge = PRI_AOC_DE_CHARGE_UNITS;
|
||||||
|
aoc_etsi_subcmd_recorded_units(&subcmd->u.aoc_e.recorded.unit,
|
||||||
|
&invoke->args.etsi.AOCEChargingUnit.charging_unit.specific.recorded);
|
||||||
|
subcmd->u.aoc_e.billing_id = aoc_etsi_subcmd_aoc_e_billing_id(
|
||||||
|
invoke->args.etsi.AOCEChargingUnit.charging_unit.specific.billing_id_present,
|
||||||
|
invoke->args.etsi.AOCEChargingUnit.charging_unit.specific.billing_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pri_aoc_events_enable(struct pri *ctrl, int enable)
|
||||||
|
{
|
||||||
|
if (ctrl) {
|
||||||
|
ctrl = PRI_MASTER(ctrl);
|
||||||
|
ctrl->aoc_support = enable ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------- */
|
||||||
|
/* end pri_aoc.c */
|
105
pri_facility.c
105
pri_facility.c
@ -2700,82 +2700,6 @@ static enum rose_error_code etsi_explicit_ect_execute_transfer(struct pri *ctrl,
|
|||||||
return error_code;
|
return error_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* AOC */
|
|
||||||
/*!
|
|
||||||
* \internal
|
|
||||||
* \brief Encode the ETSI AOCEChargingUnit invoke message.
|
|
||||||
*
|
|
||||||
* \param ctrl D channel controller for diagnostic messages or global options.
|
|
||||||
* \param pos Starting position to encode the facility ie contents.
|
|
||||||
* \param end End of facility ie contents encoding data buffer.
|
|
||||||
* \param chargedunits Number of units charged to encode.
|
|
||||||
*
|
|
||||||
* \retval Start of the next ASN.1 component to encode on success.
|
|
||||||
* \retval NULL on error.
|
|
||||||
*/
|
|
||||||
static unsigned char *enc_etsi_aoce_charging_unit(struct pri *ctrl, unsigned char *pos,
|
|
||||||
unsigned char *end, long chargedunits)
|
|
||||||
{
|
|
||||||
struct rose_msg_invoke msg;
|
|
||||||
|
|
||||||
pos = facility_encode_header(ctrl, pos, end, NULL);
|
|
||||||
if (!pos) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&msg, 0, sizeof(msg));
|
|
||||||
msg.operation = ROSE_ETSI_AOCEChargingUnit;
|
|
||||||
msg.invoke_id = get_invokeid(ctrl);
|
|
||||||
msg.args.etsi.AOCEChargingUnit.type = 1; /* charging_unit */
|
|
||||||
if (chargedunits <= 0) {
|
|
||||||
msg.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 1;
|
|
||||||
} else {
|
|
||||||
msg.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.num_records = 1;
|
|
||||||
msg.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.list[0].
|
|
||||||
number_of_units = chargedunits;
|
|
||||||
}
|
|
||||||
pos = rose_encode_invoke(ctrl, pos, end, &msg);
|
|
||||||
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \internal
|
|
||||||
* \brief Send the ETSI AOCEChargingUnit invoke message.
|
|
||||||
*
|
|
||||||
* \param ctrl D channel controller for diagnostic messages or global options.
|
|
||||||
* \param call Call leg from which to encode AOC.
|
|
||||||
* \param chargedunits Number of units charged to encode.
|
|
||||||
*
|
|
||||||
* \retval 0 on success.
|
|
||||||
* \retval -1 on error.
|
|
||||||
*/
|
|
||||||
static int aoc_aoce_charging_unit_encode(struct pri *ctrl, q931_call *call,
|
|
||||||
long chargedunits)
|
|
||||||
{
|
|
||||||
unsigned char buffer[255];
|
|
||||||
unsigned char *end;
|
|
||||||
|
|
||||||
/* sample data: [ 91 a1 12 02 02 3a 78 02 01 24 30 09 30 07 a1 05 30 03 02 01 01 ] */
|
|
||||||
|
|
||||||
end =
|
|
||||||
enc_etsi_aoce_charging_unit(ctrl, buffer, buffer + sizeof(buffer), chargedunits);
|
|
||||||
if (!end) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remember that if we queue a facility IE for a facility message we
|
|
||||||
* have to explicitly send the facility message ourselves */
|
|
||||||
if (pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL)
|
|
||||||
|| q931_facility(call->pri, call)) {
|
|
||||||
pri_message(ctrl, "Could not schedule facility message for call %d\n", call->cr);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* End AOC */
|
|
||||||
|
|
||||||
/* ===== Call Transfer Supplementary Service (ECMA-178) ===== */
|
/* ===== Call Transfer Supplementary Service (ECMA-178) ===== */
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -4129,40 +4053,23 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie
|
|||||||
case ROSE_ETSI_ChargingRequest:
|
case ROSE_ETSI_ChargingRequest:
|
||||||
/* Ignore messsage */
|
/* Ignore messsage */
|
||||||
break;
|
break;
|
||||||
#if 0 /* Not handled yet */
|
|
||||||
case ROSE_ETSI_AOCSCurrency:
|
case ROSE_ETSI_AOCSCurrency:
|
||||||
|
aoc_etsi_aoc_s_currency(ctrl, invoke);
|
||||||
break;
|
break;
|
||||||
case ROSE_ETSI_AOCSSpecialArr:
|
case ROSE_ETSI_AOCSSpecialArr:
|
||||||
|
aoc_etsi_aoc_s_special_arrangement(ctrl, invoke);
|
||||||
break;
|
break;
|
||||||
case ROSE_ETSI_AOCDCurrency:
|
case ROSE_ETSI_AOCDCurrency:
|
||||||
|
aoc_etsi_aoc_d_currency(ctrl, invoke);
|
||||||
break;
|
break;
|
||||||
case ROSE_ETSI_AOCDChargingUnit:
|
case ROSE_ETSI_AOCDChargingUnit:
|
||||||
|
aoc_etsi_aoc_d_charging_unit(ctrl, invoke);
|
||||||
break;
|
break;
|
||||||
case ROSE_ETSI_AOCECurrency:
|
case ROSE_ETSI_AOCECurrency:
|
||||||
|
aoc_etsi_aoc_e_currency(ctrl, call, invoke);
|
||||||
break;
|
break;
|
||||||
#endif /* Not handled yet */
|
|
||||||
case ROSE_ETSI_AOCEChargingUnit:
|
case ROSE_ETSI_AOCEChargingUnit:
|
||||||
call->aoc_units = 0;
|
aoc_etsi_aoc_e_charging_unit(ctrl, call, invoke);
|
||||||
if (invoke->args.etsi.AOCEChargingUnit.type == 1
|
|
||||||
&& !invoke->args.etsi.AOCEChargingUnit.charging_unit.free_of_charge) {
|
|
||||||
unsigned index;
|
|
||||||
|
|
||||||
for (index =
|
|
||||||
invoke->args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.
|
|
||||||
num_records; index--;) {
|
|
||||||
if (!invoke->args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.
|
|
||||||
list[index].not_available) {
|
|
||||||
call->aoc_units +=
|
|
||||||
invoke->args.etsi.AOCEChargingUnit.charging_unit.specific.
|
|
||||||
recorded.list[index].number_of_units;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* the following function is currently not used - just to make the compiler happy */
|
|
||||||
if (0) {
|
|
||||||
/* use this function to forward the aoc-e on a bridged channel */
|
|
||||||
aoc_aoce_charging_unit_encode(ctrl, call, call->aoc_units);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
#if 0 /* Not handled yet */
|
#if 0 /* Not handled yet */
|
||||||
case ROSE_ITU_IdentificationOfCharge:
|
case ROSE_ITU_IdentificationOfCharge:
|
||||||
|
@ -245,4 +245,11 @@ void pri_cc_qsig_request(struct pri *ctrl, q931_call *call, int msgtype, const s
|
|||||||
void pri_cc_qsig_cancel(struct pri *ctrl, q931_call *call, int msgtype, const struct rose_msg_invoke *invoke);
|
void pri_cc_qsig_cancel(struct pri *ctrl, q931_call *call, int msgtype, const struct rose_msg_invoke *invoke);
|
||||||
void pri_cc_qsig_exec_possible(struct pri *ctrl, q931_call *call, int msgtype, const struct rose_msg_invoke *invoke);
|
void pri_cc_qsig_exec_possible(struct pri *ctrl, q931_call *call, int msgtype, const struct rose_msg_invoke *invoke);
|
||||||
|
|
||||||
|
void aoc_etsi_aoc_s_currency(struct pri *ctrl, const struct rose_msg_invoke *invoke);
|
||||||
|
void aoc_etsi_aoc_s_special_arrangement(struct pri *ctrl, const struct rose_msg_invoke *invoke);
|
||||||
|
void aoc_etsi_aoc_d_currency(struct pri *ctrl, const struct rose_msg_invoke *invoke);
|
||||||
|
void aoc_etsi_aoc_d_charging_unit(struct pri *ctrl, const struct rose_msg_invoke *invoke);
|
||||||
|
void aoc_etsi_aoc_e_currency(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke);
|
||||||
|
void aoc_etsi_aoc_e_charging_unit(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke);
|
||||||
|
|
||||||
#endif /* _PRI_FACILITY_H */
|
#endif /* _PRI_FACILITY_H */
|
||||||
|
@ -107,6 +107,7 @@ struct pri {
|
|||||||
unsigned int hangup_fix_enabled:1;/* TRUE if should follow Q.931 Section 5.3.2 instead of blindly sending RELEASE_COMPLETE for certain causes */
|
unsigned int hangup_fix_enabled:1;/* TRUE if should follow Q.931 Section 5.3.2 instead of blindly sending RELEASE_COMPLETE for certain causes */
|
||||||
unsigned int cc_support:1;/* TRUE if upper layer supports call completion. */
|
unsigned int cc_support:1;/* TRUE if upper layer supports call completion. */
|
||||||
unsigned int transfer_support:1;/* TRUE if the upper layer supports ECT */
|
unsigned int transfer_support:1;/* TRUE if the upper layer supports ECT */
|
||||||
|
unsigned int aoc_support:1;/* TRUE if can send AOC events to the upper layer. */
|
||||||
|
|
||||||
/* MDL variables */
|
/* MDL variables */
|
||||||
int mdl_error;
|
int mdl_error;
|
||||||
|
@ -623,8 +623,13 @@ unsigned char *rose_enc_etsi_AOCSCurrency_ARG(struct pri *ctrl, unsigned char *p
|
|||||||
ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL));
|
ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL));
|
||||||
break;
|
break;
|
||||||
case 1: /* currency_info_list */
|
case 1: /* currency_info_list */
|
||||||
|
if (args->etsi.AOCSCurrency.currency_info.num_records) {
|
||||||
ASN1_CALL(pos, rose_enc_etsi_AOCSCurrencyInfoList(ctrl, pos, end,
|
ASN1_CALL(pos, rose_enc_etsi_AOCSCurrencyInfoList(ctrl, pos, end,
|
||||||
ASN1_TAG_SEQUENCE, &args->etsi.AOCSCurrency.currency_info));
|
ASN1_TAG_SEQUENCE, &args->etsi.AOCSCurrency.currency_info));
|
||||||
|
} else {
|
||||||
|
/* There were no records so encode as charge_not_available */
|
||||||
|
ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ASN1_ENC_ERROR(ctrl, "Unknown AOCSCurrency type");
|
ASN1_ENC_ERROR(ctrl, "Unknown AOCSCurrency type");
|
||||||
|
Loading…
Reference in New Issue
Block a user