Added ETSI Explicit Call Transfer (ECT) support.
Added ability to send and receive ETSI ECT messages to eliminate tromboned calls. Added ETSI support to an existing API call to send ECT messages when the upper level indicates to transfer specified calls. The libpri API was extended to indicate to the upper layer that the far end requests the transfer of the indicated calls. Review: https://reviewboard.asterisk.org/r/521/ git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@1723 2fbb986a-6c06-0410-b554-c9c1f0a7f128
This commit is contained in:
parent
6829faae06
commit
382e7e5c86
40
libpri.h
40
libpri.h
@ -538,6 +538,7 @@ struct pri_rerouting_data {
|
||||
#define PRI_SUBCMD_CC_CALL 14 /*!< Indicate that this call is a CC callback */
|
||||
#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_TRANSFER_CALL 17 /*!< Request to transfer the specified calls together. */
|
||||
|
||||
#if defined(STATUS_REQUEST_PLACE_HOLDER)
|
||||
struct pri_subcmd_status_request {
|
||||
@ -634,6 +635,19 @@ struct pri_subcmd_cc_cancel {
|
||||
int is_agent;
|
||||
};
|
||||
|
||||
struct pri_subcmd_transfer {
|
||||
/*! \brief Opaque call pointer for transfer with other call. */
|
||||
q931_call *call_1;
|
||||
/*! \brief Opaque call pointer for transfer with other call. */
|
||||
q931_call *call_2;
|
||||
/*! TRUE if call_1 is on hold. */
|
||||
int is_call_1_held;
|
||||
/*! TRUE if call_2 is on hold. */
|
||||
int is_call_2_held;
|
||||
/*! Invocation ID to use when sending a reply to the transfer request. */
|
||||
int invoke_id;
|
||||
};
|
||||
|
||||
struct pri_subcommand {
|
||||
/*! PRI_SUBCMD_xxx defined values */
|
||||
int cmd;
|
||||
@ -658,6 +672,7 @@ struct pri_subcommand {
|
||||
struct pri_subcmd_cc_status cc_status;
|
||||
struct pri_subcmd_cc_id cc_call;
|
||||
struct pri_subcmd_cc_cancel cc_cancel;
|
||||
struct pri_subcmd_transfer transfer;
|
||||
} u;
|
||||
};
|
||||
|
||||
@ -1216,7 +1231,7 @@ int pri_set_service_message_support(struct pri *pri, int supportflag);
|
||||
|
||||
#define PRI_2BCT
|
||||
/* Attempt to pass the channels back to the NET side if compatable and
|
||||
* suscribed. Sometimes called 2 bchannel transfer (2BCT) */
|
||||
* subscribed. Sometimes called 2 bchannel transfer (2BCT) */
|
||||
int pri_channel_bridge(q931_call *call1, q931_call *call2);
|
||||
|
||||
/* Override message and error stuff */
|
||||
@ -1340,6 +1355,29 @@ enum PRI_REROUTING_RSP_CODE {
|
||||
*/
|
||||
int pri_rerouting_rsp(struct pri *ctrl, q931_call *call, int invoke_id, enum PRI_REROUTING_RSP_CODE code);
|
||||
|
||||
/*!
|
||||
* \brief Set the call transfer feature enable flag.
|
||||
*
|
||||
* \param ctrl D channel controller.
|
||||
* \param enable TRUE to enable call transfer feature.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
void pri_transfer_enable(struct pri *ctrl, int enable);
|
||||
|
||||
/*!
|
||||
* \brief Send the call transfer response message.
|
||||
*
|
||||
* \param ctrl D channel controller.
|
||||
* \param call Q.931 call leg.
|
||||
* \param invoke_id Value given by the initiating request.
|
||||
* \param is_successful TRUE if the transfer was successful.
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on error.
|
||||
*/
|
||||
int pri_transfer_rsp(struct pri *ctrl, q931_call *call, int invoke_id, int is_successful);
|
||||
|
||||
/*!
|
||||
* \brief Set the call hold feature enable flag.
|
||||
*
|
||||
|
83
pri.c
83
pri.c
@ -1016,47 +1016,64 @@ int pri_disconnect(struct pri *pri, q931_call *call, int cause)
|
||||
|
||||
int pri_channel_bridge(q931_call *call1, q931_call *call2)
|
||||
{
|
||||
struct q931_call *winner;
|
||||
|
||||
if (!call1 || !call2)
|
||||
return -1;
|
||||
|
||||
/* Make sure we have compatible switchtypes */
|
||||
if (call1->pri->switchtype != call2->pri->switchtype)
|
||||
winner = q931_find_winning_call(call1);
|
||||
if (!winner) {
|
||||
/* Cannot transfer: Call 1 does not have a winner yet. */
|
||||
return -1;
|
||||
}
|
||||
call1 = winner;
|
||||
|
||||
winner = q931_find_winning_call(call2);
|
||||
if (!winner) {
|
||||
/* Cannot transfer: Call 2 does not have a winner yet. */
|
||||
return -1;
|
||||
}
|
||||
call2 = winner;
|
||||
|
||||
/* Check to see if we're on the same PRI */
|
||||
if (call1->pri != call2->pri) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check for bearer capability */
|
||||
if (call1->bc.transcapability != call2->bc.transcapability)
|
||||
return -1;
|
||||
|
||||
/* Check to see if we're on the same PRI */
|
||||
if (call1->pri != call2->pri)
|
||||
return -1;
|
||||
|
||||
switch (call1->pri->switchtype) {
|
||||
case PRI_SWITCH_NI2:
|
||||
case PRI_SWITCH_LUCENT5E:
|
||||
case PRI_SWITCH_ATT4ESS:
|
||||
if (eect_initiate_transfer(call1->pri, call1, call2))
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
break;
|
||||
case PRI_SWITCH_DMS100:
|
||||
if (rlt_initiate_transfer(call1->pri, call1, call2))
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
break;
|
||||
case PRI_SWITCH_QSIG:
|
||||
call1->bridged_call = call2;
|
||||
call2->bridged_call = call1;
|
||||
if (anfpr_initiate_transfer(call1->pri, call1, call2))
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
case PRI_SWITCH_NI2:
|
||||
case PRI_SWITCH_LUCENT5E:
|
||||
case PRI_SWITCH_ATT4ESS:
|
||||
if (eect_initiate_transfer(call1->pri, call1, call2)) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case PRI_SWITCH_DMS100:
|
||||
if (rlt_initiate_transfer(call1->pri, call1, call2)) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case PRI_SWITCH_QSIG:
|
||||
call1->bridged_call = call2;
|
||||
call2->bridged_call = call1;
|
||||
if (anfpr_initiate_transfer(call1->pri, call1, call2)) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case PRI_SWITCH_EUROISDN_E1:
|
||||
case PRI_SWITCH_EUROISDN_T1:
|
||||
if (etsi_initiate_transfer(call1->pri, call1, call2)) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pri_hangup_fix_enable(struct pri *ctrl, int enable)
|
||||
@ -1597,6 +1614,14 @@ void pri_sr_set_keypad_digits(struct pri_sr *sr, const char *keypad_digits)
|
||||
sr->keypad_digits = keypad_digits;
|
||||
}
|
||||
|
||||
void pri_transfer_enable(struct pri *ctrl, int enable)
|
||||
{
|
||||
if (ctrl) {
|
||||
ctrl = PRI_MASTER(ctrl);
|
||||
ctrl->transfer_support = enable ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
void pri_hold_enable(struct pri *ctrl, int enable)
|
||||
{
|
||||
if (ctrl) {
|
||||
|
392
pri_facility.c
392
pri_facility.c
@ -2362,6 +2362,344 @@ int anfpr_initiate_transfer(struct pri *ctrl, q931_call *c1, q931_call *c2)
|
||||
}
|
||||
/* End AFN-PR */
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Encode ETSI ExplicitEctExecute 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 link_id Identifier of other call involved in transfer.
|
||||
*
|
||||
* \retval Start of the next ASN.1 component to encode on success.
|
||||
* \retval NULL on error.
|
||||
*/
|
||||
static unsigned char *enc_etsi_ect_explicit_execute(struct pri *ctrl, unsigned char *pos,
|
||||
unsigned char *end, int link_id)
|
||||
{
|
||||
struct rose_msg_invoke msg;
|
||||
|
||||
pos = facility_encode_header(ctrl, pos, end, NULL);
|
||||
if (!pos) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.invoke_id = get_invokeid(ctrl);
|
||||
msg.operation = ROSE_ETSI_ExplicitEctExecute;
|
||||
|
||||
msg.args.etsi.ExplicitEctExecute.link_id = link_id;
|
||||
|
||||
pos = rose_encode_invoke(ctrl, pos, end, &msg);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief ECT LinkId 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 etsi_ect_link_id_rsp(enum APDU_CALLBACK_REASON reason, struct pri *ctrl,
|
||||
struct q931_call *call, struct apdu_event *apdu, const struct apdu_msg_data *msg)
|
||||
{
|
||||
unsigned char buffer[256];
|
||||
unsigned char *end;
|
||||
q931_call *call_2;
|
||||
|
||||
switch (reason) {
|
||||
case APDU_CALLBACK_REASON_MSG_RESULT:
|
||||
call_2 = q931_find_call(ctrl, apdu->response.user.value);
|
||||
if (!call_2) {
|
||||
break;
|
||||
}
|
||||
|
||||
end = enc_etsi_ect_explicit_execute(ctrl, buffer, buffer + sizeof(buffer),
|
||||
msg->response.result->args.etsi.EctLinkIdRequest.link_id);
|
||||
if (!end) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* 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_2, Q931_FACILITY, buffer, end - buffer, NULL)
|
||||
|| q931_facility(call_2->pri, call_2)) {
|
||||
pri_message(ctrl, "Could not schedule facility message for call %d\n",
|
||||
call_2->cr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Encode ETSI ECT LinkId request 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.
|
||||
*
|
||||
* \retval Start of the next ASN.1 component to encode on success.
|
||||
* \retval NULL on error.
|
||||
*/
|
||||
static unsigned char *enc_etsi_ect_link_id_req(struct pri *ctrl, unsigned char *pos,
|
||||
unsigned char *end)
|
||||
{
|
||||
struct rose_msg_invoke msg;
|
||||
|
||||
pos = facility_encode_header(ctrl, pos, end, NULL);
|
||||
if (!pos) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.invoke_id = get_invokeid(ctrl);
|
||||
msg.operation = ROSE_ETSI_EctLinkIdRequest;
|
||||
|
||||
pos = rose_encode_invoke(ctrl, pos, end, &msg);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Start an Explicit Call Transfer (ECT) sequence between the two calls.
|
||||
*
|
||||
* \param ctrl D channel controller for diagnostic messages or global options.
|
||||
* \param call_1 Q.931 call leg 1
|
||||
* \param call_2 Q.931 call leg 2
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on error.
|
||||
*/
|
||||
int etsi_initiate_transfer(struct pri *ctrl, q931_call *call_1, q931_call *call_2)
|
||||
{
|
||||
unsigned char buffer[256];
|
||||
unsigned char *end;
|
||||
struct apdu_callback_data response;
|
||||
|
||||
end = enc_etsi_ect_link_id_req(ctrl, buffer, buffer + sizeof(buffer));
|
||||
if (!end) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&response, 0, sizeof(response));
|
||||
response.invoke_id = ctrl->last_invoke;
|
||||
response.timeout_time = ctrl->timers[PRI_TIMER_T_RESPONSE];
|
||||
response.callback = etsi_ect_link_id_rsp;
|
||||
response.user.value = call_2->cr;
|
||||
|
||||
/* 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_1, Q931_FACILITY, buffer, end - buffer, &response)
|
||||
|| q931_facility(call_1->pri, call_1)) {
|
||||
pri_message(ctrl, "Could not schedule facility message for call %d\n",
|
||||
call_1->cr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Encode ETSI ECT LinkId result respnose 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 invoke_id Invoke id to put in result message.
|
||||
* \param link_id Requested link id to put in result message.
|
||||
*
|
||||
* \retval Start of the next ASN.1 component to encode on success.
|
||||
* \retval NULL on error.
|
||||
*/
|
||||
static unsigned char *enc_etsi_ect_link_id_rsp(struct pri *ctrl, unsigned char *pos,
|
||||
unsigned char *end, int invoke_id, int link_id)
|
||||
{
|
||||
struct rose_msg_result msg;
|
||||
|
||||
pos = facility_encode_header(ctrl, pos, end, NULL);
|
||||
if (!pos) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.invoke_id = invoke_id;
|
||||
msg.operation = ROSE_ETSI_EctLinkIdRequest;
|
||||
|
||||
msg.args.etsi.EctLinkIdRequest.link_id = link_id;
|
||||
|
||||
pos = rose_encode_result(ctrl, pos, end, &msg);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Send EctLinkIdRequest result response message.
|
||||
*
|
||||
* \param ctrl D channel controller.
|
||||
* \param call Q.931 call leg.
|
||||
* \param invoke_id Invoke id to put in result message.
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on error.
|
||||
*/
|
||||
static int send_ect_link_id_rsp(struct pri *ctrl, q931_call *call, int invoke_id)
|
||||
{
|
||||
unsigned char buffer[256];
|
||||
unsigned char *end;
|
||||
|
||||
end = enc_etsi_ect_link_id_rsp(ctrl, buffer, buffer + sizeof(buffer), invoke_id,
|
||||
call->link_id);
|
||||
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 Process the received ETSI EctExecute message.
|
||||
*
|
||||
* \param ctrl D channel controller.
|
||||
* \param call Q.931 call leg.
|
||||
* \param invoke_id Invoke id to put in response message.
|
||||
*
|
||||
* \details
|
||||
* 1) Find the active call implied by the transfer request.
|
||||
* 2) Create the PRI_SUBCMD_TRANSFER_CALL event.
|
||||
*
|
||||
* \retval ROSE_ERROR_None on success.
|
||||
* \retval error_code on error.
|
||||
*/
|
||||
static enum rose_error_code etsi_ect_execute_transfer(struct pri *ctrl, q931_call *call, int invoke_id)
|
||||
{
|
||||
enum rose_error_code error_code;
|
||||
struct pri_subcommand *subcmd;
|
||||
q931_call *call_active;
|
||||
|
||||
switch (call->ourcallstate) {
|
||||
case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
|
||||
case Q931_CALL_STATE_CALL_DELIVERED:
|
||||
case Q931_CALL_STATE_CALL_RECEIVED:
|
||||
case Q931_CALL_STATE_CONNECT_REQUEST:
|
||||
case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
|
||||
case Q931_CALL_STATE_ACTIVE:
|
||||
if (call->master_call->hold_state != Q931_HOLD_STATE_CALL_HELD) {
|
||||
/* EctExecute must be sent on the held call. */
|
||||
error_code = ROSE_ERROR_Gen_InvalidCallState;
|
||||
break;
|
||||
}
|
||||
/* Held call is being transferred. */
|
||||
call_active = q931_find_held_active_call(ctrl, call);
|
||||
if (!call_active) {
|
||||
error_code = ROSE_ERROR_Gen_NotAvailable;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Setup transfer subcommand */
|
||||
subcmd = q931_alloc_subcommand(ctrl);
|
||||
if (!subcmd) {
|
||||
error_code = ROSE_ERROR_Gen_NotAvailable;
|
||||
break;
|
||||
}
|
||||
subcmd->cmd = PRI_SUBCMD_TRANSFER_CALL;
|
||||
subcmd->u.transfer.call_1 = call->master_call;
|
||||
subcmd->u.transfer.call_2 = call_active;
|
||||
subcmd->u.transfer.is_call_1_held = 1;
|
||||
subcmd->u.transfer.is_call_2_held = 0;
|
||||
subcmd->u.transfer.invoke_id = invoke_id;
|
||||
|
||||
error_code = ROSE_ERROR_None;
|
||||
break;
|
||||
default:
|
||||
error_code = ROSE_ERROR_Gen_InvalidCallState;
|
||||
break;
|
||||
}
|
||||
|
||||
return error_code;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Process the received ETSI ExplicitEctExecute message.
|
||||
*
|
||||
* \param ctrl D channel controller.
|
||||
* \param call Q.931 call leg.
|
||||
* \param invoke_id Invoke id to put in response message.
|
||||
* \param link_id Link id of the other call involved in the transfer.
|
||||
*
|
||||
* \details
|
||||
* 1) Find the other call specified by the link_id in transfer request.
|
||||
* 2) Create the PRI_SUBCMD_TRANSFER_CALL event.
|
||||
*
|
||||
* \retval ROSE_ERROR_None on success.
|
||||
* \retval error_code on error.
|
||||
*/
|
||||
static enum rose_error_code etsi_explicit_ect_execute_transfer(struct pri *ctrl, q931_call *call, int invoke_id, int link_id)
|
||||
{
|
||||
enum rose_error_code error_code;
|
||||
struct pri_subcommand *subcmd;
|
||||
q931_call *call_2;
|
||||
|
||||
switch (call->ourcallstate) {
|
||||
case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
|
||||
case Q931_CALL_STATE_CALL_DELIVERED:
|
||||
case Q931_CALL_STATE_CALL_RECEIVED:
|
||||
case Q931_CALL_STATE_CONNECT_REQUEST:
|
||||
case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
|
||||
case Q931_CALL_STATE_ACTIVE:
|
||||
call_2 = q931_find_link_id_call(ctrl, link_id);
|
||||
if (!call_2 || call_2 == call->master_call) {
|
||||
error_code = ROSE_ERROR_Gen_NotAvailable;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Setup transfer subcommand */
|
||||
subcmd = q931_alloc_subcommand(ctrl);
|
||||
if (!subcmd) {
|
||||
error_code = ROSE_ERROR_Gen_NotAvailable;
|
||||
break;
|
||||
}
|
||||
subcmd->cmd = PRI_SUBCMD_TRANSFER_CALL;
|
||||
subcmd->u.transfer.call_1 = call->master_call;
|
||||
subcmd->u.transfer.call_2 = call_2;
|
||||
subcmd->u.transfer.is_call_1_held =
|
||||
(call->master_call->hold_state == Q931_HOLD_STATE_CALL_HELD) ? 1 : 0;
|
||||
subcmd->u.transfer.is_call_2_held =
|
||||
(call_2->hold_state == Q931_HOLD_STATE_CALL_HELD) ? 1 : 0;
|
||||
subcmd->u.transfer.invoke_id = invoke_id;
|
||||
|
||||
error_code = ROSE_ERROR_None;
|
||||
break;
|
||||
default:
|
||||
error_code = ROSE_ERROR_Gen_InvalidCallState;
|
||||
break;
|
||||
}
|
||||
|
||||
return error_code;
|
||||
}
|
||||
|
||||
/* AOC */
|
||||
/*!
|
||||
* \internal
|
||||
@ -3266,6 +3604,19 @@ int pri_rerouting_rsp(struct pri *ctrl, q931_call *call, int invoke_id, enum PRI
|
||||
return send_facility_error(ctrl, call, invoke_id, rose_err);
|
||||
}
|
||||
|
||||
int pri_transfer_rsp(struct pri *ctrl, q931_call *call, int invoke_id, int is_successful)
|
||||
{
|
||||
if (!ctrl || !call) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (is_successful) {
|
||||
return rose_result_ok_encode(ctrl, call, Q931_DISCONNECT, invoke_id);
|
||||
} else {
|
||||
return send_facility_error(ctrl, call, invoke_id, ROSE_ERROR_Gen_NotAvailable);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Handle the ROSE reject message.
|
||||
*
|
||||
@ -3517,6 +3868,7 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie
|
||||
struct q931_party_id party_id;
|
||||
struct q931_party_address party_address;
|
||||
struct q931_party_redirecting deflection;
|
||||
enum rose_error_code error_code;
|
||||
|
||||
switch (invoke->operation) {
|
||||
#if 0 /* Not handled yet */
|
||||
@ -3816,21 +4168,46 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie
|
||||
case ROSE_ITU_IdentificationOfCharge:
|
||||
break;
|
||||
#endif /* Not handled yet */
|
||||
#if 0 /* Not handled yet */
|
||||
case ROSE_ETSI_EctExecute:
|
||||
if (!PRI_MASTER(ctrl)->transfer_support) {
|
||||
send_facility_error(ctrl, call, invoke->invoke_id,
|
||||
ROSE_ERROR_Gen_NotSubscribed);
|
||||
break;
|
||||
}
|
||||
error_code = etsi_ect_execute_transfer(ctrl, call, invoke->invoke_id);
|
||||
if (error_code != ROSE_ERROR_None) {
|
||||
send_facility_error(ctrl, call, invoke->invoke_id, error_code);
|
||||
}
|
||||
break;
|
||||
case ROSE_ETSI_ExplicitEctExecute:
|
||||
error_code = etsi_explicit_ect_execute_transfer(ctrl, call, invoke->invoke_id,
|
||||
invoke->args.etsi.ExplicitEctExecute.link_id);
|
||||
if (error_code != ROSE_ERROR_None) {
|
||||
send_facility_error(ctrl, call, invoke->invoke_id, error_code);
|
||||
}
|
||||
break;
|
||||
#endif /* Not handled yet */
|
||||
case ROSE_ETSI_RequestSubaddress:
|
||||
/* Ignore since we are not handling subaddresses yet. */
|
||||
break;
|
||||
#if 0 /* Not handled yet */
|
||||
case ROSE_ETSI_SubaddressTransfer:
|
||||
break;
|
||||
case ROSE_ETSI_EctLinkIdRequest:
|
||||
break;
|
||||
#endif /* Not handled yet */
|
||||
case ROSE_ETSI_EctLinkIdRequest:
|
||||
if (!PRI_MASTER(ctrl)->transfer_support) {
|
||||
send_facility_error(ctrl, call, invoke->invoke_id,
|
||||
ROSE_ERROR_Gen_ResourceUnavailable);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Use the invoke_id sequence number as a link_id.
|
||||
* It should be safe enough to do this. If not then we will have to search
|
||||
* the call pool to ensure that the link_id is not already in use.
|
||||
*/
|
||||
call->master_call->link_id = get_invokeid(ctrl);
|
||||
call->master_call->is_link_id_valid = 1;
|
||||
send_ect_link_id_rsp(ctrl, call, invoke->invoke_id);
|
||||
break;
|
||||
case ROSE_ETSI_EctInform:
|
||||
/* redirectionNumber is put in remote_id.number */
|
||||
if (invoke->args.etsi.EctInform.redirection_present) {
|
||||
@ -3844,10 +4221,13 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie
|
||||
call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE;
|
||||
}
|
||||
break;
|
||||
#if 0 /* Not handled yet */
|
||||
case ROSE_ETSI_EctLoopTest:
|
||||
/*
|
||||
* The ETS 300 369 specification does a very poor job describing
|
||||
* how this message is used to detect loops.
|
||||
*/
|
||||
send_facility_error(ctrl, call, invoke->invoke_id, ROSE_ERROR_Gen_NotAvailable);
|
||||
break;
|
||||
#endif /* Not handled yet */
|
||||
#if defined(STATUS_REQUEST_PLACE_HOLDER)
|
||||
case ROSE_ETSI_StatusRequest:
|
||||
/* Not handled yet */
|
||||
|
@ -206,12 +206,14 @@ int eect_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2);
|
||||
|
||||
int rlt_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2);
|
||||
|
||||
int qsig_cf_callrerouting(struct pri *pri, q931_call *c, const char* dest, const char* original, const char* reason);
|
||||
int send_reroute_request(struct pri *ctrl, q931_call *call, const struct q931_party_id *caller, const struct q931_party_redirecting *deflection, int subscription_option);
|
||||
|
||||
/* starts a QSIG Path Replacement */
|
||||
int anfpr_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2);
|
||||
|
||||
int etsi_initiate_transfer(struct pri *ctrl, q931_call *call_1, q931_call *call_2);
|
||||
|
||||
int qsig_cf_callrerouting(struct pri *pri, q931_call *c, const char* dest, const char* original, const char* reason);
|
||||
int send_reroute_request(struct pri *ctrl, q931_call *call, const struct q931_party_id *caller, const struct q931_party_redirecting *deflection, int subscription_option);
|
||||
|
||||
int send_call_transfer_complete(struct pri *pri, q931_call *call, int call_status);
|
||||
|
||||
int rose_diverting_leg_information1_encode(struct pri *pri, q931_call *call);
|
||||
|
@ -106,6 +106,7 @@ struct pri {
|
||||
unsigned int deflection_support:1;/* TRUE if upper layer supports call deflection/rerouting. */
|
||||
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 transfer_support:1;/* TRUE if the upper layer supports ECT */
|
||||
|
||||
/* MDL variables */
|
||||
int mdl_error;
|
||||
@ -577,6 +578,11 @@ struct q931_call {
|
||||
int transferable; /* RLT call is transferable */
|
||||
unsigned int rlt_call_id; /* RLT call id */
|
||||
|
||||
/*! ETSI Explicit Call Transfer link id. */
|
||||
int link_id;
|
||||
/*! TRUE if link_id is valid. */
|
||||
int is_link_id_valid;
|
||||
|
||||
/* Bridged call info */
|
||||
q931_call *bridged_call; /* Pointer to other leg of bridged call (Used by Q.SIG when eliminating tromboned calls) */
|
||||
|
||||
@ -933,9 +939,13 @@ int q931_party_id_presentation(const struct q931_party_id *id);
|
||||
const char *q931_call_state_str(enum Q931_CALL_STATE callstate);
|
||||
const char *msg2str(int msg);
|
||||
|
||||
struct q931_call *q931_find_winning_call(struct q931_call *call);
|
||||
int q931_master_pass_event(struct pri *ctrl, struct q931_call *subcall, int msg_type);
|
||||
struct pri_subcommand *q931_alloc_subcommand(struct pri *ctrl);
|
||||
|
||||
struct q931_call *q931_find_link_id_call(struct pri *ctrl, int link_id);
|
||||
struct q931_call *q931_find_held_active_call(struct pri *ctrl, struct q931_call *held_call);
|
||||
|
||||
int q931_notify_redirection(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number);
|
||||
|
||||
struct pri_cc_record *pri_cc_find_by_reference(struct pri *ctrl, unsigned reference_id);
|
||||
|
54
q931.c
54
q931.c
@ -3603,6 +3603,7 @@ static inline void q931_dumpie(struct pri *ctrl, int codeset, q931_ie *ie, char
|
||||
*
|
||||
* \param ctrl D channel controller.
|
||||
* \param call Q.931 call leg.
|
||||
* \param cr Call Reference identifier.
|
||||
*
|
||||
* \note The call record is assumed to already be memset() to zero.
|
||||
*
|
||||
@ -5193,7 +5194,6 @@ static int q931_connect_acknowledge(struct pri *ctrl, q931_call *c)
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Find the winning subcall if it exists or current call if not outboundbroadcast.
|
||||
*
|
||||
* \param call Starting Q.931 call record of search.
|
||||
@ -5201,7 +5201,7 @@ static int q931_connect_acknowledge(struct pri *ctrl, q931_call *c)
|
||||
* \retval winning-call or given call if not outboundbroadcast.
|
||||
* \retval NULL if no winning call yet.
|
||||
*/
|
||||
static struct q931_call *q931_find_winning_call(struct q931_call *call)
|
||||
struct q931_call *q931_find_winning_call(struct q931_call *call)
|
||||
{
|
||||
struct q931_call *master;
|
||||
|
||||
@ -6986,7 +6986,53 @@ void q931_cc_indirect(struct pri *ctrl, struct pri_cc_record *cc_record, void (*
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Find the transfer call indicated by the given link_id.
|
||||
*
|
||||
* \param ctrl D channel controller.
|
||||
* \param link_id Link id of the other call involved in the transfer.
|
||||
*
|
||||
* \retval found-master-call on success.
|
||||
* \retval NULL on error.
|
||||
*/
|
||||
struct q931_call *q931_find_link_id_call(struct pri *ctrl, int link_id)
|
||||
{
|
||||
struct pri *master;
|
||||
struct q931_call *cur;
|
||||
struct q931_call *winner;
|
||||
struct q931_call *match;
|
||||
|
||||
match = NULL;
|
||||
master = PRI_MASTER(ctrl);
|
||||
for (cur = *master->callpool; cur; cur = cur->next) {
|
||||
if (cur->is_link_id_valid && cur->link_id == link_id) {
|
||||
/* Found the link_id call. */
|
||||
winner = q931_find_winning_call(cur);
|
||||
if (!winner) {
|
||||
/* There is no winner. */
|
||||
break;
|
||||
}
|
||||
switch (winner->ourcallstate) {
|
||||
case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
|
||||
case Q931_CALL_STATE_CALL_DELIVERED:
|
||||
case Q931_CALL_STATE_CALL_RECEIVED:
|
||||
case Q931_CALL_STATE_CONNECT_REQUEST:
|
||||
case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
|
||||
case Q931_CALL_STATE_ACTIVE:
|
||||
/* The link_id call is in a state suitable for transfer. */
|
||||
match = cur;
|
||||
break;
|
||||
default:
|
||||
/* The link_id call is not in a good state to transfer. */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Find the active call given the held call.
|
||||
*
|
||||
* \param ctrl D channel controller.
|
||||
@ -6995,7 +7041,7 @@ void q931_cc_indirect(struct pri *ctrl, struct pri_cc_record *cc_record, void (*
|
||||
* \retval master-active-call on success.
|
||||
* \retval NULL on error.
|
||||
*/
|
||||
static struct q931_call *q931_find_held_active_call(struct pri *ctrl, struct q931_call *held_call)
|
||||
struct q931_call *q931_find_held_active_call(struct pri *ctrl, struct q931_call *held_call)
|
||||
{
|
||||
struct pri *master;
|
||||
struct q931_call *cur;
|
||||
|
Loading…
Reference in New Issue
Block a user