From a2dcb6adba3d5b23fbbee38a7d736ee510d8bcfc Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Fri, 23 Oct 2009 19:50:45 +0000 Subject: [PATCH] Add BRI PTMP NT mode, HOLD/RETRIEVE, Call rerouting/deflection, and keypad facility support. * Added support for BRI PTMP NT mode. (Overlap dialing NT -> TE not supported.) * Added handling of received HOLD/RETRIEVE messages and the optional ability to transfer a held call on disconnect similar to an analog phone. * Added CallRerouting/CallDeflection support for Q.SIG, ETSI PTP, ETSI PTMP. Will reroute/deflect an outgoing call when receive the message. Can use the DAHDISendCallreroutingFacility to send the message for the supported switches. * Added ability to send/receive keypad digits in the SETUP message. Send keypad digits in SETUP message: Dial(DAHDI/g1[/K][/extension]) Access any received keypad digits in SETUP message by: ${CHANNEL(keypad_digits)} (closes issue #15048) Tested by: rmudgett, mattf git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@1242 2fbb986a-6c06-0410-b554-c9c1f0a7f128 --- libpri.h | 277 ++++++- pri.c | 178 ++++- pri_facility.c | 954 ++++++++++++++++++++++-- pri_facility.h | 9 +- pri_internal.h | 82 ++- pri_q921.h | 2 + pri_q931.h | 26 +- q921.c | 128 +++- q931.c | 1903 +++++++++++++++++++++++++++++++++++++++++++++--- 9 files changed, 3338 insertions(+), 221 deletions(-) diff --git a/libpri.h b/libpri.h index b6035d3..181d293 100644 --- a/libpri.h +++ b/libpri.h @@ -91,6 +91,12 @@ #define PRI_EVENT_KEYPAD_DIGIT 18 /* When we receive during ACTIVE state (INFORMATION) */ #define PRI_EVENT_SERVICE 19 /* SERVICE maintenance message */ #define PRI_EVENT_SERVICE_ACK 20 /* SERVICE maintenance acknowledgement message */ +#define PRI_EVENT_HOLD 21 /* HOLD request received */ +#define PRI_EVENT_HOLD_ACK 22 /* HOLD_ACKNOWLEDGE received */ +#define PRI_EVENT_HOLD_REJ 23 /* HOLD_REJECT received */ +#define PRI_EVENT_RETRIEVE 24 /* RETRIEVE request received */ +#define PRI_EVENT_RETRIEVE_ACK 25 /* RETRIEVE_ACKNOWLEDGE received */ +#define PRI_EVENT_RETRIEVE_REJ 26 /* RETRIEVE_REJECT received */ /* Simple states */ #define PRI_STATE_DOWN 0 @@ -200,6 +206,7 @@ #define PRI_CAUSE_NO_ANSWER 19 #define PRI_CAUSE_CALL_REJECTED 21 #define PRI_CAUSE_NUMBER_CHANGED 22 +#define PRI_CAUSE_NONSELECTED_USER_CLEARING 26 #define PRI_CAUSE_DESTINATION_OUT_OF_ORDER 27 #define PRI_CAUSE_INVALID_NUMBER_FORMAT 28 #define PRI_CAUSE_FACILITY_REJECTED 29 /* !Q.SIG */ @@ -212,6 +219,7 @@ #define PRI_CAUSE_ACCESS_INFO_DISCARDED 43 /* !Q.SIG */ #define PRI_CAUSE_REQUESTED_CHAN_UNAVAIL 44 #define PRI_CAUSE_PRE_EMPTED 45 /* !Q.SIG */ +#define PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED 47 #define PRI_CAUSE_FACILITY_NOT_SUBSCRIBED 50 /* !Q.SIG */ #define PRI_CAUSE_OUTGOING_CALL_BARRED 52 /* !Q.SIG */ #define PRI_CAUSE_INCOMING_CALL_BARRED 54 /* !Q.SIG */ @@ -468,9 +476,37 @@ struct pri_party_redirecting { int reason; }; +/*! + * \brief Information for rerouting/deflecting the call. + */ +struct pri_rerouting_data { + /*! + * \brief Updated caller-id information. + * \note The information may have been altered by procedure in the private network. + */ + struct pri_party_id caller; + /*! + * \note + * deflection.to is the new called number and must always be present. + */ + struct pri_party_redirecting deflection; + /*! + * \brief Diverting user subscription option to specify if caller is notified. + * \details + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2), + * notApplicable(3) (Status only.) + */ + int subscription_option; + /*! Invocation ID to use when sending a reply to the call rerouting/deflection request. */ + int invoke_id; +}; + /* Subcommands derived from supplementary services. */ #define PRI_SUBCMD_REDIRECTING 1 #define PRI_SUBCMD_CONNECTED_LINE 2 +#define PRI_SUBCMD_REROUTING 3 struct pri_subcommand { @@ -481,6 +517,7 @@ struct pri_subcommand { char reserve_space[512]; struct pri_party_connected_line connected_line; struct pri_party_redirecting redirecting; + struct pri_rerouting_data rerouting; } u; }; @@ -497,13 +534,14 @@ struct pri_subcommands { * Event channel parameter encoding: * 3322 2222 2222 1111 1111 1100 0000 0000 * 1098 7654 3210 9876 5432 1098 7654 3210 - * xxxx xxxx xxxx xxDC BBBBBBBBB AAAAAAAAA + * xxxx xxxx xxxx xEDC BBBBBBBBB AAAAAAAAA * * Bit field * A - B channel * B - Span (DS1) (0 - 127) * C - DS1 Explicit bit * D - D channel (cis_call) bit (status only) + * E - Call is held bit (status only) * * B channel values: * 0 - No channel (ISDN uses for call waiting feature) @@ -567,10 +605,16 @@ struct pri_event_facility { char callingnum[256]; /*!< Deprecated, preserved for struct pri_event_facname compatibility */ int channel; int cref; + /*! + * \brief Master call or normal call. + * \note Call pointer known about by upper layer. + * \note NULL if dummy call reference. + */ q931_call *call; int callingpres; /*!< Presentation of Calling CallerID (Deprecated, preserved for struct pri_event_facname compatibility) */ int callingplan; /*!< Dialing plan of Calling entity (Deprecated, preserved for struct pri_event_facname compatibility) */ struct pri_subcommands *subcmds; + q931_call *subcall; /*!< Subcall to send any reply toward. */ }; #define PRI_CALLINGPLANANI @@ -609,6 +653,7 @@ typedef struct pri_event_ring { struct pri_subcommands *subcmds; struct pri_party_id calling; /* Calling Party's info, initially subaddress' */ struct pri_party_subaddress called_subaddress; /* Called party's subaddress */ + char keypad_digits[64]; /* Keypad digits in the SETUP message. */ } pri_event_ring; typedef struct pri_event_hangup { @@ -616,10 +661,22 @@ typedef struct pri_event_hangup { int channel; /* Channel requested */ int cause; int cref; - q931_call *call; /* Opaque call pointer */ + q931_call *call; /* Opaque call pointer of call hanging up. */ long aoc_units; /* Advise of Charge number of charged units */ char useruserinfo[260]; /* User->User info */ struct pri_subcommands *subcmds; + /*! + * \brief Opaque held call pointer for possible transfer to active call. + * \note The call_held and call_active pointers must not be NULL if + * transfer held call on disconnect is available. + */ + q931_call *call_held; + /*! + * \brief Opaque active call pointer for possible transfer with held call. + * \note The call_held and call_active pointers must not be NULL if + * transfer held call on disconnect is available. + */ + q931_call *call_active; } pri_event_hangup; typedef struct pri_event_restart_ack { @@ -651,6 +708,7 @@ typedef struct pri_event_notify { int channel; int info; struct pri_subcommands *subcmds; + q931_call *call; } pri_event_notify; typedef struct pri_event_keypad_digit { @@ -673,6 +731,51 @@ typedef struct pri_event_service_ack { int changestatus; } pri_event_service_ack; +struct pri_event_hold { + int e; + int channel; + q931_call *call; + struct pri_subcommands *subcmds; +}; + +struct pri_event_hold_ack { + int e; + int channel; + q931_call *call; + struct pri_subcommands *subcmds; +}; + +struct pri_event_hold_rej { + int e; + int channel; + q931_call *call; + int cause; + struct pri_subcommands *subcmds; +}; + +struct pri_event_retrieve { + int e; + int channel; + q931_call *call; + int flexible; /* Are we flexible with our channel selection? */ + struct pri_subcommands *subcmds; +}; + +struct pri_event_retrieve_ack { + int e; + int channel; + q931_call *call; + struct pri_subcommands *subcmds; +}; + +struct pri_event_retrieve_rej { + int e; + int channel; + q931_call *call; + int cause; + struct pri_subcommands *subcmds; +}; + typedef union { int e; pri_event_generic gen; /* Generic view */ @@ -691,6 +794,12 @@ typedef union { pri_event_service service; /* service message */ pri_event_service_ack service_ack; /* service acknowledgement message */ struct pri_event_facility facility; + struct pri_event_hold hold; + struct pri_event_hold_ack hold_ack; + struct pri_event_hold_rej hold_rej; + struct pri_event_retrieve retrieve; + struct pri_event_retrieve_ack retrieve_ack; + struct pri_event_retrieve_rej retrieve_rej; } pri_event; struct pri; @@ -888,6 +997,16 @@ void pri_sr_set_redirecting_parties(struct pri_sr *sr, const struct pri_party_re /*! \note Use pri_sr_set_redirecting_parties() instead to pass more precise redirecting information. */ int pri_sr_set_redirecting(struct pri_sr *sr, char *num, int plan, int pres, int reason); +/*! + * \brief Set the keypad digits in the call SETUP record. + * + * \param sr New call SETUP record. + * \param keypad_digits Keypad digits to send. + * + * \return Nothing + */ +void pri_sr_set_keypad_digits(struct pri_sr *sr, const char *keypad_digits); + #define PRI_USER_USER_TX /* Set the user user field. Warning! don't send binary data accross this field */ void pri_sr_set_useruser(struct pri_sr *sr, const char *userchars); @@ -973,6 +1092,157 @@ int pri_notify(struct pri *pri, q931_call *c, int channel, int info); int pri_callrerouting_facility(struct pri *pri, q931_call *call, const char *dest, const char* original, const char* reason); +/*! + * \brief Set the call deflection/rerouting feature enable flag. + * + * \param ctrl D channel controller. + * \param enable TRUE to enable call deflection/rerouting feature. + * + * \return Nothing + */ +void pri_reroute_enable(struct pri *ctrl, int enable); + +/*! + * \brief Send the CallRerouting/CallDeflection message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param caller Call rerouting/deflecting updated caller data. (NULL if data not updated.) + * \param deflection Call rerouting/deflecting redirection data. + * \param subscription_option Diverting user subscription option to specify if caller is notified. + * + * \note + * deflection->to is the new called number and must always be present. + * \note + * subscription option: + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2) + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_reroute_call(struct pri *ctrl, q931_call *call, const struct pri_party_id *caller, const struct pri_party_redirecting *deflection, int subscription_option); + +enum PRI_REROUTING_RSP_CODE { + /*! + * Rerouting invocation accepted and the network provider option + * "served user call retention on invocation of diversion" + * is "clear call on invocation". + */ + PRI_REROUTING_RSP_OK_CLEAR, + /*! + * Rerouting invocation accepted and the network provider option + * "served user call retention on invocation of diversion" + * is "retain call until alerting begins at the deflected-to user". + */ + PRI_REROUTING_RSP_OK_RETAIN, + PRI_REROUTING_RSP_NOT_SUBSCRIBED, + PRI_REROUTING_RSP_NOT_AVAILABLE, + /*! Supplementary service interaction not allowed. */ + PRI_REROUTING_RSP_NOT_ALLOWED, + PRI_REROUTING_RSP_INVALID_NUMBER, + /*! Deflection to prohibited number (e.g., operator, police, emergency). */ + PRI_REROUTING_RSP_SPECIAL_SERVICE_NUMBER, + /*! Deflection to served user number. */ + PRI_REROUTING_RSP_DIVERSION_TO_SELF, + PRI_REROUTING_RSP_MAX_DIVERSIONS_EXCEEDED, + PRI_REROUTING_RSP_RESOURCE_UNAVAILABLE, +}; + +/*! + * \brief Send the CallRerouteing/CallDeflection response message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param invoke_id Value given by the initiating request. + * \param code The result to send. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_rerouting_rsp(struct pri *ctrl, q931_call *call, int invoke_id, enum PRI_REROUTING_RSP_CODE code); + +/*! + * \brief Set the call hold feature enable flag. + * + * \param ctrl D channel controller. + * \param enable TRUE to enable call hold feature. + * + * \return Nothing + */ +void pri_hold_enable(struct pri *ctrl, int enable); + +/*! + * \brief Send the HOLD message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_hold(struct pri *ctrl, q931_call *call); + +/*! + * \brief Send the HOLD ACKNOWLEDGE message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_hold_ack(struct pri *ctrl, q931_call *call); + +/*! + * \brief Send the HOLD REJECT message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * \param cause Q.931 cause code for rejecting the hold request. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_hold_rej(struct pri *ctrl, q931_call *call, int cause); + +/*! + * \brief Send the RETRIEVE message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * \param channel Encoded channel id to use. If zero do not send channel id. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_retrieve(struct pri *ctrl, q931_call *call, int channel); + +/*! + * \brief Send the RETRIEVE ACKNOWLEDGE message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * \param channel Encoded channel id to use. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_retrieve_ack(struct pri *ctrl, q931_call *call, int channel); + +/*! + * \brief Send the RETRIEVE REJECT message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * \param cause Q.931 cause code for rejecting the retrieve request. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_retrieve_rej(struct pri *ctrl, q931_call *call, int cause); + /* Get/Set PRI Timers */ #define PRI_GETSET_TIMERS int pri_set_timer(struct pri *pri, int timer, int value); @@ -1015,6 +1285,9 @@ enum PRI_TIMERS_AND_COUNTERS { PRI_TIMER_TM20, /*!< Maximum time awaiting XID response */ PRI_TIMER_NM20, /*!< Number of XID retransmits */ + PRI_TIMER_T_HOLD, /*!< Maximum time to wait for HOLD request response. */ + PRI_TIMER_T_RETRIEVE, /*!< Maximum time to wait for RETRIEVE request response. */ + /* Must be last in the enum list */ _PRI_MAX_TIMERS, PRI_MAX_TIMERS = (_PRI_MAX_TIMERS < 32) ? 32 : _PRI_MAX_TIMERS diff --git a/pri.c b/pri.c index bd91b41..17ee0a8 100644 --- a/pri.c +++ b/pri.c @@ -86,6 +86,8 @@ static const struct pri_timer_table pri_timer[] = { { "T320", PRI_TIMER_T320, PRI_ALL_SWITCHES }, { "T321", PRI_TIMER_T321, PRI_ALL_SWITCHES }, { "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 }, /* *INDENT-ON* */ }; @@ -150,6 +152,10 @@ static void pri_default_timers(struct pri *ctrl, int switchtype) ctrl->timers[PRI_TIMER_T313] = 4 * 1000; /* Wait for CONNECT acknowledge, CPE side only */ ctrl->timers[PRI_TIMER_TM20] = 2500; /* Max time awaiting XID response - Q.921 Appendix IV */ ctrl->timers[PRI_TIMER_NM20] = 3; /* Number of XID retransmits - Q.921 Appendix IV */ + ctrl->timers[PRI_TIMER_T303] = 4 * 1000; /* Length between SETUP retransmissions and timeout */ + + 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. */ /* Set any switch specific override default values */ switch (switchtype) { @@ -381,6 +387,12 @@ char *pri_event2str(int id) { PRI_EVENT_KEYPAD_DIGIT, "Keypad Digit" }, { PRI_EVENT_SERVICE, "Service" }, { PRI_EVENT_SERVICE_ACK, "Service ACK" }, + { PRI_EVENT_HOLD, "Hold" }, + { PRI_EVENT_HOLD_ACK, "Hold Ack" }, + { PRI_EVENT_HOLD_REJ, "Hold Rej" }, + { PRI_EVENT_RETRIEVE, "Retrieve" }, + { PRI_EVENT_RETRIEVE_ACK, "Retrieve ACK" }, + { PRI_EVENT_RETRIEVE_REJ, "Retrieve Rej" }, /* *INDENT-ON* */ }; @@ -539,15 +551,6 @@ int pri_keypad_facility(struct pri *pri, q931_call *call, const char *digits) return q931_keypad_facility(pri, call, digits); } - -int pri_callrerouting_facility(struct pri *pri, q931_call *call, const char *dest, const char* original, const char* reason) -{ - if (!pri || !call) - return -1; - - return qsig_cf_callrerouting(pri, call, dest, original, reason); -} - int pri_notify(struct pri *pri, q931_call *call, int channel, int info) { if (!pri || !call) @@ -558,7 +561,7 @@ int pri_notify(struct pri *pri, q931_call *call, int channel, int info) void pri_destroycall(struct pri *pri, q931_call *call) { if (pri && call) - __q931_destroycall(pri, call); + q931_destroycall(pri, call); return; } @@ -669,6 +672,8 @@ static void pri_copy_party_id_to_q931(struct q931_party_id *q931_id, const struc int pri_connected_line_update(struct pri *ctrl, q931_call *call, const struct pri_party_connected_line *connected) { struct q931_party_id party_id; + unsigned idx; + struct q931_call *subcall; if (!ctrl || !call) { return -1; @@ -682,6 +687,16 @@ int pri_connected_line_update(struct pri *ctrl, q931_call *call, const struct pr } call->local_id = party_id; + /* Update all subcalls with new local_id. */ + if (call->outboundbroadcast && call->master_call == call) { + for (idx = 0; idx < Q931_MAX_TEI; ++idx) { + subcall = call->subcalls[idx]; + if (subcall) { + subcall->local_id = party_id; + } + } + } + switch (call->ourcallstate) { case Q931_CALL_STATE_CALL_INITIATED: case Q931_CALL_STATE_OVERLAP_SENDING: @@ -723,6 +738,9 @@ int pri_connected_line_update(struct pri *ctrl, q931_call *call, const struct pr int pri_redirecting_update(struct pri *ctrl, q931_call *call, const struct pri_party_redirecting *redirecting) { + unsigned idx; + struct q931_call *subcall; + if (!ctrl || !call) { return -1; } @@ -732,6 +750,21 @@ int pri_redirecting_update(struct pri *ctrl, q931_call *call, const struct pri_p q931_party_id_fixup(ctrl, &call->redirecting.to); call->redirecting.reason = redirecting->reason; + /* + * Update all subcalls with new redirecting.to information and reason. + * I do not think we will ever have any subcalls when this data is relevant, + * but update it just in case. + */ + if (call->outboundbroadcast && call->master_call == call) { + for (idx = 0; idx < Q931_MAX_TEI; ++idx) { + subcall = call->subcalls[idx]; + if (subcall) { + subcall->redirecting.to = call->redirecting.to; + subcall->redirecting.reason = redirecting->reason; + } + } + } + switch (call->ourcallstate) { case Q931_CALL_STATE_NULL: /* Save the remaining redirecting information before we place a call. */ @@ -1046,7 +1079,7 @@ void pri_message(struct pri *pri, char *fmt, ...) vsnprintf(tmp, sizeof(tmp), fmt, ap); va_end(ap); if (__pri_message) - __pri_message(pri, tmp); + __pri_message(PRI_MASTER(pri), tmp); else fputs(tmp, stdout); } @@ -1059,7 +1092,7 @@ void pri_error(struct pri *pri, char *fmt, ...) vsnprintf(tmp, sizeof(tmp), fmt, ap); va_end(ap); if (__pri_error) - __pri_error(pri, tmp); + __pri_error(PRI_MASTER(pri), tmp); else fputs(tmp, stderr); } @@ -1320,3 +1353,124 @@ void pri_sr_set_reversecharge(struct pri_sr *sr, int requested) { sr->reversecharge = requested; } + +void pri_sr_set_keypad_digits(struct pri_sr *sr, const char *keypad_digits) +{ + sr->keypad_digits = keypad_digits; +} + +void pri_hold_enable(struct pri *ctrl, int enable) +{ + ctrl = PRI_MASTER(ctrl); + if (ctrl) { + ctrl->hold_support = enable ? 1 : 0; + } +} + +int pri_hold(struct pri *ctrl, q931_call *call) +{ + if (!ctrl || !call) { + return -1; + } + return q931_send_hold(ctrl, call); +} + +int pri_hold_ack(struct pri *ctrl, q931_call *call) +{ + if (!ctrl || !call) { + return -1; + } + return q931_send_hold_ack(ctrl, call); +} + +int pri_hold_rej(struct pri *ctrl, q931_call *call, int cause) +{ + if (!ctrl || !call) { + return -1; + } + return q931_send_hold_rej(ctrl, call, cause); +} + +int pri_retrieve(struct pri *ctrl, q931_call *call, int channel) +{ + if (!ctrl || !call) { + return -1; + } + return q931_send_retrieve(ctrl, call, channel); +} + +int pri_retrieve_ack(struct pri *ctrl, q931_call *call, int channel) +{ + if (!ctrl || !call) { + return -1; + } + return q931_send_retrieve_ack(ctrl, call, channel); +} + +int pri_retrieve_rej(struct pri *ctrl, q931_call *call, int cause) +{ + if (!ctrl || !call) { + return -1; + } + return q931_send_retrieve_rej(ctrl, call, cause); +} + +int pri_callrerouting_facility(struct pri *pri, q931_call *call, const char *dest, const char* original, const char* reason) +{ + if (!pri || !call || !dest) + return -1; + + return qsig_cf_callrerouting(pri, call, dest, original, reason); +} + +void pri_reroute_enable(struct pri *ctrl, int enable) +{ + ctrl = PRI_MASTER(ctrl); + if (ctrl) { + ctrl->deflection_support = enable ? 1 : 0; + } +} + +int pri_reroute_call(struct pri *ctrl, q931_call *call, const struct pri_party_id *caller, const struct pri_party_redirecting *deflection, int subscription_option) +{ + const struct q931_party_id *caller_id; + struct q931_party_id local_caller; + struct q931_party_redirecting reroute; + + if (!ctrl || !call || !deflection) { + return -1; + } + + if (caller) { + /* Convert the caller update information. */ + pri_copy_party_id_to_q931(&local_caller, caller); + q931_party_id_fixup(ctrl, &local_caller); + caller_id = &local_caller; + } else { + caller_id = NULL; + } + + /* Convert the deflection information. */ + q931_party_redirecting_init(&reroute); + pri_copy_party_id_to_q931(&reroute.from, &deflection->from); + q931_party_id_fixup(ctrl, &reroute.from); + pri_copy_party_id_to_q931(&reroute.to, &deflection->to); + q931_party_id_fixup(ctrl, &reroute.to); + pri_copy_party_id_to_q931(&reroute.orig_called, &deflection->orig_called); + q931_party_id_fixup(ctrl, &reroute.orig_called); + reroute.reason = deflection->reason; + reroute.orig_reason = deflection->orig_reason; + if (deflection->count <= 0) { + /* + * We are deflecting with an unknown count + * so assume the count is one. + */ + reroute.count = 1; + } else if (deflection->count < PRI_MAX_REDIRECTS) { + reroute.count = deflection->count; + } else { + reroute.count = PRI_MAX_REDIRECTS; + } + + return send_reroute_request(ctrl, call, caller_id, &reroute, subscription_option); +} diff --git a/pri_facility.c b/pri_facility.c index 022aa5d..b19e31b 100644 --- a/pri_facility.c +++ b/pri_facility.c @@ -517,6 +517,24 @@ static void rose_copy_number_to_q931(struct pri *ctrl, q931_number->valid = 1; } +/*! + * \internal + * \brief Copy the given rose address to the q931_party_id address. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param q931_address Q.931 party id structure to fill address + * \param rose_address ROSE address structure + * + * \note It is assumed that the q931_address has been initialized before calling. + * + * \return Nothing + */ +static void rose_copy_address_to_q931(struct pri *ctrl, + struct q931_party_id *q931_address, const struct roseAddress *rose_address) +{ + rose_copy_number_to_q931(ctrl, &q931_address->number, &rose_address->number); +} + /*! * \internal * \brief Copy the given rose presented screened party number to the q931_party_number @@ -650,6 +668,22 @@ static void q931_copy_number_to_rose(struct pri *ctrl, rose_number->length = strlen((char *) rose_number->str); } +/*! + * \internal + * \brief Copy the given q931_party_id address to the rose address. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param rose_address ROSE address structure + * \param q931_address Q.931 party id structure to give address + * + * \return Nothing + */ +static void q931_copy_address_to_rose(struct pri *ctrl, struct roseAddress *rose_address, + const struct q931_party_id *q931_address) +{ + q931_copy_number_to_rose(ctrl, &rose_address->number, &q931_address->number); +} + /*! * \internal * \brief Copy the given q931_party_number to the rose presented screened party number @@ -1658,17 +1692,24 @@ int eect_initiate_transfer(struct pri *ctrl, q931_call *c1, q931_call *c2) * \param pos Starting position to encode the facility ie contents. * \param end End of facility ie contents encoding data buffer. * \param call Q.931 call leg. - * \param calling Calling party info. - * \param dest Destination number. - * \param original Original called number. - * \param reason Rerouting reason: cfu, cfb, cfnr + * \param calling Call rerouting/deflecting updated caller data. + * \param deflection Call rerouting/deflecting redirection data. + * \param subscription_option Diverting user subscription option to specify if caller is notified. + * + * \note + * deflection->to is the new called number and must always be present. + * \note + * subscription option: + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2) * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_qsig_call_rerouting(struct pri *ctrl, unsigned char *pos, unsigned char *end, q931_call *call, const struct q931_party_id *calling, - const char *dest, const char *original, const char *reason) + const struct q931_party_redirecting *deflection, int subscription_option) { struct fac_extension_header header; struct rose_msg_invoke msg; @@ -1689,26 +1730,13 @@ static unsigned char *enc_qsig_call_rerouting(struct pri *ctrl, unsigned char *p msg.operation = ROSE_QSIG_CallRerouting; msg.invoke_id = get_invokeid(ctrl); - /* The rerouting_reason defaults to unknown */ - if (reason) { - if (!strcasecmp(reason, "cfu")) { - msg.args.qsig.CallRerouting.rerouting_reason = 1; /* cfu */ - } else if (!strcasecmp(reason, "cfb")) { - msg.args.qsig.CallRerouting.rerouting_reason = 2; /* cfb */ - } else if (!strcasecmp(reason, "cfnr")) { - msg.args.qsig.CallRerouting.rerouting_reason = 3; /* cfnr */ - } - } + msg.args.qsig.CallRerouting.rerouting_reason = + redirectingreason_from_q931(ctrl, deflection->reason); - /* calledAddress is the passed in dest number */ - msg.args.qsig.CallRerouting.called.number.plan = 1; /* public */ - msg.args.qsig.CallRerouting.called.number.ton = 0; /* unknown */ - libpri_copy_string((char *) msg.args.qsig.CallRerouting.called.number.str, dest, - sizeof(msg.args.qsig.CallRerouting.called.number.str)); - msg.args.qsig.CallRerouting.called.number.length = strlen((char *) - msg.args.qsig.CallRerouting.called.number.str); + /* calledAddress is the passed in deflection->to address */ + q931_copy_address_to_rose(ctrl, &msg.args.qsig.CallRerouting.called, &deflection->to); - msg.args.qsig.CallRerouting.diversion_counter = call->redirecting.count + 1; + msg.args.qsig.CallRerouting.diversion_counter = deflection->count; /* pSS1InfoElement */ q931ie_pos = msg.args.qsig.CallRerouting.q931ie_contents; @@ -1724,25 +1752,48 @@ static unsigned char *enc_qsig_call_rerouting(struct pri *ctrl, unsigned char *p msg.args.qsig.CallRerouting.q931ie.length = q931ie_pos - msg.args.qsig.CallRerouting.q931ie_contents; - /* lastReroutingNr is the passed in original number */ - msg.args.qsig.CallRerouting.last_rerouting.presentation = 0; /* presentationAllowedNumber */ - msg.args.qsig.CallRerouting.last_rerouting.number.plan = 1; /* public */ - msg.args.qsig.CallRerouting.last_rerouting.number.ton = 0; /* unknown */ - libpri_copy_string((char *) msg.args.qsig.CallRerouting.last_rerouting.number.str, - original, sizeof(msg.args.qsig.CallRerouting.last_rerouting.number.str)); - msg.args.qsig.CallRerouting.last_rerouting.number.length = strlen((char *) - msg.args.qsig.CallRerouting.last_rerouting.number.str); + /* lastReroutingNr is the passed in deflection->from.number */ + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.qsig.CallRerouting.last_rerouting, &deflection->from.number); - msg.args.qsig.CallRerouting.subscription_option = 0; /* noNotification */ + msg.args.qsig.CallRerouting.subscription_option = subscription_option; /* callingNumber is the passed in calling->number */ q931_copy_presented_number_screened_to_rose(ctrl, &msg.args.qsig.CallRerouting.calling, &calling->number); + /* callingPartySubaddress is the passed in calling->subaddress if valid */ + /* callingName is the passed in calling->name if valid */ if (calling->name.valid) { msg.args.qsig.CallRerouting.calling_name_present = 1; - q931_copy_name_to_rose(ctrl, &msg.args.qsig.CallRerouting.calling_name, &calling->name); + q931_copy_name_to_rose(ctrl, &msg.args.qsig.CallRerouting.calling_name, + &calling->name); + } + + if (1 < deflection->count) { + /* originalCalledNr is the deflection->orig_called.number */ + msg.args.qsig.CallRerouting.original_called_present = 1; + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.qsig.CallRerouting.original_called, + &deflection->orig_called.number); + + msg.args.qsig.CallRerouting.original_rerouting_reason_present = 1; + if (deflection->orig_called.number.valid) { + msg.args.qsig.CallRerouting.original_rerouting_reason = + redirectingreason_from_q931(ctrl, deflection->orig_reason); + } else { + msg.args.qsig.CallRerouting.original_rerouting_reason = + QSIG_DIVERT_REASON_UNKNOWN; + } + + /* originalCalledName is the deflection->orig_called.name */ + if (deflection->orig_called.name.valid) { + msg.args.qsig.CallRerouting.original_called_name_present = 1; + q931_copy_name_to_rose(ctrl, + &msg.args.qsig.CallRerouting.original_called_name, + &deflection->orig_called.name); + } } pos = rose_encode_invoke(ctrl, pos, end, &msg); @@ -1750,6 +1801,229 @@ static unsigned char *enc_qsig_call_rerouting(struct pri *ctrl, unsigned char *p return pos; } +/*! + * \internal + * \brief Encode the ETSI CallRerouting invoke 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 call Q.931 call leg. + * \param calling Call rerouting/deflecting updated caller data. + * \param deflection Call rerouting/deflecting redirection data. + * \param subscription_option Diverting user subscription option to specify if caller is notified. + * + * \note + * deflection->to is the new called number and must always be present. + * \note + * subscription option: + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2) + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_call_rerouting(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call, const struct q931_party_id *calling, + const struct q931_party_redirecting *deflection, int subscription_option) +{ + struct rose_msg_invoke msg; + unsigned char *q931ie_pos; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_ETSI_CallRerouting; + msg.invoke_id = get_invokeid(ctrl); + + msg.args.etsi.CallRerouting.rerouting_reason = + redirectingreason_from_q931(ctrl, deflection->reason); + + /* calledAddress is the passed in deflection->to address */ + q931_copy_address_to_rose(ctrl, &msg.args.etsi.CallRerouting.called_address, + &deflection->to); + + msg.args.etsi.CallRerouting.rerouting_counter = deflection->count; + + /* q931InfoElement */ + q931ie_pos = msg.args.etsi.CallRerouting.q931ie_contents; + *q931ie_pos++ = 0x04; /* Bearer Capability IE */ + *q931ie_pos++ = 0x03; /* len */ + *q931ie_pos++ = 0x80 | call->transcapability; /* Rxed transfer capability. */ + *q931ie_pos++ = 0x90; /* circuit mode, 64kbit/s */ + *q931ie_pos++ = 0xa3; /* level1 protocol, a-law */ + msg.args.etsi.CallRerouting.q931ie.length = q931ie_pos + - msg.args.etsi.CallRerouting.q931ie_contents; + + /* lastReroutingNr is the passed in deflection->from.number */ + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.etsi.CallRerouting.last_rerouting, &deflection->from.number); + + msg.args.etsi.CallRerouting.subscription_option = subscription_option; + + /* callingPartySubaddress is the passed in calling->subaddress if valid */ + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode the ETSI CallDeflection invoke 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 call Q.931 call leg. + * \param deflection Call deflection address. + * + * \note + * deflection is the new called number and must always be present. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_call_deflection(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call, const struct q931_party_id *deflection) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_ETSI_CallDeflection; + msg.invoke_id = get_invokeid(ctrl); + + /* deflectionAddress is the passed in deflection->to address */ + q931_copy_address_to_rose(ctrl, &msg.args.etsi.CallDeflection.deflection, + deflection); + + msg.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user_present = 1; + switch (deflection->number.presentation & PRI_PRES_RESTRICTION) { + case PRI_PRES_ALLOWED: + msg.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user = 1; + break; + default: + case PRI_PRES_UNAVAILABLE: + case PRI_PRES_RESTRICTED: + break; + } + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue the CallRerouting/CallDeflection message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param caller Call rerouting/deflecting updated caller data. (NULL if data not updated.) + * \param deflection Call rerouting/deflecting redirection data. + * \param subscription_option Diverting user subscription option to specify if caller is notified. + * + * \note + * deflection->to is the new called number and must always be present. + * \note + * subscription option: + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2) + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_reroute_request_encode(struct pri *ctrl, q931_call *call, + const struct q931_party_id *caller, const struct q931_party_redirecting *deflection, + int subscription_option) +{ + unsigned char buffer[256]; + unsigned char *end; + + if (!caller) { + /* + * We are deflecting an incoming call back to the network. + * Therefore, the Caller-ID is the remote party. + */ + caller = &call->remote_id; + } + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (q931_is_ptmp(ctrl)) { + end = + enc_etsi_call_deflection(ctrl, buffer, buffer + sizeof(buffer), call, + &deflection->to); + } else { + end = + enc_etsi_call_rerouting(ctrl, buffer, buffer + sizeof(buffer), call, + caller, deflection, subscription_option); + } + break; + case PRI_SWITCH_QSIG: + end = + enc_qsig_call_rerouting(ctrl, buffer, buffer + sizeof(buffer), call, caller, + deflection, subscription_option); + break; + default: + return -1; + } + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL, NULL); +} + +/*! + * \brief Send the CallRerouting/CallDeflection message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param caller Call rerouting/deflecting updated caller data. (NULL if data not updated.) + * \param deflection Call rerouting/deflecting redirection data. + * \param subscription_option Diverting user subscription option to specify if caller is notified. + * + * \note + * deflection->to is the new called number and must always be present. + * \note + * subscription option: + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2) + * + * \retval 0 on success. + * \retval -1 on error. + */ +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) +{ + if (!deflection->to.number.str[0]) { + /* Must have a deflect to number. That is the point of deflection. */ + return -1; + } + if (rose_reroute_request_encode(ctrl, call, caller, deflection, subscription_option) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for CallRerouting/CallDeflection message.\n"); + return -1; + } + + return 0; +} + /*! * \brief Send the Q.SIG CallRerouting invoke message. * @@ -1765,38 +2039,57 @@ static unsigned char *enc_qsig_call_rerouting(struct pri *ctrl, unsigned char *p int qsig_cf_callrerouting(struct pri *ctrl, q931_call *call, const char *dest, const char *original, const char *reason) { - unsigned char buffer[255]; - unsigned char *end; - int res; + struct q931_party_redirecting reroute; - /* - * We are deflecting an incoming call back to the network. - * Therefore, the Caller-ID is the remote party. - */ - end = - enc_qsig_call_rerouting(ctrl, buffer, buffer + sizeof(buffer), call, - &call->remote_id, dest, original ? original : call->called.number.str, - reason); - if (!end) { - return -1; + q931_party_redirecting_init(&reroute); + + /* Rerouting to the dest number. */ + reroute.to.number.valid = 1; + reroute.to.number.plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164; + reroute.to.number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + libpri_copy_string(reroute.to.number.str, dest, sizeof(reroute.to.number.str)); + + /* Rerouting from the original number. */ + if (original) { + reroute.from.number.valid = 1; + reroute.from.number.plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164; + libpri_copy_string(reroute.from.number.str, original, sizeof(reroute.from.number.str)); + } else { + reroute.from.number = call->called.number; + } + reroute.from.number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + + /* Decode the rerouting reason. */ + reroute.reason = PRI_REDIR_UNKNOWN; + if (!reason) { + /* No reason for rerouting given. */ + } else if (!strcasecmp(reason, "cfu")) { + reroute.reason = PRI_REDIR_UNCONDITIONAL; + } else if (!strcasecmp(reason, "cfb")) { + reroute.reason = PRI_REDIR_FORWARD_ON_BUSY; + } else if (!strcasecmp(reason, "cfnr")) { + reroute.reason = PRI_REDIR_FORWARD_ON_NO_REPLY; } - res = pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL, NULL); - if (res) { - pri_message(ctrl, "Could not queue ADPU in facility message\n"); - return -1; + reroute.count = (call->redirecting.count < PRI_MAX_REDIRECTS) + ? call->redirecting.count + 1 : PRI_MAX_REDIRECTS; + + if (!call->redirecting.orig_called.number.valid) { + /* + * Since we do not already have an originally called party, we + * must either be the first redirected to party or this call + * has not been redirected before. + * + * Preserve who redirected to us as the originally called party. + */ + reroute.orig_called = call->redirecting.from; + reroute.orig_reason = call->redirecting.reason; + } else { + reroute.orig_called = call->redirecting.orig_called; + reroute.orig_reason = call->redirecting.orig_reason; } - /* Remember that if we queue a facility IE for a facility message we - * have to explicitly send the facility message ourselves */ - - res = q931_facility(call->pri, call); - if (res) { - pri_message(ctrl, "Could not schedule facility message for call %d\n", call->cr); - return -1; - } - - return 0; + return send_reroute_request(ctrl, call, NULL, &reroute, 0 /* noNotification */); } /* End QSIG CC-CallRerouting */ @@ -2429,18 +2722,327 @@ int send_call_transfer_complete(struct pri *ctrl, q931_call *call, int call_stat return 0; } +/*! + * \internal + * \brief Encode a plain facility ETSI error code. + * + * \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 call Call leg from which to encode error message response. + * \param invoke_id Invoke id to put in error message response. + * \param code Error code to put in error message response. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_error(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call, int invoke_id, enum rose_error_code code) +{ + struct rose_msg_error msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = invoke_id; + msg.code = code; + + pos = rose_encode_error(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode a plain facility Q.SIG error code. + * + * \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 call Call leg from which to encode error message response. + * \param invoke_id Invoke id to put in error message response. + * \param code Error code to put in error message response. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_error(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call, int invoke_id, enum rose_error_code code) +{ + struct fac_extension_header header; + struct rose_msg_error msg; + + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = invoke_id; + msg.code = code; + + pos = rose_encode_error(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue a plain facility error code. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode error message response. + * \param invoke_id Invoke id to put in error message response. + * \param code Error code to put in error message response. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_facility_error_encode(struct pri *ctrl, q931_call *call, int invoke_id, + enum rose_error_code code) +{ + unsigned char buffer[256]; + unsigned char *end; + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = + enc_etsi_error(ctrl, buffer, buffer + sizeof(buffer), call, invoke_id, code); + break; + case PRI_SWITCH_QSIG: + end = + enc_qsig_error(ctrl, buffer, buffer + sizeof(buffer), call, invoke_id, code); + break; + default: + return -1; + } + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL, NULL); +} + +/*! + * \brief Encode and send a plain facility error code. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode error message response. + * \param invoke_id Invoke id to put in error message response. + * \param code Error code to put in error message response. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_facility_error(struct pri *ctrl, q931_call *call, int invoke_id, + enum rose_error_code code) +{ + if (rose_facility_error_encode(ctrl, call, invoke_id, code) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for error message.\n"); + return -1; + } + + return 0; +} + +/*! + * \internal + * \brief Encode a plain facility ETSI result ok. + * + * \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 call Call leg from which to encode result ok message response. + * \param invoke_id Invoke id to put in result ok message response. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_result_ok(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call, int invoke_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_None; + + pos = rose_encode_result(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode a plain facility Q.SIG result ok. + * + * \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 call Call leg from which to encode result ok message response. + * \param invoke_id Invoke id to put in result ok message response. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_result_ok(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call, int invoke_id) +{ + struct fac_extension_header header; + struct rose_msg_result msg; + + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = invoke_id; + msg.operation = ROSE_None; + + pos = rose_encode_result(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue a plain ROSE result ok. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode result ok message response. + * \param msgtype Q.931 message type to put facility ie in. + * \param invoke_id Invoke id to put in result ok message response. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_result_ok_encode(struct pri *ctrl, q931_call *call, int msgtype, int invoke_id) +{ + unsigned char buffer[256]; + unsigned char *end; + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = + enc_etsi_result_ok(ctrl, buffer, buffer + sizeof(buffer), call, invoke_id); + break; + case PRI_SWITCH_QSIG: + end = + enc_qsig_result_ok(ctrl, buffer, buffer + sizeof(buffer), call, invoke_id); + break; + default: + return -1; + } + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL, NULL); +} + +/*! + * \brief Encode and send a FACILITY message with a plain ROSE result ok. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode result ok message response. + * \param invoke_id Invoke id to put in result ok message response. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_facility_result_ok(struct pri *ctrl, q931_call *call, int invoke_id) +{ + if (rose_result_ok_encode(ctrl, call, Q931_FACILITY, invoke_id) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for result OK message.\n"); + return -1; + } + + return 0; +} + +int pri_rerouting_rsp(struct pri *ctrl, q931_call *call, int invoke_id, enum PRI_REROUTING_RSP_CODE code) +{ + enum rose_error_code rose_err; + + if (!ctrl || !call) { + return -1; + } + + /* Convert the public rerouting response code to an error code or result ok. */ + rose_err = ROSE_ERROR_Gen_ResourceUnavailable; + switch (code) { + case PRI_REROUTING_RSP_OK_CLEAR: + return rose_result_ok_encode(ctrl, call, Q931_DISCONNECT, invoke_id); + case PRI_REROUTING_RSP_OK_RETAIN: + return send_facility_result_ok(ctrl, call, invoke_id); + case PRI_REROUTING_RSP_NOT_SUBSCRIBED: + rose_err = ROSE_ERROR_Gen_NotSubscribed; + break; + case PRI_REROUTING_RSP_NOT_AVAILABLE: + rose_err = ROSE_ERROR_Gen_NotAvailable; + break; + case PRI_REROUTING_RSP_NOT_ALLOWED: + rose_err = ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed; + break; + case PRI_REROUTING_RSP_INVALID_NUMBER: + rose_err = ROSE_ERROR_Div_InvalidDivertedToNr; + break; + case PRI_REROUTING_RSP_SPECIAL_SERVICE_NUMBER: + rose_err = ROSE_ERROR_Div_SpecialServiceNr; + break; + case PRI_REROUTING_RSP_DIVERSION_TO_SELF: + rose_err = ROSE_ERROR_Div_DiversionToServedUserNr; + break; + case PRI_REROUTING_RSP_MAX_DIVERSIONS_EXCEEDED: + rose_err = ROSE_ERROR_Div_NumberOfDiversionsExceeded; + break; + case PRI_REROUTING_RSP_RESOURCE_UNAVAILABLE: + rose_err = ROSE_ERROR_Gen_ResourceUnavailable; + break; + } + return send_facility_error(ctrl, call, invoke_id, rose_err); +} + /*! * \brief Handle the ROSE reject message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which the message came. + * \param msgtype Q.931 message type ie is in. * \param ie Raw ie contents. * \param header Decoded facility header before ROSE. * \param reject Decoded ROSE reject message contents. * * \return Nothing */ -void rose_handle_reject(struct pri *ctrl, q931_call *call, q931_ie *ie, +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) { pri_error(ctrl, "ROSE REJECT:\n"); @@ -2455,13 +3057,14 @@ void rose_handle_reject(struct pri *ctrl, q931_call *call, q931_ie *ie, * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which the message came. + * \param msgtype Q.931 message type ie is in. * \param ie Raw ie contents. * \param header Decoded facility header before ROSE. * \param error Decoded ROSE error message contents. * * \return Nothing */ -void rose_handle_error(struct pri *ctrl, q931_call *call, q931_ie *ie, +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) { const char *dms100_operation; @@ -2497,13 +3100,14 @@ void rose_handle_error(struct pri *ctrl, q931_call *call, q931_ie *ie, * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which the message came. + * \param msgtype Q.931 message type ie is in. * \param ie Raw ie contents. * \param header Decoded facility header before ROSE. * \param result Decoded ROSE result message contents. * * \return Nothing */ -void rose_handle_result(struct pri *ctrl, q931_call *call, q931_ie *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) { switch (ctrl->switchtype) { @@ -2535,7 +3139,6 @@ void rose_handle_result(struct pri *ctrl, q931_call *call, q931_ie *ie, } switch (result->operation) { -#if 0 /* Not handled yet */ case ROSE_None: /* * This is simply a positive ACK to the invoke request. @@ -2543,7 +3146,6 @@ void rose_handle_result(struct pri *ctrl, q931_call *call, q931_ie *ie, * invoke requests. */ break; -#endif /* Not handled yet */ #if 0 /* Not handled yet */ case ROSE_ETSI_ActivationDiversion: break; @@ -2551,10 +3153,14 @@ void rose_handle_result(struct pri *ctrl, q931_call *call, q931_ie *ie, 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 */ @@ -2597,9 +3203,7 @@ void rose_handle_result(struct pri *ctrl, q931_call *call, q931_ie *ie, break; #endif /* Not handled yet */ case ROSE_QSIG_CallRerouting: - if (ctrl->debug & PRI_DEBUG_APDU) { - pri_message(ctrl, "Successfully completed QSIG CF callRerouting!\n"); - } + /* Successfully completed call rerouting. Nothing to do. */ break; #if 0 /* Not handled yet */ case ROSE_QSIG_MWIActivate: @@ -2623,17 +3227,19 @@ void rose_handle_result(struct pri *ctrl, q931_call *call, q931_ie *ie, * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which the message came. + * \param msgtype Q.931 message type ie is in. * \param ie Raw ie contents. * \param header Decoded facility header before ROSE. * \param invoke Decoded ROSE invoke message contents. * * \return Nothing */ -void rose_handle_invoke(struct pri *ctrl, q931_call *call, q931_ie *ie, +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) { struct pri_subcommand *subcmd; struct q931_party_id party_id; + struct q931_party_redirecting deflection; switch (invoke->operation) { #if 0 /* Not handled yet */ @@ -2649,10 +3255,135 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, q931_ie *ie, break; case ROSE_ETSI_DiversionInformation: break; +#endif /* Not handled yet */ case ROSE_ETSI_CallDeflection: + if (!PRI_MASTER(ctrl)->deflection_support) { + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_NotSubscribed); + break; + } + if (!q931_master_pass_event(ctrl, call, msgtype)) { + /* Some other user is further along to connecting than this call. */ + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Div_IncomingCallAccepted); + break; + } + if (call->master_call->deflection_in_progress) { + /* Someone else is already doing a call deflection. */ + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Div_RequestAlreadyAccepted); + break; + } + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + /* + * ROSE_ERROR_Gen_ResourceUnavailable was not in the list of allowed codes, + * but we will send it anyway. + */ + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_ResourceUnavailable); + pri_error(ctrl, "ERROR: Too many facility subcommands\n"); + break; + } + + call->master_call->deflection_in_progress = 1; + + q931_party_redirecting_init(&deflection); + + /* Deflecting from the called address. */ + q931_party_address_to_id(&deflection.from, &call->called); + if (invoke->args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user_present) { + deflection.from.number.presentation = + invoke->args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user + ? PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED + : PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + } else { + deflection.from.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + } + + /* Deflecting to the new address. */ + rose_copy_address_to_q931(ctrl, &deflection.to, + &invoke->args.etsi.CallDeflection.deflection); + deflection.to.number.presentation = deflection.from.number.presentation; + + deflection.count = (call->redirecting.count < PRI_MAX_REDIRECTS) + ? call->redirecting.count + 1 : PRI_MAX_REDIRECTS; + deflection.reason = PRI_REDIR_DEFLECTION; + if (deflection.count == 1) { + deflection.orig_called = deflection.from; + deflection.orig_reason = deflection.reason; + } else { + deflection.orig_called = call->redirecting.orig_called; + deflection.orig_reason = call->redirecting.orig_reason; + } + + subcmd->cmd = PRI_SUBCMD_REROUTING; + subcmd->u.rerouting.invoke_id = invoke->invoke_id; + subcmd->u.rerouting.subscription_option = 3;/* notApplicable */ + q931_party_id_copy_to_pri(&subcmd->u.rerouting.caller, &call->local_id); + q931_party_redirecting_copy_to_pri(&subcmd->u.rerouting.deflection, + &deflection); break; case ROSE_ETSI_CallRerouting: + if (!PRI_MASTER(ctrl)->deflection_support) { + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_NotSubscribed); + break; + } + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_ResourceUnavailable); + pri_error(ctrl, "ERROR: Too many facility subcommands\n"); + break; + } + + q931_party_redirecting_init(&deflection); + + /* Rerouting from the last address. */ + rose_copy_presented_number_unscreened_to_q931(ctrl, &deflection.from.number, + &invoke->args.etsi.CallRerouting.last_rerouting); + + /* Rerouting to the new address. */ + rose_copy_address_to_q931(ctrl, &deflection.to, + &invoke->args.etsi.CallRerouting.called_address); + switch (invoke->args.etsi.CallRerouting.subscription_option) { + default: + case 0: /* noNotification */ + case 1: /* notificationWithoutDivertedToNr */ + deflection.to.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + break; + case 2: /* notificationWithDivertedToNr */ + deflection.to.number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + break; + } + + /* Calling party subaddress update. */ + party_id = call->local_id; + + deflection.count = invoke->args.etsi.CallRerouting.rerouting_counter; + deflection.reason = redirectingreason_for_q931(ctrl, + invoke->args.etsi.CallRerouting.rerouting_reason); + if (deflection.count == 1) { + deflection.orig_called = deflection.from; + deflection.orig_reason = deflection.reason; + } else { + deflection.orig_called = call->redirecting.orig_called; + deflection.orig_reason = call->redirecting.orig_reason; + } + + subcmd->cmd = PRI_SUBCMD_REROUTING; + subcmd->u.rerouting.invoke_id = invoke->invoke_id; + subcmd->u.rerouting.subscription_option = + invoke->args.etsi.CallRerouting.subscription_option; + q931_party_id_copy_to_pri(&subcmd->u.rerouting.caller, &party_id); + q931_party_redirecting_copy_to_pri(&subcmd->u.rerouting.deflection, + &deflection); break; +#if 0 /* Not handled yet */ case ROSE_ETSI_InterrogateServedUserNumbers: break; #endif /* Not handled yet */ @@ -2966,9 +3697,90 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, q931_ie *ie, break; case ROSE_QSIG_CheckRestriction: break; - case ROSE_QSIG_CallRerouting: - break; #endif /* Not handled yet */ + case ROSE_QSIG_CallRerouting: + if (!PRI_MASTER(ctrl)->deflection_support) { + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_NotSubscribed); + break; + } + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_ResourceUnavailable); + pri_error(ctrl, "ERROR: Too many facility subcommands\n"); + break; + } + + q931_party_redirecting_init(&deflection); + + /* Rerouting from the last address. */ + rose_copy_presented_number_unscreened_to_q931(ctrl, &deflection.from.number, + &invoke->args.qsig.CallRerouting.last_rerouting); + if (invoke->args.qsig.CallRerouting.redirecting_name_present) { + rose_copy_name_to_q931(ctrl, &deflection.from.name, + &invoke->args.qsig.CallRerouting.redirecting_name); + } + + /* Rerouting to the new address. */ + rose_copy_address_to_q931(ctrl, &deflection.to, + &invoke->args.qsig.CallRerouting.called); + switch (invoke->args.qsig.CallRerouting.subscription_option) { + default: + case 0: /* noNotification */ + case 1: /* notificationWithoutDivertedToNr */ + deflection.to.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + break; + case 2: /* notificationWithDivertedToNr */ + deflection.to.number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + break; + } + + /* Calling party update. */ + party_id = call->local_id; + rose_copy_presented_number_screened_to_q931(ctrl, &party_id.number, + &invoke->args.qsig.CallRerouting.calling); + if (invoke->args.qsig.CallRerouting.calling_name_present) { + rose_copy_name_to_q931(ctrl, &party_id.name, + &invoke->args.qsig.CallRerouting.calling_name); + } + + deflection.count = invoke->args.qsig.CallRerouting.diversion_counter; + deflection.reason = redirectingreason_for_q931(ctrl, + invoke->args.qsig.CallRerouting.rerouting_reason); + + /* Original called party update. */ + if (deflection.count == 1) { + deflection.orig_called = deflection.from; + deflection.orig_reason = deflection.reason; + } else { + deflection.orig_called = call->redirecting.orig_called; + deflection.orig_reason = call->redirecting.orig_reason; + } + if (invoke->args.qsig.CallRerouting.original_called_present) { + rose_copy_presented_number_unscreened_to_q931(ctrl, + &deflection.orig_called.number, + &invoke->args.qsig.CallRerouting.original_called); + } + if (invoke->args.qsig.CallRerouting.original_called_name_present) { + rose_copy_name_to_q931(ctrl, &deflection.orig_called.name, + &invoke->args.qsig.CallRerouting.original_called_name); + } + if (invoke->args.qsig.CallRerouting.original_rerouting_reason_present) { + deflection.orig_reason = redirectingreason_for_q931(ctrl, + invoke->args.qsig.CallRerouting.original_rerouting_reason); + } + + subcmd->cmd = PRI_SUBCMD_REROUTING; + subcmd->u.rerouting.invoke_id = invoke->invoke_id; + subcmd->u.rerouting.subscription_option = + invoke->args.qsig.CallRerouting.subscription_option; + q931_party_id_copy_to_pri(&subcmd->u.rerouting.caller, &party_id); + q931_party_redirecting_copy_to_pri(&subcmd->u.rerouting.deflection, + &deflection); + break; case ROSE_QSIG_DivertingLegInformation1: /* * Unless otherwise indicated by CONNECT, the nominatedNr will be diff --git a/pri_facility.h b/pri_facility.h index 1decfcf..19de253 100644 --- a/pri_facility.h +++ b/pri_facility.h @@ -74,6 +74,7 @@ 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); @@ -106,9 +107,9 @@ struct rose_msg_result; struct rose_msg_error; struct rose_msg_reject; -void rose_handle_invoke(struct pri *ctrl, q931_call *call, 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, 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, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_error *error); -void rose_handle_reject(struct pri *ctrl, q931_call *call, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_reject *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); +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); #endif /* _PRI_FACILITY_H */ diff --git a/pri_internal.h b/pri_internal.h index cc688e2..53c257f 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -70,7 +70,9 @@ struct pri { int protodisc; unsigned int bri:1; unsigned int acceptinbanddisconnect:1; /* Should we allow inband progress after DISCONNECT? */ - + 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. */ + /* Q.921 State */ int q921_state; int window; /* Max window size */ @@ -287,6 +289,7 @@ struct pri_sr { int cis_call; int cis_auto_disconnect; const char *useruserinfo; + const char *keypad_digits; int transferable; int reversecharge; }; @@ -295,6 +298,8 @@ struct pri_sr { #define PRI_SWITCH_GR303_EOC_PATH 19 #define PRI_SWITCH_GR303_TMC_SWITCHING 20 +#define Q931_MAX_TEI 8 + struct apdu_event { int message; /* What message to send the ADPU in */ void (*callback)(void *data); /* Callback function for when response is received */ @@ -327,6 +332,22 @@ enum INCOMING_CT_STATE { INCOMING_CT_STATE_POST_CONNECTED_LINE }; +/*! Call hold supplementary states. */ +enum Q931_HOLD_STATE { + /*! \brief No call hold activity. */ + Q931_HOLD_STATE_IDLE, + /*! \brief Request made to hold call. */ + Q931_HOLD_STATE_HOLD_REQ, + /*! \brief Request received to hold call. */ + Q931_HOLD_STATE_HOLD_IND, + /*! \brief Call is held. */ + Q931_HOLD_STATE_CALL_HELD, + /*! \brief Request made to retrieve call. */ + Q931_HOLD_STATE_RETRIEVE_REQ, + /*! \brief Request received to retrieve call. */ + Q931_HOLD_STATE_RETRIEVE_IND, +}; + /* q931_call datastructure */ struct q931_call { struct pri *pri; /* PRI */ @@ -442,6 +463,12 @@ struct q931_call { /*! \brief Incoming call transfer state. */ enum INCOMING_CT_STATE incoming_ct_state; + /*! Call hold supplementary state. */ + enum Q931_HOLD_STATE hold_state; + /*! Call hold event timer */ + int hold_timer; + + int deflection_in_progress; /*!< CallDeflection for NT PTMP in progress. */ int useruserprotocoldisc; char useruserinfo[256]; @@ -462,6 +489,22 @@ struct q931_call { -1 - No reverse charging 1 - Reverse charging 0,2-7 - Reserved for future use */ + int t303_timer; + int t303_expirycnt; + + int hangupinitiated; + /*! \brief TRUE if we broadcast this call's SETUP message. */ + int outboundbroadcast; + int performing_fake_clearing; + /*! + * \brief Master call controlling this call. + * \note Always valid. Master and normal calls point to self. + */ + struct q931_call *master_call; + + /* These valid in master call only */ + struct q931_call *subcalls[Q931_MAX_TEI]; + int pri_winner; }; extern int pri_schedule_event(struct pri *pri, int ms, void (*function)(void *data), void *data); @@ -484,6 +527,11 @@ void __pri_free_tei(struct pri *p); void q931_party_name_init(struct q931_party_name *name); void q931_party_number_init(struct q931_party_number *number); void q931_party_subaddress_init(struct q931_party_subaddress *subaddr); +#define q931_party_address_to_id(q931_id, q931_address) \ + do { \ + (q931_id)->number = (q931_address)->number; \ + /*(q931_id)->subaddress = (q931_address)->subaddress;*/ \ + } while (0) void q931_party_address_init(struct q931_party_address *address); void q931_party_id_init(struct q931_party_id *id); void q931_party_redirecting_init(struct q931_party_redirecting *redirecting); @@ -505,8 +553,40 @@ int q931_party_id_presentation(const struct q931_party_id *id); const char *q931_call_state_str(enum Q931_CALL_STATE callstate); int q931_is_ptmp(const struct pri *ctrl); +int q931_master_pass_event(struct pri *ctrl, struct q931_call *subcall, int msg_type); struct pri_subcommand *q931_alloc_subcommand(struct pri *ctrl); int q931_notify_redirection(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number); +static inline struct pri * PRI_MASTER(struct pri *mypri) +{ + struct pri *pri = mypri; + + if (!pri) + return NULL; + + while (pri->master) + pri = pri->master; + + return pri; +} + +static inline int BRI_NT_PTMP(struct pri *mypri) +{ + struct pri *pri; + + pri = PRI_MASTER(mypri); + + return pri->bri && (((pri)->localtype == PRI_NETWORK) && ((pri)->tei == Q921_TEI_GROUP)); +} + +static inline int BRI_TE_PTMP(struct pri *mypri) +{ + struct pri *pri; + + pri = PRI_MASTER(mypri); + + return pri->bri && (((pri)->localtype == PRI_CPE) && ((pri)->tei == Q921_TEI_GROUP)); +} + #endif diff --git a/pri_q921.h b/pri_q921.h index 44bad44..e220cad 100644 --- a/pri_q921.h +++ b/pri_q921.h @@ -192,6 +192,8 @@ extern pri_event *q921_receive(struct pri *pri, q921_h *h, int len); extern int q921_transmit_iframe(struct pri *pri, void *buf, int len, int cr); +extern int q921_transmit_uiframe(struct pri *pri, void *buf, int len); + extern pri_event *q921_dchannel_up(struct pri *pri); extern pri_event *q921_dchannel_down(struct pri *pri); diff --git a/pri_q931.h b/pri_q931.h index 4667d3a..95995a0 100644 --- a/pri_q931.h +++ b/pri_q931.h @@ -424,6 +424,22 @@ enum Q931_CALL_STATE { Q931_CALL_STATE_NOT_SET = 0xFF, }; +/*! Q.931 call establishment state ranking for competing calls in PTMP NT mode. */ +enum Q931_RANKED_CALL_STATE { + /*! Call is present but has no response yet. */ + Q931_RANKED_CALL_STATE_PRESENT, + /*! Call is collecting digits. */ + Q931_RANKED_CALL_STATE_OVERLAP, + /*! Call routing is happening. */ + Q931_RANKED_CALL_STATE_PROCEEDING, + /*! Called party is being alerted of the call. */ + Q931_RANKED_CALL_STATE_ALERTING, + /*! Call is connected. A winner has been declared. */ + Q931_RANKED_CALL_STATE_CONNECT, + /*! Call is in some non-call establishment state (likely disconnecting). */ + Q931_RANKED_CALL_STATE_OTHER, +}; + /* EuroISDN */ #define Q931_SENDING_COMPLETE 0xa1 @@ -474,8 +490,16 @@ extern q931_call *q931_new_call(struct pri *pri); extern int q931_setup(struct pri *pri, q931_call *c, struct pri_sr *req); extern void q931_dump(struct pri *pri, q931_h *h, int len, int txrx); -extern void __q931_destroycall(struct pri *pri, q931_call *c); +void q931_destroycall(struct pri *pri, q931_call *c); extern void q931_dl_indication(struct pri *pri, int event); +int q931_send_hold(struct pri *ctrl, struct q931_call *call); +int q931_send_hold_ack(struct pri *ctrl, struct q931_call *call); +int q931_send_hold_rej(struct pri *ctrl, struct q931_call *call, int cause); + +int q931_send_retrieve(struct pri *ctrl, struct q931_call *call, int channel); +int q931_send_retrieve_ack(struct pri *ctrl, struct q931_call *call, int channel); +int q931_send_retrieve_rej(struct pri *ctrl, struct q931_call *call, int cause); + #endif diff --git a/q921.c b/q921.c index 5dcaaef..eeaf953 100644 --- a/q921.c +++ b/q921.c @@ -27,6 +27,7 @@ * terms granted here. */ +#include #include #include #include @@ -477,6 +478,45 @@ static void t200_expire(void *vpri) } } +int q921_transmit_uiframe(struct pri *pri, void *buf, int len) +{ + uint8_t ubuf[512]; + q921_h *h = (void *)&ubuf[0]; + + if (len >= 512) { + pri_error(pri, "Requested to send UI frame larger than 512 bytes!\n"); + return -1; + } + + memset(ubuf, 0, sizeof(ubuf)); + h->h.sapi = 0; + h->h.ea1 = 0; + h->h.ea2 = 1; + h->h.tei = pri->tei; + h->u.m3 = 0; + h->u.m2 = 0; + h->u.p_f = 0; /* Poll bit set */ + h->u.ft = Q921_FRAMETYPE_U; + + switch(pri->localtype) { + case PRI_NETWORK: + h->h.c_r = 1; + break; + case PRI_CPE: + h->h.c_r = 0; + break; + default: + pri_error(pri, "Don't know how to U/A on a type %d node\n", pri->localtype); + return -1; + } + + memcpy(h->u.data, buf, len); + + q921_transmit(pri, h, len + 3); + + return 0; +} + int q921_transmit_iframe(struct pri *pri, void *buf, int len, int cr) { q921_frame *f, *prev=NULL; @@ -515,6 +555,10 @@ int q921_transmit_iframe(struct pri *pri, void *buf, int len, int cr) pri->txqueue = f; /* Immediately transmit unless we're in a recovery state, or the window size is too big */ + if (pri->debug & PRI_DEBUG_Q921_DUMP) { + pri_message(pri, "TEI/SAPI: %d/%d state %d retran %d busy %d\n", + pri->tei, pri->sapi, pri->q921_state, pri->retrans, pri->busy); + } if ((pri->q921_state == Q921_LINK_CONNECTION_ESTABLISHED) && (!pri->retrans && !pri->busy)) { if (pri->windowlen < pri->window) { q921_send_queued_iframes(pri); @@ -558,11 +602,15 @@ static void t203_expire(void *vpri) /* Start timer T200 to resend our RR if we don't get it */ pri->t203_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T200], t200_expire, pri); } else { - if (pri->debug & PRI_DEBUG_Q921_DUMP) - pri_message(pri, "T203 counter expired in weird state %d\n", pri->q921_state); + if (pri->debug & PRI_DEBUG_Q921_DUMP) { + pri_message(pri, + "T203 counter expired in weird state %d on pri with SAPI/TEI of %d/%d\n", + pri->q921_state, pri->sapi, pri->tei); + } pri->t203_timer = 0; } } + static pri_event *q921_handle_iframe(struct pri *pri, q921_i *i, int len) { int res; @@ -879,8 +927,14 @@ static void q921_tei_release_and_reacquire(struct pri *master) static pri_event *q921_receive_MDL(struct pri *pri, q921_u *h, int len) { int ri; - struct pri *sub; + struct pri *sub = pri; int tei; + + if (!BRI_NT_PTMP(pri) && !BRI_TE_PTMP(pri)) { + pri_error(pri, "Received MDL/TEI managemement message, but configured for mode other than PTMP!\n"); + return NULL; + } + if (pri->debug & PRI_DEBUG_Q921_STATE) pri_message(pri, "Received MDL message\n"); if (h->data[0] != 0x0f) { @@ -895,17 +949,19 @@ static pri_event *q921_receive_MDL(struct pri *pri, q921_u *h, int len) tei = (h->data[4] >> 1); switch(h->data[3]) { case Q921_TEI_IDENTITY_REQUEST: + if (!BRI_NT_PTMP(pri)) { + return NULL; + } + if (tei != 127) { pri_error(pri, "Received TEI identity request with invalid TEI %d\n", tei); q921_send_tei(pri, Q921_TEI_IDENTITY_DENIED, ri, tei, 1); } - /* Go to master */ - for (sub = pri; sub->master; sub = sub->master); tei = 64; -/*! \todo XXX Error: The following loop never terminates! */ - while(sub->subchannel) { - if(sub->subchannel->tei == tei) + while (sub->subchannel) { + if (sub->subchannel->tei == tei) ++tei; + sub = sub->subchannel; } sub->subchannel = __pri_new_tei(-1, pri->localtype, pri->switchtype, pri, NULL, NULL, NULL, tei, 1); if (!sub->subchannel) { @@ -915,6 +971,9 @@ static pri_event *q921_receive_MDL(struct pri *pri, q921_u *h, int len) q921_send_tei(pri, Q921_TEI_IDENTITY_ASSIGNED, ri, tei, 1); break; case Q921_TEI_IDENTITY_ASSIGNED: + if (!BRI_TE_PTMP(pri)) + return NULL; + if (ri != pri->ri) { pri_message(pri, "TEI assignment received for invalid Ri %02x (our is %02x)\n", ri, pri->ri); return NULL; @@ -936,6 +995,8 @@ static pri_event *q921_receive_MDL(struct pri *pri, q921_u *h, int len) pri->q921_state = Q921_TEI_ASSIGNED; break; case Q921_TEI_IDENTITY_CHECK_REQUEST: + if (!BRI_TE_PTMP(pri)) + return NULL; /* We're assuming one TEI per PRI in TE PTMP mode */ /* If no subchannel (TEI) ignore */ @@ -948,6 +1009,8 @@ static pri_event *q921_receive_MDL(struct pri *pri, q921_u *h, int len) break; case Q921_TEI_IDENTITY_REMOVE: + if (!BRI_TE_PTMP(pri)) + return NULL; /* XXX: Assuming multiframe mode has been disconnected already */ if (!pri->subchannel) return NULL; @@ -989,6 +1052,10 @@ static pri_event *__q921_receive_qualified(struct pri *pri, q921_h *h, int len) pri_error(pri, "!! Received short I-frame (expected 4, got %d)\n", len); break; } + + /* T203 is rescheduled only on reception of I frames or S frames */ + reschedule_t203(pri); + return q921_handle_iframe(pri, &h->i, len); break; case 1: @@ -1000,6 +1067,10 @@ static pri_event *__q921_receive_qualified(struct pri *pri, q921_h *h, int len) pri_error(pri, "!! Received short S-frame (expected 4, got %d)\n", len); break; } + + /* T203 is rescheduled only on reception of I frames or S frames */ + reschedule_t203(pri); + switch(h->s.ss) { case 0: /* Receiver Ready */ @@ -1149,7 +1220,6 @@ static pri_event *__q921_receive_qualified(struct pri *pri, q921_h *h, int len) /* Acknowledge */ q921_send_ua(pri, h->u.p_f); ev = q921_dchannel_down(pri); - q921_restart(pri, 0); return ev; case 3: if (h->u.m2 == 3) { @@ -1209,6 +1279,34 @@ static pri_event *__q921_receive_qualified(struct pri *pri, q921_h *h, int len) return NULL; } +static pri_event *q921_handle_unmatched_frame(struct pri *pri, q921_h *h, int len) +{ + pri = PRI_MASTER(pri); + + if (h->h.tei < 64) { + pri_error(pri, "Do not support manual TEI range. Discarding\n"); + return NULL; + } + + if (h->h.sapi != Q921_SAPI_CALL_CTRL) { + pri_error(pri, "Message with SAPI other than CALL CTRL is discarded\n"); + return NULL; + } + + if (pri->debug & PRI_DEBUG_Q921_DUMP) { + pri_message(pri, + "Could not find candidate subchannel for received frame with SAPI/TEI of %d/%d.\n", + h->h.sapi, h->h.tei); + pri_message(pri, "Sending TEI release, in order to re-establish TEI state\n"); + } + + /* Q.921 says we should send the remove message twice, in case of link corruption */ + q921_send_tei(pri, Q921_TEI_IDENTITY_REMOVE, 0, h->h.tei, 1); + q921_send_tei(pri, Q921_TEI_IDENTITY_REMOVE, 0, h->h.tei, 1); + + return NULL; +} + static pri_event *__q921_receive(struct pri *pri, q921_h *h, int len) { pri_event *ev; @@ -1222,26 +1320,22 @@ static pri_event *__q921_receive(struct pri *pri, q921_h *h, int len) if (h->h.ea1 || !(h->h.ea2)) return NULL; -#if 0 /* Will be rejected by subchannel analyzis */ - /* Check for broadcasts - not yet handled */ - if (h->h.tei == Q921_TEI_GROUP) - return NULL; -#endif - if (!((h->h.sapi == pri->sapi) && ((h->h.tei == pri->tei) || (h->h.tei == Q921_TEI_GROUP)))) { /* Check for SAPIs we don't yet handle */ /* If it's not us, try any subchannels we have */ if (pri->subchannel) return q921_receive(pri->subchannel, h, len + 2); else { - return NULL; + /* This means we couldn't find a candidate subchannel for it... + * Time for some corrective action */ + + return q921_handle_unmatched_frame(pri, h, len); } } if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "Handling message for SAPI/TEI=%d/%d\n", h->h.sapi, h->h.tei); ev = __q921_receive_qualified(pri, h, len); - reschedule_t203(pri); return ev; } diff --git a/q931.c b/q931.c index a33da71..aeb8b66 100644 --- a/q931.c +++ b/q931.c @@ -80,10 +80,10 @@ static struct msgtype msgs[] = { /* Call Management */ { Q931_HOLD, "HOLD" }, { Q931_HOLD_ACKNOWLEDGE, "HOLD ACKNOWLEDGE" }, - { Q931_HOLD_REJECT, "HOLD REJECT" }, + { Q931_HOLD_REJECT, "HOLD REJECT", { Q931_CAUSE } }, { Q931_RETRIEVE, "RETRIEVE" }, { Q931_RETRIEVE_ACKNOWLEDGE, "RETRIEVE ACKNOWLEDGE" }, - { Q931_RETRIEVE_REJECT, "RETRIEVE REJECT" }, + { Q931_RETRIEVE_REJECT, "RETRIEVE REJECT", { Q931_CAUSE } }, { Q931_RESUME, "RESUME" }, { Q931_RESUME_ACKNOWLEDGE, "RESUME ACKNOWLEDGE", { Q931_CHANNEL_IDENT } }, { Q931_RESUME_REJECT, "RESUME REJECT", { Q931_CAUSE } }, @@ -91,7 +91,9 @@ static struct msgtype msgs[] = { { Q931_SUSPEND_ACKNOWLEDGE, "SUSPEND ACKNOWLEDGE" }, { Q931_SUSPEND_REJECT, "SUSPEND REJECT" }, }; + static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int missingmand); +static void nt_ptmp_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int *allow_event, int *allow_posthandle); struct msgtype att_maintenance_msgs[] = { { ATT_SERVICE, "SERVICE", { Q931_CHANNEL_IDENT } }, @@ -116,6 +118,7 @@ static struct msgtype causes[] = { { PRI_CAUSE_NO_ANSWER, "User alerting, no answer" }, { PRI_CAUSE_CALL_REJECTED, "Call Rejected" }, { PRI_CAUSE_NUMBER_CHANGED, "Number changed" }, + { PRI_CAUSE_NONSELECTED_USER_CLEARING, "Non-selected user clearing" }, { PRI_CAUSE_DESTINATION_OUT_OF_ORDER, "Destination out of order" }, { PRI_CAUSE_INVALID_NUMBER_FORMAT, "Invalid number format" }, { PRI_CAUSE_FACILITY_REJECTED, "Facility rejected" }, @@ -128,13 +131,14 @@ static struct msgtype causes[] = { { PRI_CAUSE_ACCESS_INFO_DISCARDED, "Access information discarded" }, { PRI_CAUSE_REQUESTED_CHAN_UNAVAIL, "Requested channel not available" }, { PRI_CAUSE_PRE_EMPTED, "Pre-empted" }, + { PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED, "Resource unavailable, unspecified" }, { PRI_CAUSE_FACILITY_NOT_SUBSCRIBED, "Facility not subscribed" }, { PRI_CAUSE_OUTGOING_CALL_BARRED, "Outgoing call barred" }, { PRI_CAUSE_INCOMING_CALL_BARRED, "Incoming call barred" }, { PRI_CAUSE_BEARERCAPABILITY_NOTAUTH, "Bearer capability not authorized" }, { PRI_CAUSE_BEARERCAPABILITY_NOTAVAIL, "Bearer capability not available" }, - { PRI_CAUSE_BEARERCAPABILITY_NOTIMPL, "Bearer capability not implemented" }, { PRI_CAUSE_SERVICEOROPTION_NOTAVAIL, "Service or option not available, unspecified" }, + { PRI_CAUSE_BEARERCAPABILITY_NOTIMPL, "Bearer capability not implemented" }, { PRI_CAUSE_CHAN_NOT_IMPLEMENTED, "Channel not implemented" }, { PRI_CAUSE_FACILITY_NOT_IMPLEMENTED, "Facility not implemented" }, { PRI_CAUSE_INVALID_CALL_REFERENCE, "Invalid call reference value" }, @@ -233,15 +237,39 @@ static char *msg2str(int msg); #if 1 /* Update call state with transition trace. */ -#define UPDATE_OURCALLSTATE(ctrl,c,newstate) do {\ - if (ctrl->debug & (PRI_DEBUG_Q931_STATE) && c->ourcallstate != newstate) \ - pri_message(ctrl, DBGHEAD "call %d on channel %d enters state %d (%s)\n", DBGINFO, \ - c->cr, c->channelno, newstate, q931_call_state_str(newstate)); \ - c->ourcallstate = newstate; \ +#define UPDATE_OURCALLSTATE(ctrl, call, newstate) \ + do { \ + if (((ctrl)->debug & PRI_DEBUG_Q931_STATE) && (call)->ourcallstate != (newstate)) { \ + pri_message((ctrl), \ + DBGHEAD "%s %d enters state %d (%s). Hold state: %s\n", \ + DBGINFO, ((call) == (call)->master_call) ? "Call" : "Subcall", \ + (call)->cr, (newstate), q931_call_state_str(newstate), \ + q931_hold_state_str((call)->master_call->hold_state)); \ + } \ + (call)->ourcallstate = (newstate); \ } while (0) #else /* Update call state with no trace. */ -#define UPDATE_OURCALLSTATE(ctrl,c,newstate) c->ourcallstate = newstate +#define UPDATE_OURCALLSTATE(ctrl, call, newstate) (call)->ourcallstate = (newstate) +#endif + +#if 1 +/* Update hold state with transition trace. */ +#define UPDATE_HOLD_STATE(ctrl, master_call, newstate) \ + do { \ + if (((ctrl)->debug & PRI_DEBUG_Q931_STATE) \ + && (master_call)->hold_state != (newstate)) { \ + pri_message((ctrl), \ + DBGHEAD "Call %d in state %d (%s) enters Hold state: %s\n", \ + DBGINFO, (master_call)->cr, (master_call)->ourcallstate, \ + q931_call_state_str((master_call)->ourcallstate), \ + q931_hold_state_str(newstate)); \ + } \ + (master_call)->hold_state = (newstate); \ + } while (0) +#else +/* Update hold state with no trace. */ +#define UPDATE_HOLD_STATE(ctrl, master_call, newstate) (master_call)->hold_state = (newstate) #endif struct ie { @@ -269,8 +297,28 @@ struct ie { */ static int q931_encode_channel(const q931_call *call) { - return call->channelno | (call->ds1no << 8) | (call->ds1explicit << 16) - | (call->cis_call << 17); + int held_call; + int channelno; + int ds1no; + + switch (call->master_call->hold_state) { + case Q931_HOLD_STATE_CALL_HELD: + case Q931_HOLD_STATE_RETRIEVE_REQ: + case Q931_HOLD_STATE_RETRIEVE_IND: + held_call = 1 << 18; + + /* So a -1 does not wipe out the held_call flag. */ + channelno = call->channelno & 0xFF; + ds1no = call->ds1no & 0xFF; + break; + default: + held_call = 0; + channelno = call->channelno; + ds1no = call->ds1no; + break; + } + return channelno | (ds1no << 8) | (call->ds1explicit << 16) | (call->cis_call << 17) + | held_call; } /*! @@ -921,7 +969,7 @@ static int transmit_channel_id(int full_ie, struct pri *ctrl, q931_call *call, i return 0; } - if (((ctrl->switchtype != PRI_SWITCH_QSIG) && (call->ds1no > 0)) || call->ds1explicit) { + if (!ctrl->bri && (((ctrl->switchtype != PRI_SWITCH_QSIG) && (call->ds1no > 0)) || call->ds1explicit)) { /* We are specifying the interface. Octet 3.1 */ ie->data[pos++] |= 0x40; ie->data[pos++] = 0x80 | call->ds1no; @@ -2276,16 +2324,16 @@ static int receive_facility(int full_ie, struct pri *ctrl, q931_call *call, int } switch (rose.type) { case ROSE_COMP_TYPE_INVOKE: - rose_handle_invoke(ctrl, call, ie, &header, &rose.component.invoke); + rose_handle_invoke(ctrl, call, msgtype, ie, &header, &rose.component.invoke); break; case ROSE_COMP_TYPE_RESULT: - rose_handle_result(ctrl, call, ie, &header, &rose.component.result); + rose_handle_result(ctrl, call, msgtype, ie, &header, &rose.component.result); break; case ROSE_COMP_TYPE_ERROR: - rose_handle_error(ctrl, call, ie, &header, &rose.component.error); + rose_handle_error(ctrl, call, msgtype, ie, &header, &rose.component.error); break; case ROSE_COMP_TYPE_REJECT: - rose_handle_reject(ctrl, call, ie, &header, &rose.component.reject); + rose_handle_reject(ctrl, call, msgtype, ie, &header, &rose.component.reject); break; default: return -1; @@ -2407,6 +2455,29 @@ const char *q931_call_state_str(enum Q931_CALL_STATE callstate) return code2str(callstate, callstates, ARRAY_LEN(callstates)); } +/*! + * \internal + * \brief Convert the Q.932 supplementary hold state to a string. + * + * \param state Q.932 supplementary hold state. + * + * \return String equivalent of the given hold state. + */ +static const char *q931_hold_state_str(enum Q931_HOLD_STATE state) +{ + static struct msgtype hold_states[] = { +/* *INDENT-OFF* */ + { Q931_HOLD_STATE_IDLE, "Idle" }, + { Q931_HOLD_STATE_HOLD_REQ, "Hold Request" }, + { Q931_HOLD_STATE_HOLD_IND, "Hold Indication" }, + { Q931_HOLD_STATE_CALL_HELD, "Call Held" }, + { Q931_HOLD_STATE_RETRIEVE_REQ, "Retrieve Request" }, + { Q931_HOLD_STATE_RETRIEVE_IND, "Retrieve Indication" }, +/* *INDENT-ON* */ + }; + return code2str(state, hold_states, ARRAY_LEN(hold_states)); +} + static void dump_call_state(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { pri_message(ctrl, "%c Call State (len=%2d) [ Ext: %d Coding: %s (%d) Call state: %s (%d)\n", @@ -3274,11 +3345,7 @@ static q931_call *q931_getcall(struct pri *ctrl, int cr) struct pri *master; /* Find the master - He has the call pool */ - if (ctrl->master) { - master = ctrl->master; - } else { - master = ctrl; - } + master = PRI_MASTER(ctrl); cur = *master->callpool; prev = NULL; @@ -3338,6 +3405,8 @@ static q931_call *q931_getcall(struct pri *ctrl, int cr) cur->aoc_units = -1; cur->changestatus = -1; cur->reversecharge = -1; + cur->pri_winner = -1; + cur->master_call = cur; q931_party_number_init(&cur->redirection_number); q931_party_address_init(&cur->called); q931_party_id_init(&cur->local_id); @@ -3345,7 +3414,7 @@ static q931_call *q931_getcall(struct pri *ctrl, int cr) q931_party_redirecting_init(&cur->redirecting); /* PRI is set to whoever called us */ - if (q931_is_ptmp(ctrl) && (ctrl->localtype == PRI_CPE)) { + if (BRI_TE_PTMP(ctrl)) { /* * Point to the master to avoid stale pointer problems if * the TEI is removed later. @@ -3389,48 +3458,145 @@ q931_call *q931_new_call(struct pri *ctrl) return q931_getcall(ctrl, ctrl->cref | 0x8000); } -static void q931_destroy(struct pri *ctrl, int cr, q931_call *c) +static void stop_t303(struct q931_call *call); + +static void cleanup_and_free_call(struct q931_call *cur) { - q931_call *cur, *prev; + stop_t303(cur); + pri_schedule_del(cur->pri, cur->retranstimer); + pri_call_apdu_queue_cleanup(cur); + free(cur); +} + +static void pri_create_fake_clearing(struct q931_call *c, struct pri *master); + +void q931_destroycall(struct pri *ctrl, q931_call *c) +{ + q931_call *cur; + q931_call *prev; + q931_call *slave; + int i; + int slavesleft; + int slaveidx; + + if (c->master_call != c) { + slave = c; + c = slave->master_call; + } else { + slave = NULL; + } /* For destroying, make sure we are using the master span, since it maintains the call pool */ - for (;ctrl->master; ctrl = ctrl->master); + ctrl = PRI_MASTER(ctrl); prev = NULL; cur = *ctrl->callpool; - while(cur) { - if ((c && (cur == c)) || (!c && (cur->cr == cr))) { + while (cur) { + if (cur == c) { + slaveidx = -1; + if (slave) { + for (i = 0; i < Q931_MAX_TEI; i++) { + if (cur->subcalls[i] == slave) { + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Destroying subcall %p of call %p at %d\n", + slave, cur, i); + } + cleanup_and_free_call(slave); + cur->subcalls[i] = NULL; + slaveidx = i; + break; + } + } + } + + slavesleft = 0; + for (i = 0; i < Q931_MAX_TEI; i++) { + if (cur->subcalls[i]) { + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Subcall still present at %d\n", i); + } + slavesleft++; + } + } + + /* We have 3 different phases to deal with: + * 1.) Sent outbound call, but no response, indicated by t203 present + * 2.) Sent outbound call, with responses, indicated by lack of t203 and subcalls present + * 3.) Outbound call connected, indicated by pri_winner > -1 + * + * If chan_dahdi hangs up in phase: + * 1.) T303 will be present, and we will fake clear in this case + * 2.) pri_winner will be < 0 and subcalls will be present. + * 3.) pri_winner will be > -1 and we will free the master when the winner dies. + * + * If remote ends hang up in phase: + * 1.) Impossible, defined by phase. + * 2.) When last end hangs up, we should cause a fake clearing. + * 3.) Pass events to winner up and be freed when winner is freed + * + * Exceptional conditions in phase: + * 1.) None. + * 2.) None. + * 3.) We hang up a call so quickly that it hangs up before other competing lines finish hangup sequence + * Subcalls present still even though we have hung up the winner. + * + * So, we could say: + * If, when the library user hangs up the master call, and there are more than one subcall up, we fake clear + * regardless of whether or not we drop down to one subcall left in the clearing process. + * + * If there are only one call up, we mirror what it does. + * + * OR + * + * Phase 2. them clearing: + * For handling of Phase 2 (indicated by not running and pri_winner not present): + * We create a fake hangup sequence after all the subcalls have been destroyed and after + * + * "" us clearing: + * For we need to start the fake clearing, but it needs to be half of a fake clearing, not a full one (since we already had a hangup). + * + * For handling of Phase 3 plus exceptions: + * + * If pri_winner exists, we mirror him in terms of events (which provides our hangup sequence), and when we have the complete + * hangup sequence completed (destroy called on master call), if there still exist non winner subcalls at this time, we declare the master + * call as dead and free it when the last subcall clears. + */ + + if ((slave && !slavesleft) && + ((cur->pri_winner < 0) || (slave && slaveidx != cur->pri_winner))) { + pri_create_fake_clearing(cur, ctrl); + return; + } + + if (slavesleft) { + return; + } + + /* Master call or normal call destruction. */ + if ((cur->pri_winner > -1) && cur->outboundbroadcast) { + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, + "Since we already had a winner, we should just be able to kill the call anyways\n"); + } + } if (prev) prev->next = cur->next; else *ctrl->callpool = cur->next; if (ctrl->debug & PRI_DEBUG_Q931_STATE) pri_message(ctrl, - "NEW_HANGUP DEBUG: Destroying the call, ourstate %s, peerstate %s\n", + "NEW_HANGUP DEBUG: Destroying the call, ourstate %s, peerstate %s, hold-state %s\n", q931_call_state_str(cur->ourcallstate), - q931_call_state_str(cur->peercallstate)); - pri_schedule_del(ctrl, cur->retranstimer); - pri_call_apdu_queue_cleanup(cur); - free(cur); + q931_call_state_str(cur->peercallstate), + q931_hold_state_str(cur->hold_state)); + pri_schedule_del(ctrl, cur->hold_timer); + cleanup_and_free_call(cur); return; } prev = cur; cur = cur->next; } - pri_error(ctrl, "Can't destroy call %d!\n", cr); -} - -static void q931_destroycall(struct pri *ctrl, int cr) -{ - q931_destroy(ctrl, cr, NULL); -} - - -void __q931_destroycall(struct pri *ctrl, q931_call *call) -{ - if (ctrl && call) { - q931_destroy(ctrl, 0, call); - } + pri_error(ctrl, "Can't destroy call %p cref:%d!\n", c, c->cr); } static int add_ie(struct pri *ctrl, q931_call *call, int msgtype, int ie, q931_ie *iet, int maxlen, int *codeset) @@ -3607,9 +3773,20 @@ static void init_header(struct pri *ctrl, q931_call *call, unsigned char *buf, q *mhb = mh; } -static int q931_xmit(struct pri *ctrl, q931_h *h, int len, int cr) +static int q931_xmit(struct pri *ctrl, q931_h *h, int len, int cr, int uiframe) { - q921_transmit_iframe(ctrl, h, len, cr); + /* + * For NT-PTMP mode, we need to check the following: + * MODE = NT-PTMP + * MESSAGE = SETUP + * + * If those are true, we need to send the SETUP in a UI frame + * instead of an I-frame. + */ + if (BRI_NT_PTMP(ctrl) && uiframe) + q921_transmit_uiframe(ctrl, h, len); + else + q921_transmit_iframe(ctrl, h, len, cr); /* The transmit operation might dump the q921 header, so logging the q931 message body after the transmit puts the sections of the message in the right order in the log */ @@ -3647,6 +3824,13 @@ static int send_message(struct pri *ctrl, q931_call *call, int msgtype, int ies[ int x; int codeset; + if (call->outboundbroadcast && call->master_call == call && msgtype != Q931_SETUP) { + pri_error(ctrl, + "Attempting to use master call record to send %s on BRI PTMP NT %p\n", + msg2str(msgtype), ctrl); + return -1; + } + memset(buf, 0, sizeof(buf)); len = sizeof(buf); init_header(ctrl, call, buf, &h, &mh, &len, (msgtype >> 8)); @@ -3668,7 +3852,7 @@ static int send_message(struct pri *ctrl, q931_call *call, int msgtype, int ies[ len = sizeof(buf) - len; ctrl = call->pri; - if (q931_is_ptmp(ctrl) && (ctrl->localtype == PRI_CPE)) { + if (BRI_TE_PTMP(ctrl)) { /* * Must use the BRI subchannel structure to send with the correct TEI. * Note: If the subchannel is NULL then there is no TEI assigned and @@ -3677,7 +3861,14 @@ static int send_message(struct pri *ctrl, q931_call *call, int msgtype, int ies[ ctrl = ctrl->subchannel; } if (ctrl) { - q931_xmit(ctrl, h, len, 1); + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, + "Sending message for call %p on %p TEI/SAPI %d/%d, call->pri is %p, TEI/SAPI %d/%d\n", + call, + ctrl, ctrl->tei, ctrl->sapi, + call->pri, call->pri->tei, call->pri->sapi); + } + q931_xmit(ctrl, h, len, 1, (msgtype == Q931_SETUP) ? 1 : 0); } call->acked = 1; return 0; @@ -3799,6 +3990,29 @@ int q931_facility(struct pri*ctrl, q931_call *c) static int notify_ies[] = { Q931_IE_NOTIFY_IND, Q931_IE_REDIRECTION_NUMBER, -1 }; +/*! + * \internal + * \brief Actually send a NOTIFY message with optional redirection number. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * \param notify Notification indicator + * \param number Redirection number to send if not NULL. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int q931_notify_redirection_helper(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number) +{ + if (number) { + call->redirection_number = *number; + } else { + q931_party_number_init(&call->redirection_number); + } + call->notify = notify; + return send_message(ctrl, call, Q931_NOTIFY, notify_ies); +} + /*! * \brief Send a NOTIFY message with optional redirection number. * @@ -3812,13 +4026,33 @@ static int notify_ies[] = { Q931_IE_NOTIFY_IND, Q931_IE_REDIRECTION_NUMBER, -1 } */ int q931_notify_redirection(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number) { - if (number) { - call->redirection_number = *number; + int status; + unsigned idx; + struct q931_call *subcall; + + if (call->outboundbroadcast && call->master_call == call) { + status = 0; + for (idx = 0; idx < Q931_MAX_TEI; ++idx) { + subcall = call->subcalls[idx]; + if (subcall) { + /* Send to all subcalls that have given a positive response. */ + switch (subcall->ourcallstate) { + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_ACTIVE: + if (q931_notify_redirection_helper(ctrl, subcall, notify, number)) { + status = -1; + } + break; + default: + break; + } + } + } } else { - q931_party_number_init(&call->redirection_number); + status = q931_notify_redirection_helper(ctrl, call, notify, number); } - call->notify = notify; - return send_message(ctrl, call, Q931_NOTIFY, notify_ies); + return status; } int q931_notify(struct pri *ctrl, q931_call *c, int channel, int info) @@ -4020,10 +4254,12 @@ static void pri_release_finaltimeout(void *data) ctrl->ev.hangup.channel = q931_encode_channel(c); ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; - ctrl->ev.hangup.call = c; + ctrl->ev.hangup.call = c->master_call; ctrl->ev.hangup.aoc_units = c->aoc_units; + ctrl->ev.hangup.call_held = NULL; + ctrl->ev.hangup.call_active = NULL; libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); - q931_hangup(ctrl, c, c->cause); + pri_hangup(ctrl, c, c->cause); } /* T305 expiry, first time */ @@ -4146,7 +4382,7 @@ int q931_restart(struct pri *ctrl, int channel) return send_message(ctrl, c, Q931_RESTART, restart_ies); } -static int disconnect_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, -1 }; +static int disconnect_ies[] = { Q931_CAUSE, Q931_IE_FACILITY, Q931_IE_USER_USER, -1 }; int q931_disconnect(struct pri *ctrl, q931_call *c, int cause) { @@ -4172,6 +4408,7 @@ static int setup_ies[] = { Q931_PROGRESS_INDICATOR, Q931_NETWORK_SPEC_FAC, Q931_DISPLAY, + Q931_IE_KEYPAD_FACILITY, Q931_REVERSE_CHARGE_INDIC, Q931_CALLING_PARTY_NUMBER, Q931_CALLING_PARTY_SUBADDR, @@ -4196,6 +4433,7 @@ static int cis_setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, Q931_IE_FACILITY, + Q931_IE_KEYPAD_FACILITY, Q931_CALLING_PARTY_NUMBER, Q931_CALLING_PARTY_SUBADDR, Q931_CALLED_PARTY_NUMBER, @@ -4204,11 +4442,74 @@ static int cis_setup_ies[] = { -1 }; +static void stop_t303(struct q931_call *call) +{ + /* T303 should only be running on the master call */ + pri_schedule_del(call->master_call->pri, call->master_call->t303_timer); + call->master_call->t303_timer = 0; +} + +static void t303_expiry(void *data); + +static void start_t303(struct q931_call *call) +{ + if (call->t303_timer) { + pri_error(call->pri, "Should not have T303 set when starting again. Stopping first\n"); + stop_t303(call); + } + + //pri_error(call->pri, "T303 should be %d\n", call->pri->timers[PRI_TIMER_T303]); + call->t303_timer = pri_schedule_event(call->pri, call->pri->timers[PRI_TIMER_T303], t303_expiry, call); +} + +static void pri_fake_clearing(void *data); + +static void t303_expiry(void *data) +{ + struct q931_call *c = data; + struct pri *ctrl = c->pri; + int res; + + c->t303_expirycnt++; + c->t303_timer = 0; + + if (c->t303_expirycnt < 2) { + if (ctrl->subchannel && !ctrl->bri) + res = send_message(ctrl, c, Q931_SETUP, gr303_setup_ies); + else if (c->cis_call) + res = send_message(ctrl, c, Q931_SETUP, cis_setup_ies); + else + res = send_message(ctrl, c, Q931_SETUP, setup_ies); + + if (res) { + pri_error(c->pri, "Error resending setup message!\n"); + } + start_t303(c); + } else { + c->cause = PRI_CAUSE_NO_USER_RESPONSE; + pri_fake_clearing(c); + } +} + int q931_setup(struct pri *ctrl, q931_call *c, struct pri_sr *req) { int res; - - + + if (!req->called.number.valid && (!req->keypad_digits || !req->keypad_digits[0])) { + /* No called number or keypad digits to send. */ + return -1; + } + + c->called = req->called; + libpri_copy_string(c->overlap_digits, req->called.number.str, sizeof(c->overlap_digits)); + + if (req->keypad_digits) { + libpri_copy_string(c->keypad_digits, req->keypad_digits, + sizeof(c->keypad_digits)); + } else { + c->keypad_digits[0] = '\0'; + } + c->transcapability = req->transmode; c->transmoderate = TRANS_MODE_64_CIRCUIT; if (!req->userl1) @@ -4249,12 +4550,6 @@ int q931_setup(struct pri *ctrl, q931_call *c, struct pri_sr *req) q931_party_id_fixup(ctrl, &c->redirecting.orig_called); } - if (req->called.number.valid) { - c->called = req->called; - libpri_copy_string(c->overlap_digits, req->called.number.str, sizeof(c->overlap_digits)); - } else - return -1; - if (req->useruserinfo) libpri_copy_string(c->useruserinfo, req->useruserinfo, sizeof(c->useruserinfo)); else @@ -4280,7 +4575,12 @@ int q931_setup(struct pri *ctrl, q931_call *c, struct pri_sr *req) /* make sure we call PRI_EVENT_HANGUP_ACK once we send/receive RELEASE_COMPLETE */ c->sendhangupack = 1; UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_INITIATED); - c->peercallstate = Q931_CALL_STATE_OVERLAP_SENDING; + c->peercallstate = Q931_CALL_STATE_CALL_PRESENT; + c->t303_expirycnt = 0; + if (BRI_NT_PTMP(ctrl)) { + c->outboundbroadcast = 1; + } + start_t303(c); } return res; @@ -4303,7 +4603,7 @@ static int q931_release_complete(struct pri *ctrl, q931_call *c, int cause) res = send_message(ctrl, c, Q931_RELEASE_COMPLETE, release_complete_ies); c->alive = 0; /* release the structure */ - res += q931_hangup(ctrl,c,cause); + res += pri_hangup(ctrl, c, cause); return res; } @@ -4321,16 +4621,491 @@ static int q931_connect_acknowledge(struct pri *ctrl, q931_call *c) return 0; } -int q931_hangup(struct pri *ctrl, q931_call *c, int cause) +/*! + * \internal + * \brief Find the winning subcall if it exists or current call if not outboundbroadcast. + * + * \param call Starting Q.931 call record of search. + * + * \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 *master; + + master = call->master_call; + if (master->outboundbroadcast) { + /* We have potential subcalls. Now get the winning call if declared yet. */ + if (master->pri_winner < 0) { + /* Winner not declared yet.*/ + call = NULL; + } else { + call = master->subcalls[master->pri_winner]; + } + } + return call; +} + +/*! + * \internal + * \brief Send HOLD message response wait timeout. + * + * \param data Q.931 call leg. (Master Q.931 subcall structure) + * + * \return Nothing + */ +static void q931_hold_timeout(void *data) +{ + struct q931_call *call = data; + struct pri *ctrl = call->pri; + + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Time-out waiting for HOLD response\n"); + } + + /* Ensure that the timer is deleted. */ + pri_schedule_del(ctrl, call->hold_timer); + call->hold_timer = 0; + + UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_IDLE); + + q931_clr_subcommands(ctrl); + ctrl->schedev = 1; + ctrl->ev.e = PRI_EVENT_HOLD_REJ; + ctrl->ev.hold_rej.channel = q931_encode_channel(call); + ctrl->ev.hold_rej.call = call; + ctrl->ev.hold_rej.cause = PRI_CAUSE_MESSAGE_TYPE_NONEXIST; + ctrl->ev.hold_rej.subcmds = &ctrl->subcmds; +} + +/*! + * \internal + * \brief Determine if a hold request is allowed now. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (Master Q.931 subcall structure) + * + * \retval TRUE if we can send a HOLD request. + * \retval FALSE if not allowed. + */ +static int q931_is_hold_allowed(const struct pri *ctrl, const struct q931_call *call) +{ + int allowed; + + allowed = 0; + switch (call->ourcallstate) { + case Q931_CALL_STATE_CALL_RECEIVED: + case Q931_CALL_STATE_CONNECT_REQUEST: + case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: + if (q931_is_ptmp(ctrl)) { + /* HOLD request only allowed in these states if point-to-point mode. */ + break; + } + /* Fall through */ + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_ACTIVE: + switch (call->hold_state) { + case Q931_HOLD_STATE_IDLE: + allowed = 1; + break; + default: + break; + } + break; + case Q931_CALL_STATE_DISCONNECT_INDICATION: + case Q931_CALL_STATE_RELEASE_REQUEST: + /* Ignore HOLD request in these states. */ + break; + default: + break; + } + + return allowed; +} + +static int hold_ies[] = { + -1 +}; + +/*! + * \brief Send the HOLD message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (Master Q.931 subcall structure) + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_send_hold(struct pri *ctrl, struct q931_call *call) +{ + struct q931_call *winner; + + winner = q931_find_winning_call(call); + if (!winner || !q931_is_hold_allowed(ctrl, call)) { + return -1; + } + pri_schedule_del(ctrl, call->hold_timer); + call->hold_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T_HOLD], + q931_hold_timeout, call); + if (send_message(ctrl, winner, Q931_HOLD, hold_ies)) { + pri_schedule_del(ctrl, call->hold_timer); + call->hold_timer = 0; + return -1; + } + UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_HOLD_REQ); + return 0; +} + +static int hold_ack_ies[] = { + -1 +}; + +/*! + * \brief Send the HOLD ACKNOWLEDGE message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (Master Q.931 subcall structure) + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_send_hold_ack(struct pri *ctrl, struct q931_call *call) +{ + struct q931_call *winner; + + UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_CALL_HELD); + + winner = q931_find_winning_call(call); + if (!winner) { + return -1; + } + + /* Call is now on hold so forget the channel. */ + winner->channelno = 0;/* No channel */ + winner->ds1no = 0; + winner->ds1explicit = 0; + winner->chanflags = 0; + + return send_message(ctrl, winner, Q931_HOLD_ACKNOWLEDGE, hold_ack_ies); +} + +static int hold_reject_ies[] = { + Q931_CAUSE, + -1 +}; + +/*! + * \internal + * \brief Send the HOLD REJECT message only. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (subcall) + * \param cause Q.931 cause code for rejecting the hold request. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int q931_send_hold_rej_msg(struct pri *ctrl, struct q931_call *call, int cause) +{ + call->cause = cause; + call->causecode = CODE_CCITT; + call->causeloc = LOC_PRIV_NET_LOCAL_USER; + return send_message(ctrl, call, Q931_HOLD_REJECT, hold_reject_ies); +} + +/*! + * \brief Send the HOLD REJECT message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (Master Q.931 subcall structure) + * \param cause Q.931 cause code for rejecting the hold request. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_send_hold_rej(struct pri *ctrl, struct q931_call *call, int cause) +{ + struct q931_call *winner; + + UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_IDLE); + + winner = q931_find_winning_call(call); + if (!winner) { + return -1; + } + + return q931_send_hold_rej_msg(ctrl, winner, cause); +} + +/*! + * \internal + * \brief Send RETRIEVE message response wait timeout. + * + * \param data Q.931 call leg. (Master Q.931 subcall structure) + * + * \return Nothing + */ +static void q931_retrieve_timeout(void *data) +{ + struct q931_call *call = data; + struct pri *ctrl = call->pri; + struct q931_call *winner; + + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Time-out waiting for RETRIEVE response\n"); + } + + /* Ensure that the timer is deleted. */ + pri_schedule_del(ctrl, call->hold_timer); + call->hold_timer = 0; + + UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_CALL_HELD); + + winner = q931_find_winning_call(call); + if (winner) { + /* Call is still on hold so forget the channel. */ + winner->channelno = 0;/* No channel */ + winner->ds1no = 0; + winner->ds1explicit = 0; + winner->chanflags = 0; + } + + q931_clr_subcommands(ctrl); + ctrl->schedev = 1; + ctrl->ev.e = PRI_EVENT_RETRIEVE_REJ; + ctrl->ev.retrieve_rej.channel = q931_encode_channel(call); + ctrl->ev.retrieve_rej.call = call; + ctrl->ev.retrieve_rej.cause = PRI_CAUSE_MESSAGE_TYPE_NONEXIST; + ctrl->ev.retrieve_rej.subcmds = &ctrl->subcmds; +} + +/*! + * \internal + * \brief Determine if a retrieve request is allowed now. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (Master Q.931 subcall structure) + * + * \retval TRUE if we can send a RETRIEVE request. + * \retval FALSE if not allowed. + */ +static int q931_is_retrieve_allowed(const struct pri *ctrl, const struct q931_call *call) +{ + int allowed; + + allowed = 0; + switch (call->ourcallstate) { + case Q931_CALL_STATE_CALL_RECEIVED: + case Q931_CALL_STATE_CONNECT_REQUEST: + case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: + if (q931_is_ptmp(ctrl)) { + /* RETRIEVE request only allowed in these states if point-to-point mode. */ + break; + } + /* Fall through */ + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_ACTIVE: + switch (call->hold_state) { + case Q931_HOLD_STATE_CALL_HELD: + allowed = 1; + break; + default: + break; + } + break; + case Q931_CALL_STATE_DISCONNECT_INDICATION: + case Q931_CALL_STATE_RELEASE_REQUEST: + /* Ignore RETRIEVE request in these states. */ + break; + default: + break; + } + + return allowed; +} + +static int retrieve_ies[] = { + Q931_CHANNEL_IDENT, + -1 +}; + +/*! + * \brief Send the RETRIEVE message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (Master Q.931 subcall structure) + * \param channel Encoded channel id to use. If zero do not send channel id. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_send_retrieve(struct pri *ctrl, struct q931_call *call, int channel) +{ + struct q931_call *winner; + + winner = q931_find_winning_call(call); + if (!winner || !q931_is_retrieve_allowed(ctrl, call)) { + return -1; + } + + if (channel) { + winner->ds1no = (channel & 0xff00) >> 8; + winner->ds1explicit = (channel & 0x10000) >> 16; + winner->channelno = channel & 0xff; + if (ctrl->localtype == PRI_NETWORK) { + winner->chanflags = FLAG_EXCLUSIVE; + } else { + winner->chanflags = FLAG_PREFERRED; + } + } else { + /* Do not send Q931_CHANNEL_IDENT */ + winner->chanflags = 0; + } + + pri_schedule_del(ctrl, call->hold_timer); + call->hold_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T_RETRIEVE], + q931_retrieve_timeout, call); + if (send_message(ctrl, winner, Q931_RETRIEVE, retrieve_ies)) { + pri_schedule_del(ctrl, call->hold_timer); + call->hold_timer = 0; + + /* Call is still on hold so forget the channel. */ + winner->channelno = 0;/* No channel */ + winner->ds1no = 0; + winner->ds1explicit = 0; + winner->chanflags = 0; + return -1; + } + UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_RETRIEVE_REQ); + return 0; +} + +static int retrieve_ack_ies[] = { + Q931_CHANNEL_IDENT, + -1 +}; + +/*! + * \brief Send the RETRIEVE ACKNOWLEDGE message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (Master Q.931 subcall structure) + * \param channel Encoded channel id to use. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_send_retrieve_ack(struct pri *ctrl, struct q931_call *call, int channel) +{ + struct q931_call *winner; + + winner = q931_find_winning_call(call); + if (!winner) { + return -1; + } + + winner->ds1no = (channel & 0xff00) >> 8; + winner->ds1explicit = (channel & 0x10000) >> 16; + winner->channelno = channel & 0xff; + winner->chanflags = FLAG_EXCLUSIVE; + + UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_IDLE); + + return send_message(ctrl, winner, Q931_RETRIEVE_ACKNOWLEDGE, retrieve_ack_ies); +} + +static int retrieve_reject_ies[] = { + Q931_CAUSE, + -1 +}; + +/*! + * \internal + * \brief Send the RETRIEVE REJECT message only. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (subcall) + * \param cause Q.931 cause code for rejecting the retrieve request. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int q931_send_retrieve_rej_msg(struct pri *ctrl, struct q931_call *call, int cause) +{ + call->cause = cause; + call->causecode = CODE_CCITT; + call->causeloc = LOC_PRIV_NET_LOCAL_USER; + return send_message(ctrl, call, Q931_RETRIEVE_REJECT, retrieve_reject_ies); +} + +/*! + * \brief Send the RETRIEVE REJECT message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (Master Q.931 subcall structure) + * \param cause Q.931 cause code for rejecting the retrieve request. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_send_retrieve_rej(struct pri *ctrl, struct q931_call *call, int cause) +{ + struct q931_call *winner; + + UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_CALL_HELD); + + winner = q931_find_winning_call(call); + if (!winner) { + return -1; + } + + /* Call is still on hold so forget the channel. */ + winner->channelno = 0;/* No channel */ + winner->ds1no = 0; + winner->ds1explicit = 0; + winner->chanflags = 0; + + return q931_send_retrieve_rej_msg(ctrl, winner, cause); +} + +static int pri_internal_clear(void *data); + +/* Fake RELEASE for NT-PTMP initiated SETUPs w/o response */ +static void pri_fake_clearing(void *data) +{ + struct q931_call *c = data; + struct pri *ctrl = c->pri; + + c->performing_fake_clearing = 1; + if (pri_internal_clear(c) == Q931_RES_HAVEEVENT) + ctrl->schedev = 1; +} + +static void pri_create_fake_clearing(struct q931_call *c, struct pri *master) +{ + c->pri = master; + + pri_schedule_del(master, c->retranstimer); + c->retranstimer = pri_schedule_event(master, 0, pri_fake_clearing, c); +} + +//static int q931_get_subcall_count(struct q931_call *call); + +static int __q931_hangup(struct pri *ctrl, q931_call *c, int cause) { int disconnect = 1; int release_compl = 0; + int t303_was_running = c->master_call->t303_timer; if (ctrl->debug & PRI_DEBUG_Q931_STATE) pri_message(ctrl, - "NEW_HANGUP DEBUG: Calling q931_hangup, ourstate %s, peerstate %s\n", + "NEW_HANGUP DEBUG: Calling q931_hangup, ourstate %s, peerstate %s, hold-state %s\n", q931_call_state_str(c->ourcallstate), - q931_call_state_str(c->peercallstate)); + q931_call_state_str(c->peercallstate), + q931_hold_state_str(c->master_call->hold_state)); if (!ctrl || !c) return -1; /* If mandatory IE was missing, insist upon that cause code */ @@ -4348,7 +5123,7 @@ int q931_hangup(struct pri *ctrl, q931_call *c, int cause) break; case PRI_CAUSE_CHANNEL_UNACCEPTABLE: case PRI_CAUSE_CALL_AWARDED_DELIVERED: - case 26: + case PRI_CAUSE_NONSELECTED_USER_CLEARING: /* We'll send RELEASE with these causes */ disconnect = 0; break; @@ -4359,16 +5134,30 @@ int q931_hangup(struct pri *ctrl, q931_call *c, int cause) disconnect = 0; } + c->hangupinitiated = 1; + stop_t303(c); + /* All other causes we send with DISCONNECT */ switch(c->ourcallstate) { case Q931_CALL_STATE_NULL: if (c->peercallstate == Q931_CALL_STATE_NULL) /* free the resources if we receive or send REL_COMPL */ - q931_destroycall(ctrl, c->cr); + pri_destroycall(ctrl, c); else if (c->peercallstate == Q931_CALL_STATE_RELEASE_REQUEST) q931_release_complete(ctrl,c,cause); break; case Q931_CALL_STATE_CALL_INITIATED: + if (c->outboundbroadcast && c->master_call == c && t303_was_running) { + //c->fakeclearing = 1; + //c->alive = 0; + /* We need to fake a received clearing sequence in this case... */ + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Faking clearing\n"); + } + pri_create_fake_clearing(c, PRI_MASTER(ctrl)); + /* This means that we never got a response from a TEI */ + return 0; + } /* we sent SETUP */ case Q931_CALL_STATE_OVERLAP_SENDING: /* received SETUP_ACKNOWLEDGE */ @@ -4452,6 +5241,56 @@ int q931_hangup(struct pri *ctrl, q931_call *c, int cause) return 0; } +static void initiate_hangup_if_needed(struct pri *pri, q931_call *call, int cause); + +int q931_hangup(struct pri *ctrl, q931_call *call, int cause) +{ + int i; + + if (call->master_call->outboundbroadcast) { + if (call->master_call == call) { + int slaves = 0; + + /* Master is called with hangup - initiate hangup with slaves */ + for (i = 0; i < Q931_MAX_TEI; i++) { + if (call->subcalls[i]) { + slaves++; + if (i == call->master_call->pri_winner) { + __q931_hangup(call->subcalls[i]->pri, call->subcalls[i], cause); + } else { + initiate_hangup_if_needed(call->subcalls[i]->pri, call->subcalls[i], cause); + } + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "%s: Hanging up %d, winner %d\n", __FUNCTION__, + i, call->master_call->pri_winner); + } + } + } + + call->hangupinitiated = 1; + + if ((!slaves && (call->master_call->pri_winner < 0)) || (call->performing_fake_clearing)) { + __q931_hangup(ctrl, call, cause); + } + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "%s: Slaves %d\n", __FUNCTION__, slaves); + } + return 0; + } else { + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "%s: Slave hangup\n", __FUNCTION__); + } + return __q931_hangup(ctrl, call, cause); + } + } else { + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "%s: other hangup\n", __FUNCTION__); + } + return __q931_hangup(ctrl, call, cause); + } + return 0; +} + static int prepare_to_handle_maintenance_message(struct pri *ctrl, q931_mh *mh, q931_call *c) { if ((!ctrl) || (!mh) || (!c)) { @@ -4523,6 +5362,13 @@ static int prepare_to_handle_q931_message(struct pri *ctrl, q931_mh *mh, q931_ca q931_party_id_init(&c->remote_id); q931_party_redirecting_init(&c->redirecting); + /* + * Make sure that keypad and overlap digit buffers are empty in + * case they are not in the message. + */ + c->keypad_digits[0] = '\0'; + c->overlap_digits[0] = '\0'; + c->useruserprotocoldisc = -1; c->useruserinfo[0] = '\0'; c->complete = 0; @@ -4587,15 +5433,26 @@ static int prepare_to_handle_q931_message(struct pri *ctrl, q931_mh *mh, q931_ca case Q931_NOTIFY: q931_party_number_init(&c->redirection_number); break; + case Q931_HOLD: + break; + case Q931_HOLD_ACKNOWLEDGE: + break; + case Q931_HOLD_REJECT: + c->cause = -1; + break; + case Q931_RETRIEVE: + c->channelno = 0xFF; + c->ds1no = 0; + c->ds1explicit = 0; + break; + case Q931_RETRIEVE_ACKNOWLEDGE: + break; + case Q931_RETRIEVE_REJECT: + c->cause = -1; + break; case Q931_USER_INFORMATION: case Q931_SEGMENT: case Q931_CONGESTION_CONTROL: - case Q931_HOLD: - case Q931_HOLD_ACKNOWLEDGE: - case Q931_HOLD_REJECT: - case Q931_RETRIEVE: - case Q931_RETRIEVE_ACKNOWLEDGE: - case Q931_RETRIEVE_REJECT: case Q931_RESUME: case Q931_RESUME_ACKNOWLEDGE: case Q931_RESUME_REJECT: @@ -4608,12 +5465,125 @@ static int prepare_to_handle_q931_message(struct pri *ctrl, q931_mh *mh, q931_ca pri_error(ctrl, "!! Don't know how to pre-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg); q931_status(ctrl,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST); if (c->newcall) - q931_destroycall(ctrl,c->cr); + pri_destroycall(ctrl, c); return -1; } return 0; } +static struct q931_call *q931_get_subcall_winner(struct q931_call *master) +{ + if (master->pri_winner < 0) { + return NULL; + } else { + return master->subcalls[master->pri_winner]; + } +} + +static void initiate_hangup_if_needed(struct pri *pri, q931_call *call, int cause) +{ + if (!call->hangupinitiated) { + q931_hangup(pri, call, cause); + call->alive = 0; + } +} + +#if 0 +static int q931_get_subcall_count(struct q931_call *call) +{ + int count = 0; + int i; + + call = call->master_call; + for (i = 0; i < Q931_MAX_TEI; i++) { + if (call->subcalls[i]) + count++; + } + + return count; +} +#endif + +static void q931_set_subcall_winner(struct q931_call *subcall) +{ + struct q931_call *realcall = subcall->master_call; + int i; + + /* Set the winner first */ + for (i = 0; i < Q931_MAX_TEI; i++) { + if (realcall->subcalls[i] && realcall->subcalls[i] == subcall) { + realcall->pri_winner = i; + } + } + if (realcall->pri_winner < 0) { + pri_error(subcall->pri, "We should always find the winner in the list!\n"); + return; + } + + /* Start tear down of calls that were not chosen */ + for (i = 0; i < Q931_MAX_TEI; i++) { + if (realcall->subcalls[i] && realcall->subcalls[i] != subcall) { + initiate_hangup_if_needed(realcall->subcalls[i]->pri, realcall->subcalls[i], + PRI_CAUSE_NONSELECTED_USER_CLEARING); + } + } +} + +static struct q931_call *q931_get_subcall(struct pri *ctrl, struct q931_call *master_call) +{ + int i; + struct q931_call *cur; + int firstfree = -1; + + /* First try to locate our subcall */ + for (i = 0; i < Q931_MAX_TEI; i++) { + if (master_call->subcalls[i]) { + if (master_call->subcalls[i]->pri == ctrl) { + return master_call->subcalls[i]; + } + } else if (firstfree == -1) { + firstfree = i; + } + } + if (firstfree < 0) { + pri_error(ctrl, "Tried to add more than %d TEIs to call and failed\n", + Q931_MAX_TEI); + return NULL; + } + + /* Create new subcall. */ + cur = malloc(sizeof(*cur)); + if (!cur) { + pri_error(ctrl, "Unable to allocate call\n"); + return NULL; + } + *cur = *master_call; + cur->pri = ctrl; + cur->next = NULL; + cur->apdus = NULL; + cur->bridged_call = NULL; + //cur->master_call = master_call; /* We get this assignment for free. */ + for (i = 0; i < Q931_MAX_TEI; ++i) { + cur->subcalls[i] = NULL; + } + cur->t303_timer = 0;/* T303 should only be on on the master call */ + cur->hold_timer = 0; + cur->retranstimer = 0; + + /* Assume we sent a SETUP and this is the first response to it from this peer. */ + cur->ourcallstate = Q931_CALL_STATE_CALL_INITIATED; + cur->peercallstate = Q931_CALL_STATE_CALL_PRESENT; + + master_call->subcalls[firstfree] = cur; + + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Adding subcall %p for TEI %d to call %p at position %d\n", + cur, ctrl->tei, master_call, firstfree); + } + /* Should only get here if the TEI is not found */ + return cur; +} + int q931_receive(struct pri *ctrl, q931_h *h, int len) { q931_mh *mh; @@ -4648,7 +5618,7 @@ int q931_receive(struct pri *ctrl, q931_h *h, int len) KLUDGE this by changing byte 4 from a 0xf (SERVICE) to a 0x7 (SERVICE ACKNOWLEDGE) */ h->raw[h->crlen + 2] -= 0x8; - q931_xmit(ctrl, h, len, 1); + q931_xmit(ctrl, h, len, 1, 0); return 0; } @@ -4658,6 +5628,22 @@ int q931_receive(struct pri *ctrl, q931_h *h, int len) pri_error(ctrl, "Unable to locate call %d\n", cref); return -1; } + if (c->master_call->outboundbroadcast && ctrl != PRI_MASTER(ctrl)) { + c = q931_get_subcall(ctrl, c->master_call); + if (!c) { + pri_error(ctrl, "Unable to locate subcall for %d\n", cref); + return -1; + } + } + + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, + "Received message for call %p on %p TEI/SAPI %d/%d, call->pri is %p TEI/SAPI %d/%d\n", + c, + ctrl, ctrl->tei, ctrl->sapi, + c->pri, c->pri->tei, c->pri->sapi); + } + /* Preliminary handling */ if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) { prepare_to_handle_maintenance_message(ctrl, mh, c); @@ -4757,7 +5743,21 @@ int q931_receive(struct pri *ctrl, q931_h *h, int len) if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) { res = post_handle_maintenance_message(ctrl, h->pd, mh, c); } else { - res = post_handle_q931_message(ctrl, mh, c, missingmand); + int allow_event = 1, allow_posthandle = 1; + + if (c->master_call->outboundbroadcast) { + nt_ptmp_handle_q931_message(ctrl, mh, c, &allow_event, &allow_posthandle); + } + + if (allow_posthandle) { + res = post_handle_q931_message(ctrl, mh, c, missingmand); + + if (res == Q931_RES_HAVEEVENT && !allow_event) { + res = 0; + } + } else { + res = 0; + } } return res; } @@ -4817,6 +5817,272 @@ static int post_handle_maintenance_message(struct pri *ctrl, int protodisc, stru return -1; } +/*! + * \internal + * \brief Rank the given Q.931 call state for call etablishment. + * + * \param state Q.931 call state to rank for competing PTMP NT calls. + * + * \return Call establishment state ranking. + */ +static enum Q931_RANKED_CALL_STATE q931_rank_state(enum Q931_CALL_STATE state) +{ + enum Q931_RANKED_CALL_STATE rank; + + switch (state) { + case Q931_CALL_STATE_CALL_INITIATED: + case Q931_CALL_STATE_CALL_PRESENT: + rank = Q931_RANKED_CALL_STATE_PRESENT; + break; + case Q931_CALL_STATE_OVERLAP_SENDING: + case Q931_CALL_STATE_OVERLAP_RECEIVING: + rank = Q931_RANKED_CALL_STATE_OVERLAP; + break; + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: + rank = Q931_RANKED_CALL_STATE_PROCEEDING; + break; + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_CALL_RECEIVED: + case Q931_CALL_STATE_CONNECT_REQUEST: + rank = Q931_RANKED_CALL_STATE_ALERTING; + break; + case Q931_CALL_STATE_ACTIVE: + case Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE: + rank = Q931_RANKED_CALL_STATE_CONNECT; + break; + default: + rank = Q931_RANKED_CALL_STATE_OTHER; + break; + } + + return rank; +} + +/*! + * \brief Determine if the master will pass an event to the upper layer. + * + * \param ctrl D channel controller. + * \param subcall Q.931 call leg. + * \param msg_type Current message type being processed. + * + * \note This function must parallel nt_ptmp_handle_q931_message(). + * + * \retval TRUE if the master will pass an event to the upper layer. + * \retval FALSE if the event will be blocked. + */ +int q931_master_pass_event(struct pri *ctrl, struct q931_call *subcall, int msg_type) +{ + struct q931_call *winner; + struct q931_call *master; + enum Q931_RANKED_CALL_STATE master_rank; + enum Q931_RANKED_CALL_STATE subcall_rank; + int will_pass; /*!< TRUE if the master will pass an event to the upper layer. */ + + master = subcall->master_call; + if (subcall == master) { + /* We are the master call so of course the master will pass an event. */ + return 1; + } + + winner = q931_get_subcall_winner(master); + if (winner && subcall == winner) { + /* We are the winner so of course the master will pass an event. */ + return 1; + } + + master_rank = q931_rank_state(master->ourcallstate); + will_pass = 0; + switch (msg_type) { + case Q931_SETUP_ACKNOWLEDGE: +#if 0 /* Overlap dialing in PTMP NT mode not supported at the present time. */ + if (master_rank < Q931_RANKED_CALL_STATE_OVERLAP) { + will_pass = 1; + } +#endif /* Overlap dialing in PTMP NT mode not supported at the present time. */ + break; + case Q931_CALL_PROCEEDING: + if (master_rank < Q931_RANKED_CALL_STATE_PROCEEDING) { + will_pass = 1; + } + break; + case Q931_PROGRESS: + /* + * We will just ignore this message since there could be multiple devices + * competing for this call. Who has access to the B channel at this time + * to give in-band signals anyway? + */ + break; + case Q931_ALERTING: + if (master_rank < Q931_RANKED_CALL_STATE_ALERTING) { + will_pass = 1; + } + break; + case Q931_CONNECT: + if (master_rank < Q931_RANKED_CALL_STATE_CONNECT) { + /* We are expected to be the winner for the next message. */ + will_pass = 1; + } + break; + case Q931_DISCONNECT: + case Q931_RELEASE: + case Q931_RELEASE_COMPLETE: + /* Only deal with the winner. */ + break; + case Q931_FACILITY: + case Q931_NOTIFY: + if (!winner) { + /* The overlap rank does not count here. */ + if (master_rank == Q931_RANKED_CALL_STATE_OVERLAP) { + master_rank = Q931_RANKED_CALL_STATE_PRESENT; + } + subcall_rank = q931_rank_state(subcall->ourcallstate); + if (subcall_rank == Q931_RANKED_CALL_STATE_OVERLAP) { + subcall_rank = Q931_RANKED_CALL_STATE_PRESENT; + } + if (master_rank == subcall_rank) { + /* + * No winner yet but the subcall is as advanced as the master. + * Allow the supplementary service event to pass. + */ + will_pass = 1; + } + } + break; + default: + /* Only deal with the winner. */ + break; + } + + return will_pass; +} + +/*! + * \internal + * \brief Handle outboundbroadcast incoming messages for the master_call's state. + * + * \param ctrl D channel controller. + * \param mh Q.931 message type header. + * \param subcall Q.931 call leg. + * \param allow_event Where to set the allow event to upper layer flag. + * \param allow_posthandle Where to set the allow post handle event flag. + * + * \details + * This is where we interact the subcalls state with the master_call's state. + * + * \note This function must parallel q931_master_pass_event(). + * + * \return Nothing + */ +static void nt_ptmp_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *subcall, int *allow_event, int *allow_posthandle) +{ + struct q931_call *master = subcall->master_call; + struct q931_call *winner = q931_get_subcall_winner(master); + enum Q931_RANKED_CALL_STATE master_rank; + enum Q931_RANKED_CALL_STATE subcall_rank; + enum Q931_CALL_STATE newstate; + + /* For broadcast calls, we default to not allowing events to keep events received to a minimum + * and to allow post processing, since that is where hangup and subcall state handling and other processing is done */ + *allow_event = 0; + *allow_posthandle = 1; + + master_rank = q931_rank_state(master->ourcallstate); + + switch (mh->msg) { + case Q931_SETUP_ACKNOWLEDGE: +#if 0 /* Overlap dialing in PTMP NT mode not supported at the present time. */ + if (master_rank < Q931_RANKED_CALL_STATE_OVERLAP) { + *allow_event = 1; + UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_OVERLAP_SENDING); + } +#endif /* Overlap dialing in PTMP NT mode not supported at the present time. */ + break; + case Q931_CALL_PROCEEDING: + if (master_rank < Q931_RANKED_CALL_STATE_PROCEEDING) { + *allow_event = 1; + UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING); + } + break; + case Q931_PROGRESS: + /* + * We will just ignore this message since there could be multiple devices + * competing for this call. Who has access to the B channel at this time + * to give in-band signals anyway? + */ + break; + case Q931_ALERTING: + if (master_rank < Q931_RANKED_CALL_STATE_ALERTING) { + *allow_event = 1; + UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_CALL_DELIVERED); + } + break; + case Q931_CONNECT: + if (master_rank < Q931_RANKED_CALL_STATE_CONNECT) { + UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_ACTIVE); + q931_set_subcall_winner(subcall); + *allow_event = 1; + } else { + /* Call clearing of non selected calls occurs in + * q931_set_subcall_winner() - All we need to do is make sure + * that this connect is not acknowledged */ + *allow_posthandle = 0; + } + break; + case Q931_DISCONNECT: + newstate = Q931_CALL_STATE_DISCONNECT_INDICATION; + goto process_hangup; + case Q931_RELEASE: + case Q931_RELEASE_COMPLETE: + newstate = Q931_CALL_STATE_NULL; +process_hangup: + if (!winner) { + /* If there's not a winner, we just take the cause and pass it up to the + * master_call */ + master->cause = subcall->cause; + } else { + /* There *is* a winner */ + if (subcall == winner) { + /* .. and we're it: */ + *allow_event = 1; + UPDATE_OURCALLSTATE(ctrl, master, newstate); + } + } + break; + case Q931_FACILITY: + case Q931_NOTIFY: + if (winner) { + if (subcall == winner) { + /* Only deal with the winner. */ + *allow_event = 1; + } + } else { + /* The overlap rank does not count here. */ + if (master_rank == Q931_RANKED_CALL_STATE_OVERLAP) { + master_rank = Q931_RANKED_CALL_STATE_PRESENT; + } + subcall_rank = q931_rank_state(subcall->ourcallstate); + if (subcall_rank == Q931_RANKED_CALL_STATE_OVERLAP) { + subcall_rank = Q931_RANKED_CALL_STATE_PRESENT; + } + if (master_rank == subcall_rank) { + /* + * No winner yet but the subcall is as advanced as the master. + * Allow the supplementary service event to pass. + */ + *allow_event = 1; + } + } + break; + default: + if (winner && subcall == winner) { + /* Only deal with the winner. */ + *allow_event = 1; + } + break; + } +} + /*! * \internal * \brief Fill in the FACILITY event fields. @@ -4832,7 +6098,8 @@ static void q931_fill_facility_event(struct pri *ctrl, struct q931_call *call) ctrl->ev.facility.subcmds = &ctrl->subcmds; ctrl->ev.facility.channel = q931_encode_channel(call); ctrl->ev.facility.cref = call->cr; - ctrl->ev.facility.call = call; + ctrl->ev.facility.call = call->master_call; + ctrl->ev.facility.subcall = call; /* Need to do this for backward compatibility with struct pri_event_facname */ libpri_copy_string(ctrl->ev.facility.callingname, call->remote_id.name.str, @@ -4843,6 +6110,114 @@ static void q931_fill_facility_event(struct pri *ctrl, struct q931_call *call) ctrl->ev.facility.callingplan = call->remote_id.number.plan; } +/*! + * \internal + * \brief Find the active call given the held call. + * + * \param ctrl D channel controller. + * \param held_call Held call to help locate a compatible active call. + * + * \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 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->hold_state == Q931_HOLD_STATE_IDLE) { + /* Found an active call. */ + winner = q931_find_winning_call(cur); + if (!winner || (BRI_NT_PTMP(ctrl) && winner->pri != held_call->pri)) { + /* There is no winner or the active call does not go to the same TEI. */ + continue; + } + 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: + break; + default: + /* Active call not in a good state to transfer. */ + continue; + } + if (q931_party_number_cmp(&winner->remote_id.number, + &held_call->remote_id.number)) { + /* The remote party number does not match. This is a weak match. */ + match = cur; + continue; + } + /* Found an exact match. */ + match = cur; + break; + } + } + + return match; +} + +/*! + * \internal + * \brief Find the held call given the active call. + * + * \param ctrl D channel controller. + * \param active_call Active call to help locate a compatible held call. + * + * \retval master-held-call on success. + * \retval NULL on error. + */ +static struct q931_call *q931_find_held_call(struct pri *ctrl, struct q931_call *active_call) +{ + 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->hold_state == Q931_HOLD_STATE_CALL_HELD) { + /* Found a held call. */ + winner = q931_find_winning_call(cur); + if (!winner || (BRI_NT_PTMP(ctrl) && winner->pri != active_call->pri)) { + /* There is no winner or the held call does not go to the same TEI. */ + continue; + } + 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: + break; + default: + /* Held call not in a good state to transfer. */ + continue; + } + if (q931_party_number_cmp(&winner->remote_id.number, + &active_call->remote_id.number)) { + /* The remote party number does not match. This is a weak match. */ + match = cur; + continue; + } + /* Found an exact match. */ + match = cur; + break; + } + } + + return match; +} + /*! * \internal * \brief Process the decoded information in the Q.931 message. @@ -4861,12 +6236,13 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct int res; struct apdu_event *cur = NULL; struct pri_subcommand *subcmd; + struct q931_call *master_call; switch(mh->msg) { case Q931_RESTART: if (missingmand) { q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING); - q931_destroycall(ctrl, c->cr); + pri_destroycall(ctrl, c); break; } UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_RESTART); @@ -4977,9 +6353,12 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct libpri_copy_string(ctrl->ev.ring.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.ring.useruserinfo)); c->useruserinfo[0] = '\0'; + libpri_copy_string(ctrl->ev.ring.keypad_digits, c->keypad_digits, + sizeof(ctrl->ev.ring.keypad_digits)); + ctrl->ev.ring.flexible = ! (c->chanflags & FLAG_EXCLUSIVE); ctrl->ev.ring.cref = c->cr; - ctrl->ev.ring.call = c; + ctrl->ev.ring.call = c->master_call; ctrl->ev.ring.layer1 = c->userl1; ctrl->ev.ring.complete = c->complete; ctrl->ev.ring.ctype = c->transcapability; @@ -4999,6 +6378,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct return Q931_RES_HAVEEVENT; case Q931_ALERTING: + stop_t303(c); if (c->newcall) { q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; @@ -5009,7 +6389,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct ctrl->ev.ringing.subcmds = &ctrl->subcmds; ctrl->ev.ringing.channel = q931_encode_channel(c); ctrl->ev.ringing.cref = c->cr; - ctrl->ev.ringing.call = c; + ctrl->ev.ringing.call = c->master_call; ctrl->ev.ringing.progress = c->progress; ctrl->ev.ringing.progressmask = c->progressmask; @@ -5027,6 +6407,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct return Q931_RES_HAVEEVENT; case Q931_CONNECT: + stop_t303(c); if (c->newcall) { q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; @@ -5042,7 +6423,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct ctrl->ev.answer.subcmds = &ctrl->subcmds; ctrl->ev.answer.channel = q931_encode_channel(c); ctrl->ev.answer.cref = c->cr; - ctrl->ev.answer.call = c; + ctrl->ev.answer.call = c->master_call; ctrl->ev.answer.progress = c->progress; ctrl->ev.answer.progressmask = c->progressmask; libpri_copy_string(ctrl->ev.answer.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.answer.useruserinfo)); @@ -5091,13 +6472,14 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct case Q931_PROGRESS: if (missingmand) { q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING); - q931_destroycall(ctrl, c->cr); + pri_destroycall(ctrl, c); break; } ctrl->ev.e = PRI_EVENT_PROGRESS; ctrl->ev.proceeding.cause = c->cause; /* Fall through */ case Q931_CALL_PROCEEDING: + stop_t303(c); ctrl->ev.proceeding.subcmds = &ctrl->subcmds; if (c->newcall) { q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); @@ -5119,7 +6501,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct ctrl->ev.proceeding.progress = c->progress; ctrl->ev.proceeding.progressmask = c->progressmask; ctrl->ev.proceeding.cref = c->cr; - ctrl->ev.proceeding.call = c; + ctrl->ev.proceeding.call = c->master_call; cur = c->apdus; while (cur) { @@ -5147,7 +6529,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct case Q931_STATUS: if (missingmand) { q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING); - q931_destroycall(ctrl, c->cr); + pri_destroycall(ctrl, c); break; } if (c->newcall) { @@ -5174,8 +6556,10 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct ctrl->ev.hangup.channel = q931_encode_channel(c); ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; - ctrl->ev.hangup.call = c; + ctrl->ev.hangup.call = c->master_call; ctrl->ev.hangup.aoc_units = c->aoc_units; + ctrl->ev.hangup.call_held = NULL; + ctrl->ev.hangup.call_active = NULL; libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); /* Free resources */ UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); @@ -5187,9 +6571,9 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct } else if (c->sendhangupack) { res = Q931_RES_HAVEEVENT; ctrl->ev.e = PRI_EVENT_HANGUP_ACK; - q931_hangup(ctrl, c, c->cause); + pri_hangup(ctrl, c, c->cause); } else { - q931_hangup(ctrl, c, c->cause); + pri_hangup(ctrl, c, c->cause); res = 0; } if (res) @@ -5197,14 +6581,18 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct } break; case Q931_RELEASE_COMPLETE: + stop_t303(c); + c->hangupinitiated = 1; UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; ctrl->ev.hangup.subcmds = &ctrl->subcmds; ctrl->ev.hangup.channel = q931_encode_channel(c); ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; - ctrl->ev.hangup.call = c; + ctrl->ev.hangup.call = c->master_call; ctrl->ev.hangup.aoc_units = c->aoc_units; + ctrl->ev.hangup.call_held = NULL; + ctrl->ev.hangup.call_active = NULL; libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; /* Free resources */ @@ -5222,9 +6610,10 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct if (res) return res; else - q931_hangup(ctrl,c,c->cause); + pri_hangup(ctrl,c,c->cause); break; case Q931_RELEASE: + c->hangupinitiated = 1; if (missingmand) { /* Force cause to be mandatory IE missing */ c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; @@ -5240,18 +6629,23 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct ctrl->ev.hangup.channel = q931_encode_channel(c); ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; - ctrl->ev.hangup.call = c; + ctrl->ev.hangup.call = c->master_call; ctrl->ev.hangup.aoc_units = c->aoc_units; + ctrl->ev.hangup.call_held = NULL; + ctrl->ev.hangup.call_active = NULL; libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; /* Don't send release complete if they send us release while we sent it, assume a NULL state */ if (c->newcall) q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); - else + else if (c->outboundbroadcast && (c != q931_get_subcall_winner(c->master_call))) + return pri_hangup(ctrl, c, -1); + else return Q931_RES_HAVEEVENT; break; case Q931_DISCONNECT: + c->hangupinitiated = 1; if (missingmand) { /* Still let user call release */ c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; @@ -5260,6 +6654,53 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } + + /* + * Determine if there are any calls that can be proposed for + * a transfer of held call on disconnect. + */ + ctrl->ev.hangup.call_held = NULL; + ctrl->ev.hangup.call_active = NULL; + switch (c->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 (c->master_call->hold_state == Q931_HOLD_STATE_CALL_HELD) { + /* Held call is being disconnected first. */ + ctrl->ev.hangup.call_held = c->master_call; + ctrl->ev.hangup.call_active = q931_find_held_active_call(ctrl, c); + } else { + /* Active call is being disconnected first. */ + if (q931_find_winning_call(c) == c) { + /* + * Only a normal call or the winning call of a broadcast SETUP + * can participate in a transfer of held call on disconnet. + */ + ctrl->ev.hangup.call_active = c->master_call; + ctrl->ev.hangup.call_held = q931_find_held_call(ctrl, c); + } + } + break; + default: + break; + } + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + if (ctrl->ev.hangup.call_held) { + pri_message(ctrl, "-- Found held call: %p cref:%d\n", + ctrl->ev.hangup.call_held, ctrl->ev.hangup.call_held->cr); + } + if (ctrl->ev.hangup.call_active) { + pri_message(ctrl, "-- Found active call: %p cref:%d\n", + ctrl->ev.hangup.call_active, ctrl->ev.hangup.call_active->cr); + } + if (ctrl->ev.hangup.call_held && ctrl->ev.hangup.call_active) { + pri_message(ctrl, "-- Transfer held call on disconnect possible.\n"); + } + } + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_DISCONNECT_INDICATION); c->peercallstate = Q931_CALL_STATE_DISCONNECT_REQUEST; c->sendhangupack = 1; @@ -5275,14 +6716,14 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct ctrl->ev.hangup.channel = q931_encode_channel(c); ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; - ctrl->ev.hangup.call = c; + ctrl->ev.hangup.call = c->master_call; ctrl->ev.hangup.aoc_units = c->aoc_units; libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; if (c->alive) return Q931_RES_HAVEEVENT; else - q931_hangup(ctrl,c,c->cause); + pri_hangup(ctrl,c,c->cause); break; case Q931_RESTART_ACKNOWLEDGE: UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); @@ -5302,14 +6743,14 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct if (c->ourcallstate != Q931_CALL_STATE_OVERLAP_RECEIVING) { ctrl->ev.e = PRI_EVENT_KEYPAD_DIGIT; ctrl->ev.digit.subcmds = &ctrl->subcmds; - ctrl->ev.digit.call = c; + ctrl->ev.digit.call = c->master_call; ctrl->ev.digit.channel = q931_encode_channel(c); libpri_copy_string(ctrl->ev.digit.digits, c->keypad_digits, sizeof(ctrl->ev.digit.digits)); return Q931_RES_HAVEEVENT; } ctrl->ev.e = PRI_EVENT_INFO_RECEIVED; ctrl->ev.ring.subcmds = &ctrl->subcmds; - ctrl->ev.ring.call = c; + ctrl->ev.ring.call = c->master_call; ctrl->ev.ring.channel = q931_encode_channel(c); libpri_copy_string(ctrl->ev.ring.callednum, c->overlap_digits, sizeof(ctrl->ev.ring.callednum)); @@ -5330,6 +6771,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_status(ctrl,c, 0); break; case Q931_SETUP_ACKNOWLEDGE: + stop_t303(c); if (c->newcall) { q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; @@ -5339,7 +6781,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct ctrl->ev.e = PRI_EVENT_SETUP_ACK; ctrl->ev.setup_ack.subcmds = &ctrl->subcmds; ctrl->ev.setup_ack.channel = q931_encode_channel(c); - ctrl->ev.setup_ack.call = c; + ctrl->ev.setup_ack.call = c->master_call; cur = c->apdus; while (cur) { @@ -5411,19 +6853,249 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct ctrl->ev.notify.subcmds = &ctrl->subcmds; ctrl->ev.notify.channel = q931_encode_channel(c); ctrl->ev.notify.info = c->notify; + ctrl->ev.notify.call = c->master_call; res = Q931_RES_HAVEEVENT; break; } return res; + case Q931_HOLD: + res = 0; + if (!PRI_MASTER(ctrl)->hold_support) { + /* + * Blocking any calls from getting on HOLD effectively + * disables HOLD/RETRIEVE. + */ + q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_FACILITY_NOT_IMPLEMENTED); + break; + } + switch (c->ourcallstate) { + case Q931_CALL_STATE_CALL_RECEIVED: + case Q931_CALL_STATE_CONNECT_REQUEST: + case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: + if (q931_is_ptmp(ctrl)) { + /* HOLD request only allowed in these states if point-to-point mode. */ + q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + /* Fall through */ + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_ACTIVE: + if (!q931_find_winning_call(c)) { + /* + * Only the winning call of a broadcast SETUP can do hold since the + * call must be answered first. + */ + q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + master_call = c->master_call; + switch (master_call->hold_state) { + case Q931_HOLD_STATE_HOLD_REQ: + if (ctrl->localtype == PRI_NETWORK) { + /* The network ignores HOLD request on a hold collision. */ + break; + } + /* Fall through */ + case Q931_HOLD_STATE_IDLE: + ctrl->ev.e = PRI_EVENT_HOLD; + ctrl->ev.hold.channel = q931_encode_channel(c); + ctrl->ev.hold.call = master_call; + ctrl->ev.hold.subcmds = &ctrl->subcmds; + res = Q931_RES_HAVEEVENT; + + UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_HOLD_IND); + + /* Stop any T-HOLD timer from possible hold collision. */ + pri_schedule_del(ctrl, master_call->hold_timer); + master_call->hold_timer = 0; + break; + default: + q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + break; + case Q931_CALL_STATE_DISCONNECT_INDICATION: + case Q931_CALL_STATE_RELEASE_REQUEST: + /* Ignore HOLD request in these states. */ + break; + default: + q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + return res; + case Q931_HOLD_ACKNOWLEDGE: + res = 0; + master_call = c->master_call; + switch (master_call->hold_state) { + case Q931_HOLD_STATE_HOLD_REQ: + ctrl->ev.e = PRI_EVENT_HOLD_ACK; + ctrl->ev.hold_ack.channel = q931_encode_channel(c); + ctrl->ev.hold_ack.call = master_call; + ctrl->ev.hold_ack.subcmds = &ctrl->subcmds; + res = Q931_RES_HAVEEVENT; + + UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_CALL_HELD); + + /* Call is now on hold so forget the channel. */ + c->channelno = 0;/* No channel */ + c->ds1no = 0; + c->ds1explicit = 0; + c->chanflags = 0; + + /* Stop T-HOLD timer */ + pri_schedule_del(ctrl, master_call->hold_timer); + master_call->hold_timer = 0; + break; + default: + /* Ignore response. Response is late or spurrious. */ + break; + } + return res; + case Q931_HOLD_REJECT: + res = 0; + master_call = c->master_call; + switch (master_call->hold_state) { + case Q931_HOLD_STATE_HOLD_REQ: + if (missingmand) { + /* Still, let hold rejection continue. */ + c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; + } + ctrl->ev.e = PRI_EVENT_HOLD_REJ; + ctrl->ev.hold_rej.channel = q931_encode_channel(c); + ctrl->ev.hold_rej.call = master_call; + ctrl->ev.hold_rej.cause = c->cause; + ctrl->ev.hold_rej.subcmds = &ctrl->subcmds; + res = Q931_RES_HAVEEVENT; + + UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_IDLE); + + /* Stop T-HOLD timer */ + pri_schedule_del(ctrl, master_call->hold_timer); + master_call->hold_timer = 0; + break; + default: + /* Ignore response. Response is late or spurrious. */ + break; + } + return res; + case Q931_RETRIEVE: + res = 0; + switch (c->ourcallstate) { + case Q931_CALL_STATE_CALL_RECEIVED: + case Q931_CALL_STATE_CONNECT_REQUEST: + case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: + if (q931_is_ptmp(ctrl)) { + /* RETRIEVE request only allowed in these states if point-to-point mode. */ + q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + /* Fall through */ + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_ACTIVE: + if (!q931_find_winning_call(c)) { + /* + * Only the winning call of a broadcast SETUP can do hold since the + * call must be answered first. + */ + q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + master_call = c->master_call; + switch (master_call->hold_state) { + case Q931_HOLD_STATE_RETRIEVE_REQ: + if (ctrl->localtype == PRI_NETWORK) { + /* The network ignores RETRIEVE request on a retrieve collision. */ + break; + } + /* Fall through */ + case Q931_HOLD_STATE_CALL_HELD: + ctrl->ev.e = PRI_EVENT_RETRIEVE; + ctrl->ev.retrieve.channel = q931_encode_channel(c); + ctrl->ev.retrieve.call = master_call; + ctrl->ev.retrieve.flexible = !(c->chanflags & FLAG_EXCLUSIVE); + ctrl->ev.retrieve.subcmds = &ctrl->subcmds; + res = Q931_RES_HAVEEVENT; + + UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_RETRIEVE_IND); + + /* Stop any T-RETRIEVE timer from possible retrieve collision. */ + pri_schedule_del(ctrl, master_call->hold_timer); + master_call->hold_timer = 0; + break; + default: + q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + break; + case Q931_CALL_STATE_DISCONNECT_INDICATION: + case Q931_CALL_STATE_RELEASE_REQUEST: + /* Ignore RETRIEVE request in these states. */ + break; + default: + q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + return res; + case Q931_RETRIEVE_ACKNOWLEDGE: + res = 0; + master_call = c->master_call; + switch (master_call->hold_state) { + case Q931_HOLD_STATE_RETRIEVE_REQ: + UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_IDLE); + + /* Stop T-RETRIEVE timer */ + pri_schedule_del(ctrl, master_call->hold_timer); + master_call->hold_timer = 0; + + ctrl->ev.e = PRI_EVENT_RETRIEVE_ACK; + ctrl->ev.retrieve_ack.channel = q931_encode_channel(c); + ctrl->ev.retrieve_ack.call = master_call; + ctrl->ev.retrieve_ack.subcmds = &ctrl->subcmds; + res = Q931_RES_HAVEEVENT; + break; + default: + /* Ignore response. Response is late or spurrious. */ + break; + } + return res; + case Q931_RETRIEVE_REJECT: + res = 0; + master_call = c->master_call; + switch (master_call->hold_state) { + case Q931_HOLD_STATE_RETRIEVE_REQ: + UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_CALL_HELD); + + /* Call is still on hold so forget the channel. */ + c->channelno = 0;/* No channel */ + c->ds1no = 0; + c->ds1explicit = 0; + c->chanflags = 0; + + /* Stop T-RETRIEVE timer */ + pri_schedule_del(ctrl, master_call->hold_timer); + master_call->hold_timer = 0; + + if (missingmand) { + /* Still, let retrive rejection continue. */ + c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; + } + ctrl->ev.e = PRI_EVENT_RETRIEVE_REJ; + ctrl->ev.retrieve_rej.channel = q931_encode_channel(c); + ctrl->ev.retrieve_rej.call = master_call; + ctrl->ev.retrieve_rej.cause = c->cause; + ctrl->ev.retrieve_rej.subcmds = &ctrl->subcmds; + res = Q931_RES_HAVEEVENT; + break; + default: + /* Ignore response. Response is late or spurrious. */ + break; + } + return res; case Q931_USER_INFORMATION: case Q931_SEGMENT: case Q931_CONGESTION_CONTROL: - case Q931_HOLD: - case Q931_HOLD_ACKNOWLEDGE: - case Q931_HOLD_REJECT: - case Q931_RETRIEVE: - case Q931_RETRIEVE_ACKNOWLEDGE: - case Q931_RETRIEVE_REJECT: case Q931_RESUME: case Q931_RESUME_ACKNOWLEDGE: case Q931_RESUME_REJECT: @@ -5436,7 +7108,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct pri_error(ctrl, "!! Don't know how to post-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg); q931_status(ctrl,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST); if (c->newcall) - q931_destroycall(ctrl,c->cr); + pri_destroycall(ctrl, c); return -1; } return 0; @@ -5452,7 +7124,7 @@ static int pri_internal_clear(void *data) pri_schedule_del(ctrl, c->retranstimer); c->retranstimer = 0; c->useruserinfo[0] = '\0'; - c->cause = -1; + //c->cause = -1; c->causecode = -1; c->causeloc = -1; c->sugcallstate = Q931_CALL_STATE_NOT_SET; @@ -5465,10 +7137,15 @@ static int pri_internal_clear(void *data) ctrl->ev.hangup.channel = q931_encode_channel(c); ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; - ctrl->ev.hangup.call = c; + ctrl->ev.hangup.call = c->master_call; ctrl->ev.hangup.aoc_units = c->aoc_units; + ctrl->ev.hangup.call_held = NULL; + ctrl->ev.hangup.call_active = NULL; libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "clearing, alive %d, hangupack %d\n", c->alive, c->sendhangupack); + } /* Free resources */ if (c->alive) { ctrl->ev.e = PRI_EVENT_HANGUP; @@ -5477,10 +7154,10 @@ static int pri_internal_clear(void *data) } else if (c->sendhangupack) { res = Q931_RES_HAVEEVENT; ctrl->ev.e = PRI_EVENT_HANGUP_ACK; - q931_hangup(ctrl, c, c->cause); + pri_hangup(ctrl, c, c->cause); } else { res = 0; - q931_hangup(ctrl, c, c->cause); + pri_hangup(ctrl, c, c->cause); } return res;