Improved FAX disconnect handling

This commit is contained in:
Steve Underwood 2014-06-11 10:52:54 +08:00
parent 4ccdc1def3
commit 94ab52cd01
2 changed files with 72 additions and 54 deletions

View File

@ -1528,7 +1528,7 @@
<step type="WAIT" value="75"/>
<step dir="T" type="PREAMBLE" modem="V.21"/>
<step dir="T" type="HDLC" tag="CTC" value="FF C8 48"/>
<step dir="T" type="HDLC" tag="CTC" value="FF C8 48 00 10"/>
<step dir="T" type="POSTAMBLE"/>
<step dir="R" type="HDLC" modem="V.21" tag="CTR" value="FF C8 23"/>
@ -5330,7 +5330,7 @@
<step dir="T" type="HDLC" modem="V.21" tag="PPR" value="FF C8 3D 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF"/>
<step dir="T" type="POSTAMBLE"/>
<step dir="R" type="HDLC" modem="V.21" tag="CTC" value="FF C8 C8"/>
<step dir="R" type="HDLC" modem="V.21" tag="CTC" value="FF C8 C8 00 10"/>
<step dir="R" type="SILENCE"/>
<step type="WAIT" value="75"/>
@ -5442,7 +5442,7 @@
<step dir="T" type="HDLC" modem="V.21" tag="PPR" value="FF C8 3D 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF"/>
<step dir="T" type="POSTAMBLE"/>
<step dir="R" type="HDLC" modem="V.21" tag="CTC+" value="FF C8 C8"/>
<step dir="R" type="HDLC" modem="V.21" tag="CTC+" value="FF C8 C8 00 10"/>
<step dir="R" type="SILENCE"/>
<step type="WAIT" value="75"/>

View File

@ -370,7 +370,8 @@ enum
#define DEFAULT_TIMER_T8 10000
/*! Final time we allow for things to flush through the system, before we disconnect, in milliseconds.
200ms should be fine for a PSTN call. For a T.38 call something longer is desirable. */
200ms should be fine for a PSTN call. For a T.38 call something longer is desirable. This delay is
to allow sufficient time for the last message to be flushed all the way through to the far end. */
#define FINAL_FLUSH_TIME 1000
/*! The number of PPRs received before CTC or EOR is sent in ECM mode. T.30 defines this as 4,
@ -432,7 +433,8 @@ static void send_frame(t30_state_t *s, const uint8_t *fr, int frlen);
static void send_simple_frame(t30_state_t *s, int type);
static void send_dcn(t30_state_t *s);
static void repeat_last_command(t30_state_t *s);
static void disconnect(t30_state_t *s);
static void terminate_call(t30_state_t *s);
static void start_final_pause(t30_state_t *s);
static void decode_20digit_msg(t30_state_t *s, char *msg, const uint8_t *pkt, int len);
static void decode_url_msg(t30_state_t *s, char *msg, const uint8_t *pkt, int len);
static int decode_nsf_nss_nsc(t30_state_t *s, uint8_t *msg[], const uint8_t *pkt, int len);
@ -2440,9 +2442,7 @@ static int send_cfr_sequence(t30_state_t *s, int start)
/* CFR is usually a simple frame, but can become a sequence with Internet
FAXing. */
if (start)
{
s->step = 0;
}
switch (s->step)
{
case 0:
@ -2465,9 +2465,32 @@ static int send_cfr_sequence(t30_state_t *s, int start)
}
/*- End of function --------------------------------------------------------*/
static void disconnect(t30_state_t *s)
static void terminate_call(t30_state_t *s)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Disconnecting\n");
/* Make sure any FAX in progress is tidied up. If the tidying up has
already happened, repeating it here is harmless. */
terminate_operation_in_progress(s);
s->timer_t0_t1 = 0;
s->timer_t2_t4 = 0;
s->timer_t3 = 0;
s->timer_t5 = 0;
if (s->phase_e_handler)
s->phase_e_handler(s->phase_e_user_data, s->current_status);
set_state(s, T30_STATE_CALL_FINISHED);
set_phase(s, T30_PHASE_CALL_FINISHED);
release_resources(s);
span_log(&s->logging, SPAN_LOG_FLOW, "Call completed\n");
}
/*- End of function --------------------------------------------------------*/
static void start_final_pause(t30_state_t *s)
{
/* We need to allow some time for the last part of our FAX signalling to flush through
to the far end before we declare the call finished. If we say it is finished too soon,
the call disconnect message could cause buffers downstream to be flushed, rather than
played out to completion. If that clips the final message, the far end might declare
that the call prematrurely terminated. */
span_log(&s->logging, SPAN_LOG_FLOW, "Starting final pause before disconnecting\n");
/* Make sure any FAX in progress is tidied up. If the tidying up has
already happened, repeating it here is harmless. */
terminate_operation_in_progress(s);
@ -3079,7 +3102,7 @@ static void process_rx_ppr(t30_state_t *s, const uint8_t *msg, int len)
and there is little possibility that causing a retransmission will help. It is best
to just give up. */
t30_set_status(s, T30_ERR_TX_ECMPHD);
disconnect(s);
terminate_call(s);
return;
}
/* Check which frames are OK, and mark them as OK. */
@ -3315,7 +3338,7 @@ static void process_state_answering(t30_state_t *s, const uint8_t *msg, int len)
break;
case T30_DCN:
t30_set_status(s, T30_ERR_TX_GOTDCN);
disconnect(s);
terminate_call(s);
break;
default:
/* We don't know what to do with this. */
@ -3383,7 +3406,7 @@ static void process_state_d(t30_state_t *s, const uint8_t *msg, int len)
{
case T30_DCN:
t30_set_status(s, T30_ERR_TX_BADDCS);
disconnect(s);
terminate_call(s);
break;
case T30_CRP:
repeat_last_command(s);
@ -3409,7 +3432,7 @@ static void process_state_d_tcf(t30_state_t *s, const uint8_t *msg, int len)
{
case T30_DCN:
t30_set_status(s, T30_ERR_TX_BADDCS);
disconnect(s);
terminate_call(s);
break;
case T30_CRP:
repeat_last_command(s);
@ -3480,7 +3503,7 @@ static void process_state_d_post_tcf(t30_state_t *s, const uint8_t *msg, int len
break;
case T30_DCN:
t30_set_status(s, T30_ERR_TX_BADDCS);
disconnect(s);
terminate_call(s);
break;
case T30_CRP:
repeat_last_command(s);
@ -3633,7 +3656,7 @@ static void process_state_f_doc_non_ecm(t30_state_t *s, const uint8_t *msg, int
break;
case T30_DCN:
t30_set_status(s, T30_ERR_RX_DCNDATA);
disconnect(s);
terminate_call(s);
break;
case T30_CRP:
repeat_last_command(s);
@ -3740,7 +3763,7 @@ static void process_state_f_post_doc_non_ecm(t30_state_t *s, const uint8_t *msg,
break;
case T30_DCN:
t30_set_status(s, T30_ERR_RX_DCNFAX);
disconnect(s);
terminate_call(s);
break;
case T30_CRP:
repeat_last_command(s);
@ -3824,7 +3847,7 @@ static void process_state_f_doc_and_post_doc_ecm(t30_state_t *s, const uint8_t *
break;
case T30_DCN:
t30_set_status(s, T30_ERR_RX_DCNDATA);
disconnect(s);
terminate_call(s);
break;
case T30_CRP:
repeat_last_command(s);
@ -3855,7 +3878,7 @@ static void process_state_f_post_rcp_mcf(t30_state_t *s, const uint8_t *msg, int
process_rx_fnv(s, msg, len);
break;
case T30_DCN:
disconnect(s);
terminate_call(s);
break;
default:
/* We don't know what to do with this. */
@ -3947,7 +3970,7 @@ static void process_state_r(t30_state_t *s, const uint8_t *msg, int len)
case T30_DCN:
/* Received a DCN while waiting for a DIS or DCN */
t30_set_status(s, T30_ERR_RX_DCNWHY);
disconnect(s);
terminate_call(s);
break;
case T30_CRP:
repeat_last_command(s);
@ -3975,7 +3998,7 @@ static void process_state_t(t30_state_t *s, const uint8_t *msg, int len)
break;
case T30_DCN:
t30_set_status(s, T30_ERR_TX_GOTDCN);
disconnect(s);
terminate_call(s);
break;
case T30_CRP:
repeat_last_command(s);
@ -4230,7 +4253,7 @@ static void process_state_ii_q(t30_state_t *s, const uint8_t *msg, int len)
t30_set_status(s, T30_ERR_TX_BADPG);
break;
}
disconnect(s);
terminate_call(s);
break;
case T30_CRP:
repeat_last_command(s);
@ -4274,7 +4297,7 @@ static void process_state_iii_q_mcf(t30_state_t *s, const uint8_t *msg, int len)
process_rx_fnv(s, msg, len);
break;
case T30_DCN:
disconnect(s);
terminate_call(s);
break;
default:
/* We don't know what to do with this. */
@ -4346,7 +4369,7 @@ static void process_state_iii_q_rtn(t30_state_t *s, const uint8_t *msg, int len)
break;
case T30_DCN:
t30_set_status(s, T30_ERR_RX_DCNNORTN);
disconnect(s);
terminate_call(s);
break;
default:
/* We don't know what to do with this. */
@ -4453,7 +4476,7 @@ static void process_state_iv_pps_null(t30_state_t *s, const uint8_t *msg, int le
break;
case T30_DCN:
t30_set_status(s, T30_ERR_TX_BADPG);
disconnect(s);
terminate_call(s);
break;
case T30_CRP:
repeat_last_command(s);
@ -4557,7 +4580,7 @@ static void process_state_iv_pps_q(t30_state_t *s, const uint8_t *msg, int len)
break;
case T30_DCN:
t30_set_status(s, T30_ERR_TX_BADPG);
disconnect(s);
terminate_call(s);
break;
case T30_CRP:
repeat_last_command(s);
@ -4669,7 +4692,7 @@ static void process_state_iv_pps_rnr(t30_state_t *s, const uint8_t *msg, int len
break;
case T30_DCN:
t30_set_status(s, T30_ERR_RX_DCNRRD);
disconnect(s);
terminate_call(s);
break;
case T30_CRP:
repeat_last_command(s);
@ -4793,7 +4816,7 @@ static void process_state_iv_eor_rnr(t30_state_t *s, const uint8_t *msg, int len
break;
case T30_DCN:
t30_set_status(s, T30_ERR_RX_DCNRRD);
disconnect(s);
terminate_call(s);
break;
case T30_CRP:
repeat_last_command(s);
@ -5227,9 +5250,8 @@ static void set_phase(t30_state_t *s, int phase)
s->set_tx_type_handler(s->set_tx_type_user_data, fallback_sequence[s->current_fallback].modem_type, fallback_sequence[s->current_fallback].bit_rate, s->short_train, true);
break;
case T30_PHASE_E:
/* Send a little silence before ending things, to ensure the
buffers are all flushed through, and the far end has seen
the last message we sent. */
/* Send a little silence before ending things, to ensure the buffers are flushed all they way
through to the far end, and the far end has been able to see the last message we sent. */
s->tcf_test_bits = 0;
s->tcf_current_zeros = 0;
s->tcf_most_zeros = 0;
@ -5278,7 +5300,7 @@ static void repeat_last_command(t30_state_t *s)
t30_set_status(s, T30_ERR_TX_PHDDEAD);
break;
default:
/* Disconnected after permitted retries */
/* Disconnect after permitted retries */
t30_set_status(s, T30_ERR_RETRYDCN);
break;
}
@ -5462,7 +5484,7 @@ static void timer_t0_expired(t30_state_t *s)
span_log(&s->logging, SPAN_LOG_FLOW, "T0 expired in state %s\n", state_names[s->state]);
t30_set_status(s, T30_ERR_T0_EXPIRED);
/* Just end the call */
disconnect(s);
terminate_call(s);
}
/*- End of function --------------------------------------------------------*/
@ -5477,7 +5499,7 @@ static void timer_t1_expired(t30_state_t *s)
{
case T30_STATE_T:
/* Just end the call */
disconnect(s);
terminate_call(s);
break;
case T30_STATE_R:
/* Send disconnect, and then end the call. Since we have not
@ -5494,7 +5516,7 @@ static void timer_t1a_expired(t30_state_t *s)
{
span_log(&s->logging, SPAN_LOG_FLOW, "T1A expired in phase %s, state %s. An HDLC frame lasted too long.\n", phase_names[s->phase], state_names[s->state]);
t30_set_status(s, T30_ERR_HDLC_CARRIER);
disconnect(s);
terminate_call(s);
}
/*- End of function --------------------------------------------------------*/
@ -5573,7 +5595,7 @@ static void timer_t2a_expired(t30_state_t *s)
{
span_log(&s->logging, SPAN_LOG_FLOW, "T2A expired in phase %s, state %s. An HDLC frame lasted too long.\n", phase_names[s->phase], state_names[s->state]);
t30_set_status(s, T30_ERR_HDLC_CARRIER);
disconnect(s);
terminate_call(s);
}
/*- End of function --------------------------------------------------------*/
@ -5588,7 +5610,7 @@ static void timer_t3_expired(t30_state_t *s)
{
span_log(&s->logging, SPAN_LOG_FLOW, "T3 expired in phase %s, state %s\n", phase_names[s->phase], state_names[s->state]);
t30_set_status(s, T30_ERR_T3_EXPIRED);
disconnect(s);
terminate_call(s);
}
/*- End of function --------------------------------------------------------*/
@ -5612,7 +5634,7 @@ static void timer_t4a_expired(t30_state_t *s)
{
span_log(&s->logging, SPAN_LOG_FLOW, "T4A expired in phase %s, state %s. An HDLC frame lasted too long.\n", phase_names[s->phase], state_names[s->state]);
t30_set_status(s, T30_ERR_HDLC_CARRIER);
disconnect(s);
terminate_call(s);
}
/*- End of function --------------------------------------------------------*/
@ -6253,7 +6275,7 @@ SPAN_DECLARE(void) t30_front_end_status(void *user_data, int status)
break;
default:
span_log(&s->logging, SPAN_LOG_FLOW, "Unknown next rx step - %d\n", s->next_rx_step);
disconnect(s);
terminate_call(s);
break;
}
}
@ -6282,11 +6304,7 @@ SPAN_DECLARE(void) t30_front_end_status(void *user_data, int status)
case T30_STATE_B:
/* We have now allowed time for the last message to flush through
the system, so it is safe to report the end of the call. */
if (s->phase_e_handler)
s->phase_e_handler(s->phase_e_user_data, s->current_status);
set_state(s, T30_STATE_CALL_FINISHED);
set_phase(s, T30_PHASE_CALL_FINISHED);
release_resources(s);
terminate_call(s);
break;
case T30_STATE_C:
if (s->step == 0)
@ -6296,8 +6314,9 @@ SPAN_DECLARE(void) t30_front_end_status(void *user_data, int status)
}
else
{
/* We just sent the disconnect message. Now it is time to disconnect. */
disconnect(s);
/* We just sent the disconnect message. Now it is time to clean up and
end the call. */
start_final_pause(s);
}
break;
case T30_STATE_D:
@ -6527,14 +6546,17 @@ SPAN_DECLARE(void) t30_terminate(t30_state_t *s)
{
case T30_STATE_C:
/* We were sending the final disconnect, so just hussle things along. */
disconnect(s);
break;
case T30_STATE_B:
/* We were in the final wait for everything to flush through, so just
hussle things along. */
/* We were in the final pause, waiting for everything to flush through,
so just hussle things along. */
break;
default:
/* If we have seen a genuine EOP or PRI_EOP, that's good enough. */
/* If we have seen a genuine EOP or PRI_EOP, and that's good enough for us.
The far end might not agree, as it might not have seen the MCF we sent
in response to EOP or PRI_EOP. This might cause it to say the call did
not complete properly. However, if this function has been called we can
do no more. */
if (!s->end_of_procedure_detected)
{
/* The call terminated prematurely. */
@ -6542,11 +6564,7 @@ SPAN_DECLARE(void) t30_terminate(t30_state_t *s)
}
break;
}
if (s->phase_e_handler)
s->phase_e_handler(s->phase_e_user_data, s->current_status);
set_state(s, T30_STATE_CALL_FINISHED);
set_phase(s, T30_PHASE_CALL_FINISHED);
release_resources(s);
terminate_call(s);
}
}
/*- End of function --------------------------------------------------------*/