[libpri-commits] rmudgett: branch 1.4 r1322 - /branches/1.4/

SVN commits to the libpri project libpri-commits at lists.digium.com
Fri Nov 13 18:20:57 CST 2009


Author: rmudgett
Date: Fri Nov 13 18:20:53 2009
New Revision: 1322

URL: http://svnview.digium.com/svn/libpri?view=rev&rev=1322
Log:
Reimplement callback mechanism to handle APDU response messages that we care about.

1) No sent messages will remain in the APDU queue unless they have an
active timer to remove them.  The dummy call reference call and global
call reference call structures will not act like a memory leak to sent
messages.

2) The new T-RESPONSE timer will be the generic response guard if the
standards do not otherwise specify a timer for a message response.

3) The callback will be called.  If it is called because of a response
message, then the callback has an opportunity to indicate if more
responses are expected.

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

Modified: branches/1.4/libpri.h
URL: http://svnview.digium.com/svn/libpri/branches/1.4/libpri.h?view=diff&rev=1322&r1=1321&r2=1322
==============================================================================
--- branches/1.4/libpri.h (original)
+++ branches/1.4/libpri.h Fri Nov 13 18:20:53 2009
@@ -1296,6 +1296,8 @@
 	PRI_TIMER_T_HOLD,	/*!< Maximum time to wait for HOLD request response. */
 	PRI_TIMER_T_RETRIEVE,	/*!< Maximum time to wait for RETRIEVE request response. */
 
+	PRI_TIMER_T_RESPONSE,	/*!< Maximum time to wait for a typical APDU response. */
+
 	/* Must be last in the enum list */
 	PRI_MAX_TIMERS
 };

Modified: branches/1.4/pri.c
URL: http://svnview.digium.com/svn/libpri/branches/1.4/pri.c?view=diff&rev=1322&r1=1321&r2=1322
==============================================================================
--- branches/1.4/pri.c (original)
+++ branches/1.4/pri.c Fri Nov 13 18:20:53 2009
@@ -88,6 +88,7 @@
 	{ "T322",           PRI_TIMER_T322,             PRI_ALL_SWITCHES },
 	{ "T-HOLD",         PRI_TIMER_T_HOLD,           PRI_ALL_SWITCHES },
 	{ "T-RETRIEVE",     PRI_TIMER_T_RETRIEVE,       PRI_ALL_SWITCHES },
+	{ "T-RESPONSE",     PRI_TIMER_T_RESPONSE,       PRI_ALL_SWITCHES },
 /* *INDENT-ON* */
 };
 
@@ -156,6 +157,8 @@
 
 	ctrl->timers[PRI_TIMER_T_HOLD] = 4 * 1000;	/* Wait for HOLD request response. */
 	ctrl->timers[PRI_TIMER_T_RETRIEVE] = 4 * 1000;/* Wait for RETRIEVE request response. */
+
+	ctrl->timers[PRI_TIMER_T_RESPONSE] = 4 * 1000;	/* Maximum time to wait for a typical APDU response. */
 
 	/* Set any switch specific override default values */
 	switch (switchtype) {

Modified: branches/1.4/pri_facility.c
URL: http://svnview.digium.com/svn/libpri/branches/1.4/pri_facility.c?view=diff&rev=1322&r1=1321&r2=1322
==============================================================================
--- branches/1.4/pri_facility.c (original)
+++ branches/1.4/pri_facility.c Fri Nov 13 18:20:53 2009
@@ -1023,7 +1023,7 @@
 		return -1;
 	}
 
-	return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer);
+	return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL);
 }
 
 /*!
@@ -1192,7 +1192,7 @@
 		return -1;
 	}
 
-	return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer);
+	return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL);
 }
 
 /*!
@@ -1314,7 +1314,7 @@
 		return -1;
 	}
 
-	return pri_call_apdu_queue(call, messagetype, buffer, end - buffer);
+	return pri_call_apdu_queue(call, messagetype, buffer, end - buffer, NULL);
 }
 
 /*!
@@ -1385,7 +1385,7 @@
 		return -1;
 	}
 
-	if (pri_call_apdu_queue(apdubearer, Q931_FACILITY, buffer, end - buffer)) {
+	if (pri_call_apdu_queue(apdubearer, Q931_FACILITY, buffer, end - buffer, NULL)) {
 		return -1;
 	}
 
@@ -1448,7 +1448,7 @@
 		return -1;
 	}
 
-	return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer);
+	return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL);
 }
 
 /*!
@@ -1557,7 +1557,7 @@
 			return -1;
 		}
 
-		if (pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer)) {
+		if (pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL)) {
 			return -1;
 		}
 
@@ -1580,7 +1580,7 @@
 		mymessage = Q931_FACILITY;
 	}
 
-	return pri_call_apdu_queue(call, mymessage, buffer, end - buffer);
+	return pri_call_apdu_queue(call, mymessage, buffer, end - buffer, NULL);
 }
 /* End Callername */
 
@@ -1714,7 +1714,7 @@
 		return -1;
 	}
 
-	return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer);
+	return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL);
 }
 /* End MWI */
 
@@ -1773,7 +1773,7 @@
 		return -1;
 	}
 
-	if (pri_call_apdu_queue(c1, Q931_FACILITY, buffer, end - buffer)) {
+	if (pri_call_apdu_queue(c1, Q931_FACILITY, buffer, end - buffer, NULL)) {
 		pri_message(ctrl, "Could not queue APDU in facility message\n");
 		return -1;
 	}
@@ -2094,7 +2094,7 @@
 		return -1;
 	}
 
-	return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer);
+	return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL);
 }
 
 /*!
@@ -2221,14 +2221,10 @@
 {
 	int res;
 
-	res = pri_call_apdu_queue_cleanup(call->bridged_call);
-	if (res) {
-		pri_message(ctrl, "Could not Clear queue ADPU\n");
-		return -1;
-	}
+	pri_call_apdu_queue_cleanup(call->bridged_call);
 
 	/* Send message */
-	res = pri_call_apdu_queue(call->bridged_call, Q931_FACILITY, ie->data, ie->len);
+	res = pri_call_apdu_queue(call->bridged_call, Q931_FACILITY, ie->data, ie->len, NULL);
 	if (res) {
 		pri_message(ctrl, "Could not queue ADPU in facility message\n");
 		return -1;
@@ -2296,7 +2292,7 @@
 		return -1;
 	}
 
-	res = pri_call_apdu_queue(c1, Q931_FACILITY, buffer, pos - buffer);
+	res = pri_call_apdu_queue(c1, Q931_FACILITY, buffer, pos - buffer, NULL);
 	if (res) {
 		pri_message(ctrl, "Could not queue ADPU in facility message\n");
 		return -1;
@@ -2325,7 +2321,7 @@
 		return -1;
 	}
 
-	res = pri_call_apdu_queue(c2, Q931_FACILITY, buffer, pos - buffer);
+	res = pri_call_apdu_queue(c2, Q931_FACILITY, buffer, pos - buffer, NULL);
 	if (res) {
 		pri_message(ctrl, "Could not queue ADPU in facility message\n");
 		return -1;
@@ -2410,7 +2406,7 @@
 
 	/* 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)
+	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;
@@ -2556,7 +2552,7 @@
 		return -1;
 	}
 
-	return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer);
+	return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL);
 }
 
 /* ===== End Call Transfer Supplementary Service (ECMA-178) ===== */
@@ -2624,7 +2620,7 @@
 		return -1;
 	}
 
-	return pri_call_apdu_queue(call, messagetype, buffer, end - buffer);
+	return pri_call_apdu_queue(call, messagetype, buffer, end - buffer, NULL);
 }
 
 /*!
@@ -2690,7 +2686,7 @@
 		return -1;
 	}
 
-	return pri_call_apdu_queue(call, messagetype, buffer, end - buffer);
+	return pri_call_apdu_queue(call, messagetype, buffer, end - buffer, NULL);
 }
 
 /*!
@@ -2700,11 +2696,18 @@
  * \param messagetype Q.931 message type.
  * \param apdu Facility ie contents buffer.
  * \param apdu_len Length of the contents buffer.
+ * \param response Sender supplied information to handle APDU response messages.
+ *        NULL if don't care about responses.
+ *
+ * \note
+ * Only APDU messages with an invoke component can supply a response pointer.
+ * If any other APDU messages supply a response pointer then aliasing of the
+ * invoke_id can occur.
  *
  * \retval 0 on success.
  * \retval -1 on error.
  */
-int pri_call_apdu_queue(q931_call *call, int messagetype, const unsigned char *apdu, int apdu_len)
+int pri_call_apdu_queue(q931_call *call, int messagetype, const unsigned char *apdu, int apdu_len, struct apdu_callback_data *response)
 {
 	struct apdu_event *cur = NULL;
 	struct apdu_event *new_event = NULL;
@@ -2731,11 +2734,16 @@
 		return -1;
 	}
 
+	/* Fill in the APDU event */
 	new_event->message = messagetype;
+	if (response) {
+		new_event->response = *response;
+	}
+	new_event->call = call;
 	new_event->apdu_len = apdu_len;
 	memcpy(new_event->apdu, apdu, apdu_len);
 
-	/* Append APDU to the end of the list. */
+	/* Append APDU event to the end of the list. */
 	if (call->apdus) {
 		for (cur = call->apdus; cur->next; cur = cur->next) {
 		}
@@ -2747,22 +2755,85 @@
 	return 0;
 }
 
-int pri_call_apdu_queue_cleanup(q931_call *call)
-{
-	struct apdu_event *cur_event = NULL, *free_event = NULL;
-
-	if (call && call->apdus) {
+/* Used by q931.c to cleanup the apdu queue upon destruction of a call */
+void pri_call_apdu_queue_cleanup(q931_call *call)
+{
+	struct apdu_event *cur_event;
+	struct apdu_event *free_event;
+
+	if (call) {
 		cur_event = call->apdus;
 		call->apdus = NULL;
 		while (cur_event) {
-			/* TODO: callbacks, some way of giving return res on status of apdu */
+			if (cur_event->response.callback) {
+				/* Indicate to callback that the APDU is being cleaned up. */
+				cur_event->response.callback(APDU_CALLBACK_REASON_CLEANUP, call->pri,
+					call, cur_event, NULL);
+
+				/* Stop any response timeout. */
+				pri_schedule_del(call->pri, cur_event->timer);
+			}
 			free_event = cur_event;
 			cur_event = cur_event->next;
 			free(free_event);
 		}
 	}
-
-	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Find an outstanding APDU with the given invoke id.
+ *
+ * \param call Call to find APDU.
+ * \param invoke_id Invoke id to match outstanding APDUs in queue.
+ *
+ * \retval apdu_event if found.
+ * \retval NULL if not found.
+ */
+static struct apdu_event *pri_call_apdu_find(struct q931_call *call, int invoke_id)
+{
+	struct apdu_event *apdu;
+
+	for (apdu = call->apdus; apdu; apdu = apdu->next) {
+		/*
+		 * Note: The APDU cannot be sent and still in the queue without a
+		 * callback and timeout timer active.  Therefore, an invoke_id of
+		 * zero is valid and not just the result of a memset().
+		 */
+		if (apdu->response.invoke_id == invoke_id && apdu->sent) {
+			break;
+		}
+	}
+	return apdu;
+}
+
+/*!
+ * \brief Delete the given APDU event from the given call.
+ *
+ * \param call Call to remove the APDU.
+ * \param doomed APDU event to delete.
+ *
+ * \return Nothing
+ */
+void pri_call_apdu_delete(struct q931_call *call, struct apdu_event *doomed)
+{
+	struct apdu_event **prev;
+	struct apdu_event *cur;
+
+	/* Find APDU in list. */
+	for (prev = &call->apdus, cur = call->apdus;
+		cur;
+		prev = &cur->next, cur = cur->next) {
+		if (cur == doomed) {
+			/* Stop any response timeout. */
+			pri_schedule_del(call->pri, cur->timer);
+
+			/* Remove APDU from list. */
+			*prev = cur->next;
+			free(cur);
+			break;
+		}
+	}
 }
 
 /*! \note Only called when sending the SETUP message. */
@@ -2948,7 +3019,7 @@
 		return -1;
 	}
 
-	return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer);
+	return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL);
 }
 
 /*!
@@ -3080,7 +3151,7 @@
 		return -1;
 	}
 
-	return pri_call_apdu_queue(call, msgtype, buffer, end - buffer);
+	return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL);
 }
 
 /*!
@@ -3163,11 +3234,39 @@
 void rose_handle_reject(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie,
 	const struct fac_extension_header *header, const struct rose_msg_reject *reject)
 {
+	struct apdu_event *apdu;
+	union apdu_msg_data msg;
+
+	/* Gripe to the user about getting rejected. */
 	pri_error(ctrl, "ROSE REJECT:\n");
 	if (reject->invoke_id_present) {
 		pri_error(ctrl, "\tINVOKE ID: %d\n", reject->invoke_id);
 	}
 	pri_error(ctrl, "\tPROBLEM: %s\n", rose_reject2str(reject->code));
+
+	switch (ctrl->switchtype) {
+	case PRI_SWITCH_DMS100:
+		/* The DMS-100 switch apparently handles invoke_id as an invoke operation. */
+		return;
+	default:
+		break;
+	}
+
+	if (!reject->invoke_id_present) {
+		/*
+		 * No invoke id to look up so we cannot match it to any outstanding APDUs.
+		 * This REJECT is apparently meant for someone monitoring the link.
+		 */
+		return;
+	}
+	apdu = pri_call_apdu_find(call, reject->invoke_id);
+	if (!apdu) {
+		return;
+	}
+	msg.reject = reject;
+	if (apdu->response.callback(APDU_CALLBACK_REASON_MSG_REJECT, ctrl, call, apdu, &msg)) {
+		pri_call_apdu_delete(call, apdu);
+	}
 }
 
 /*!
@@ -3186,7 +3285,10 @@
 	const struct fac_extension_header *header, const struct rose_msg_error *error)
 {
 	const char *dms100_operation;
-
+	struct apdu_event *apdu;
+	union apdu_msg_data msg;
+
+	/* Gripe to the user about getting an error. */
 	pri_error(ctrl, "ROSE RETURN ERROR:\n");
 	switch (ctrl->switchtype) {
 	case PRI_SWITCH_DMS100:
@@ -3211,6 +3313,23 @@
 		break;
 	}
 	pri_error(ctrl, "\tERROR: %s\n", rose_error2str(error->code));
+
+	switch (ctrl->switchtype) {
+	case PRI_SWITCH_DMS100:
+		/* The DMS-100 switch apparently handles invoke_id as an invoke operation. */
+		return;
+	default:
+		break;
+	}
+
+	apdu = pri_call_apdu_find(call, error->invoke_id);
+	if (!apdu) {
+		return;
+	}
+	msg.error = error;
+	if (apdu->response.callback(APDU_CALLBACK_REASON_MSG_ERROR, ctrl, call, apdu, &msg)) {
+		pri_call_apdu_delete(call, apdu);
+	}
 }
 
 /*!
@@ -3228,8 +3347,12 @@
 void rose_handle_result(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie,
 	const struct fac_extension_header *header, const struct rose_msg_result *result)
 {
+	struct apdu_event *apdu;
+	union apdu_msg_data msg;
+
 	switch (ctrl->switchtype) {
 	case PRI_SWITCH_DMS100:
+		/* The DMS-100 switch apparently handles invoke_id as an invoke operation. */
 		switch (result->invoke_id) {
 		case ROSE_DMS100_RLT_OPERATION_IND:
 			if (result->operation != ROSE_DMS100_RLT_OperationInd) {
@@ -3256,87 +3379,13 @@
 		break;
 	}
 
-	switch (result->operation) {
-	case ROSE_None:
-		/*
-		 * This is simply a positive ACK to the invoke request.
-		 * The invoke ID must be used to distinguish between outstanding
-		 * invoke requests.
-		 */
-		break;
-#if 0	/* Not handled yet */
-	case ROSE_ETSI_ActivationDiversion:
-		break;
-	case ROSE_ETSI_DeactivationDiversion:
-		break;
-	case ROSE_ETSI_InterrogationDiversion:
-		break;
-#endif	/* Not handled yet */
-	case ROSE_ETSI_CallDeflection:
-		/* Successfully completed call deflection.  Nothing to do. */
-		break;
-	case ROSE_ETSI_CallRerouting:
-		/* Successfully completed call rerouting.  Nothing to do. */
-		break;
-#if 0	/* Not handled yet */
-	case ROSE_ETSI_InterrogateServedUserNumbers:
-		break;
-#endif	/* Not handled yet */
-#if 0	/* Not handled yet */
-	case ROSE_ETSI_ChargingRequest:
-		break;
-#endif	/* Not handled yet */
-#if 0	/* Not handled yet */
-	case ROSE_ETSI_EctExecute:
-		break;
-	case ROSE_ETSI_ExplicitEctExecute:
-		break;
-	case ROSE_ETSI_EctLinkIdRequest:
-		break;
-	case ROSE_ETSI_EctLoopTest:
-		break;
-#endif	/* Not handled yet */
-#if 0	/* Not handled yet */
-	case ROSE_QSIG_ChargeRequest:
-		break;
-	case ROSE_QSIG_AocComplete:
-		break;
-#endif	/* Not handled yet */
-#if 0	/* Not handled yet */
-	case ROSE_QSIG_CallTransferIdentify:
-		break;
-	case ROSE_QSIG_CallTransferInitiate:
-		break;
-	case ROSE_QSIG_CallTransferSetup:
-		break;
-#endif	/* Not handled yet */
-#if 0	/* Not handled yet */
-	case ROSE_QSIG_ActivateDiversionQ:
-		break;
-	case ROSE_QSIG_DeactivateDiversionQ:
-		break;
-	case ROSE_QSIG_InterrogateDiversionQ:
-		break;
-	case ROSE_QSIG_CheckRestriction:
-		break;
-#endif	/* Not handled yet */
-	case ROSE_QSIG_CallRerouting:
-		/* Successfully completed call rerouting.  Nothing to do. */
-		break;
-#if 0	/* Not handled yet */
-	case ROSE_QSIG_MWIActivate:
-		break;
-	case ROSE_QSIG_MWIDeactivate:
-		break;
-	case ROSE_QSIG_MWIInterrogate:
-		break;
-#endif	/* Not handled yet */
-	default:
-		if (ctrl->debug & PRI_DEBUG_APDU) {
-			pri_message(ctrl, "!! ROSE result operation not handled! %s\n",
-				rose_operation2str(result->operation));
-		}
-		break;
+	apdu = pri_call_apdu_find(call, result->invoke_id);
+	if (!apdu) {
+		return;
+	}
+	msg.result = result;
+	if (apdu->response.callback(APDU_CALLBACK_REASON_MSG_RESULT, ctrl, call, apdu, &msg)) {
+		pri_call_apdu_delete(call, apdu);
 	}
 }
 

Modified: branches/1.4/pri_facility.h
URL: http://svnview.digium.com/svn/libpri/branches/1.4/pri_facility.h?view=diff&rev=1322&r1=1321&r2=1322
==============================================================================
--- branches/1.4/pri_facility.h (original)
+++ branches/1.4/pri_facility.h Fri Nov 13 18:20:53 2009
@@ -31,6 +31,13 @@
 #define _PRI_FACILITY_H
 #include "pri_q931.h"
 
+/* Forward declare some structs */
+struct fac_extension_header;
+struct rose_msg_invoke;
+struct rose_msg_result;
+struct rose_msg_error;
+struct rose_msg_reject;
+
 /* Protocol Profile field */
 #define Q932_PROTOCOL_MASK			0x1F
 #define Q932_PROTOCOL_ROSE			0x11	/* X.219 & X.229 */
@@ -65,6 +72,107 @@
 #define QSIG_NOTIFICATION_WITHOUT_DIVERTED_TO_NR	0x01
 #define QSIG_NOTIFICATION_WITH_DIVERTED_TO_NR		0x02
 
+/*! Reasons an APDU callback is called. */
+enum APDU_CALLBACK_REASON {
+	/*!
+	 * \brief Send setup error.  Abort and cleanup.
+	 * \note The message may or may not actually get sent.
+	 * \note The callback cannot generate an event subcmd.
+	 * \note The callback should not send messages.  Out of order messages will result.
+	 */
+	APDU_CALLBACK_REASON_ERROR,
+	/*!
+	 * \brief Abort and cleanup.
+	 * \note The APDU queue is being destroyed.
+	 * \note The callback cannot generate an event subcmd.
+	 * \note The callback cannot send messages as the call is likely being destroyed.
+	 */
+	APDU_CALLBACK_REASON_CLEANUP,
+	/*!
+	 * \brief Timeout waiting for responses to the message.
+	 * \note The callback can generate an event subcmd.
+	 * \note The callback can send messages.
+	 */
+	APDU_CALLBACK_REASON_TIMEOUT,
+	/*!
+	 * \brief Received a facility response message.
+	 * \note The callback can generate an event subcmd.
+	 * \note The callback can send messages.
+	 */
+	APDU_CALLBACK_REASON_MSG_RESULT,
+	/*!
+	 * \brief Received a facility error message.
+	 * \note The callback can generate an event subcmd.
+	 * \note The callback can send messages.
+	 */
+	APDU_CALLBACK_REASON_MSG_ERROR,
+	/*!
+	 * \brief Received a facility reject message.
+	 * \note The callback can generate an event subcmd.
+	 * \note The callback can send messages.
+	 */
+	APDU_CALLBACK_REASON_MSG_REJECT,
+};
+
+union apdu_msg_data {
+	const struct rose_msg_result *result;
+	const struct rose_msg_error *error;
+	const struct rose_msg_reject *reject;
+};
+
+union apdu_callback_param {
+	void *ptr;
+	long value;
+	char pad[8];
+};
+
+struct apdu_callback_data {
+	/*! APDU invoke id to match with any response messages. (Result/Error/Reject) */
+	int invoke_id;
+	/*!
+	 * \brief Time to wait for responses to APDU in ms.
+	 * \note Set to 0 if send the message only.
+	 * \note Set to less than 0 for PRI_TIMER_T_RESPONSE time.
+	 */
+	int timeout_time;
+	/*!
+	 * \brief APDU 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.)
+	 *
+	 * \note
+	 * A callback must be supplied if the sender cares about any APDU_CALLBACK_REASON.
+	 *
+	 * \return TRUE if no more responses are expected.
+	 */
+	int (*callback)(enum APDU_CALLBACK_REASON reason, struct pri *ctrl, struct q931_call *call, struct apdu_event *apdu, const union apdu_msg_data *msg);
+	/*! \brief Sender data for the callback function to identify the particular APDU. */
+	union apdu_callback_param user;
+};
+
+struct apdu_event {
+	/*! Linked list pointer */
+	struct apdu_event *next;
+	/*! TRUE if this APDU has been sent. */
+	int sent;
+	/*! What message to send the ADPU in */
+	int message;
+	/*! Sender supplied information to handle APDU response messages. */
+	struct apdu_callback_data response;
+	/*! Q.931 call leg.  (Needed for the APDU timeout.) */
+	struct q931_call *call;
+	/*! Response timeout timer. */
+	int timer;
+	/*! Length of ADPU */
+	int apdu_len;
+	/*! ADPU to send */
+	unsigned char apdu[255];
+};
+
 /* Queues an MWI apdu on a the given call */
 int mwi_message_send(struct pri *pri, q931_call *call, struct pri_sr *req, int activate);
 
@@ -87,22 +195,14 @@
 int rose_connected_name_encode(struct pri *pri, q931_call *call, int messagetype);
 int rose_called_name_encode(struct pri *pri, q931_call *call, int messagetype);
 
-int pri_call_apdu_queue(q931_call *call, int messagetype, const unsigned char *apdu, int apdu_len);
-
-/* Used by q931.c to cleanup the apdu queue upon destruction of a call */
-int pri_call_apdu_queue_cleanup(q931_call *call);
+int pri_call_apdu_queue(q931_call *call, int messagetype, const unsigned char *apdu, int apdu_len, struct apdu_callback_data *response);
+void pri_call_apdu_queue_cleanup(q931_call *call);
+void pri_call_apdu_delete(struct q931_call *call, struct apdu_event *doomed);
 
 /* Adds the "standard" APDUs to a call */
 int pri_call_add_standard_apdus(struct pri *pri, q931_call *call);
 
 void asn1_dump(struct pri *ctrl, const unsigned char *start_asn1, const unsigned char *end);
-
-/* Forward declare some ROSE structures for the following prototypes */
-struct fac_extension_header;
-struct rose_msg_invoke;
-struct rose_msg_result;
-struct rose_msg_error;
-struct rose_msg_reject;
 
 void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_invoke *invoke);
 void rose_handle_result(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_result *result);

Modified: branches/1.4/pri_internal.h
URL: http://svnview.digium.com/svn/libpri/branches/1.4/pri_internal.h?view=diff&rev=1322&r1=1321&r2=1322
==============================================================================
--- branches/1.4/pri_internal.h (original)
+++ branches/1.4/pri_internal.h Fri Nov 13 18:20:53 2009
@@ -40,6 +40,9 @@
 #define DBGHEAD __FILE__ ":%d %s: "
 #define DBGINFO __LINE__,__PRETTY_FUNCTION__
 
+/* Forward declare some structs */
+struct apdu_event;
+
 struct pri_sched {
 	struct timeval when;
 	void (*callback)(void *data);
@@ -307,13 +310,6 @@
 
 #define Q931_MAX_TEI	8
 
-struct apdu_event {
-	struct apdu_event *next;	/* Linked list pointer */
-	int message;			/* What message to send the ADPU in */
-	int apdu_len; 			/* Length of ADPU */
-	unsigned char apdu[255];			/* ADPU to send */
-};
-
 /*! \brief Incoming call transfer states. */
 enum INCOMING_CT_STATE {
 	/*!

Modified: branches/1.4/q931.c
URL: http://svnview.digium.com/svn/libpri/branches/1.4/q931.c?view=diff&rev=1322&r1=1321&r2=1322
==============================================================================
--- branches/1.4/q931.c (original)
+++ branches/1.4/q931.c Fri Nov 13 18:20:53 2009
@@ -2266,6 +2266,8 @@
 	return 0;
 }
 
+static void q931_apdu_timeout(void *data);
+
 static int transmit_facility(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
 {
 	struct apdu_event **prev;
@@ -2275,9 +2277,7 @@
 	for (prev = &call->apdus, cur = call->apdus;
 		cur;
 		prev = &cur->next, cur = cur->next) {
-		if (cur->message == msgtype) {
-			/* Remove APDU from list. */
-			*prev = cur->next;
+		if (!cur->sent && cur->message == msgtype) {
 			break;
 		}
 	}
@@ -2292,15 +2292,52 @@
 		facility_decode_dump(ctrl, cur->apdu, cur->apdu_len);
 	}
 
-	if (cur->apdu_len > 235) { /* TODO: find out how much space we can use */
-		pri_message(ctrl, "Requested APDU (%d bytes) is too long\n", cur->apdu_len);
+	if (len < cur->apdu_len) { 
+		pri_error(ctrl,
+			"Could not fit facility ie in message.  Size needed:%d  Available space:%d\n",
+			cur->apdu_len + 2, len);
+
+		/* Remove APDU from list. */
+		*prev = cur->next;
+
+		if (cur->response.callback) {
+			/* Indicate to callback that the APDU had a problem getting sent. */
+			cur->response.callback(APDU_CALLBACK_REASON_ERROR, ctrl, call, cur, NULL);
+		}
+
 		free(cur);
 		return 0;
 	}
 
 	memcpy(ie->data, cur->apdu, cur->apdu_len);
 	apdu_len = cur->apdu_len;
-	free(cur);
+	cur->sent = 1;
+
+	if (cur->response.callback && cur->response.timeout_time) {
+		int duration;
+
+		if (0 < cur->response.timeout_time) {
+			/* Sender specified timeout duration. */
+			duration = cur->response.timeout_time;
+		} else {
+			/* Sender wants to use the typical timeout duration. */
+			duration = ctrl->timers[PRI_TIMER_T_RESPONSE];
+		}
+		cur->timer = pri_schedule_event(ctrl, duration, q931_apdu_timeout, cur);
+		if (!cur->timer) {
+			/* Remove APDU from list. */
+			*prev = cur->next;
+
+			/* Indicate to callback that the APDU had a problem getting sent. */
+			cur->response.callback(APDU_CALLBACK_REASON_ERROR, ctrl, call, cur, NULL);
+
+			free(cur);
+		}
+	} else if (!cur->timer) {
+		/* Remove APDU from list. */
+		*prev = cur->next;
+		free(cur);
+	}
 
 	return apdu_len + 2;
 }
@@ -6225,6 +6262,34 @@
 
 /*!
  * \internal
+ * \brief APDU wait for response message timeout.
+ *
+ * \param data Callback data pointer.
+ *
+ * \return Nothing
+ */
+static void q931_apdu_timeout(void *data)
+{
+	struct apdu_event *apdu;
+	struct pri *ctrl;
+	struct q931_call *call;
+
+	apdu = data;
+	call = apdu->call;
+	ctrl = call->pri;
+
+	q931_clr_subcommands(ctrl);
+	apdu->response.callback(APDU_CALLBACK_REASON_TIMEOUT, ctrl, call, apdu, NULL);
+	if (ctrl->subcmds.counter_subcmd) {
+		q931_fill_facility_event(ctrl, call);
+		ctrl->schedev = 1;
+	}
+
+	pri_call_apdu_delete(call, apdu);
+}
+
+/*!
+ * \internal
  * \brief Find the active call given the held call.
  *
  * \param ctrl D channel controller.
@@ -6510,7 +6575,7 @@
 		c->useruserinfo[0] = '\0';
 
 		for (cur = c->apdus; cur; cur = cur->next) {
-			if (cur->message == Q931_FACILITY) {
+			if (!cur->sent && cur->message == Q931_FACILITY) {
 				q931_facility(ctrl, c);
 				break;
 			}
@@ -6615,7 +6680,7 @@
 		ctrl->ev.proceeding.call = c->master_call;
 
 		for (cur = c->apdus; cur; cur = cur->next) {
-			if (cur->message == Q931_FACILITY) {
+			if (!cur->sent && cur->message == Q931_FACILITY) {
 				q931_facility(ctrl, c);
 				break;
 			}
@@ -6892,7 +6957,7 @@
 		ctrl->ev.setup_ack.call = c->master_call;
 
 		for (cur = c->apdus; cur; cur = cur->next) {
-			if (cur->message == Q931_FACILITY) {
+			if (!cur->sent && cur->message == Q931_FACILITY) {
 				q931_facility(ctrl, c);
 				break;
 			}




More information about the libpri-commits mailing list