#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,
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);
/* CFR is usually a simple frame, but can become a sequence with Internet
FAXing. */
if (start)
- {
s->step = 0;
- }
switch (s->step)
{
case 0:
}
/*- End of function --------------------------------------------------------*/
-static void disconnect(t30_state_t *s)
+static void terminate_call(t30_state_t *s)
+{
+ /* 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)
{
- span_log(&s->logging, SPAN_LOG_FLOW, "Disconnecting\n");
+ /* 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);
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. */
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. */
{
case T30_DCN:
t30_set_status(s, T30_ERR_TX_BADDCS);
- disconnect(s);
+ terminate_call(s);
break;
case T30_CRP:
repeat_last_command(s);
{
case T30_DCN:
t30_set_status(s, T30_ERR_TX_BADDCS);
- disconnect(s);
+ terminate_call(s);
break;
case T30_CRP:
repeat_last_command(s);
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);
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);
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);
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);
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. */
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);
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);
t30_set_status(s, T30_ERR_TX_BADPG);
break;
}
- disconnect(s);
+ terminate_call(s);
break;
case T30_CRP:
repeat_last_command(s);
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. */
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. */
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);
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);
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);
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);
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;
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;
}
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 --------------------------------------------------------*/
{
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
{
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 --------------------------------------------------------*/
{
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 --------------------------------------------------------*/
{
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 --------------------------------------------------------*/
{
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 --------------------------------------------------------*/
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;
}
}
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)
}
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:
{
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. */
}
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 --------------------------------------------------------*/