[libpri-commits] dvossel: branch 1.4 r1776 - /branches/1.4/

SVN commits to the libpri project libpri-commits at lists.digium.com
Fri May 28 17:34:26 CDT 2010


Author: dvossel
Date: Fri May 28 17:34:24 2010
New Revision: 1776

URL: http://svnview.digium.com/svn/libpri?view=rev&rev=1776
Log:
support for sending ETSI advice of charge

Review: https://reviewboard.asterisk.org/r/619/

Modified:
    branches/1.4/libpri.h
    branches/1.4/pri_aoc.c
    branches/1.4/pri_facility.c
    branches/1.4/pri_facility.h
    branches/1.4/pri_internal.h
    branches/1.4/q931.c

Modified: branches/1.4/libpri.h
URL: http://svnview.digium.com/svn/libpri/branches/1.4/libpri.h?view=diff&rev=1776&r1=1775&r2=1776
==============================================================================
--- branches/1.4/libpri.h (original)
+++ branches/1.4/libpri.h Fri May 28 17:34:24 2010
@@ -543,8 +543,8 @@
 #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 */
+#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 */
 #define PRI_SUBCMD_MCID_REQ					23	/*!< Malicious Call ID Request */
 #define PRI_SUBCMD_MCID_RSP					24	/*!< Malicious Call ID Request response */
 
@@ -875,6 +875,64 @@
 	} recorded;
 	/*! Charging association. */
 	struct pri_aoc_e_charging_association associated;
+};
+
+enum PRI_AOC_REQ_RSP {
+	/* Error Results */
+	PRI_AOC_REQ_RSP_ERROR_NOT_IMPLEMENTED,
+	PRI_AOC_REQ_RSP_ERROR_NOT_AVAILABLE,
+	PRI_AOC_REQ_RSP_ERROR_TIMEOUT,
+	PRI_AOC_REQ_RSP_ERROR_REJECT,
+	/* generic error result all other errors are lumped into */
+	PRI_AOC_REQ_RSP_ERROR,
+
+	/* AOC Results */
+	PRI_AOC_REQ_RSP_CHARGING_INFO_FOLLOWS,
+	PRI_AOC_REQ_RSP_CURRENCY_INFO_LIST,
+	PRI_AOC_REQ_RSP_SPECIAL_ARR,
+};
+
+enum PRI_AOC_REQUEST {
+	PRI_AOC_REQUEST_S = (1 << 0),
+	PRI_AOC_REQUEST_D = (1 << 1),
+	PRI_AOC_REQUEST_E = (1 << 2),
+};
+
+struct pri_subcmd_aoc_request_response {
+	/*!
+	 * \brief aoc_s data from response
+	 */
+	struct pri_subcmd_aoc_s aoc_s;
+
+	/*!
+	 * \brief if the aoc_s msg is present, this will be set
+	 */
+	int valid_aoc_s;
+
+	/*!
+	 * \brief What type of aoc was requested.
+	 * \see enum PRI_AOC_REQUEST
+	 */
+	int charging_request;
+
+	/*!
+	 * \brief response to the charging_request
+	 * \see enum PRI_AOC_REQ_RSP
+	 */
+	int charging_response;
+};
+
+struct pri_subcmd_aoc_request {
+	/*!
+	 * \brief What types of aoc are being requested.
+	 * \see enum PRI_AOC_REQUEST
+	 */
+	int charging_request;
+
+	/*!
+	 * \brief Value given by the initiating request.
+	 */
+	 int invoke_id;
 };
 
 struct pri_subcmd_mcid_req {
@@ -934,6 +992,8 @@
 		struct pri_subcmd_cc_id cc_call;
 		struct pri_subcmd_cc_cancel cc_cancel;
 		struct pri_subcmd_transfer transfer;
+		struct pri_subcmd_aoc_request aoc_request;
+		struct pri_subcmd_aoc_request_response aoc_request_response;
 		struct pri_subcmd_aoc_s aoc_s;
 		struct pri_subcmd_aoc_d aoc_d;
 		struct pri_subcmd_aoc_e aoc_e;
@@ -1587,6 +1647,79 @@
    (and maybe some timers) */
 void pri_enslave(struct pri *master, struct pri *slave);
 
+/*!
+ * \brief  Request AOC on call setup
+ *
+ * \param call setup struct to set charging request info on
+ * \param charging request to set on setup struct
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int pri_sr_set_aoc_charging_request(struct pri_sr *sr, int charging_request);
+
+/*!
+ * \brief Send AOC Request Response to a request for AOC-S
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param invoke_id for response message
+ * \param aoc_s message for response
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int pri_aoc_s_request_response_send(struct pri *ctrl, q931_call *call, int invoke_id, const struct pri_subcmd_aoc_s *aoc_s);
+
+/*!
+ * \brief Send AOC Request Response to a request for AOC-D or AOC-E
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param response in form of enum PRI_AOC_REQ_RSP
+ * \param invoke_id for response message
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int pri_aoc_de_request_response_send(struct pri *ctrl, q931_call *call, int response, int invoke_id);
+
+/*!
+ * \brief Send AOC-S message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param aoc_s message to send
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int pri_aoc_s_send(struct pri *ctrl, q931_call *c, const struct pri_subcmd_aoc_s *aoc_s);
+
+/*!
+ * \brief Send AOC-D message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param aoc_d message to send
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int pri_aoc_d_send(struct pri *ctrl, q931_call *c, const struct pri_subcmd_aoc_d *aoc_d);
+
+/*!
+ * \brief Send AOC-E message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param aoc_e message to send
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int pri_aoc_e_send(struct pri *ctrl, q931_call *c, const struct pri_subcmd_aoc_e *aoc_e);
+
 #define PRI_GR303_SUPPORT
 #define PRI_ENSLAVE_SUPPORT
 #define PRI_SETUP_CALL

Modified: branches/1.4/pri_aoc.c
URL: http://svnview.digium.com/svn/libpri/branches/1.4/pri_aoc.c?view=diff&rev=1776&r1=1775&r2=1776
==============================================================================
--- branches/1.4/pri_aoc.c (original)
+++ branches/1.4/pri_aoc.c Fri May 28 17:34:24 2010
@@ -4,6 +4,7 @@
  * Copyright (C) 2010 Digium, Inc.
  *
  * Richard Mudgett <rmudgett at digium.com>
+ * David Vossel <dvossel at digium.com>
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk project. Please do not directly contact
@@ -57,6 +58,21 @@
 
 /*!
  * \internal
+ * \brief Fill in the ETSI amount from the AOC subcmd amount.
+ *
+ * \param subcmd_amount AOC subcmd amount.
+ * \param etsi_amount AOC ETSI amount.
+ *
+ * \return Nothing
+ */
+static void aoc_enc_etsi_subcmd_amount(const struct pri_aoc_amount *subcmd_amount, struct roseEtsiAOCAmount *etsi_amount)
+{
+	etsi_amount->currency = subcmd_amount->cost;
+	etsi_amount->multiplier = subcmd_amount->multiplier;
+}
+
+/*!
+ * \internal
  * \brief Fill in the AOC subcmd time from the ETSI time.
  *
  * \param subcmd_time AOC subcmd time.
@@ -68,6 +84,21 @@
 {
 	subcmd_time->length = etsi_time->length;
 	subcmd_time->scale = etsi_time->scale;
+}
+
+/*!
+ * \internal
+ * \brief Fill in the ETSI Time from the AOC subcmd time.
+ *
+ * \param subcmd_time AOC subcmd time.
+ * \param etsi_time AOC ETSI time.
+ *
+ * \return Nothing
+ */
+static void aoc_enc_etsi_subcmd_time(const struct pri_aoc_time *subcmd_time, struct roseEtsiAOCTime *etsi_time)
+{
+	etsi_time->length = subcmd_time->length;
+	etsi_time->scale = subcmd_time->scale;
 }
 
 /*!
@@ -84,6 +115,23 @@
 	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 the ETSI recorded currency from the subcmd currency info
+ *
+ * \param subcmd_recorded AOC subcmd recorded currency.
+ * \param etsi_recorded AOC ETSI recorded currency.
+ *
+ * \return Nothing
+ */
+static void aoc_enc_etsi_subcmd_recorded_currency(const struct pri_aoc_recorded_currency *subcmd_recorded, struct roseEtsiAOCRecordedCurrency *etsi_recorded)
+{
+	aoc_enc_etsi_subcmd_amount(&subcmd_recorded->amount, &etsi_recorded->amount);
+	libpri_copy_string((char *) etsi_recorded->currency,
+			subcmd_recorded->currency,
+			sizeof(etsi_recorded->currency));
 }
 
 /*!
@@ -114,6 +162,84 @@
 		}
 	}
 	subcmd_recorded->num_items = idx;
+}
+
+/*!
+ * \internal
+ * \brief Fill in the ETSI recorded units from the AOC subcmd recorded units.
+ *
+ * \param subcmd_recorded AOC subcmd recorded units list.
+ * \param etsi_recorded AOC ETSI recorded units list.
+ *
+ * \return Nothing
+ */
+static void aoc_enc_etsi_subcmd_recorded_units(const struct pri_aoc_recorded_units *subcmd_recorded, struct roseEtsiAOCRecordedUnitsList *etsi_recorded)
+{
+	int i;
+
+	/* Fill in the itemized list of recorded units. */
+	for (i = 0; i < subcmd_recorded->num_items; i++) {
+		if (subcmd_recorded->item[i].number >= 0) {
+			etsi_recorded->list[i].number_of_units = subcmd_recorded->item[i].number;
+		} else {
+			etsi_recorded->list[i].not_available = 1;
+		}
+		if (subcmd_recorded->item[i].type > 0) {
+			etsi_recorded->list[i].type_of_unit = subcmd_recorded->item[i].type;
+			etsi_recorded->list[i].type_of_unit_present = 1;
+		}
+	}
+	etsi_recorded->num_records = i;
+
+	if (!etsi_recorded->num_records) {
+		etsi_recorded->list[0].not_available = 1;
+		etsi_recorded->list[0].type_of_unit_present = 0;
+		etsi_recorded->num_records = 1;
+	}
+}
+
+/*!
+ * \brief Handle the ETSI ChargingRequest.
+ *
+ * \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_request(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke)
+{
+	struct pri_subcommand *subcmd;
+	int request;
+
+	if (!PRI_MASTER(ctrl)->aoc_support) {
+		send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_NotSubscribed);
+		return;
+	}
+	switch (invoke->args.etsi.ChargingRequest.charging_case) {
+	case 0:/* chargingInformationAtCallSetup */
+		request = PRI_AOC_REQUEST_S;
+		break;
+	case 1:/* chargingDuringACall */
+		request = PRI_AOC_REQUEST_D;
+		break;
+	case 2:/* chargingAtTheEndOfACall */
+		request = PRI_AOC_REQUEST_E;
+		break;
+	default:
+		send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_NotImplemented);
+		return;
+	}
+
+	subcmd = q931_alloc_subcommand(ctrl);
+	if (!subcmd) {
+		send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_NotAvailable);
+		return;
+	}
+
+	subcmd->cmd = PRI_SUBCMD_AOC_CHARGING_REQ;
+	subcmd->u.aoc_request.invoke_id = invoke->invoke_id;
+	subcmd->u.aoc_request.charging_request = request;
 }
 
 /*!
@@ -209,6 +335,99 @@
 }
 
 /*!
+ * \internal
+ * \brief Fill in the currency info list of chargeable items from a aoc_s subcmd
+ *
+ * \param aoc_s AOC-S info list of chargeable items.
+ * \param info ETSI info list of chargeable items.
+ *
+ * \return Nothing
+ */
+static void enc_etsi_subcmd_aoc_s_currency_info(const struct pri_subcmd_aoc_s *aoc_s, struct roseEtsiAOCSCurrencyInfoList *info)
+{
+	int idx;
+
+	for (idx = 0; idx < aoc_s->num_items && idx < ARRAY_LEN(info->list); ++idx) {
+		/* What is being charged. */
+		switch (aoc_s->item[idx].chargeable) {
+		default:
+		case PRI_AOC_CHARGED_ITEM_BASIC_COMMUNICATION:
+			info->list[idx].charged_item = 0;/* basicCommunication */
+			break;
+		case PRI_AOC_CHARGED_ITEM_CALL_ATTEMPT:
+			info->list[idx].charged_item = 1;/* callAttempt */
+			break;
+		case PRI_AOC_CHARGED_ITEM_CALL_SETUP:
+			info->list[idx].charged_item = 2;/* callSetup */
+			break;
+		case PRI_AOC_CHARGED_ITEM_USER_USER_INFO:
+			info->list[idx].charged_item = 3;/* userToUserInfo */
+			break;
+		case PRI_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE:
+			info->list[idx].charged_item = 4;/* operationOfSupplementaryServ */
+			break;
+		}
+
+		/* Rate method being used. */
+		switch (aoc_s->item[idx].rate_type) {
+		case PRI_AOC_RATE_TYPE_SPECIAL_CODE:
+			info->list[idx].currency_type = 0;/* specialChargingCode */
+			info->list[idx].u.special_charging_code = aoc_s->item[idx].rate.special;
+			break;
+		case PRI_AOC_RATE_TYPE_DURATION:
+			info->list[idx].currency_type = 1;/* durationCurrency */
+			aoc_enc_etsi_subcmd_amount(&aoc_s->item[idx].rate.duration.amount,
+				&info->list[idx].u.duration.amount);
+			aoc_enc_etsi_subcmd_time(&aoc_s->item[idx].rate.duration.time,
+				&info->list[idx].u.duration.time);
+			if (aoc_s->item[idx].rate.duration.granularity.length) {
+				info->list[idx].u.duration.granularity_present = 1;
+				aoc_enc_etsi_subcmd_time(&aoc_s->item[idx].rate.duration.granularity,
+					&info->list[idx].u.duration.granularity);
+			} else {
+				info->list[idx].u.duration.granularity_present = 0;
+			}
+			info->list[idx].u.duration.charging_type = aoc_s->item[idx].rate.duration.charging_type;
+			libpri_copy_string((char *) info->list[idx].u.duration.currency,
+				aoc_s->item[idx].rate.duration.currency,
+				sizeof((char *) info->list[idx].u.duration.currency));
+			break;
+		case PRI_AOC_RATE_TYPE_FLAT:
+			info->list[idx].currency_type = 2;/* flatRateCurrency */
+			aoc_enc_etsi_subcmd_amount(&aoc_s->item[idx].rate.flat.amount,
+				&info->list[idx].u.flat_rate.amount);
+			libpri_copy_string((char *) info->list[idx].u.flat_rate.currency,
+				aoc_s->item[idx].rate.flat.currency,
+				sizeof((char *) info->list[idx].u.flat_rate.currency));
+			break;
+		case PRI_AOC_RATE_TYPE_VOLUME:
+			info->list[idx].currency_type = 3;/* volumeRateCurrency */
+			aoc_enc_etsi_subcmd_amount(&aoc_s->item[idx].rate.volume.amount,
+				&info->list[idx].u.volume_rate.amount);
+			info->list[idx].u.volume_rate.unit = aoc_s->item[idx].rate.volume.unit;
+			libpri_copy_string((char *) info->list[idx].u.volume_rate.currency,
+				aoc_s->item[idx].rate.volume.currency,
+				sizeof((char *) info->list[idx].u.volume_rate.currency));
+			break;
+		case PRI_AOC_RATE_TYPE_FREE:
+			info->list[idx].currency_type = 4;/* freeOfCharge */
+			break;
+		default:
+		case PRI_AOC_RATE_TYPE_NOT_AVAILABLE:
+			info->list[idx].currency_type = 5;/* currencyInfoNotAvailable */
+			break;
+		}
+	}
+	if (!idx) {
+		/* We cannot send an empty list so create a dummy list element. */
+		info->list[idx].charged_item = 0;/* basicCommunication */
+		info->list[idx].currency_type = 5;/* currencyInfoNotAvailable */
+		++idx;
+	}
+	info->num_records = idx;
+}
+
+/*!
  * \brief Handle the ETSI AOCSCurrency message.
  *
  * \param ctrl D channel controller for diagnostic messages or global options.
@@ -390,80 +609,6 @@
 
 /*!
  * \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.
@@ -540,6 +685,66 @@
 		value = PRI_AOC_E_BILLING_ID_NOT_AVAILABLE;
 	}
 	return value;
+}
+
+/*!
+ * \internal
+ * \brief Determine the ETSI AOC-E billing_id value from the subcmd.
+ *
+ * \param billing_id from upper layer.
+ *
+ * \retval -1 failure
+ * \retval etsi billing id
+ */
+static int aoc_subcmd_aoc_e_etsi_billing_id(enum PRI_AOC_E_BILLING_ID billing_id)
+{
+	switch (billing_id) {
+	case PRI_AOC_E_BILLING_ID_NORMAL:
+		return 0;/* normalCharging */
+	case PRI_AOC_E_BILLING_ID_REVERSE:
+		return 1;/* reverseCharging */
+	case PRI_AOC_E_BILLING_ID_CREDIT_CARD:
+		return 2;/* creditCardCharging */
+	case PRI_AOC_E_BILLING_ID_CALL_FORWARDING_UNCONDITIONAL:
+		return 3;/* callForwardingUnconditional */
+	case PRI_AOC_E_BILLING_ID_CALL_FORWARDING_BUSY:
+		return 4;/* callForwardingBusy */
+	case PRI_AOC_E_BILLING_ID_CALL_FORWARDING_NO_REPLY:
+		return 5;/* callForwardingNoReply */
+	case PRI_AOC_E_BILLING_ID_CALL_DEFLECTION:
+		return 6;/* callDeflection */
+	case PRI_AOC_E_BILLING_ID_CALL_TRANSFER:
+		return 7;/* callTransfer */
+	case PRI_AOC_E_BILLING_ID_NOT_AVAILABLE:
+		break;
+	}
+
+	return -1;
+}
+
+/*!
+ * \internal
+ * \brief Determine the ETSI AOC-D billing_id value from the subcmd.
+ *
+ * \param billing_id from upper layer.
+ *
+ * \retval -1 failure
+ * \retval etsi billing id
+ */
+static int aoc_subcmd_aoc_d_etsi_billing_id(enum PRI_AOC_D_BILLING_ID billing_id)
+{
+	switch (billing_id) {
+	case PRI_AOC_D_BILLING_ID_NORMAL:
+		return 0;/* normalCharging */
+	case PRI_AOC_D_BILLING_ID_REVERSE:
+		return 1;/* reverseCharging */
+	case PRI_AOC_D_BILLING_ID_CREDIT_CARD:
+		return 2;/* creditCardCharging */
+	case PRI_AOC_D_BILLING_ID_NOT_AVAILABLE:
+		break;
+	}
+
+	return -1;
 }
 
 /*!
@@ -622,11 +827,6 @@
 			}
 		}
 	}
-	/* 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;
@@ -674,5 +874,892 @@
 	}
 }
 
+/*!
+ * \internal
+ * \brief Encode the ETSI AOCECurrency 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 aoc_e the AOC-E data to encode.
+ *
+ * \retval Start of the next ASN.1 component to encode on success.
+ * \retval NULL on error.
+ */
+static unsigned char *enc_etsi_aoce_currency(struct pri *ctrl, unsigned char *pos,
+	unsigned char *end, const struct pri_subcmd_aoc_e *aoc_e)
+{
+	struct rose_msg_invoke msg;
+	struct q931_party_number q931_number;
+
+	pos = facility_encode_header(ctrl, pos, end, NULL);
+	if (!pos) {
+		return NULL;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	msg.operation = ROSE_ETSI_AOCECurrency;
+	msg.invoke_id = get_invokeid(ctrl);
+
+	if (aoc_e->charge == PRI_AOC_DE_CHARGE_FREE) {
+		msg.args.etsi.AOCECurrency.type = 1;	/* currency_info */
+		msg.args.etsi.AOCECurrency.currency_info.free_of_charge = 1;
+	} else if ((aoc_e->charge == PRI_AOC_DE_CHARGE_CURRENCY) && (aoc_e->recorded.money.amount.cost >= 0)) {
+		msg.args.etsi.AOCECurrency.type = 1;	/* currency_info */
+		aoc_enc_etsi_subcmd_recorded_currency(&aoc_e->recorded.money,
+			&msg.args.etsi.AOCECurrency.currency_info.specific.recorded);
+	} else {
+		msg.args.etsi.AOCECurrency.type = 0;	/* charge_not_available */
+	}
+
+	if (aoc_subcmd_aoc_e_etsi_billing_id(aoc_e->billing_id) != -1) {
+		msg.args.etsi.AOCECurrency.currency_info.specific.billing_id_present = 1;
+		msg.args.etsi.AOCECurrency.currency_info.specific.billing_id =
+			aoc_subcmd_aoc_e_etsi_billing_id(aoc_e->billing_id);
+	}
+
+	switch (aoc_e->associated.charging_type) {
+	case PRI_AOC_E_CHARGING_ASSOCIATION_NUMBER:
+		msg.args.etsi.AOCECurrency.currency_info.charging_association_present = 1;
+		msg.args.etsi.AOCECurrency.currency_info.charging_association.type = 1; /* charged_number */
+		pri_copy_party_number_to_q931(&q931_number, &aoc_e->associated.charge.number);
+		q931_copy_number_to_rose(ctrl,
+			&msg.args.etsi.AOCECurrency.currency_info.charging_association.number,
+			&q931_number);
+		break;
+	case PRI_AOC_E_CHARGING_ASSOCIATION_ID:
+		msg.args.etsi.AOCECurrency.currency_info.charging_association_present = 1;
+		msg.args.etsi.AOCECurrency.currency_info.charging_association.type = 0; /* charge_identifier */
+		msg.args.etsi.AOCECurrency.currency_info.charging_association.id =
+			aoc_e->associated.charge.id;
+		break;
+	default:
+		/* do nothing */
+		break;
+	}
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \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 aoc_e the AOC-E data 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, const struct pri_subcmd_aoc_e *aoc_e)
+{
+	struct rose_msg_invoke msg;
+	struct q931_party_number q931_number;
+
+	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);
+
+	if (aoc_e->charge == PRI_AOC_DE_CHARGE_FREE) {
+		msg.args.etsi.AOCEChargingUnit.type = 1;	/* charging_unit */
+		msg.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 1;
+
+	} else if ((aoc_e->charge == PRI_AOC_DE_CHARGE_UNITS) &&  (aoc_e->recorded.unit.num_items > 0)) {
+		msg.args.etsi.AOCEChargingUnit.type = 1;	/* charging_unit */
+		aoc_enc_etsi_subcmd_recorded_units(&aoc_e->recorded.unit,
+			&msg.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded);
+	} else {
+		msg.args.etsi.AOCEChargingUnit.type = 0;	/* charge_not_available */
+	}
+
+	if (aoc_subcmd_aoc_e_etsi_billing_id(aoc_e->billing_id) != -1) {
+		msg.args.etsi.AOCEChargingUnit.charging_unit.specific.billing_id_present = 1;
+		msg.args.etsi.AOCEChargingUnit.charging_unit.specific.billing_id =
+			aoc_subcmd_aoc_e_etsi_billing_id(aoc_e->billing_id);
+	}
+
+	switch (aoc_e->associated.charging_type) {
+	case PRI_AOC_E_CHARGING_ASSOCIATION_NUMBER:
+		msg.args.etsi.AOCEChargingUnit.charging_unit.charging_association_present = 1;
+		msg.args.etsi.AOCEChargingUnit.charging_unit.charging_association.type = 1; /* charged_number */
+		pri_copy_party_number_to_q931(&q931_number, &aoc_e->associated.charge.number);
+		q931_copy_number_to_rose(ctrl,
+			&msg.args.etsi.AOCEChargingUnit.charging_unit.charging_association.number,
+			&q931_number);
+		break;
+	case PRI_AOC_E_CHARGING_ASSOCIATION_ID:
+		msg.args.etsi.AOCEChargingUnit.charging_unit.charging_association_present = 1;
+		msg.args.etsi.AOCEChargingUnit.charging_unit.charging_association.type = 0; /* charge_identifier */
+		msg.args.etsi.AOCEChargingUnit.charging_unit.charging_association.id =
+			aoc_e->associated.charge.id;
+		break;
+	default:
+		/* do nothing */
+		break;
+	}
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode the ETSI AOCDChargingUnit 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 aoc_d the AOC-D data to encode.
+ *
+ * \retval Start of the next ASN.1 component to encode on success.
+ * \retval NULL on error.
+ */
+static unsigned char *enc_etsi_aocd_charging_unit(struct pri *ctrl, unsigned char *pos,
+	unsigned char *end, const struct pri_subcmd_aoc_d *aoc_d)
+{
+	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_AOCDChargingUnit;
+	msg.invoke_id = get_invokeid(ctrl);
+
+	if (aoc_d->charge == PRI_AOC_DE_CHARGE_FREE) {
+		msg.args.etsi.AOCDChargingUnit.type = 1;	/* free_of_charge */
+	} else if ((aoc_d->charge == PRI_AOC_DE_CHARGE_UNITS) &&  (aoc_d->recorded.unit.num_items > 0)) {
+		msg.args.etsi.AOCDChargingUnit.type = 2;	/* specific_charging_units */
+		aoc_enc_etsi_subcmd_recorded_units(&aoc_d->recorded.unit,
+			&msg.args.etsi.AOCDChargingUnit.specific.recorded);
+	} else {
+		msg.args.etsi.AOCDChargingUnit.type = 0;	/* charge_not_available */
+	}
+
+	if (aoc_subcmd_aoc_d_etsi_billing_id(aoc_d->billing_id) != -1) {
+		msg.args.etsi.AOCDChargingUnit.specific.billing_id_present = 1;
+		msg.args.etsi.AOCDChargingUnit.specific.billing_id =
+			aoc_subcmd_aoc_d_etsi_billing_id(aoc_d->billing_id);
+	}
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode the ETSI AOCDCurrency 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 aoc_d the AOC-D data to encode.
+ *
+ * \retval Start of the next ASN.1 component to encode on success.
+ * \retval NULL on error.
+ */
+static unsigned char *enc_etsi_aocd_currency(struct pri *ctrl, unsigned char *pos,
+	unsigned char *end, const struct pri_subcmd_aoc_d *aoc_d)
+{
+	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_AOCDCurrency;
+	msg.invoke_id = get_invokeid(ctrl);
+
+	if (aoc_d->charge == PRI_AOC_DE_CHARGE_FREE) {
+		msg.args.etsi.AOCDCurrency.type = 1;	/* free_of_charge */
+	} else if ((aoc_d->charge == PRI_AOC_DE_CHARGE_CURRENCY) && (aoc_d->recorded.money.amount.cost >= 0)) {
+		msg.args.etsi.AOCDCurrency.type = 2;	/* specific_currency */
+		aoc_enc_etsi_subcmd_recorded_currency(&aoc_d->recorded.money,
+			&msg.args.etsi.AOCDCurrency.specific.recorded);
+	} else {
+		msg.args.etsi.AOCDCurrency.type = 0;	/* charge_not_available */
+	}
+
+	if (aoc_subcmd_aoc_d_etsi_billing_id(aoc_d->billing_id) != -1) {
+		msg.args.etsi.AOCDCurrency.specific.billing_id_present = 1;
+		msg.args.etsi.AOCDCurrency.specific.billing_id =
+			aoc_subcmd_aoc_d_etsi_billing_id(aoc_d->billing_id);
+	}
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode the ETSI AOCSSpecialArr 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 aoc_s the AOC-S data to encode.
+ *
+ * \retval Start of the next ASN.1 component to encode on success.
+ * \retval NULL on error.
+ */
+static unsigned char *enc_etsi_aocs_special_arrangement(struct pri *ctrl, unsigned char *pos,
+	unsigned char *end, const struct pri_subcmd_aoc_s *aoc_s)
+{
+	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_AOCSSpecialArr;
+	msg.invoke_id = get_invokeid(ctrl);
+
+	if (!aoc_s->num_items || (aoc_s->item[0].rate_type != PRI_AOC_RATE_TYPE_SPECIAL_CODE)) {
+		msg.args.etsi.AOCSSpecialArr.type = 0;/* charge_not_available */
+	} else {
+		msg.args.etsi.AOCSSpecialArr.type = 1;/* special_arrangement_info */
+		msg.args.etsi.AOCSSpecialArr.special_arrangement = aoc_s->item[0].rate.special;
+	}
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode the ETSI AOCSCurrency 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 aoc_s the AOC-S data to encode.
+ *
+ * \retval Start of the next ASN.1 component to encode on success.
+ * \retval NULL on error.
+ */
+static unsigned char *enc_etsi_aocs_currency(struct pri *ctrl, unsigned char *pos,
+	unsigned char *end, const struct pri_subcmd_aoc_s *aoc_s)
+{
+	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_AOCSCurrency;
+	msg.invoke_id = get_invokeid(ctrl);
+
+	if (aoc_s->num_items) {
+		msg.args.etsi.AOCSCurrency.type = 1; /* currency_info_list */
+		enc_etsi_subcmd_aoc_s_currency_info(aoc_s, &msg.args.etsi.AOCSCurrency.currency_info);
+	} else {
+		msg.args.etsi.AOCSCurrency.type = 0; /* charge_not_available */
+	}
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode the ETSI ChargingRequest Response 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 response the response to the request
+ * \param invoke_id the request's invoke id
+ * \param aoc_s the rate list associated with a response to AOC-S request
+ *    Could be NULL.
+ *
+ * \retval Start of the next ASN.1 component to encode on success.
+ * \retval NULL on error.
+ */
+static unsigned char *enc_etsi_aoc_request_response(struct pri *ctrl, unsigned char *pos,
+	unsigned char *end, enum PRI_AOC_REQ_RSP response, int invoke_id, const struct pri_subcmd_aoc_s *aoc_s)
+{
+	struct rose_msg_result msg_result = { 0, };
+	struct rose_msg_error msg_error = { 0, };
+	int is_error = 0;
+
+	pos = facility_encode_header(ctrl, pos, end, NULL);
+	if (!pos) {
+		return NULL;
+	}
+
+	switch (response) {
+	case PRI_AOC_REQ_RSP_CURRENCY_INFO_LIST:
+		if (!aoc_s) {
+			return NULL;
+		}
+		enc_etsi_subcmd_aoc_s_currency_info(aoc_s, &msg_result.args.etsi.ChargingRequest.u.currency_info);
+		msg_result.args.etsi.ChargingRequest.type = 0;/* currency_info_list */
+		break;
+	case PRI_AOC_REQ_RSP_SPECIAL_ARR:
+		if (!aoc_s) {
+			return NULL;
+		}
+		msg_result.args.etsi.ChargingRequest.type = 1;/* special_arrangement_info */
+		msg_result.args.etsi.ChargingRequest.u.special_arrangement = aoc_s->item[0].rate.special;
+		break;
+	case PRI_AOC_REQ_RSP_CHARGING_INFO_FOLLOWS:
+		msg_result.args.etsi.ChargingRequest.type = 2;/* charging_info_follows */
+		break;
+	case PRI_AOC_REQ_RSP_ERROR_NOT_IMPLEMENTED:
+		msg_error.code = ROSE_ERROR_Gen_NotImplemented;
+		is_error = 1;
+		break;
+	default:
+	case PRI_AOC_REQ_RSP_ERROR_NOT_AVAILABLE:
+		is_error = 1;
+		msg_error.code = ROSE_ERROR_Gen_NotAvailable;
+		break;
+	}
+
+	if (is_error) {
+		msg_error.invoke_id = invoke_id;
+		pos = rose_encode_error(ctrl, pos, end, &msg_error);
+	} else {
+		msg_result.operation = ROSE_ETSI_ChargingRequest;
+		msg_result.invoke_id = invoke_id;
+		pos = rose_encode_result(ctrl, pos, end, &msg_result);
+	}
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode the ETSI ChargingRequest 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 aoc_request the aoc charging request data to encode.
+ *
+ * \retval Start of the next ASN.1 component to encode on success.
+ * \retval NULL on error.
+ */
+static unsigned char *enc_etsi_aoc_request(struct pri *ctrl, unsigned char *pos,
+	unsigned char *end, enum PRI_AOC_REQUEST request)
+{
+	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_ChargingRequest;
+	msg.invoke_id = get_invokeid(ctrl);
+
+	switch (request) {
+	case PRI_AOC_REQUEST_S:
+		msg.args.etsi.ChargingRequest.charging_case = 0;/* chargingInformationAtCallSetup */
+		break;
+	case PRI_AOC_REQUEST_D:
+		msg.args.etsi.ChargingRequest.charging_case = 1;/* chargingDuringACall */
+		break;
+	case PRI_AOC_REQUEST_E:
+		msg.args.etsi.ChargingRequest.charging_case = 2;/* chargingAtTheEndOfACall */
+		break;
+	default:
+		/* no valid request parameters are present */
+		return NULL;
+	}
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Send the ETSI AOC Request Response message for an AOC-S request
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode AOC.
+ * \param invoke_id the request's invoke id
+ * \param aoc_s Optional AOC-S rate list for response
+ *
+ * \note if aoc_s is NULL, then a response will be sent back as AOC-S not available.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int aoc_s_request_response_encode(struct pri *ctrl, q931_call *call, int invoke_id, const struct pri_subcmd_aoc_s *aoc_s)
+{
+	unsigned char buffer[255];
+	unsigned char *end = NULL;
+	int response;
+
+	if (!aoc_s) {
+		response = PRI_AOC_REQ_RSP_ERROR_NOT_AVAILABLE;
+	} else if (aoc_s->num_items
+		&& aoc_s->item[0].chargeable == PRI_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT) {
+		response = PRI_AOC_REQ_RSP_SPECIAL_ARR;
+	} else {
+		response = PRI_AOC_REQ_RSP_CURRENCY_INFO_LIST;
+	}
+
+	end = enc_etsi_aoc_request_response(ctrl, buffer, buffer + sizeof(buffer), response, invoke_id, aoc_s);
+	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 aoc request response facility message for call %d\n", call->cr);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Send the ETSI AOC Request Response message for AOC-D and AOC-E requests
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode AOC.
+ * \param response the response to the request
+ * \param invoke_id the request's invoke id
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int aoc_de_request_response_encode(struct pri *ctrl, q931_call *call, enum PRI_AOC_REQ_RSP response, int invoke_id)
+{
+	unsigned char buffer[255];
+	unsigned char *end = NULL;
+
+	end = enc_etsi_aoc_request_response(ctrl, buffer, buffer + sizeof(buffer), response, invoke_id, NULL);
+	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 aoc request response facility message for call %d\n", call->cr);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief AOC-Request response callback function.
+ *
+ * \param reason Reason callback is called.
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param apdu APDU queued entry.  Do not change!
+ * \param msg APDU response message data.  (NULL if was not the reason called.)
+ *
+ * \return TRUE if no more responses are expected.
+ */
+static int pri_aoc_request_get_response(enum APDU_CALLBACK_REASON reason, struct pri *ctrl, struct q931_call *call, struct apdu_event *apdu, const struct apdu_msg_data *msg)
+{
+	struct pri_subcommand *subcmd;
+
+	if ((reason == APDU_CALLBACK_REASON_ERROR) ||
+		(reason == APDU_CALLBACK_REASON_CLEANUP)) {
+		return 1;
+	}
+
+	subcmd = q931_alloc_subcommand(ctrl);
+	if (!subcmd) {
+		return 1;
+	}
+
+	memset(&subcmd->u.aoc_request_response, 0, sizeof(subcmd->u.aoc_request_response));
+	subcmd->u.aoc_request_response.charging_request = apdu->response.user.value;
+	subcmd->cmd = PRI_SUBCMD_AOC_CHARGING_REQ_RSP;
+
+	switch (reason) {
+	case APDU_CALLBACK_REASON_MSG_ERROR:
+		switch (msg->response.error->code) {
+		case ROSE_ERROR_Gen_NotImplemented:
+			subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_ERROR_NOT_IMPLEMENTED;
+			break;
+		case ROSE_ERROR_Gen_NotAvailable:
+			subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_ERROR_NOT_AVAILABLE;
+			break;
+		default:
+			subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_ERROR;
+			break;
+		}
+		break;
+	case APDU_CALLBACK_REASON_MSG_REJECT:
+		subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_ERROR_REJECT;
+		break;
+	case APDU_CALLBACK_REASON_TIMEOUT:
+		subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_ERROR_TIMEOUT;
+		break;
+	case APDU_CALLBACK_REASON_MSG_RESULT:
+		switch (msg->response.result->args.etsi.ChargingRequest.type) {
+		case 0:/* currency_info_list */
+			subcmd->u.aoc_request_response.valid_aoc_s = 1;
+			subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_CURRENCY_INFO_LIST;
+			aoc_etsi_subcmd_aoc_s_currency_info(&subcmd->u.aoc_request_response.aoc_s,
+				&msg->response.result->args.etsi.ChargingRequest.u.currency_info);
+			break;
+		case 1:/* special_arrangement_info */
+			subcmd->u.aoc_request_response.valid_aoc_s = 1;
+			subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_SPECIAL_ARR;
+			subcmd->u.aoc_request_response.aoc_s.num_items = 1;
+			subcmd->u.aoc_request_response.aoc_s.item[0].chargeable = PRI_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT;
+			subcmd->u.aoc_request_response.aoc_s.item[0].rate_type = PRI_AOC_RATE_TYPE_SPECIAL_CODE;
+			subcmd->u.aoc_request_response.aoc_s.item[0].rate.special =
+				msg->response.result->args.etsi.ChargingRequest.u.special_arrangement;
+			break;
+		case 2:/* charging_info_follows */
+			subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_CHARGING_INFO_FOLLOWS;
+			break;
+		default:
+			subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_ERROR;
+			break;
+		}
+		break;
+	default:
+		subcmd->u.aoc_request_response.charging_response = PRI_AOC_REQ_RSP_ERROR;
+		break;
+	}
+
+	return 1;
+}
+
+/*!
+ * \internal
+ * \brief Send the ETSI AOC Request invoke message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode AOC.
+ * \param aoc_request the aoc charging request payload data to encode.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int aoc_charging_request_encode(struct pri *ctrl, q931_call *call, enum PRI_AOC_REQUEST request)
+{
+	unsigned char buffer[255];
+	unsigned char *end = NULL;
+	struct apdu_callback_data response;
+
+	end = enc_etsi_aoc_request(ctrl, buffer, buffer + sizeof(buffer), request);
+	if (!end) {
+		return -1;
+	}
+
+	memset(&response, 0, sizeof(response));
+	response.invoke_id = ctrl->last_invoke;

[... 491 lines stripped ...]



More information about the libpri-commits mailing list