[libpri-commits] rmudgett: branch rmudgett/ect r1498 - /team/rmudgett/ect/

SVN commits to the libpri project libpri-commits at lists.digium.com
Tue Feb 16 17:48:38 CST 2010


Author: rmudgett
Date: Tue Feb 16 17:48:33 2010
New Revision: 1498

URL: http://svnview.digium.com/svn/libpri?view=rev&rev=1498
Log:
Add code to handle received ETSI ECT requests.

Modified:
    team/rmudgett/ect/libpri.h
    team/rmudgett/ect/pri.c
    team/rmudgett/ect/pri_facility.c
    team/rmudgett/ect/pri_internal.h
    team/rmudgett/ect/q931.c

Modified: team/rmudgett/ect/libpri.h
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/ect/libpri.h?view=diff&rev=1498&r1=1497&r2=1498
==============================================================================
--- team/rmudgett/ect/libpri.h (original)
+++ team/rmudgett/ect/libpri.h Tue Feb 16 17:48:33 2010
@@ -538,6 +538,7 @@
 #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 {
@@ -632,6 +633,19 @@
 	 * list it should search for the cc_id.
 	 */
 	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 {
@@ -658,6 +672,7 @@
 		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;
 };
 
@@ -1330,6 +1345,29 @@
 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.
  *
  * \param ctrl D channel controller.

Modified: team/rmudgett/ect/pri.c
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/ect/pri.c?view=diff&rev=1498&r1=1497&r2=1498
==============================================================================
--- team/rmudgett/ect/pri.c (original)
+++ team/rmudgett/ect/pri.c Tue Feb 16 17:48:33 2010
@@ -1591,6 +1591,14 @@
 	sr->keypad_digits = keypad_digits;
 }
 
+void pri_transfer_enable(struct pri *ctrl, int enable)
+{
+	ctrl = PRI_MASTER(ctrl);
+	if (ctrl) {
+		ctrl->transfer_support = enable ? 1 : 0;
+	}
+}
+
 void pri_hold_enable(struct pri *ctrl, int enable)
 {
 	ctrl = PRI_MASTER(ctrl);

Modified: team/rmudgett/ect/pri_facility.c
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/ect/pri_facility.c?view=diff&rev=1498&r1=1497&r2=1498
==============================================================================
--- team/rmudgett/ect/pri_facility.c (original)
+++ team/rmudgett/ect/pri_facility.c Tue Feb 16 17:48:33 2010
@@ -2511,6 +2511,196 @@
 	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
@@ -3413,6 +3603,19 @@
 		break;
 	}
 	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);
+	}
 }
 
 /*!
@@ -3666,6 +3869,7 @@
 	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 */
@@ -3965,21 +4169,50 @@
 	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);
+		} else {
+			send_facility_result_ok(ctrl, call, invoke->invoke_id);
+		}
 		break;
 	case ROSE_ETSI_ExplicitEctExecute:
-		break;
-#endif	/* Not handled yet */
+		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);
+		} else {
+			send_facility_result_ok(ctrl, call, invoke->invoke_id);
+		}
+		break;
 	case ROSE_ETSI_RequestSubaddress:
 		/* Ignore since we are not handling subaddresses yet. */
 		break;
 #if 0	/* Not handled yet */
 	case ROSE_ETSI_SubaddressTransfer:
 		break;
+#endif	/* Not handled yet */
 	case ROSE_ETSI_EctLinkIdRequest:
-		break;
-#endif	/* Not handled yet */
+		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) {
@@ -3993,10 +4226,13 @@
 			call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE;
 		}
 		break;
-#if 0	/* Not handled yet */
 	case ROSE_ETSI_EctLoopTest:
-		break;
-#endif	/* Not handled yet */
+		/*
+		 * 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;
 #if defined(STATUS_REQUEST_PLACE_HOLDER)
 	case ROSE_ETSI_StatusRequest:
 		/* Not handled yet */

Modified: team/rmudgett/ect/pri_internal.h
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/ect/pri_internal.h?view=diff&rev=1498&r1=1497&r2=1498
==============================================================================
--- team/rmudgett/ect/pri_internal.h (original)
+++ team/rmudgett/ect/pri_internal.h Tue Feb 16 17:48:33 2010
@@ -98,9 +98,11 @@
 	int protodisc;
 	unsigned int bri:1;
 	unsigned int acceptinbanddisconnect:1;	/* Should we allow inband progress after DISCONNECT? */
+	unsigned int sendfacility:1;
 	unsigned int hold_support:1;/* TRUE if upper layer supports call hold. */
 	unsigned int deflection_support:1;/* TRUE if upper layer supports call deflection/rerouting. */
 	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;
@@ -173,7 +175,6 @@
 #endif
 
 	short last_invoke;	/* Last ROSE invoke ID (Valid in master record only) */
-	unsigned char sendfacility;
 
 	/*! Call completion (Valid in master record only) */
 	struct {
@@ -582,6 +583,11 @@
 
 	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) */
@@ -940,6 +946,9 @@
 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);

Modified: team/rmudgett/ect/q931.c
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/ect/q931.c?view=diff&rev=1498&r1=1497&r2=1498
==============================================================================
--- team/rmudgett/ect/q931.c (original)
+++ team/rmudgett/ect/q931.c Tue Feb 16 17:48:33 2010
@@ -6886,7 +6886,53 @@
 }
 
 /*!
- * \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.
@@ -6895,7 +6941,7 @@
  * \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;




More information about the libpri-commits mailing list