diff --git a/libpri.h b/libpri.h index bfab67c..f9986c5 100644 --- a/libpri.h +++ b/libpri.h @@ -1296,6 +1296,8 @@ enum PRI_TIMERS_AND_COUNTERS { 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 }; diff --git a/pri.c b/pri.c index 964da7a..b3c6877 100644 --- a/pri.c +++ b/pri.c @@ -88,6 +88,7 @@ static const struct pri_timer_table pri_timer[] = { { "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* */ }; @@ -157,6 +158,8 @@ static void pri_default_timers(struct pri *ctrl, int switchtype) 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) { default: diff --git a/pri_facility.c b/pri_facility.c index 284136b..fa34e3c 100644 --- a/pri_facility.c +++ b/pri_facility.c @@ -1023,7 +1023,7 @@ int rose_diverting_leg_information1_encode(struct pri *ctrl, q931_call *call) 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 @@ static int rose_diverting_leg_information2_encode(struct pri *ctrl, q931_call *c 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 @@ int rose_diverting_leg_information3_encode(struct pri *ctrl, q931_call *call, 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 @@ int rlt_initiate_transfer(struct pri *ctrl, q931_call *c1, q931_call *c2) 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 @@ static int add_dms100_transfer_ability_apdu(struct pri *ctrl, q931_call *call) 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 @@ static int add_callername_facility_ies(struct pri *ctrl, q931_call *call, int cp 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 @@ static int add_callername_facility_ies(struct pri *ctrl, q931_call *call, int cp 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 @@ int mwi_message_send(struct pri *ctrl, q931_call *call, struct pri_sr *req, int 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 @@ int eect_initiate_transfer(struct pri *ctrl, q931_call *c1, q931_call *c2) 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 @@ static int rose_reroute_request_encode(struct pri *ctrl, q931_call *call, 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 @@ static int anfpr_pathreplacement_respond(struct pri *ctrl, q931_call *call, q931 { 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 @@ int anfpr_initiate_transfer(struct pri *ctrl, q931_call *c1, q931_call *c2) 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 @@ int anfpr_initiate_transfer(struct pri *ctrl, q931_call *c1, q931_call *c2) 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 @@ static int aoc_aoce_charging_unit_encode(struct pri *ctrl, q931_call *call, /* 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 @@ static int rose_call_transfer_complete_encode(struct pri *ctrl, q931_call *call, 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 @@ int rose_called_name_encode(struct pri *ctrl, q931_call *call, int messagetype) 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 @@ int rose_connected_name_encode(struct pri *ctrl, q931_call *call, int messagetyp 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 @@ int rose_connected_name_encode(struct pri *ctrl, q931_call *call, int messagetyp * \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 @@ int pri_call_apdu_queue(q931_call *call, int messagetype, const unsigned char *a 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 @@ int pri_call_apdu_queue(q931_call *call, int messagetype, const unsigned char *a return 0; } -int pri_call_apdu_queue_cleanup(q931_call *call) +/* 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 = NULL, *free_event = NULL; + struct apdu_event *cur_event; + struct apdu_event *free_event; - if (call && call->apdus) { + 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 @@ static int rose_facility_error_encode(struct pri *ctrl, q931_call *call, int inv 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 @@ static int rose_result_ok_encode(struct pri *ctrl, q931_call *call, int msgtype, 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 @@ int pri_rerouting_rsp(struct pri *ctrl, q931_call *call, int invoke_id, enum PRI 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 @@ void rose_handle_error(struct pri *ctrl, q931_call *call, int msgtype, q931_ie * 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 @@ void rose_handle_error(struct pri *ctrl, q931_call *call, int msgtype, q931_ie * 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_error(struct pri *ctrl, q931_call *call, int msgtype, q931_ie * 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 @@ void rose_handle_result(struct pri *ctrl, q931_call *call, int msgtype, q931_ie 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); } } diff --git a/pri_facility.h b/pri_facility.h index 6c9bbdd..018e7d2 100644 --- a/pri_facility.h +++ b/pri_facility.h @@ -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,23 +195,15 @@ int rose_diverting_leg_information3_encode(struct pri *pri, q931_call *call, int 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); void rose_handle_error(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_error *error); diff --git a/pri_internal.h b/pri_internal.h index fbde7e1..7181c75 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -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 @@ struct pri_sr { #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 { /*! diff --git a/q931.c b/q931.c index 0c117b0..3640722 100644 --- a/q931.c +++ b/q931.c @@ -2266,6 +2266,8 @@ static int receive_progress_indicator(int full_ie, struct pri *ctrl, q931_call * 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 @@ static int transmit_facility(int full_ie, struct pri *ctrl, q931_call *call, int 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 @@ static int transmit_facility(int full_ie, struct pri *ctrl, q931_call *call, int 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; } @@ -6223,6 +6260,34 @@ static void q931_fill_facility_event(struct pri *ctrl, struct q931_call *call) ctrl->ev.facility.callingplan = call->remote_id.number.plan; } +/*! + * \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. @@ -6510,7 +6575,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct 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 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct 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 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct 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; }