diff --git a/libpri.h b/libpri.h index 0e1dbb4..fca0b0d 100644 --- a/libpri.h +++ b/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. * diff --git a/pri.c b/pri.c index ea6d176..f4e6333 100644 --- a/pri.c +++ b/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) { diff --git a/pri_facility.c b/pri_facility.c index 9035848..e48fa1d 100644 --- a/pri_facility.c +++ b/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 */ diff --git a/pri_facility.h b/pri_facility.h index fdd9403..0180397 100644 --- a/pri_facility.h +++ b/pri_facility.h @@ -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); diff --git a/pri_internal.h b/pri_internal.h index 62bac10..f8d2211 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -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); diff --git a/q931.c b/q931.c index 5785e03..2e9c642 100644 --- a/q931.c +++ b/q931.c @@ -3603,7 +3603,8 @@ 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. * * \return Nothing @@ -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;