Skip to content
Snippets Groups Projects
sig_pri.c 295 KiB
Newer Older
  • Learn to ignore specific revisions
  • 
    #if defined(HAVE_PRI_AOC_EVENTS)
    /*!
     * \internal
    
     * \brief Convert AST_AOC_MULTIPLER to PRI_AOC_MULTIPLIER.
    
     * \return pri enum equivalent.
    
    static int sig_pri_aoc_multiplier_from_ast(enum ast_aoc_currency_multiplier mult)
    
    	switch (mult) {
    	case AST_AOC_MULT_ONETHOUSANDTH:
    		return PRI_AOC_MULTIPLIER_THOUSANDTH;
    	case AST_AOC_MULT_ONEHUNDREDTH:
    		return PRI_AOC_MULTIPLIER_HUNDREDTH;
    	case AST_AOC_MULT_ONETENTH:
    		return PRI_AOC_MULTIPLIER_TENTH;
    	case AST_AOC_MULT_ONE:
    		return PRI_AOC_MULTIPLIER_ONE;
    	case AST_AOC_MULT_TEN:
    		return PRI_AOC_MULTIPLIER_TEN;
    	case AST_AOC_MULT_HUNDRED:
    		return PRI_AOC_MULTIPLIER_HUNDRED;
    	case AST_AOC_MULT_THOUSAND:
    		return PRI_AOC_MULTIPLIER_THOUSAND;
    
    		return PRI_AOC_MULTIPLIER_ONE;
    
    	}
    }
    #endif	/* defined(HAVE_PRI_AOC_EVENTS) */
    
    #if defined(HAVE_PRI_AOC_EVENTS)
    /*!
     * \internal
    
     * \brief Convert PRI_AOC_MULTIPLIER to AST_AOC_MULTIPLIER
    
     * \return ast enum equivalent.
    
    static int sig_pri_aoc_multiplier_from_pri(const int mult)
    
    	switch (mult) {
    	case PRI_AOC_MULTIPLIER_THOUSANDTH:
    		return AST_AOC_MULT_ONETHOUSANDTH;
    	case PRI_AOC_MULTIPLIER_HUNDREDTH:
    		return AST_AOC_MULT_ONEHUNDREDTH;
    	case PRI_AOC_MULTIPLIER_TENTH:
    		return AST_AOC_MULT_ONETENTH;
    	case PRI_AOC_MULTIPLIER_ONE:
    		return AST_AOC_MULT_ONE;
    	case PRI_AOC_MULTIPLIER_TEN:
    		return AST_AOC_MULT_TEN;
    	case PRI_AOC_MULTIPLIER_HUNDRED:
    		return AST_AOC_MULT_HUNDRED;
    	case PRI_AOC_MULTIPLIER_THOUSAND:
    		return AST_AOC_MULT_THOUSAND;
    
    		return AST_AOC_MULT_ONE;
    
    	}
    }
    #endif	/* defined(HAVE_PRI_AOC_EVENTS) */
    
    #if defined(HAVE_PRI_AOC_EVENTS)
    /*!
     * \internal
    
     * \brief Convert ast_aoc_time_scale representation to PRI_AOC_TIME_SCALE
    
     * \param value Value to convert to ast representation
    
     * \return PRI_AOC_TIME_SCALE
    
    static enum PRI_AOC_TIME_SCALE sig_pri_aoc_scale_to_pri(enum ast_aoc_time_scale value)
    
    	case AST_AOC_TIME_SCALE_HUNDREDTH_SECOND:
    		return PRI_AOC_TIME_SCALE_HUNDREDTH_SECOND;
    	case AST_AOC_TIME_SCALE_TENTH_SECOND:
    		return PRI_AOC_TIME_SCALE_TENTH_SECOND;
    	case AST_AOC_TIME_SCALE_SECOND:
    		return PRI_AOC_TIME_SCALE_SECOND;
    	case AST_AOC_TIME_SCALE_TEN_SECOND:
    		return PRI_AOC_TIME_SCALE_TEN_SECOND;
    	case AST_AOC_TIME_SCALE_MINUTE:
    		return PRI_AOC_TIME_SCALE_MINUTE;
    	case AST_AOC_TIME_SCALE_HOUR:
    		return PRI_AOC_TIME_SCALE_HOUR;
    	case AST_AOC_TIME_SCALE_DAY:
    		return PRI_AOC_TIME_SCALE_DAY;
    
    	}
    }
    #endif	/* defined(HAVE_PRI_AOC_EVENTS) */
    
    #if defined(HAVE_PRI_AOC_EVENTS)
    /*!
     * \internal
    
     * \brief Convert PRI_AOC_TIME_SCALE to ast aoc representation
    
     * \param value Value to convert to ast representation
    
     * \return ast aoc time scale
    
    static enum ast_aoc_time_scale sig_pri_aoc_scale_to_ast(enum PRI_AOC_TIME_SCALE value)
    
    {
    	switch (value) {
    	default:
    	case PRI_AOC_TIME_SCALE_HUNDREDTH_SECOND:
    
    		return AST_AOC_TIME_SCALE_HUNDREDTH_SECOND;
    
    	case PRI_AOC_TIME_SCALE_TENTH_SECOND:
    
    		return AST_AOC_TIME_SCALE_TENTH_SECOND;
    
    	case PRI_AOC_TIME_SCALE_SECOND:
    
    		return AST_AOC_TIME_SCALE_SECOND;
    
    	case PRI_AOC_TIME_SCALE_TEN_SECOND:
    
    		return AST_AOC_TIME_SCALE_TEN_SECOND;
    
    	case PRI_AOC_TIME_SCALE_MINUTE:
    
    		return AST_AOC_TIME_SCALE_MINUTE;
    
    	case PRI_AOC_TIME_SCALE_HOUR:
    
    		return AST_AOC_TIME_SCALE_HOUR;
    
    	case PRI_AOC_TIME_SCALE_DAY:
    
    		return AST_AOC_TIME_SCALE_DAY;
    
    	return AST_AOC_TIME_SCALE_HUNDREDTH_SECOND;
    
    }
    #endif	/* defined(HAVE_PRI_AOC_EVENTS) */
    
    #if defined(HAVE_PRI_AOC_EVENTS)
    /*!
     * \internal
    
     * \brief Handle AOC-S control frame
    
     * \param aoc_s AOC-S event parameters.
     * \param owner Asterisk channel associated with the call.
     * \param passthrough indicating if this message should be queued on the ast channel
    
     * \note Assumes the pri->lock is already obtained.
     * \note Assumes the sig_pri private is locked
     * \note Assumes the owner channel lock is already obtained.
     *
     * \return Nothing
    
    static void sig_pri_aoc_s_from_pri(const struct pri_subcmd_aoc_s *aoc_s, struct ast_channel *owner, int passthrough)
    
    	struct ast_aoc_decoded *decoded = NULL;
    	struct ast_aoc_encoded *encoded = NULL;
    	size_t encoded_size = 0;
    	int idx;
    
    	if (!owner || !aoc_s) {
    		return;
    	}
    
    	if (!(decoded = ast_aoc_create(AST_AOC_S, 0, 0))) {
    		return;
    	}
    
    	for (idx = 0; idx < aoc_s->num_items; ++idx) {
    		enum ast_aoc_s_charged_item charged_item;
    
    		charged_item = sig_pri_aoc_charged_item_to_ast(aoc_s->item[idx].chargeable);
    		if (charged_item == AST_AOC_CHARGED_ITEM_NA) {
    			/* Delete the unknown charged item from the list. */
    			continue;
    		}
    		switch (aoc_s->item[idx].rate_type) {
    		case PRI_AOC_RATE_TYPE_DURATION:
    			ast_aoc_s_add_rate_duration(decoded,
    				charged_item,
    				aoc_s->item[idx].rate.duration.amount.cost,
    				sig_pri_aoc_multiplier_from_pri(aoc_s->item[idx].rate.duration.amount.multiplier),
    				aoc_s->item[idx].rate.duration.currency,
    				aoc_s->item[idx].rate.duration.time.length,
    				sig_pri_aoc_scale_to_ast(aoc_s->item[idx].rate.duration.time.scale),
    				aoc_s->item[idx].rate.duration.granularity.length,
    				sig_pri_aoc_scale_to_ast(aoc_s->item[idx].rate.duration.granularity.scale),
    				aoc_s->item[idx].rate.duration.charging_type);
    			break;
    		case PRI_AOC_RATE_TYPE_FLAT:
    			ast_aoc_s_add_rate_flat(decoded,
    				charged_item,
    				aoc_s->item[idx].rate.flat.amount.cost,
    				sig_pri_aoc_multiplier_from_pri(aoc_s->item[idx].rate.flat.amount.multiplier),
    				aoc_s->item[idx].rate.flat.currency);
    			break;
    		case PRI_AOC_RATE_TYPE_VOLUME:
    			ast_aoc_s_add_rate_volume(decoded,
    				charged_item,
    				aoc_s->item[idx].rate.volume.unit,
    				aoc_s->item[idx].rate.volume.amount.cost,
    				sig_pri_aoc_multiplier_from_pri(aoc_s->item[idx].rate.volume.amount.multiplier),
    				aoc_s->item[idx].rate.volume.currency);
    			break;
    		case PRI_AOC_RATE_TYPE_SPECIAL_CODE:
    			ast_aoc_s_add_rate_special_charge_code(decoded,
    				charged_item,
    				aoc_s->item[idx].rate.special);
    			break;
    		case PRI_AOC_RATE_TYPE_FREE:
    			ast_aoc_s_add_rate_free(decoded, charged_item, 0);
    			break;
    		case PRI_AOC_RATE_TYPE_FREE_FROM_BEGINNING:
    			ast_aoc_s_add_rate_free(decoded, charged_item, 1);
    			break;
    		default:
    			ast_aoc_s_add_rate_na(decoded, charged_item);
    			break;
    		}
    	}
    
    	if (passthrough && (encoded = ast_aoc_encode(decoded, &encoded_size, owner))) {
    		ast_queue_control_data(owner, AST_CONTROL_AOC, encoded, encoded_size);
    
    
    	ast_aoc_manager_event(decoded, owner);
    
    	ast_aoc_destroy_decoded(decoded);
    	ast_aoc_destroy_encoded(encoded);
    
    }
    #endif	/* defined(HAVE_PRI_AOC_EVENTS) */
    
    #if defined(HAVE_PRI_AOC_EVENTS)
    /*!
     * \internal
    
     * \brief Generate AOC Request Response
    
     * \param aoc_request
     *
     * \note Assumes the pri->lock is already obtained.
     * \note Assumes the sig_pri private is locked
     * \note Assumes the owner channel lock is already obtained.
     *
     * \return Nothing
     */
    static void sig_pri_aoc_request_from_pri(const struct pri_subcmd_aoc_request *aoc_request, struct sig_pri_chan *pvt, q931_call *call)
    {
    	int request;
    
    	if (!aoc_request) {
    		return;
    	}
    
    	request = aoc_request->charging_request;
    
    	if (request & PRI_AOC_REQUEST_S) {
    		if (pvt->pri->aoc_passthrough_flag & SIG_PRI_AOC_GRANT_S) {
    			/* An AOC-S response must come from the other side, so save off this invoke_id
    			 * and see if an AOC-S message comes in before the call is answered. */
    			pvt->aoc_s_request_invoke_id = aoc_request->invoke_id;
    			pvt->aoc_s_request_invoke_id_valid = 1;
    
    		} else {
    			pri_aoc_s_request_response_send(pvt->pri->pri,
    				call,
    				aoc_request->invoke_id,
    				NULL);
    		}
    	}
    
    	if (request & PRI_AOC_REQUEST_D) {
    		if (pvt->pri->aoc_passthrough_flag & SIG_PRI_AOC_GRANT_D) {
    			pri_aoc_de_request_response_send(pvt->pri->pri,
    				call,
    				PRI_AOC_REQ_RSP_CHARGING_INFO_FOLLOWS,
    				aoc_request->invoke_id);
    		} else {
    			pri_aoc_de_request_response_send(pvt->pri->pri,
    				call,
    				PRI_AOC_REQ_RSP_ERROR_NOT_AVAILABLE,
    				aoc_request->invoke_id);
    		}
    	}
    
    	if (request & PRI_AOC_REQUEST_E) {
    		if (pvt->pri->aoc_passthrough_flag & SIG_PRI_AOC_GRANT_E) {
    			pri_aoc_de_request_response_send(pvt->pri->pri,
    				call,
    				PRI_AOC_REQ_RSP_CHARGING_INFO_FOLLOWS,
    				aoc_request->invoke_id);
    		} else {
    			pri_aoc_de_request_response_send(pvt->pri->pri,
    				call,
    				PRI_AOC_REQ_RSP_ERROR_NOT_AVAILABLE,
    				aoc_request->invoke_id);
    		}
    	}
    }
    #endif	/* defined(HAVE_PRI_AOC_EVENTS) */
    
    #if defined(HAVE_PRI_AOC_EVENTS)
    /*!
     * \internal
     * \brief Generate AOC-D AST_CONTROL_AOC frame
     * \since 1.8
     *
     * \param aoc_e AOC-D event parameters.
     * \param owner Asterisk channel associated with the call.
     * \param passthrough indicating if this message should be queued on the ast channel
     *
     * \note Assumes the pri->lock is already obtained.
     * \note Assumes the sig_pri private is locked
     * \note Assumes the owner channel lock is already obtained.
    
     * \return Nothing
    
    static void sig_pri_aoc_d_from_pri(const struct pri_subcmd_aoc_d *aoc_d, struct ast_channel *owner, int passthrough)
    
    	struct ast_aoc_decoded *decoded = NULL;
    	struct ast_aoc_encoded *encoded = NULL;
    	size_t encoded_size = 0;
    	enum ast_aoc_charge_type type;
    
    	if (!owner || !aoc_d) {
    		return;
    	}
    
    	switch (aoc_d->charge) {
    	case PRI_AOC_DE_CHARGE_CURRENCY:
    		type = AST_AOC_CHARGE_CURRENCY;
    		break;
    	case PRI_AOC_DE_CHARGE_UNITS:
    		type = AST_AOC_CHARGE_UNIT;
    		break;
    	case PRI_AOC_DE_CHARGE_FREE:
    		type = AST_AOC_CHARGE_FREE;
    		break;
    
    		type = AST_AOC_CHARGE_NA;
    
    	}
    
    	if (!(decoded = ast_aoc_create(AST_AOC_D, type, 0))) {
    		return;
    	}
    
    	switch (aoc_d->billing_accumulation) {
    	default:
    		ast_debug(1, "AOC-D billing accumulation has unknown value: %d\n",
    			aoc_d->billing_accumulation);
    		/* Fall through */
    	case 0:/* subTotal */
    		ast_aoc_set_total_type(decoded, AST_AOC_SUBTOTAL);
    		break;
    	case 1:/* total */
    		ast_aoc_set_total_type(decoded, AST_AOC_TOTAL);
    		break;
    	}
    
    	switch (aoc_d->billing_id) {
    
    	case PRI_AOC_D_BILLING_ID_NORMAL:
    
    		ast_aoc_set_billing_id(decoded, AST_AOC_BILLING_NORMAL);
    
    		break;
    	case PRI_AOC_D_BILLING_ID_REVERSE:
    
    		ast_aoc_set_billing_id(decoded, AST_AOC_BILLING_REVERSE_CHARGE);
    
    		break;
    	case PRI_AOC_D_BILLING_ID_CREDIT_CARD:
    
    		ast_aoc_set_billing_id(decoded, AST_AOC_BILLING_CREDIT_CARD);
    		break;
    	case PRI_AOC_D_BILLING_ID_NOT_AVAILABLE:
    	default:
    		ast_aoc_set_billing_id(decoded, AST_AOC_BILLING_NA);
    
    
    	switch (aoc_d->charge) {
    	case PRI_AOC_DE_CHARGE_CURRENCY:
    		ast_aoc_set_currency_info(decoded,
    			aoc_d->recorded.money.amount.cost,
    			sig_pri_aoc_multiplier_from_pri(aoc_d->recorded.money.amount.multiplier),
    			aoc_d->recorded.money.currency);
    		break;
    	case PRI_AOC_DE_CHARGE_UNITS:
    		{
    			int i;
    			for (i = 0; i < aoc_d->recorded.unit.num_items; ++i) {
    				/* if type or number are negative, then they are not present */
    				ast_aoc_add_unit_entry(decoded,
    					(aoc_d->recorded.unit.item[i].number >= 0 ? 1 : 0),
    					aoc_d->recorded.unit.item[i].number,
    					(aoc_d->recorded.unit.item[i].type >= 0 ? 1 : 0),
    					aoc_d->recorded.unit.item[i].type);
    			}
    		}
    		break;
    	}
    
    	if (passthrough && (encoded = ast_aoc_encode(decoded, &encoded_size, owner))) {
    		ast_queue_control_data(owner, AST_CONTROL_AOC, encoded, encoded_size);
    	}
    
    	ast_aoc_manager_event(decoded, owner);
    
    	ast_aoc_destroy_decoded(decoded);
    	ast_aoc_destroy_encoded(encoded);
    
    }
    #endif	/* defined(HAVE_PRI_AOC_EVENTS) */
    
    #if defined(HAVE_PRI_AOC_EVENTS)
    /*!
     * \internal
    
     * \brief Generate AOC-E AST_CONTROL_AOC frame
    
     * \param aoc_e AOC-E event parameters.
     * \param owner Asterisk channel associated with the call.
     * \param passthrough indicating if this message should be queued on the ast channel
    
     * \note Assumes the pri->lock is already obtained.
     * \note Assumes the sig_pri private is locked
     * \note Assumes the owner channel lock is already obtained.
     * \note owner channel may be NULL. In that case, generate event only
     *
     * \return Nothing
    
    static void sig_pri_aoc_e_from_pri(const struct pri_subcmd_aoc_e *aoc_e, struct ast_channel *owner, int passthrough)
    
    	struct ast_aoc_decoded *decoded = NULL;
    	struct ast_aoc_encoded *encoded = NULL;
    	size_t encoded_size = 0;
    	enum ast_aoc_charge_type type;
    
    	if (!aoc_e) {
    		return;
    	}
    
    	switch (aoc_e->charge) {
    	case PRI_AOC_DE_CHARGE_CURRENCY:
    		type = AST_AOC_CHARGE_CURRENCY;
    		break;
    	case PRI_AOC_DE_CHARGE_UNITS:
    		type = AST_AOC_CHARGE_UNIT;
    		break;
    	case PRI_AOC_DE_CHARGE_FREE:
    		type = AST_AOC_CHARGE_FREE;
    		break;
    
    		type = AST_AOC_CHARGE_NA;
    		break;
    	}
    
    	if (!(decoded = ast_aoc_create(AST_AOC_E, type, 0))) {
    		return;
    	}
    
    	switch (aoc_e->associated.charging_type) {
    	case PRI_AOC_E_CHARGING_ASSOCIATION_NUMBER:
    		if (!aoc_e->associated.charge.number.valid) {
    			break;
    		}
    		ast_aoc_set_association_number(decoded, aoc_e->associated.charge.number.str, aoc_e->associated.charge.number.plan);
    
    	case PRI_AOC_E_CHARGING_ASSOCIATION_ID:
    		ast_aoc_set_association_id(decoded, aoc_e->associated.charge.id);
    		break;
    	default:
    		break;
    	}
    
    	switch (aoc_e->billing_id) {
    
    	case PRI_AOC_E_BILLING_ID_NORMAL:
    
    		ast_aoc_set_billing_id(decoded, AST_AOC_BILLING_NORMAL);
    
    		break;
    	case PRI_AOC_E_BILLING_ID_REVERSE:
    
    		ast_aoc_set_billing_id(decoded, AST_AOC_BILLING_REVERSE_CHARGE);
    
    		break;
    	case PRI_AOC_E_BILLING_ID_CREDIT_CARD:
    
    		ast_aoc_set_billing_id(decoded, AST_AOC_BILLING_CREDIT_CARD);
    
    		break;
    	case PRI_AOC_E_BILLING_ID_CALL_FORWARDING_UNCONDITIONAL:
    
    		ast_aoc_set_billing_id(decoded, AST_AOC_BILLING_CALL_FWD_UNCONDITIONAL);
    
    		break;
    	case PRI_AOC_E_BILLING_ID_CALL_FORWARDING_BUSY:
    
    		ast_aoc_set_billing_id(decoded, AST_AOC_BILLING_CALL_FWD_BUSY);
    
    		break;
    	case PRI_AOC_E_BILLING_ID_CALL_FORWARDING_NO_REPLY:
    
    		ast_aoc_set_billing_id(decoded, AST_AOC_BILLING_CALL_FWD_NO_REPLY);
    
    		break;
    	case PRI_AOC_E_BILLING_ID_CALL_DEFLECTION:
    
    		ast_aoc_set_billing_id(decoded, AST_AOC_BILLING_CALL_DEFLECTION);
    
    		break;
    	case PRI_AOC_E_BILLING_ID_CALL_TRANSFER:
    
    		ast_aoc_set_billing_id(decoded, AST_AOC_BILLING_CALL_TRANSFER);
    		break;
    	case PRI_AOC_E_BILLING_ID_NOT_AVAILABLE:
    	default:
    		ast_aoc_set_billing_id(decoded, AST_AOC_BILLING_NA);
    
    	switch (aoc_e->charge) {
    	case PRI_AOC_DE_CHARGE_CURRENCY:
    		ast_aoc_set_currency_info(decoded,
    			aoc_e->recorded.money.amount.cost,
    			sig_pri_aoc_multiplier_from_pri(aoc_e->recorded.money.amount.multiplier),
    			aoc_e->recorded.money.currency);
    		break;
    	case PRI_AOC_DE_CHARGE_UNITS:
    		{
    			int i;
    			for (i = 0; i < aoc_e->recorded.unit.num_items; ++i) {
    				/* if type or number are negative, then they are not present */
    				ast_aoc_add_unit_entry(decoded,
    					(aoc_e->recorded.unit.item[i].number >= 0 ? 1 : 0),
    					aoc_e->recorded.unit.item[i].number,
    					(aoc_e->recorded.unit.item[i].type >= 0 ? 1 : 0),
    					aoc_e->recorded.unit.item[i].type);
    			}
    		}
    	}
    
    	if (passthrough && owner && (encoded = ast_aoc_encode(decoded, &encoded_size, owner))) {
    		ast_queue_control_data(owner, AST_CONTROL_AOC, encoded, encoded_size);
    	}
    
    	ast_aoc_manager_event(decoded, owner);
    
    	ast_aoc_destroy_decoded(decoded);
    	ast_aoc_destroy_encoded(encoded);
    
    }
    #endif	/* defined(HAVE_PRI_AOC_EVENTS) */
    
    #if defined(HAVE_PRI_AOC_EVENTS)
    /*!
     * \internal
    
     * \brief send an AOC-S message on the current call
    
     * \param pvt sig_pri private channel structure.
     * \param generic decoded ast AOC message
    
     *
     * \note Assumes that the PRI lock is already obtained.
    
    static void sig_pri_aoc_s_from_ast(struct sig_pri_chan *pvt, struct ast_aoc_decoded *decoded)
    
    	struct pri_subcmd_aoc_s aoc_s = { 0, };
    	const struct ast_aoc_s_entry *entry;
    
    	for (idx = 0; idx < ast_aoc_s_get_count(decoded); idx++) {
    		if (!(entry = ast_aoc_s_get_rate_info(decoded, idx))) {
    			break;
    
    
    		aoc_s.item[idx].chargeable = sig_pri_aoc_charged_item_to_pri(entry->charged_item);
    
    		switch (entry->rate_type) {
    		case AST_AOC_RATE_TYPE_DURATION:
    			aoc_s.item[idx].rate_type = PRI_AOC_RATE_TYPE_DURATION;
    			aoc_s.item[idx].rate.duration.amount.cost = entry->rate.duration.amount;
    			aoc_s.item[idx].rate.duration.amount.multiplier =
    				sig_pri_aoc_multiplier_from_ast(entry->rate.duration.multiplier);
    			aoc_s.item[idx].rate.duration.time.length = entry->rate.duration.time;
    			aoc_s.item[idx].rate.duration.time.scale =
    				sig_pri_aoc_scale_to_pri(entry->rate.duration.time_scale);
    			aoc_s.item[idx].rate.duration.granularity.length = entry->rate.duration.granularity_time;
    			aoc_s.item[idx].rate.duration.granularity.scale =
    				sig_pri_aoc_scale_to_pri(entry->rate.duration.granularity_time_scale);
    			aoc_s.item[idx].rate.duration.charging_type = entry->rate.duration.charging_type;
    
    			if (!ast_strlen_zero(entry->rate.duration.currency_name)) {
    				ast_copy_string(aoc_s.item[idx].rate.duration.currency,
    					entry->rate.duration.currency_name,
    					sizeof(aoc_s.item[idx].rate.duration.currency));
    
    		case AST_AOC_RATE_TYPE_FLAT:
    			aoc_s.item[idx].rate_type = PRI_AOC_RATE_TYPE_FLAT;
    			aoc_s.item[idx].rate.flat.amount.cost = entry->rate.flat.amount;
    			aoc_s.item[idx].rate.flat.amount.multiplier =
    				sig_pri_aoc_multiplier_from_ast(entry->rate.flat.multiplier);
    
    			if (!ast_strlen_zero(entry->rate.flat.currency_name)) {
    				ast_copy_string(aoc_s.item[idx].rate.flat.currency,
    					entry->rate.flat.currency_name,
    					sizeof(aoc_s.item[idx].rate.flat.currency));
    			}
    
    		case AST_AOC_RATE_TYPE_VOLUME:
    			aoc_s.item[idx].rate_type = PRI_AOC_RATE_TYPE_VOLUME;
    			aoc_s.item[idx].rate.volume.unit = entry->rate.volume.volume_unit;
    			aoc_s.item[idx].rate.volume.amount.cost = entry->rate.volume.amount;
    			aoc_s.item[idx].rate.volume.amount.multiplier =
    				sig_pri_aoc_multiplier_from_ast(entry->rate.volume.multiplier);
    
    			if (!ast_strlen_zero(entry->rate.volume.currency_name)) {
    				ast_copy_string(aoc_s.item[idx].rate.volume.currency,
    					entry->rate.volume.currency_name,
    					sizeof(aoc_s.item[idx].rate.volume.currency));
    			}
    
    		case AST_AOC_RATE_TYPE_SPECIAL_CODE:
    			aoc_s.item[idx].rate_type = PRI_AOC_RATE_TYPE_SPECIAL_CODE;
    			aoc_s.item[idx].rate.special = entry->rate.special_code;
    			break;
    		case AST_AOC_RATE_TYPE_FREE:
    			aoc_s.item[idx].rate_type = PRI_AOC_RATE_TYPE_FREE;
    			break;
    		case AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING:
    			aoc_s.item[idx].rate_type = PRI_AOC_RATE_TYPE_FREE_FROM_BEGINNING;
    
    		case AST_AOC_RATE_TYPE_NA:
    			aoc_s.item[idx].rate_type = PRI_AOC_RATE_TYPE_NOT_AVAILABLE;
    
    	aoc_s.num_items = idx;
    
    	/* if this rate should be sent as a response to an AOC-S request we will
    	 * have an aoc_s_request_invoke_id associated with this pvt */
    	if (pvt->aoc_s_request_invoke_id_valid) {
    		pri_aoc_s_request_response_send(pvt->pri->pri, pvt->call, pvt->aoc_s_request_invoke_id, &aoc_s);
    		pvt->aoc_s_request_invoke_id_valid = 0;
    	} else {
    		pri_aoc_s_send(pvt->pri->pri, pvt->call, &aoc_s);
    	}
    
    }
    #endif	/* defined(HAVE_PRI_AOC_EVENTS) */
    
    #if defined(HAVE_PRI_AOC_EVENTS)
    /*!
     * \internal
    
     * \brief send an AOC-D message on the current call
    
     * \param pvt sig_pri private channel structure.
     * \param generic decoded ast AOC message
    
     *
     * \note Assumes that the PRI lock is already obtained.
    
    static void sig_pri_aoc_d_from_ast(struct sig_pri_chan *pvt, struct ast_aoc_decoded *decoded)
    
    	struct pri_subcmd_aoc_d aoc_d = { 0, };
    
    	aoc_d.billing_accumulation = (ast_aoc_get_total_type(decoded) == AST_AOC_TOTAL) ? 1 : 0;
    
    	switch (ast_aoc_get_billing_id(decoded)) {
    	case AST_AOC_BILLING_NORMAL:
    		aoc_d.billing_id = PRI_AOC_D_BILLING_ID_NORMAL;
    		break;
    	case AST_AOC_BILLING_REVERSE_CHARGE:
    		aoc_d.billing_id = PRI_AOC_D_BILLING_ID_REVERSE;
    		break;
    	case AST_AOC_BILLING_CREDIT_CARD:
    		aoc_d.billing_id = PRI_AOC_D_BILLING_ID_CREDIT_CARD;
    
    	case AST_AOC_BILLING_NA:
    
    		aoc_d.billing_id = PRI_AOC_D_BILLING_ID_NOT_AVAILABLE;
    
    
    	switch (ast_aoc_get_charge_type(decoded)) {
    	case AST_AOC_CHARGE_FREE:
    		aoc_d.charge = PRI_AOC_DE_CHARGE_FREE;
    
    	case AST_AOC_CHARGE_CURRENCY:
    		{
    			const char *currency_name = ast_aoc_get_currency_name(decoded);
    			aoc_d.charge = PRI_AOC_DE_CHARGE_CURRENCY;
    			aoc_d.recorded.money.amount.cost = ast_aoc_get_currency_amount(decoded);
    			aoc_d.recorded.money.amount.multiplier = sig_pri_aoc_multiplier_from_ast(ast_aoc_get_currency_multiplier(decoded));
    			if (!ast_strlen_zero(currency_name)) {
    				ast_copy_string(aoc_d.recorded.money.currency, currency_name, sizeof(aoc_d.recorded.money.currency));
    
    		break;
    	case AST_AOC_CHARGE_UNIT:
    		{
    			const struct ast_aoc_unit_entry *entry;
    			int i;
    			aoc_d.charge = PRI_AOC_DE_CHARGE_UNITS;
    			for (i = 0; i < ast_aoc_get_unit_count(decoded); i++) {
    				if ((entry = ast_aoc_get_unit_info(decoded, i)) && i < ARRAY_LEN(aoc_d.recorded.unit.item)) {
    					if (entry->valid_amount) {
    						aoc_d.recorded.unit.item[i].number = entry->amount;
    					} else {
    						aoc_d.recorded.unit.item[i].number = -1;
    					}
    					if (entry->valid_type) {
    						aoc_d.recorded.unit.item[i].type = entry->type;
    					} else {
    						aoc_d.recorded.unit.item[i].type = -1;
    					}
    					aoc_d.recorded.unit.num_items++;
    				} else {
    					break;
    				}
    
    	case AST_AOC_CHARGE_NA:
    
    		aoc_d.charge = PRI_AOC_DE_CHARGE_NOT_AVAILABLE;
    
    	pri_aoc_d_send(pvt->pri->pri, pvt->call, &aoc_d);
    
    }
    #endif	/* defined(HAVE_PRI_AOC_EVENTS) */
    
    #if defined(HAVE_PRI_AOC_EVENTS)
    /*!
     * \internal
    
     * \brief send an AOC-E message on the current call
    
     * \param pvt sig_pri private channel structure.
     * \param generic decoded ast AOC message
    
     *
     * \note Assumes that the PRI lock is already obtained.
    
    static void sig_pri_aoc_e_from_ast(struct sig_pri_chan *pvt, struct ast_aoc_decoded *decoded)
    
    	struct pri_subcmd_aoc_e *aoc_e = &pvt->aoc_e;
    	const struct ast_aoc_charging_association *ca = ast_aoc_get_association_info(decoded);
    
    	memset(aoc_e, 0, sizeof(*aoc_e));
    	pvt->holding_aoce = 1;
    
    	switch (ca->charging_type) {
    	case AST_AOC_CHARGING_ASSOCIATION_NUMBER:
    		aoc_e->associated.charge.number.valid = 1;
    		ast_copy_string(aoc_e->associated.charge.number.str,
    			ca->charge.number.number,
    			sizeof(aoc_e->associated.charge.number.str));
    		aoc_e->associated.charge.number.plan = ca->charge.number.plan;
    		aoc_e->associated.charging_type = PRI_AOC_E_CHARGING_ASSOCIATION_NUMBER;
    
    	case AST_AOC_CHARGING_ASSOCIATION_ID:
    		aoc_e->associated.charge.id = ca->charge.id;
    		aoc_e->associated.charging_type = PRI_AOC_E_CHARGING_ASSOCIATION_ID;
    
    	case AST_AOC_CHARGING_ASSOCIATION_NA:
    
    	switch (ast_aoc_get_billing_id(decoded)) {
    	case AST_AOC_BILLING_NORMAL:
    		aoc_e->billing_id = PRI_AOC_E_BILLING_ID_NORMAL;
    		break;
    	case AST_AOC_BILLING_REVERSE_CHARGE:
    		aoc_e->billing_id = PRI_AOC_E_BILLING_ID_REVERSE;
    		break;
    	case AST_AOC_BILLING_CREDIT_CARD:
    		aoc_e->billing_id = PRI_AOC_E_BILLING_ID_CREDIT_CARD;
    		break;
    	case AST_AOC_BILLING_CALL_FWD_UNCONDITIONAL:
    		aoc_e->billing_id = PRI_AOC_E_BILLING_ID_CALL_FORWARDING_UNCONDITIONAL;
    
    	case AST_AOC_BILLING_CALL_FWD_BUSY:
    		aoc_e->billing_id = PRI_AOC_E_BILLING_ID_CALL_FORWARDING_BUSY;
    		break;
    	case AST_AOC_BILLING_CALL_FWD_NO_REPLY:
    		aoc_e->billing_id = PRI_AOC_E_BILLING_ID_CALL_FORWARDING_NO_REPLY;
    		break;
    	case AST_AOC_BILLING_CALL_DEFLECTION:
    		aoc_e->billing_id = PRI_AOC_E_BILLING_ID_CALL_DEFLECTION;
    		break;
    	case AST_AOC_BILLING_CALL_TRANSFER:
    		aoc_e->billing_id = PRI_AOC_E_BILLING_ID_CALL_TRANSFER;
    		break;
    	case AST_AOC_BILLING_NA:
    
    		aoc_e->billing_id = PRI_AOC_E_BILLING_ID_NOT_AVAILABLE;
    
    
    	switch (ast_aoc_get_charge_type(decoded)) {
    	case AST_AOC_CHARGE_FREE:
    		aoc_e->charge = PRI_AOC_DE_CHARGE_FREE;
    
    	case AST_AOC_CHARGE_CURRENCY:
    		{
    			const char *currency_name = ast_aoc_get_currency_name(decoded);
    			aoc_e->charge = PRI_AOC_DE_CHARGE_CURRENCY;
    			aoc_e->recorded.money.amount.cost = ast_aoc_get_currency_amount(decoded);
    			aoc_e->recorded.money.amount.multiplier = sig_pri_aoc_multiplier_from_ast(ast_aoc_get_currency_multiplier(decoded));
    			if (!ast_strlen_zero(currency_name)) {
    				ast_copy_string(aoc_e->recorded.money.currency, currency_name, sizeof(aoc_e->recorded.money.currency));
    
    		break;
    	case AST_AOC_CHARGE_UNIT:
    		{
    			const struct ast_aoc_unit_entry *entry;
    			int i;
    			aoc_e->charge = PRI_AOC_DE_CHARGE_UNITS;
    			for (i = 0; i < ast_aoc_get_unit_count(decoded); i++) {
    				if ((entry = ast_aoc_get_unit_info(decoded, i)) && i < ARRAY_LEN(aoc_e->recorded.unit.item)) {
    					if (entry->valid_amount) {
    						aoc_e->recorded.unit.item[i].number = entry->amount;
    					} else {
    						aoc_e->recorded.unit.item[i].number = -1;
    					}
    					if (entry->valid_type) {
    						aoc_e->recorded.unit.item[i].type = entry->type;
    					} else {
    						aoc_e->recorded.unit.item[i].type = -1;
    					}
    					aoc_e->recorded.unit.num_items++;
    				}
    
    	case AST_AOC_CHARGE_NA:
    
    		aoc_e->charge = PRI_AOC_DE_CHARGE_NOT_AVAILABLE;
    
    }
    #endif	/* defined(HAVE_PRI_AOC_EVENTS) */
    
    #if defined(HAVE_PRI_AOC_EVENTS)
    /*!
     * \internal
     * \brief send an AOC-E termination request on ast_channel and set
     * hangup delay.
     *
    
     * \param pri PRI span control structure.
    
     * \param chanpos Channel position in the span.
    
     * \param ms to delay hangup
     *
    
     * \note Assumes the pri->lock is already obtained.
     * \note Assumes the sig_pri_lock_private(pri->pvts[chanpos]) is already obtained.
    
     *
     * \return Nothing
     */
    
    static void sig_pri_send_aoce_termination_request(struct sig_pri_span *pri, int chanpos, unsigned int ms)
    
    	struct sig_pri_chan *pvt;
    
    	struct ast_aoc_decoded *decoded = NULL;
    	struct ast_aoc_encoded *encoded = NULL;
    	size_t encoded_size;
    	struct timeval whentohangup = { 0, };
    
    
    	sig_pri_lock_owner(pri, chanpos);
    	pvt = pri->pvts[chanpos];
    	if (!pvt->owner) {
    		return;
    	}
    
    
    	if (!(decoded = ast_aoc_create(AST_AOC_REQUEST, 0, AST_AOC_REQUEST_E))) {
    		ast_softhangup_nolock(pvt->owner, AST_SOFTHANGUP_DEV);
    		goto cleanup_termination_request;
    	}
    
    	ast_aoc_set_termination_request(decoded);
    
    	if (!(encoded = ast_aoc_encode(decoded, &encoded_size, pvt->owner))) {
    		ast_softhangup_nolock(pvt->owner, AST_SOFTHANGUP_DEV);
    		goto cleanup_termination_request;
    	}
    
    	/* convert ms to timeval */
    	whentohangup.tv_usec = (ms % 1000) * 1000;
    	whentohangup.tv_sec = ms / 1000;
    
    	if (ast_queue_control_data(pvt->owner, AST_CONTROL_AOC, encoded, encoded_size)) {
    		ast_softhangup_nolock(pvt->owner, AST_SOFTHANGUP_DEV);
    		goto cleanup_termination_request;
    	}
    
    	pvt->waiting_for_aoce = 1;
    	ast_channel_setwhentohangup_tv(pvt->owner, whentohangup);
    
    	ast_debug(1, "Delaying hangup on %s for aoc-e msg\n", ast_channel_name(pvt->owner));
    
    cleanup_termination_request:
    
    	ast_channel_unlock(pvt->owner);
    
    	ast_aoc_destroy_decoded(decoded);
    	ast_aoc_destroy_encoded(encoded);
    
    }
    #endif	/* defined(HAVE_PRI_AOC_EVENTS) */
    
    
    /*!
     * \internal
     * \brief TRUE if PRI event came in on a CIS call.
     * \since 1.8
     *
     * \param channel PRI encoded span/channel
     *
     * \retval non-zero if CIS call.
     */
    static int sig_pri_is_cis_call(int channel)
    {
    	return channel != -1 && (channel & PRI_CIS_CALL);
    }
    
    /*!
     * \internal
     * \brief Handle the CIS associated PRI subcommand events.
     * \since 1.8
     *
    
     * \param pri PRI span control structure.
    
     * \param event_id PRI event id
     * \param subcmds Subcommands to process if any. (Could be NULL).
     * \param call_rsp libpri opaque call structure to send any responses toward.
     * Could be NULL either because it is not available or the call is for the
     * dummy call reference.  However, this should not be NULL in the cases that
     * need to use the pointer to send a response message back.
     *
     * \note Assumes the pri->lock is already obtained.
     *
     * \return Nothing
     */
    
    static void sig_pri_handle_cis_subcmds(struct sig_pri_span *pri, int event_id,
    
    	const struct pri_subcommands *subcmds, q931_call *call_rsp)
    {
    	int index;
    #if defined(HAVE_PRI_CCSS)
    	struct ast_cc_agent *agent;
    	struct sig_pri_cc_agent_prv *agent_prv;
    	struct sig_pri_cc_monitor_instance *monitor;
    #endif	/* defined(HAVE_PRI_CCSS) */
    
    	if (!subcmds) {
    		return;
    	}
    	for (index = 0; index < subcmds->counter_subcmd; ++index) {
    		const struct pri_subcommand *subcmd = &subcmds->subcmd[index];
    
    		switch (subcmd->cmd) {
    #if defined(STATUS_REQUEST_PLACE_HOLDER)
    		case PRI_SUBCMD_STATUS_REQ:
    		case PRI_SUBCMD_STATUS_REQ_RSP:
    			/* Ignore for now. */
    			break;
    #endif	/* defined(STATUS_REQUEST_PLACE_HOLDER) */
    #if defined(HAVE_PRI_CCSS)
    		case PRI_SUBCMD_CC_REQ:
    			agent = sig_pri_find_cc_agent_by_cc_id(pri, subcmd->u.cc_request.cc_id);
    			if (!agent) {
    				pri_cc_cancel(pri->pri, subcmd->u.cc_request.cc_id);
    				break;
    			}
    			if (!ast_cc_request_is_within_limits()) {
    				if (pri_cc_req_rsp(pri->pri, subcmd->u.cc_request.cc_id,
    					5/* queue_full */)) {
    					pri_cc_cancel(pri->pri, subcmd->u.cc_request.cc_id);
    				}
    				ast_cc_failed(agent->core_id, "%s agent system CC queue full",
    					sig_pri_cc_type_name);
    				ao2_ref(agent, -1);
    				break;
    			}
    			agent_prv = agent->private_data;
    			agent_prv->cc_request_response_pending = 1;
    			if (ast_cc_agent_accept_request(agent->core_id,
    				"%s caller accepted CC offer.", sig_pri_cc_type_name)) {
    				agent_prv->cc_request_response_pending = 0;
    				if (pri_cc_req_rsp(pri->pri, subcmd->u.cc_request.cc_id,
    					2/* short_term_denial */)) {
    					pri_cc_cancel(pri->pri, subcmd->u.cc_request.cc_id);
    				}
    				ast_cc_failed(agent->core_id, "%s agent CC core request accept failed",
    					sig_pri_cc_type_name);
    			}
    			ao2_ref(agent, -1);
    			break;
    #endif	/* defined(HAVE_PRI_CCSS) */
    #if defined(HAVE_PRI_CCSS)
    		case PRI_SUBCMD_CC_REQ_RSP:
    			monitor = sig_pri_find_cc_monitor_by_cc_id(pri,
    				subcmd->u.cc_request_rsp.cc_id);
    			if (!monitor) {
    				pri_cc_cancel(pri->pri, subcmd->u.cc_request_rsp.cc_id);
    				break;
    			}
    			switch (subcmd->u.cc_request_rsp.status) {
    			case 0:/* success */
    				ast_cc_monitor_request_acked(monitor->core_id,
    					"%s far end accepted CC request", sig_pri_cc_type_name);
    				break;
    			case 1:/* timeout */
    				ast_verb(2, "core_id:%d %s CC request timeout\n", monitor->core_id,
    					sig_pri_cc_type_name);
    				ast_cc_monitor_failed(monitor->core_id, monitor->name,
    					"%s CC request timeout", sig_pri_cc_type_name);
    				break;
    			case 2:/* error */
    				ast_verb(2, "core_id:%d %s CC request error: %s\n", monitor->core_id,
    					sig_pri_cc_type_name,
    					pri_facility_error2str(subcmd->u.cc_request_rsp.fail_code));
    				ast_cc_monitor_failed(monitor->core_id, monitor->name,
    					"%s CC request error", sig_pri_cc_type_name);
    				break;
    			case 3:/* reject */
    				ast_verb(2, "core_id:%d %s CC request reject: %s\n", monitor->core_id,
    					sig_pri_cc_type_name,
    					pri_facility_reject2str(subcmd->u.cc_request_rsp.fail_code));
    				ast_cc_monitor_failed(monitor->core_id, monitor->name,
    					"%s CC request reject", sig_pri_cc_type_name);
    				break;