]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
More movement towards colour FAX
authorSteve Underwood <steveu@coppice.org>
Wed, 17 Apr 2013 15:43:41 +0000 (23:43 +0800)
committerSteve Underwood <steveu@coppice.org>
Wed, 17 Apr 2013 15:43:41 +0000 (23:43 +0800)
libs/spandsp/src/spandsp/private/t30.h
libs/spandsp/src/t30.c
libs/spandsp/src/t4_rx.c
libs/spandsp/src/t4_tx.c

index c28feafed2f42535a391efe0fa8f5628c17a99b9..851c9b950156185291e0c06bd6f85a76e0810103 100644 (file)
@@ -159,8 +159,6 @@ struct t30_state_s
     int local_dis_dtc_len;
     /*! \brief The last DIS or DTC message received form the far end. */
     uint8_t far_dis_dtc_frame[T30_MAX_DIS_DTC_DCS_LEN];
-    /*! \brief The length of the last DIS or DTC message received form the far end. */
-    int far_dis_dtc_len;
     /*! \brief TRUE if a valid DIS has been received from the far end. */
     int dis_received;
 
index 32900c5e1a9a53bc679101ee65e584dad6ea86e7..56d0ab4bdad167e7e9d3f79bf9db56798eb9eba5 100644 (file)
@@ -453,6 +453,22 @@ static void timer_t2_t4_stop(t30_state_t *s);
 /*! Clear a specified bit within a DIS, DTC or DCS frame */
 #define clr_ctrl_bit(s,bit) (s)[3 + ((bit - 1)/8)] &= ~(1 << ((bit - 1)%8))
 
+static int find_fallback_entry(int dcs_code)
+{
+    int i;
+
+    /* The table is short, and not searched often, so a brain-dead linear scan seems OK */
+    for (i = 0;  fallback_sequence[i].bit_rate;  i++)
+    {
+        if (fallback_sequence[i].dcs_code == dcs_code)
+            break;
+    }
+    if (fallback_sequence[i].bit_rate == 0)
+        return -1;
+    return i;
+}
+/*- End of function --------------------------------------------------------*/
+
 static int terminate_operation_in_progress(t30_state_t *s)
 {
     /* Make sure any FAX in progress is tidied up. If the tidying up has
@@ -1125,48 +1141,6 @@ static int send_pps_frame(t30_state_t *s)
 }
 /*- End of function --------------------------------------------------------*/
 
-static int set_dis_or_dtc(t30_state_t *s)
-{
-    /* Whether we use a DIS or a DTC is determined by whether we have received a DIS.
-       We just need to edit the prebuilt message. */
-    s->local_dis_dtc_frame[2] = (uint8_t) (T30_DIS | s->dis_received);
-    /* If we have a file name to receive into, then we are receive capable */
-    if (s->rx_file[0])
-        set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_READY_TO_RECEIVE_FAX_DOCUMENT);
-    else
-        clr_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_READY_TO_RECEIVE_FAX_DOCUMENT);
-    /* If we have a file name to transmit, then we are ready to transmit (polling) */
-    if (s->tx_file[0])
-        set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_READY_TO_TRANSMIT_FAX_DOCUMENT);
-    else
-        clr_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_READY_TO_TRANSMIT_FAX_DOCUMENT);
-    return 0;
-}
-/*- End of function --------------------------------------------------------*/
-
-static int prune_dis_dtc(t30_state_t *s)
-{
-    int i;
-
-    /* Find the last octet that is really needed, set the extension bits, and trim the message length */
-    for (i = 18;  i >= 6;  i--)
-    {
-        /* Strip the top bit */
-        s->local_dis_dtc_frame[i] &= (DISBIT1 | DISBIT2 | DISBIT3 | DISBIT4 | DISBIT5 | DISBIT6 | DISBIT7);
-        /* Check if there is some real message content here */
-        if (s->local_dis_dtc_frame[i])
-            break;
-    }
-    s->local_dis_dtc_len = i + 1;
-    /* Fill in any required extension bits */
-    s->local_dis_dtc_frame[i] &= ~DISBIT8;
-    for (i--;  i > 4;  i--)
-        s->local_dis_dtc_frame[i] |= DISBIT8;
-    t30_decode_dis_dtc_dcs(s, s->local_dis_dtc_frame, s->local_dis_dtc_len);
-    return s->local_dis_dtc_len;
-}
-/*- End of function --------------------------------------------------------*/
-
 int t30_build_dis_or_dtc(t30_state_t *s)
 {
     int i;
@@ -1179,7 +1153,7 @@ int t30_build_dis_or_dtc(t30_state_t *s)
     s->local_dis_dtc_frame[0] = ADDRESS_FIELD;
     s->local_dis_dtc_frame[1] = CONTROL_FIELD_FINAL_FRAME;
     s->local_dis_dtc_frame[2] = (uint8_t) (T30_DIS | s->dis_received);
-    for (i = 3;  i < 19;  i++)
+    for (i = 3;  i < T30_MAX_DIS_DTC_DCS_LEN;  i++)
         s->local_dis_dtc_frame[i] = 0x00;
 
     /* Always say 256 octets per ECM frame preferred, as 64 is never used in the
@@ -1219,7 +1193,7 @@ int t30_build_dis_or_dtc(t30_state_t *s)
         set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_NORTH_AMERICAN_LEGAL_CAPABLE);
 
     /* No scan-line padding required, but some may be specified by the application. */
-    set_ctrl_bits(s->local_dis_dtc_frame, s->local_min_scan_time_code, 21);
+    set_ctrl_bits(s->local_dis_dtc_frame, s->local_min_scan_time_code, T30_DIS_BIT_MIN_SCAN_LINE_TIME_CAPABILITY_1);
 
     if ((s->supported_compressions & T30_SUPPORT_COMPRESSION_T4_2D))
         set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_2D_CAPABLE);
@@ -1383,11 +1357,52 @@ int t30_build_dis_or_dtc(t30_state_t *s)
         set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T38_FAX_CAPABLE);
     /* No T.88/T.89 profile */
     s->local_dis_dtc_len = 19;
-    //t30_decode_dis_dtc_dcs(s, s->local_dis_dtc_frame, s->local_dis_dtc_len);
     return 0;
 }
 /*- End of function --------------------------------------------------------*/
 
+static int set_dis_or_dtc(t30_state_t *s)
+{
+    /* Whether we use a DIS or a DTC is determined by whether we have received a DIS.
+       We just need to edit the prebuilt message. */
+    s->local_dis_dtc_frame[2] = (uint8_t) (T30_DIS | s->dis_received);
+    /* If we have a file name to receive into, then we are receive capable */
+    if (s->rx_file[0])
+        set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_READY_TO_RECEIVE_FAX_DOCUMENT);
+    else
+        clr_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_READY_TO_RECEIVE_FAX_DOCUMENT);
+    /* If we have a file name to transmit, then we are ready to transmit (polling) */
+    if (s->tx_file[0])
+        set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_READY_TO_TRANSMIT_FAX_DOCUMENT);
+    else
+        clr_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_READY_TO_TRANSMIT_FAX_DOCUMENT);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int prune_dis_dtc(t30_state_t *s)
+{
+    int i;
+
+    /* Find the last octet that is really needed, set the extension bits, and trim the message length */
+    for (i = T30_MAX_DIS_DTC_DCS_LEN - 1;  i >= 6;  i--)
+    {
+        /* Strip the top bit */
+        s->local_dis_dtc_frame[i] &= (DISBIT1 | DISBIT2 | DISBIT3 | DISBIT4 | DISBIT5 | DISBIT6 | DISBIT7);
+        /* Check if there is some real message content here */
+        if (s->local_dis_dtc_frame[i])
+            break;
+    }
+    s->local_dis_dtc_len = i + 1;
+    /* Fill in any required extension bits */
+    s->local_dis_dtc_frame[i] &= ~DISBIT8;
+    for (i--;  i > 4;  i--)
+        s->local_dis_dtc_frame[i] |= DISBIT8;
+    t30_decode_dis_dtc_dcs(s, s->local_dis_dtc_frame, s->local_dis_dtc_len);
+    return s->local_dis_dtc_len;
+}
+/*- End of function --------------------------------------------------------*/
+
 static int build_dcs(t30_state_t *s)
 {
     int i;
@@ -1408,28 +1423,48 @@ static int build_dcs(t30_state_t *s)
     s->dcs_frame[0] = ADDRESS_FIELD;
     s->dcs_frame[1] = CONTROL_FIELD_FINAL_FRAME;
     s->dcs_frame[2] = (uint8_t) (T30_DCS | s->dis_received);
-    for (i = 3;  i < 19;  i++)
+    for (i = 3;  i < T30_MAX_DIS_DTC_DCS_LEN;  i++)
         s->dcs_frame[i] = 0x00;
 
+    /* We have a file to send, so tell the far end to go into receive mode. */
+    set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_RECEIVE_FAX_DOCUMENT);
+
 #if 0
     /* Check for T.37 simple mode. */
-    if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T37))
+    if ((s->iaf & T30_IAF_MODE_T37)  &&  test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T37))
         set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T37);
     /* Check for T.38 mode. */
-    if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T38))
+    if ((s->iaf & T30_IAF_MODE_T38)  &&  test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T38))
         set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T38);
 #endif
 
     /* Set to required modem rate */
     s->dcs_frame[4] |= fallback_sequence[s->current_fallback].dcs_code;
 
-    /* We have a file to send, so tell the far end to go into receive mode. */
-    set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_RECEIVE_FAX_DOCUMENT);
-
     /* Select the compression to use. */
     use_bilevel = TRUE;
     switch (s->line_encoding)
     {
+    case T4_COMPRESSION_T4_1D:
+        /* There is nothing to set to select this encoding. */
+        set_ctrl_bits(s->dcs_frame, s->min_scan_time_code, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1);
+        break;
+    case T4_COMPRESSION_T4_2D:
+        set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_2D_MODE);
+        set_ctrl_bits(s->dcs_frame, s->min_scan_time_code, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1);
+        break;
+    case T4_COMPRESSION_T6:
+        set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T6_MODE);
+        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1);
+        break;
+    case T4_COMPRESSION_T85:
+        set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T85_MODE);
+        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1);
+        break;
+    case T4_COMPRESSION_T85_L0:
+        set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T85_L0_MODE);
+        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1);
+        break;
     case T4_COMPRESSION_T42_T81:
         set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T81_MODE);
         //if (image_type == T4_IMAGE_TYPE_COLOUR_8BIT  ||  image_type == T4_IMAGE_TYPE_COLOUR_12BIT)
@@ -1438,7 +1473,7 @@ static int build_dcs(t30_state_t *s)
         //    set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_12BIT_COMPONENT);
         //if (???????? & T4_COMPRESSION_?????))
         //    set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_NO_SUBSAMPLING);
-        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, 21);
+        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1);
         use_bilevel = FALSE;
         break;
 #if defined(SPANDSP_SUPPORT_T43)
@@ -1450,37 +1485,18 @@ static int build_dcs(t30_state_t *s)
         //    set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_12BIT_COMPONENT);
         //if (???????? & T4_COMPRESSION_?????))
         //    set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_NO_SUBSAMPLING);
-        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, 21);
+        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1);
         use_bilevel = FALSE;
         break;
 #endif
-    case T4_COMPRESSION_T85_L0:
-        set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T85_L0_MODE);
-        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, 21);
-        break;
-    case T4_COMPRESSION_T85:
-        set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T85_MODE);
-        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, 21);
-        break;
-    case T4_COMPRESSION_T6:
-        set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T6_MODE);
-        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, 21);
-        break;
-    case T4_COMPRESSION_T4_2D:
-        set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_2D_MODE);
-        set_ctrl_bits(s->dcs_frame, s->min_scan_time_code, 21);
-        break;
-    case T4_COMPRESSION_T4_1D:
-        set_ctrl_bits(s->dcs_frame, s->min_scan_time_code, 21);
-        break;
     default:
-        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, 21);
+        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1);
         break;
     }
 
     /* Set the Y resolution bits */
-    bad = T30_ERR_NORESSUPPORT;
     row_squashing_ratio = 1;
+    bad = T30_ERR_NORESSUPPORT;
     if (use_bilevel)
     {
         switch (s->y_resolution)
@@ -1747,7 +1763,6 @@ static int build_dcs(t30_state_t *s)
         set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T38_FAX_MODE);
     }
     s->dcs_len = 19;
-    //t30_decode_dis_dtc_dcs(s, s->dcs_frame, s->dcs_len);
     return 0;
 }
 /*- End of function --------------------------------------------------------*/
@@ -1757,7 +1772,7 @@ static int prune_dcs(t30_state_t *s)
     int i;
 
     /* Find the last octet that is really needed, set the extension bits, and trim the message length */
-    for (i = 18;  i >= 6;  i--)
+    for (i = T30_MAX_DIS_DTC_DCS_LEN - 1;  i >= 6;  i--)
     {
         /* Strip the top bit */
         s->dcs_frame[i] &= (DISBIT1 | DISBIT2 | DISBIT3 | DISBIT4 | DISBIT5 | DISBIT6 | DISBIT7);
@@ -1775,1017 +1790,1027 @@ static int prune_dcs(t30_state_t *s)
 }
 /*- End of function --------------------------------------------------------*/
 
-static int step_fallback_entry(t30_state_t *s)
+static int analyze_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len)
 {
-    int min_row_bits;
-
-    while (fallback_sequence[++s->current_fallback].which)
+    t30_decode_dis_dtc_dcs(s, msg, len);
+    if (len < 6)
     {
-        if ((fallback_sequence[s->current_fallback].which & s->current_permitted_modems))
-            break;
-    }
-    if (fallback_sequence[s->current_fallback].which == 0)
+        span_log(&s->logging, SPAN_LOG_FLOW, "Short DIS/DTC frame\n");
         return -1;
-    /* TODO: This only sets the minimum row time for future pages. It doesn't fix up the
-             current page, though it is benign - fallback will only result in an excessive
-             minimum. */
-    min_row_bits = set_min_scan_time_code(s);
-    t4_tx_set_min_bits_per_row(&s->t4.tx, min_row_bits);
-    /* We need to rebuild the DCS message we will send. */
-    build_dcs(s);
-    return s->current_fallback;
-}
-/*- End of function --------------------------------------------------------*/
+    }
 
-static int find_fallback_entry(int dcs_code)
-{
-    int i;
+    if (msg[2] == T30_DIS)
+        s->dis_received = TRUE;
 
-    /* The table is short, and not searched often, so a brain-dead linear scan seems OK */
-    for (i = 0;  fallback_sequence[i].bit_rate;  i++)
+    /* Make a local copy of the message, padded to the maximum possible length with zeros. This allows
+       us to simply pick out the bits, without worrying about whether they were set from the remote side. */
+    if (len > T30_MAX_DIS_DTC_DCS_LEN)
+        len = T30_MAX_DIS_DTC_DCS_LEN;
+    memcpy(s->far_dis_dtc_frame, msg, len);
+    if (len < T30_MAX_DIS_DTC_DCS_LEN)
+        memset(s->far_dis_dtc_frame + len, 0, T30_MAX_DIS_DTC_DCS_LEN - len);
+
+    s->error_correcting_mode = (s->ecm_allowed  &&  test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_ECM_CAPABLE));
+    /* Always use 256 octets per ECM frame, whatever the other end says it is capable of */
+    s->octets_per_ecm_frame = 256;
+
+    /* Now we know if we are going to use ECM, select the compressions which we can use. */
+    s->mutual_compressions = s->supported_compressions;
+    if (!s->error_correcting_mode)
     {
-        if (fallback_sequence[i].dcs_code == dcs_code)
-            break;
+        /* Remove any compression schemes which need error correction to work. */
+        s->mutual_compressions &= (0xF0000000 | T30_SUPPORT_COMPRESSION_NONE | T30_SUPPORT_COMPRESSION_T4_1D | T30_SUPPORT_COMPRESSION_T4_2D);
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_2D_CAPABLE))
+            s->mutual_compressions &= ~T30_SUPPORT_COMPRESSION_T4_2D;
     }
-    if (fallback_sequence[i].bit_rate == 0)
-        return -1;
-    return i;
-}
-/*- End of function --------------------------------------------------------*/
+    else
+    {
+        /* Check the bi-level capabilities */
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_2D_CAPABLE))
+            s->mutual_compressions &= ~T30_SUPPORT_COMPRESSION_T4_2D;
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T6_CAPABLE))
+            s->mutual_compressions &= ~T30_SUPPORT_COMPRESSION_T6;
+        /* T.85 L0 capable without T.85 capable is an invalid combination, so let
+           just zap both capabilities if the far end is not T.85 capable. */
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T85_CAPABLE))
+            s->mutual_compressions &= ~(T30_SUPPORT_COMPRESSION_T85 | T30_SUPPORT_COMPRESSION_T85_L0);
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T85_L0_CAPABLE))
+            s->mutual_compressions &= ~T30_SUPPORT_COMPRESSION_T85_L0;
 
-static void send_dcn(t30_state_t *s)
-{
-    queue_phase(s, T30_PHASE_D_TX);
-    set_state(s, T30_STATE_C);
-    send_simple_frame(s, T30_DCN);
-}
-/*- End of function --------------------------------------------------------*/
+        /* Check for full colour or only gray-scale from the multi-level codecs */
+        //if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_FULL_COLOUR_CAPABLE))
+        //    s->mutual_compressions &= ~T4_SUPPORT_COMPRESSION_COLOUR;
 
-static void return_to_phase_b(t30_state_t *s, int with_fallback)
-{
-    /* This is what we do after things like T30_EOM is exchanged. */
-#if 0
-    if (step_fallback_entry(s) < 0)
+        /* Check the colour capabilities */
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T81_CAPABLE))
+            s->mutual_compressions &= ~T30_SUPPORT_COMPRESSION_T42_T81;
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_SYCC_T81_CAPABLE))
+            s->mutual_compressions &= ~T30_SUPPORT_COMPRESSION_SYCC_T81;
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T43_CAPABLE))
+            s->mutual_compressions &= ~T30_SUPPORT_COMPRESSION_T43;
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T45_CAPABLE))
+            s->mutual_compressions &= ~T30_SUPPORT_COMPRESSION_T45;
+
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_12BIT_CAPABLE))
+            s->mutual_compressions &= ~T30_SUPPORT_COMPRESSION_12BIT;
+        //if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_NO_SUBSAMPLING))
+        //    ???? = T4_COMPRESSION_T42_T81_SUBSAMPLING;
+
+        /* bit74 custom illuminant */
+        /* bit75 custom gamut range */
+    }
+
+    s->mutual_bilevel_resolutions = s->supported_bilevel_resolutions;
+    s->mutual_colour_resolutions = s->supported_colour_resolutions;
+    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_1200_1200_CAPABLE))
     {
-        /* We have fallen back as far as we can go. Give up. */
-        s->current_fallback = 0;
-        t30_set_status(s, T30_ERR_CANNOT_TRAIN);
-        send_dcn(s);
+        s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_1200_1200;
+        s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_1200_1200;
     }
     else
     {
-        if (s->calling_party)
-            set_state(s, T30_STATE_T);
-        else
-            set_state(s, T30_STATE_R);
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_COLOUR_GRAY_1200_1200_CAPABLE))
+            s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_1200_1200;
+    }
+    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_600_1200_CAPABLE))
+        s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_600_1200;
+    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_600_600_CAPABLE))
+    {
+        s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_600_600;
+        s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_600_600;
     }
-#else
-    if (s->calling_party)
-        set_state(s, T30_STATE_T);
     else
-        set_state(s, T30_STATE_R);
-#endif
-}
-/*- End of function --------------------------------------------------------*/
-
-static int send_dis_or_dtc_sequence(t30_state_t *s, int start)
-{
-    /* (NSF) (CSI) DIS */
-    /* (NSC) (CIG) (PWD) (SEP) (PSA) (CIA) (ISP) DTC */
-    if (start)
     {
-        set_dis_or_dtc(s);
-        set_state(s, T30_STATE_R);
-        s->step = 0;
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_COLOUR_GRAY_600_600_CAPABLE))
+            s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_600_600;
     }
-    if (!s->dis_received)
+    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_400_800_CAPABLE))
+        s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_400_800;
+    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_400_400_CAPABLE))
     {
-        /* DIS sequence */
-        switch (s->step)
-        {
-        case 0:
-            s->step++;
-            if (send_nsf_frame(s))
-                break;
-            /* Fall through */
-        case 1:
-            s->step++;
-            if (send_ident_frame(s, T30_CSI))
-                break;
-            /* Fall through */
-        case 2:
-            s->step++;
-            prune_dis_dtc(s);
-            send_frame(s, s->local_dis_dtc_frame, s->local_dis_dtc_len);
-            break;
-        case 3:
-            s->step++;
-            shut_down_hdlc_tx(s);
-            break;
-        default:
-            return -1;
-        }
+        s->mutual_bilevel_resolutions &= ~(T4_SUPPORT_RESOLUTION_400_400 | T4_SUPPORT_RESOLUTION_R16_SUPERFINE);
+        s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_400_400;
     }
     else
     {
-        /* DTC sequence */
-        switch (s->step)
-        {
-        case 0:
-            s->step++;
-            if (send_nsc_frame(s))
-                break;
-            /* Fall through */
-        case 1:
-            s->step++;
-            if (send_ident_frame(s, T30_CIG))
-                break;
-            /* Fall through */
-        case 2:
-            s->step++;
-            if (send_pwd_frame(s))
-                break;
-            /* Fall through */
-        case 3:
-            s->step++;
-            if (send_sep_frame(s))
-                break;
-            /* Fall through */
-        case 4:
-            s->step++;
-            if (send_psa_frame(s))
-                break;
-            /* Fall through */
-        case 5:
-            s->step++;
-            if (send_cia_frame(s))
-                break;
-            /* Fall through */
-        case 6:
-            s->step++;
-            if (send_isp_frame(s))
-                break;
-            /* Fall through */
-        case 7:
-            s->step++;
-            prune_dis_dtc(s);
-            send_frame(s, s->local_dis_dtc_frame, s->local_dis_dtc_len);
-            break;
-        case 8:
-            s->step++;
-            shut_down_hdlc_tx(s);
-            break;
-        default:
-            return -1;
-        }
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_COLOUR_GRAY_300_300_400_400_CAPABLE))
+            s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_400_400;
     }
-    return 0;
-}
-/*- End of function --------------------------------------------------------*/
+    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_300_600_CAPABLE))
+        s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_300_600;
+    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_300_300_CAPABLE))
+    {
+        s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_300_300;
+        s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_300_300;
+    }
+    else
+    {
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_COLOUR_GRAY_300_300_400_400_CAPABLE))
+            s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_300_300;
+    }
+    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_400_CAPABLE))
+    {
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_INCH_RESOLUTION_PREFERRED))
+            s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_200_400;
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_METRIC_RESOLUTION_PREFERRED))
+            s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_R8_SUPERFINE;
+    }
+    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_200_CAPABLE))
+    {
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_INCH_RESOLUTION_PREFERRED))
+            s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_200_200;
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_METRIC_RESOLUTION_PREFERRED))
+            s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_R8_FINE;
+        s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_200_200;
+    }
+    else
+    {
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_INCH_RESOLUTION_PREFERRED))
+            s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_200_200;
+    }
+    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_INCH_RESOLUTION_PREFERRED))
+        s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_200_100;
+    /* Never suppress T4_SUPPORT_RESOLUTION_R8_STANDARD */
+    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_COLOUR_GRAY_100_100_CAPABLE))
+        s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_100_100;
 
-static int send_dcs_sequence(t30_state_t *s, int start)
-{
-    /* (NSS) (TSI) (SUB) (SID) (TSA) (IRA) DCS */
-    /* Schedule training after the messages */
-    if (start)
+    s->mutual_image_sizes = s->supported_image_sizes;
+    /* 215mm wide is always supported */
+    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_215MM_255MM_303MM_WIDTH_CAPABLE))
     {
-        prune_dcs(s);
-        set_state(s, T30_STATE_D);
-        s->step = 0;
+        s->mutual_image_sizes &= ~T4_SUPPORT_WIDTH_303MM;
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_215MM_255MM_WIDTH_CAPABLE))
+            s->mutual_image_sizes &= ~T4_SUPPORT_WIDTH_255MM;
     }
-    switch (s->step)
+    /* A4 is always supported. */
+    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_UNLIMITED_LENGTH_CAPABLE))
     {
-    case 0:
-        s->step++;
-        if (send_nss_frame(s))
-            break;
-        /* Fall through */
-    case 1:
-        s->step++;
-        if (send_ident_frame(s, T30_TSI))
-            break;
-        /* Fall through */
-    case 2:
-        s->step++;
-        if (send_sub_frame(s))
-            break;
-        /* Fall through */
-    case 3:
-        s->step++;
-        if (send_sid_frame(s))
-            break;
-        /* Fall through */
-    case 4:
-        s->step++;
-        if (send_tsa_frame(s))
+        s->mutual_image_sizes &= ~T4_SUPPORT_LENGTH_UNLIMITED;
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_A4_B4_LENGTH_CAPABLE))
+            s->mutual_image_sizes &= ~T4_SUPPORT_LENGTH_B4;
+    }
+    if (!test_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_NORTH_AMERICAN_LETTER_CAPABLE))
+        s->mutual_image_sizes &= ~T4_SUPPORT_LENGTH_US_LETTER;
+    if (!test_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_NORTH_AMERICAN_LEGAL_CAPABLE))
+        s->mutual_image_sizes &= ~T4_SUPPORT_LENGTH_US_LEGAL;
+
+    switch (s->far_dis_dtc_frame[4] & (DISBIT6 | DISBIT5 | DISBIT4 | DISBIT3))
+    {
+    case (DISBIT6 | DISBIT4 | DISBIT3):
+        if ((s->supported_modems & T30_SUPPORT_V17))
+        {
+            s->current_permitted_modems = T30_SUPPORT_V17 | T30_SUPPORT_V29 | T30_SUPPORT_V27TER;
+            s->current_fallback = T30_V17_FALLBACK_START;
             break;
+        }
         /* Fall through */
-    case 5:
-        s->step++;
-        if (send_ira_frame(s))
+    case (DISBIT4 | DISBIT3):
+        if ((s->supported_modems & T30_SUPPORT_V29))
+        {
+            s->current_permitted_modems = T30_SUPPORT_V29 | T30_SUPPORT_V27TER;
+            s->current_fallback = T30_V29_FALLBACK_START;
             break;
+        }
         /* Fall through */
-    case 6:
-        s->step++;
-        prune_dcs(s);
-        send_frame(s, s->dcs_frame, s->dcs_len);
+    case DISBIT4:
+        s->current_permitted_modems = T30_SUPPORT_V27TER;
+        s->current_fallback = T30_V27TER_FALLBACK_START;
         break;
-    case 7:
-        s->step++;
-        shut_down_hdlc_tx(s);
+    case 0:
+        s->current_permitted_modems = T30_SUPPORT_V27TER;
+        s->current_fallback = T30_V27TER_FALLBACK_START + 1;
         break;
+    case DISBIT3:
+        if ((s->supported_modems & T30_SUPPORT_V29))
+        {
+            /* TODO: this doesn't allow for skipping the V.27ter modes */
+            s->current_permitted_modems = T30_SUPPORT_V29;
+            s->current_fallback = T30_V29_FALLBACK_START;
+            break;
+        }
+        /* Fall through */
     default:
+        span_log(&s->logging, SPAN_LOG_FLOW, "Remote does not support a compatible modem\n");
+        /* We cannot talk to this machine! */
+        t30_set_status(s, T30_ERR_INCOMPATIBLE);
         return -1;
     }
     return 0;
 }
 /*- End of function --------------------------------------------------------*/
 
-static int send_cfr_sequence(t30_state_t *s, int start)
-{
-    /* (CSA) CFR */
-    /* CFR is usually a simple frame, but can become a sequence with Internet
-       FAXing. */
-    send_simple_frame(s, T30_CFR);
-    return 0;
-}
-/*- End of function --------------------------------------------------------*/
-
-static void disconnect(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;
-    set_phase(s, T30_PHASE_E);
-    set_state(s, T30_STATE_B);
-}
-/*- End of function --------------------------------------------------------*/
-
-static int set_min_scan_time_code(t30_state_t *s)
+static int analyze_rx_dcs(t30_state_t *s, const uint8_t *msg, int len)
 {
-    /* Translation between the codes for the minimum scan times the other end needs,
-       and the codes for what we say will be used. We need 0 minimum. */
-    static const uint8_t translate_min_scan_time[3][8] =
+    static const int widths[6][4] =
     {
-        {T30_MIN_SCAN_20MS, T30_MIN_SCAN_5MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_20MS, T30_MIN_SCAN_40MS, T30_MIN_SCAN_40MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_0MS}, /* normal */
-        {T30_MIN_SCAN_20MS, T30_MIN_SCAN_5MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_40MS, T30_MIN_SCAN_20MS, T30_MIN_SCAN_5MS,  T30_MIN_SCAN_0MS}, /* fine */
-        {T30_MIN_SCAN_10MS, T30_MIN_SCAN_5MS, T30_MIN_SCAN_5MS,  T30_MIN_SCAN_5MS,  T30_MIN_SCAN_20MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_5MS,  T30_MIN_SCAN_0MS}  /* superfine, when half fine time is selected */
+        { T4_WIDTH_100_A4,  T4_WIDTH_100_B4,  T4_WIDTH_100_A3, -1}, /* 100/inch */
+        { T4_WIDTH_200_A4,  T4_WIDTH_200_B4,  T4_WIDTH_200_A3, -1}, /* 200/inch / R8 resolution */
+        { T4_WIDTH_300_A4,  T4_WIDTH_300_B4,  T4_WIDTH_300_A3, -1}, /* 300/inch resolution */
+        { T4_WIDTH_400_A4,  T4_WIDTH_400_B4,  T4_WIDTH_400_A3, -1}, /* 400/inch / R16 resolution */
+        { T4_WIDTH_600_A4,  T4_WIDTH_600_B4,  T4_WIDTH_600_A3, -1}, /* 600/inch resolution */
+        {T4_WIDTH_1200_A4, T4_WIDTH_1200_B4, T4_WIDTH_1200_A3, -1}  /* 1200/inch resolution */
     };
-    /* Translation between the codes for the minimum scan time we will use, and milliseconds. */
-    static const int min_scan_times[8] =
+    uint8_t dcs_frame[T30_MAX_DIS_DTC_DCS_LEN];
+    int i;
+    int x;
+
+    t30_decode_dis_dtc_dcs(s, msg, len);
+    if (len < 6)
     {
-        20, 5, 10, 0, 40, 0, 0, 0
-    };
-    int min_bits_field;
+        span_log(&s->logging, SPAN_LOG_FLOW, "Short DCS frame\n");
+        return -1;
+    }
 
-    /* Set the minimum scan time bits */
-    if (s->error_correcting_mode)
-        min_bits_field = T30_MIN_SCAN_0MS;
-    else
-        min_bits_field = (s->far_dis_dtc_frame[5] >> 4) & 7;
-    switch (s->y_resolution)
-    {
-    case T4_Y_RESOLUTION_SUPERFINE:
-        if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_400_CAPABLE))
-        {
-            s->min_scan_time_code = translate_min_scan_time[(test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_MIN_SCAN_TIME_HALVES))  ?  2  :  1][min_bits_field];
-            break;
-        }
-        span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support super-fine resolution. Squashing image.\n");
-        /* Fall through */
-    case T4_Y_RESOLUTION_FINE:
-        if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_200_CAPABLE))
-        {
-            s->min_scan_time_code = translate_min_scan_time[1][min_bits_field];
-            break;
-        }
-        span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support fine resolution. Squashing image.\n");
-        /* Fall through */
-    default:
-    case T4_Y_RESOLUTION_STANDARD:
-        s->min_scan_time_code = translate_min_scan_time[0][min_bits_field];
-        break;
-    }
-    if (!s->error_correcting_mode  &&  (s->iaf & T30_IAF_MODE_NO_FILL_BITS))
-        return 0;
-    return fallback_sequence[s->current_fallback].bit_rate*min_scan_times[s->min_scan_time_code]/1000;
-}
-/*- End of function --------------------------------------------------------*/
-
-static int start_sending_document(t30_state_t *s)
-{
-    int min_row_bits;
-
-    if (s->tx_file[0] == '\0')
-    {
-        /* There is nothing to send */
-        span_log(&s->logging, SPAN_LOG_FLOW, "No document to send\n");
-        return -1;
-    }
-    span_log(&s->logging, SPAN_LOG_FLOW, "Start sending document\n");
-    if (t4_tx_init(&s->t4.tx, s->tx_file, s->tx_start_page, s->tx_stop_page) == NULL)
-    {
-        span_log(&s->logging, SPAN_LOG_WARNING, "Cannot open source TIFF file '%s'\n", s->tx_file);
-        t30_set_status(s, T30_ERR_FILEERROR);
-        return -1;
-    }
-    s->operation_in_progress = OPERATION_IN_PROGRESS_T4_TX;
-    t4_tx_get_pages_in_file(&s->t4.tx);
-    t4_tx_set_tx_encoding(&s->t4.tx, s->line_encoding);
-    t4_tx_set_local_ident(&s->t4.tx, s->tx_info.ident);
-    t4_tx_set_header_info(&s->t4.tx, s->header_info);
-    if (s->use_own_tz)
-        t4_tx_set_header_tz(&s->t4.tx, &s->tz);
-
-    if (tx_start_page(s))
-    {
-        span_log(&s->logging, SPAN_LOG_WARNING, "Something seems to be wrong in the file\n");
-        t30_set_status(s, T30_ERR_FILEERROR);
-        return -1;
-    }
-
-    s->x_resolution = t4_tx_get_x_resolution(&s->t4.tx);
-    s->y_resolution = t4_tx_get_y_resolution(&s->t4.tx);
-    s->image_width = t4_tx_get_image_width(&s->t4.tx);
-    /* The minimum scan time to be used can't be evaluated until we know the Y resolution, and
-       must be evaluated before the minimum scan row bits can be evaluated. */
-    if ((min_row_bits = set_min_scan_time_code(s)) < 0)
-    {
-        terminate_operation_in_progress(s);
-        return -1;
-    }
-    span_log(&s->logging, SPAN_LOG_FLOW, "Minimum bits per row will be %d\n", min_row_bits);
-    t4_tx_set_min_bits_per_row(&s->t4.tx, min_row_bits);
+    /* Make an ASCII string format copy of the message, for logging in the
+       received file. This string does not include the frame header octets. */
+    sprintf(s->rx_dcs_string, "%02X", bit_reverse8(msg[3]));
+    for (i = 4;  i < len;  i++)
+        sprintf(s->rx_dcs_string + 3*i - 10, " %02X", bit_reverse8(msg[i]));
 
-    if (s->error_correcting_mode)
-    {
-        if (get_partial_ecm_page(s) == 0)
-            span_log(&s->logging, SPAN_LOG_WARNING, "No image data to send\n");
-    }
-    return 0;
-}
-/*- End of function --------------------------------------------------------*/
+    /* Make a local copy of the message, padded to the maximum possible length with zeros. This allows
+       us to simply pick out the bits, without worrying about whether they were set from the remote side. */
+    if (len > T30_MAX_DIS_DTC_DCS_LEN)
+        len = T30_MAX_DIS_DTC_DCS_LEN;
+    memcpy(dcs_frame, msg, len);
+    if (len < T30_MAX_DIS_DTC_DCS_LEN)
+        memset(dcs_frame + len, 0, T30_MAX_DIS_DTC_DCS_LEN - len);
 
-static int restart_sending_document(t30_state_t *s)
-{
-    t4_tx_restart_page(&s->t4.tx);
-    s->retries = 0;
-    s->ecm_block = 0;
-    send_dcs_sequence(s, TRUE);
-    return 0;
-}
-/*- End of function --------------------------------------------------------*/
+    s->error_correcting_mode = (test_ctrl_bit(dcs_frame, T30_DCS_BIT_ECM_MODE) != 0);
+    s->octets_per_ecm_frame = test_ctrl_bit(dcs_frame, T30_DCS_BIT_64_OCTET_ECM_FRAMES)  ?  256  :  64;
 
-static int start_receiving_document(t30_state_t *s)
-{
-    if (s->rx_file[0] == '\0')
+    s->x_resolution = -1;
+    s->y_resolution = -1;
+    s->current_page_resolution = 0;
+    x = -1;
+    if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T81_MODE)  ||  test_ctrl_bit(dcs_frame, T30_DCS_BIT_T43_MODE))
     {
-        /* There is nothing to receive to */
-        span_log(&s->logging, SPAN_LOG_FLOW, "No document to receive\n");
-        return -1;
-    }
-    span_log(&s->logging, SPAN_LOG_FLOW, "Start receiving document\n");
-    queue_phase(s, T30_PHASE_B_TX);
-    s->ecm_block = 0;
-    send_dis_or_dtc_sequence(s, TRUE);
-    return 0;
-}
-/*- End of function --------------------------------------------------------*/
-
-static void unexpected_non_final_frame(t30_state_t *s, const uint8_t *msg, int len)
-{
-    span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s frame in state %s\n", t30_frametype(msg[2]), state_names[s->state]);
-    if (s->current_status == T30_ERR_OK)
-        t30_set_status(s, T30_ERR_UNEXPECTED);
-}
-/*- End of function --------------------------------------------------------*/
-
-static void unexpected_final_frame(t30_state_t *s, const uint8_t *msg, int len)
-{
-    span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s frame in state %s\n", t30_frametype(msg[2]), state_names[s->state]);
-    if (s->current_status == T30_ERR_OK)
-        t30_set_status(s, T30_ERR_UNEXPECTED);
-    send_dcn(s);
-}
-/*- End of function --------------------------------------------------------*/
-
-static void unexpected_frame_length(t30_state_t *s, const uint8_t *msg, int len)
-{
-    span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s frame length - %d\n", t30_frametype(msg[0]), len);
-    if (s->current_status == T30_ERR_OK)
-        t30_set_status(s, T30_ERR_UNEXPECTED);
-    send_dcn(s);
-}
-/*- End of function --------------------------------------------------------*/
-
-static int process_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len)
-{
-    int new_status;
+        /* Gray scale or colour image */
 
-    t30_decode_dis_dtc_dcs(s, msg, len);
-    if (len < 6)
-    {
-        span_log(&s->logging, SPAN_LOG_FLOW, "Short DIS/DTC frame\n");
-        return -1;
-    }
+        /* Note 35 of Table 2/T.30 */
+        if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE))
+        {
+            /* We are going to work in full colour mode */
+        }
 
-    if (msg[2] == T30_DIS)
-        s->dis_received = TRUE;
-    /* Make a local copy of the message, padded to the maximum possible length with zeros. This allows
-       us to simply pick out the bits, without worrying about whether they were set from the remote side. */
-    s->far_dis_dtc_len = (len > T30_MAX_DIS_DTC_DCS_LEN)  ?  T30_MAX_DIS_DTC_DCS_LEN  :  len;
-    memcpy(s->far_dis_dtc_frame, msg, s->far_dis_dtc_len);
-    if (s->far_dis_dtc_len < T30_MAX_DIS_DTC_DCS_LEN)
-        memset(s->far_dis_dtc_frame + s->far_dis_dtc_len, 0, T30_MAX_DIS_DTC_DCS_LEN - s->far_dis_dtc_len);
+        if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_12BIT_COMPONENT))
+        {
+            /* We are going to work in 12 bit mode */
+        }
 
-    s->error_correcting_mode = (s->ecm_allowed  &&  test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_ECM_CAPABLE));
-    /* 256 octets per ECM frame */
-    s->octets_per_ecm_frame = 256;
+        if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_NO_SUBSAMPLING))
+        {
+            //???? = T30_SUPPORT_COMPRESSION_T42_T81_SUBSAMPLING;
+        }
 
-    /* Now we know if we are going to use ECM, select the compressions which we can use. */
-    s->mutual_compressions = s->supported_compressions;
-    if (!s->error_correcting_mode)
-    {
-        /* Remove any compression schemes which need error correction to work. */
-        s->mutual_compressions &= (0xF0000000 | T30_SUPPORT_COMPRESSION_NONE | T30_SUPPORT_COMPRESSION_T4_1D | T30_SUPPORT_COMPRESSION_T4_2D);
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_2D_CAPABLE))
-            s->mutual_compressions &= ~T30_SUPPORT_COMPRESSION_T4_2D;
+        if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_COLOUR_GRAY_1200_1200))
+        {
+            if ((s->supported_colour_resolutions & T4_SUPPORT_RESOLUTION_1200_1200))
+            {
+                s->x_resolution = T4_X_RESOLUTION_1200;
+                s->y_resolution = T4_Y_RESOLUTION_1200;
+                s->current_page_resolution = T4_RESOLUTION_1200_1200;
+                x = 5;
+            }
+        }
+        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_COLOUR_GRAY_600_600))
+        {
+            if ((s->supported_colour_resolutions & T4_SUPPORT_RESOLUTION_600_600))
+            {
+                s->x_resolution = T4_X_RESOLUTION_600;
+                s->y_resolution = T4_Y_RESOLUTION_600;
+                s->current_page_resolution = T4_RESOLUTION_600_600;
+                x = 4;
+            }
+        }
+        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_400_400))
+        {
+            if ((s->supported_colour_resolutions & T4_SUPPORT_RESOLUTION_400_400))
+            {
+                s->x_resolution = T4_X_RESOLUTION_400;
+                s->y_resolution = T4_Y_RESOLUTION_400;
+                s->current_page_resolution = T4_RESOLUTION_400_400;
+                x = 3;
+            }
+        }
+        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_300_300))
+        {
+            if ((s->supported_colour_resolutions & T4_SUPPORT_RESOLUTION_300_300))
+            {
+                s->x_resolution = T4_X_RESOLUTION_300;
+                s->y_resolution = T4_Y_RESOLUTION_300;
+                s->current_page_resolution = T4_RESOLUTION_300_300;
+                x = 2;
+            }
+        }
+        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_200_200))
+        {
+            if ((s->supported_colour_resolutions & T4_SUPPORT_RESOLUTION_200_200))
+            {
+                s->x_resolution = T4_X_RESOLUTION_200;
+                s->y_resolution = T4_Y_RESOLUTION_200;
+                s->current_page_resolution = T4_RESOLUTION_200_200;
+                x = 1;
+            }
+        }
+        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_COLOUR_GRAY_100_100))
+        {
+            if ((s->supported_colour_resolutions & T4_SUPPORT_RESOLUTION_100_100))
+            {
+                s->x_resolution = T4_X_RESOLUTION_100;
+                s->y_resolution = T4_Y_RESOLUTION_100;
+                s->current_page_resolution = T4_RESOLUTION_100_100;
+                x = 0;
+            }
+        }
     }
     else
     {
-        /* Check the bi-level capabilities */
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_2D_CAPABLE))
-            s->mutual_compressions &= ~T30_SUPPORT_COMPRESSION_T4_2D;
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T6_CAPABLE))
-            s->mutual_compressions &= ~T30_SUPPORT_COMPRESSION_T6;
-        /* T.85 L0 capable without T.85 capable is an invalid combination, so let
-           just zap both capabilities if the far end is not T.85 capable. */
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T85_CAPABLE))
-            s->mutual_compressions &= ~(T30_SUPPORT_COMPRESSION_T85 | T30_SUPPORT_COMPRESSION_T85_L0);
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T85_L0_CAPABLE))
-            s->mutual_compressions &= ~T30_SUPPORT_COMPRESSION_T85_L0;
+        /* Bi-level image */
+        if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_1200_1200))
+        {
+            if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_1200_1200))
+            {
+                s->x_resolution = T4_X_RESOLUTION_1200;
+                s->y_resolution = T4_Y_RESOLUTION_1200;
+                s->current_page_resolution = T4_RESOLUTION_1200_1200;
+                x = 5;
+            }
+        }
+        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_600_1200))
+        {
+            if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_600_1200))
+            {
+                s->x_resolution = T4_X_RESOLUTION_600;
+                s->y_resolution = T4_Y_RESOLUTION_1200;
+                s->current_page_resolution = T4_RESOLUTION_600_1200;
+                x = 4;
+            }
+        }
+        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_600_600))
+        {
+            if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_600_600))
+            {
+                s->x_resolution = T4_X_RESOLUTION_600;
+                s->y_resolution = T4_Y_RESOLUTION_600;
+                s->current_page_resolution = T4_RESOLUTION_600_600;
+                x = 4;
+            }
+        }
+        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_400_800))
+        {
+            if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_400_800))
+            {
+                s->x_resolution = T4_X_RESOLUTION_400;
+                s->y_resolution = T4_Y_RESOLUTION_800;
+                s->current_page_resolution = T4_RESOLUTION_400_800;
+                x = 3;
+            }
+        }
+        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_400_400))
+        {
+            if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_INCH_RESOLUTION))
+            {
+                if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_400_400))
+                {
+                    s->x_resolution = T4_X_RESOLUTION_400;
+                    s->y_resolution = T4_Y_RESOLUTION_400;
+                    s->current_page_resolution = T4_RESOLUTION_400_400;
+                    x = 3;
+                }
+            }
+            else
+            {
+                if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_R16_SUPERFINE))
+                {
+                    s->x_resolution = T4_X_RESOLUTION_R16;
+                    s->y_resolution = T4_Y_RESOLUTION_SUPERFINE;
+                    s->current_page_resolution = T4_RESOLUTION_R16_SUPERFINE;
+                    x = 3;
+                }
+            }
+        }
+        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_300_600))
+        {
+            if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_300_600))
+            {
+                s->x_resolution = T4_X_RESOLUTION_300;
+                s->y_resolution = T4_Y_RESOLUTION_600;
+                s->current_page_resolution = T4_RESOLUTION_300_600;
+                x = 2;
+            }
+        }
+        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_300_300))
+        {
+            if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_300_300))
+            {
+                s->x_resolution = T4_X_RESOLUTION_300;
+                s->y_resolution = T4_Y_RESOLUTION_300;
+                s->current_page_resolution = T4_RESOLUTION_300_300;
+                x = 2;
+            }
+        }
+        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_200_400))
+        {
+            if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_INCH_RESOLUTION))
+            {
+                if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_200_400))
+                {
+                    s->x_resolution = T4_X_RESOLUTION_200;
+                    s->y_resolution = T4_Y_RESOLUTION_400;
+                    s->current_page_resolution = T4_RESOLUTION_200_400;
+                    x = 1;
+                }
+            }
+            else
+            {
+                if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_R8_SUPERFINE))
+                {
+                    s->x_resolution = T4_X_RESOLUTION_R8;
+                    s->y_resolution = T4_Y_RESOLUTION_SUPERFINE;
+                    s->current_page_resolution = T4_RESOLUTION_R8_SUPERFINE;
+                    x = 1;
+                }
+            }
+        }
+        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_200_200))
+        {
+            if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_INCH_RESOLUTION))
+            {
+                if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_200_200))
+                {
+                    s->x_resolution = T4_X_RESOLUTION_200;
+                    s->y_resolution = T4_Y_RESOLUTION_200;
+                    s->current_page_resolution = T4_RESOLUTION_200_200;
+                    x = 1;
+                }
+            }
+            else
+            {
+                if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_R8_FINE))
+                {
+                    s->x_resolution = T4_X_RESOLUTION_R8;
+                    s->y_resolution = T4_Y_RESOLUTION_FINE;
+                    s->current_page_resolution = T4_RESOLUTION_R8_FINE;
+                    x = 1;
+                }
+            }
+        }
+        else
+        {
+            if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_INCH_RESOLUTION))
+            {
+                s->x_resolution = T4_X_RESOLUTION_200;
+                s->y_resolution = T4_Y_RESOLUTION_100;
+                s->current_page_resolution = T4_RESOLUTION_200_100;
+                x = 1;
+            }
+            else
+            {
+                s->x_resolution = T4_X_RESOLUTION_R8;
+                s->y_resolution = T4_Y_RESOLUTION_STANDARD;
+                s->current_page_resolution = T4_RESOLUTION_R8_STANDARD;
+                x = 1;
+            }
+        }
     }
 
-    /* Choose a compression scheme from amongst those mutually available */
-    if ((s->mutual_compressions & T30_SUPPORT_COMPRESSION_T85_L0))
-        s->line_encoding = T4_COMPRESSION_T85_L0;
-    else if ((s->mutual_compressions & T30_SUPPORT_COMPRESSION_T85))
-        s->line_encoding = T4_COMPRESSION_T85;
-    else if ((s->mutual_compressions & T30_SUPPORT_COMPRESSION_T6))
-        s->line_encoding = T4_COMPRESSION_T6;
-    else if ((s->mutual_compressions & T30_SUPPORT_COMPRESSION_T4_2D))
-        s->line_encoding = T4_COMPRESSION_T4_2D;
-    else
-        s->line_encoding = T4_COMPRESSION_T4_1D;
+    if (x < 0)
+    {
+        t30_set_status(s, T30_ERR_NORESSUPPORT);
+        return -1;
+    }
 
-    span_log(&s->logging, SPAN_LOG_FLOW, "Choose compression %s (%d)\n", t4_encoding_to_str(s->line_encoding), s->line_encoding);
+    s->image_width = widths[x][dcs_frame[5] & (DISBIT2 | DISBIT1)];
 
-    s->mutual_bilevel_resolutions = s->supported_bilevel_resolutions;
-    s->mutual_colour_resolutions = s->supported_colour_resolutions;
-    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_1200_1200_CAPABLE))
+    /* Check which compression the far end has decided to use. */
+#if defined(SPANDSP_SUPPORT_T42)
+    if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T81_MODE))
     {
-        s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_1200_1200;
-        s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_1200_1200;
+        s->line_encoding = T4_COMPRESSION_T42_T81;
     }
     else
+#endif
+#if defined(SPANDSP_SUPPORT_T43)
+    if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T43_MODE))
     {
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_COLOUR_GRAY_1200_1200_CAPABLE))
-            s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_1200_1200;
-    }
-    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_600_1200_CAPABLE))
-        s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_600_1200;
-    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_600_600_CAPABLE))
-    {
-        s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_600_600;
-        s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_600_600;
+        s->line_encoding = T4_COMPRESSION_T43;
     }
     else
+#endif
+    if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T85_L0_MODE))
     {
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_COLOUR_GRAY_600_600_CAPABLE))
-            s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_600_600;
+        s->line_encoding = T4_COMPRESSION_T85_L0;
     }
-    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_400_800_CAPABLE))
-        s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_400_800;
-    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_400_400_CAPABLE))
+    else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T85_MODE))
     {
-        s->mutual_bilevel_resolutions &= ~(T4_SUPPORT_RESOLUTION_400_400 | T4_SUPPORT_RESOLUTION_R16_SUPERFINE);
-        s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_400_400;
+        s->line_encoding = T4_COMPRESSION_T85;
     }
-    else
+    else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T6_MODE))
     {
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_COLOUR_GRAY_300_300_400_400_CAPABLE))
-            s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_400_400;
+        s->line_encoding = T4_COMPRESSION_T6;
     }
-    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_300_600_CAPABLE))
-        s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_300_600;
-    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_300_300_CAPABLE))
+    else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_2D_MODE))
     {
-        s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_300_300;
-        s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_300_300;
+        s->line_encoding = T4_COMPRESSION_T4_2D;
     }
     else
     {
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_COLOUR_GRAY_300_300_400_400_CAPABLE))
-            s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_300_300;
-    }
-    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_400_CAPABLE))
-    {
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_INCH_RESOLUTION_PREFERRED))
-            s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_200_400;
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_METRIC_RESOLUTION_PREFERRED))
-            s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_R8_SUPERFINE;
+        s->line_encoding = T4_COMPRESSION_T4_1D;
     }
-    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_200_CAPABLE))
+    span_log(&s->logging, SPAN_LOG_FLOW, "Far end selected compression %s (%d)\n", t4_encoding_to_str(s->line_encoding), s->line_encoding);
+    if (!test_ctrl_bit(dcs_frame, T30_DCS_BIT_RECEIVE_FAX_DOCUMENT))
+        span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Remote is not requesting receive in DCS\n");
+
+    if ((s->current_fallback = find_fallback_entry(dcs_frame[4] & (DISBIT6 | DISBIT5 | DISBIT4 | DISBIT3))) < 0)
     {
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_INCH_RESOLUTION_PREFERRED))
-            s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_200_200;
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_METRIC_RESOLUTION_PREFERRED))
-            s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_R8_FINE;
-        s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_200_200;
+        span_log(&s->logging, SPAN_LOG_FLOW, "Remote asked for a modem standard we do not support\n");
+        return -1;
     }
-    else
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int step_fallback_entry(t30_state_t *s)
+{
+    int min_row_bits;
+
+    while (fallback_sequence[++s->current_fallback].which)
     {
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_INCH_RESOLUTION_PREFERRED))
-            s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_200_200;
+        if ((fallback_sequence[s->current_fallback].which & s->current_permitted_modems))
+            break;
     }
-    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_INCH_RESOLUTION_PREFERRED))
-        s->mutual_bilevel_resolutions &= ~T4_SUPPORT_RESOLUTION_200_100;
-    /* Never suppress T4_SUPPORT_RESOLUTION_R8_STANDARD */
-    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_COLOUR_GRAY_100_100_CAPABLE))
-        s->mutual_colour_resolutions &= ~T4_SUPPORT_RESOLUTION_100_100;
+    if (fallback_sequence[s->current_fallback].which == 0)
+        return -1;
+    /* TODO: This only sets the minimum row time for future pages. It doesn't fix up the
+             current page, though it is benign - fallback will only result in an excessive
+             minimum. */
+    min_row_bits = set_min_scan_time_code(s);
+    t4_tx_set_min_bits_per_row(&s->t4.tx, min_row_bits);
+    /* We need to rebuild the DCS message we will send. */
+    build_dcs(s);
+    return s->current_fallback;
+}
+/*- End of function --------------------------------------------------------*/
 
-    s->mutual_image_sizes = s->supported_image_sizes;
-    /* 215mm wide is always supported */
-    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_215MM_255MM_303MM_WIDTH_CAPABLE))
-    {
-        s->mutual_image_sizes &= ~T4_SUPPORT_WIDTH_303MM;
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_215MM_255MM_WIDTH_CAPABLE))
-            s->mutual_image_sizes &= ~T4_SUPPORT_WIDTH_255MM;
-    }
-    /* A4 is always supported. */
-    if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_UNLIMITED_LENGTH_CAPABLE))
+static void send_dcn(t30_state_t *s)
+{
+    queue_phase(s, T30_PHASE_D_TX);
+    set_state(s, T30_STATE_C);
+    send_simple_frame(s, T30_DCN);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void return_to_phase_b(t30_state_t *s, int with_fallback)
+{
+    /* This is what we do after things like T30_EOM is exchanged. */
+    if (s->calling_party)
+        set_state(s, T30_STATE_T);
+    else
+        set_state(s, T30_STATE_R);
+}
+/*- End of function --------------------------------------------------------*/
+
+static int send_dis_or_dtc_sequence(t30_state_t *s, int start)
+{
+    /* (NSF) (CSI) DIS */
+    /* (NSC) (CIG) (PWD) (SEP) (PSA) (CIA) (ISP) DTC */
+    if (start)
     {
-        s->mutual_image_sizes &= ~T4_SUPPORT_LENGTH_UNLIMITED;
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_A4_B4_LENGTH_CAPABLE))
-            s->mutual_image_sizes &= ~T4_SUPPORT_LENGTH_B4;
+        set_dis_or_dtc(s);
+        set_state(s, T30_STATE_R);
+        s->step = 0;
     }
-    if (!test_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_NORTH_AMERICAN_LETTER_CAPABLE))
-        s->mutual_image_sizes &= ~T4_SUPPORT_LENGTH_US_LETTER;
-    if (!test_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_NORTH_AMERICAN_LEGAL_CAPABLE))
-        s->mutual_image_sizes &= ~T4_SUPPORT_LENGTH_US_LEGAL;
-
-    switch (s->far_dis_dtc_frame[4] & (DISBIT6 | DISBIT5 | DISBIT4 | DISBIT3))
+    if (!s->dis_received)
     {
-    case (DISBIT6 | DISBIT4 | DISBIT3):
-        if ((s->supported_modems & T30_SUPPORT_V17))
-        {
-            s->current_permitted_modems = T30_SUPPORT_V17 | T30_SUPPORT_V29 | T30_SUPPORT_V27TER;
-            s->current_fallback = T30_V17_FALLBACK_START;
-            break;
-        }
-        /* Fall through */
-    case (DISBIT4 | DISBIT3):
-        if ((s->supported_modems & T30_SUPPORT_V29))
+        /* DIS sequence */
+        switch (s->step)
         {
-            s->current_permitted_modems = T30_SUPPORT_V29 | T30_SUPPORT_V27TER;
-            s->current_fallback = T30_V29_FALLBACK_START;
+        case 0:
+            s->step++;
+            if (send_nsf_frame(s))
+                break;
+            /* Fall through */
+        case 1:
+            s->step++;
+            if (send_ident_frame(s, T30_CSI))
+                break;
+            /* Fall through */
+        case 2:
+            s->step++;
+            prune_dis_dtc(s);
+            send_frame(s, s->local_dis_dtc_frame, s->local_dis_dtc_len);
             break;
-        }
-        /* Fall through */
-    case DISBIT4:
-        s->current_permitted_modems = T30_SUPPORT_V27TER;
-        s->current_fallback = T30_V27TER_FALLBACK_START;
-        break;
-    case 0:
-        s->current_permitted_modems = T30_SUPPORT_V27TER;
-        s->current_fallback = T30_V27TER_FALLBACK_START + 1;
-        break;
-    case DISBIT3:
-        if ((s->supported_modems & T30_SUPPORT_V29))
-        {
-            /* TODO: this doesn't allow for skipping the V.27ter modes */
-            s->current_permitted_modems = T30_SUPPORT_V29;
-            s->current_fallback = T30_V29_FALLBACK_START;
+        case 3:
+            s->step++;
+            shut_down_hdlc_tx(s);
             break;
-        }
-        /* Fall through */
-    default:
-        span_log(&s->logging, SPAN_LOG_FLOW, "Remote does not support a compatible modem\n");
-        /* We cannot talk to this machine! */
-        t30_set_status(s, T30_ERR_INCOMPATIBLE);
-        return -1;
-    }
-    if (s->phase_b_handler)
-    {
-        new_status = s->phase_b_handler(s, s->phase_b_user_data, msg[2]);
-        if (new_status != T30_ERR_OK)
-        {
-            span_log(&s->logging, SPAN_LOG_FLOW, "Application rejected DIS/DTC - '%s'\n", t30_completion_code_to_str(new_status));
-            t30_set_status(s, new_status);
-            /* TODO: If FNV is allowed, process it here */
-            send_dcn(s);
-            return -1;
-        }
-    }
-    queue_phase(s, T30_PHASE_B_TX);
-    /* Try to send something */
-    if (s->tx_file[0])
-    {
-        span_log(&s->logging, SPAN_LOG_FLOW, "Trying to send file '%s'\n", s->tx_file);
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_READY_TO_RECEIVE_FAX_DOCUMENT))
-        {
-            span_log(&s->logging, SPAN_LOG_FLOW, "%s far end cannot receive\n", t30_frametype(msg[2]));
-            t30_set_status(s, T30_ERR_RX_INCAPABLE);
-            send_dcn(s);
-        }
-        if (start_sending_document(s))
-        {
-            send_dcn(s);
-            return -1;
-        }
-        if (build_dcs(s))
-        {
-            span_log(&s->logging, SPAN_LOG_FLOW, "The far end is incompatible\n", s->tx_file);
-            send_dcn(s);
+        default:
             return -1;
         }
-        s->retries = 0;
-        send_dcs_sequence(s, TRUE);
-        return 0;
     }
-    span_log(&s->logging, SPAN_LOG_FLOW, "%s nothing to send\n", t30_frametype(msg[2]));
-    /* ... then try to receive something */
-    if (s->rx_file[0])
+    else
     {
-        span_log(&s->logging, SPAN_LOG_FLOW, "Trying to receive file '%s'\n", s->rx_file);
-        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_READY_TO_TRANSMIT_FAX_DOCUMENT))
-        {
-            span_log(&s->logging, SPAN_LOG_FLOW, "%s far end cannot transmit\n", t30_frametype(msg[2]));
-            t30_set_status(s, T30_ERR_TX_INCAPABLE);
-            send_dcn(s);
-            return -1;
-        }
-        if (start_receiving_document(s))
-        {
-            send_dcn(s);
-            return -1;
-        }
-        if (set_dis_or_dtc(s))
+        /* DTC sequence */
+        switch (s->step)
         {
-            t30_set_status(s, T30_ERR_INCOMPATIBLE);
-            send_dcn(s);
+        case 0:
+            s->step++;
+            if (send_nsc_frame(s))
+                break;
+            /* Fall through */
+        case 1:
+            s->step++;
+            if (send_ident_frame(s, T30_CIG))
+                break;
+            /* Fall through */
+        case 2:
+            s->step++;
+            if (send_pwd_frame(s))
+                break;
+            /* Fall through */
+        case 3:
+            s->step++;
+            if (send_sep_frame(s))
+                break;
+            /* Fall through */
+        case 4:
+            s->step++;
+            if (send_psa_frame(s))
+                break;
+            /* Fall through */
+        case 5:
+            s->step++;
+            if (send_cia_frame(s))
+                break;
+            /* Fall through */
+        case 6:
+            s->step++;
+            if (send_isp_frame(s))
+                break;
+            /* Fall through */
+        case 7:
+            s->step++;
+            prune_dis_dtc(s);
+            send_frame(s, s->local_dis_dtc_frame, s->local_dis_dtc_len);
+            break;
+        case 8:
+            s->step++;
+            shut_down_hdlc_tx(s);
+            break;
+        default:
             return -1;
         }
-        s->retries = 0;
-        send_dis_or_dtc_sequence(s, TRUE);
-        return 0;
     }
-    span_log(&s->logging, SPAN_LOG_FLOW, "%s nothing to receive\n", t30_frametype(msg[2]));
-    /* There is nothing to do, or nothing we are able to do. */
-    send_dcn(s);
-    return -1;
+    return 0;
 }
 /*- End of function --------------------------------------------------------*/
 
-static int process_rx_dcs(t30_state_t *s, const uint8_t *msg, int len)
+static int send_dcs_sequence(t30_state_t *s, int start)
 {
-    static const int widths[6][4] =
-    {
-        { T4_WIDTH_100_A4,  T4_WIDTH_100_B4,  T4_WIDTH_100_A3, -1}, /* 100/inch */
-        { T4_WIDTH_200_A4,  T4_WIDTH_200_B4,  T4_WIDTH_200_A3, -1}, /* 200/inch / R8 resolution */
-        { T4_WIDTH_300_A4,  T4_WIDTH_300_B4,  T4_WIDTH_300_A3, -1}, /* 300/inch resolution */
-        { T4_WIDTH_400_A4,  T4_WIDTH_400_B4,  T4_WIDTH_400_A3, -1}, /* 400/inch / R16 resolution */
-        { T4_WIDTH_600_A4,  T4_WIDTH_600_B4,  T4_WIDTH_600_A3, -1}, /* 600/inch resolution */
-        {T4_WIDTH_1200_A4, T4_WIDTH_1200_B4, T4_WIDTH_1200_A3, -1}  /* 1200/inch resolution */
-    };
-    uint8_t dcs_frame[T30_MAX_DIS_DTC_DCS_LEN];
-    int i;
-    int x;
-    int new_status;
-
-    t30_decode_dis_dtc_dcs(s, msg, len);
-
-    /* Check DCS frame from remote */
-    if (len < 6)
+    /* (NSS) (TSI) (SUB) (SID) (TSA) (IRA) DCS */
+    /* Schedule training after the messages */
+    if (start)
     {
-        span_log(&s->logging, SPAN_LOG_FLOW, "Short DCS frame\n");
-        return -1;
+        prune_dcs(s);
+        set_state(s, T30_STATE_D);
+        s->step = 0;
     }
-
-    /* Make an ASCII string format copy of the message, for logging in the
-       received file. This string does not include the frame header octets. */
-    sprintf(s->rx_dcs_string, "%02X", bit_reverse8(msg[3]));
-    for (i = 4;  i < len;  i++)
-        sprintf(s->rx_dcs_string + 3*i - 10, " %02X", bit_reverse8(msg[i]));
-    /* Make a local copy of the message, padded to the maximum possible length with zeros. This allows
-       us to simply pick out the bits, without worrying about whether they were set from the remote side. */
-    if (len > T30_MAX_DIS_DTC_DCS_LEN)
-    {
-        memcpy(dcs_frame, msg, T30_MAX_DIS_DTC_DCS_LEN);
-    }
-    else
+    switch (s->step)
     {
-        memcpy(dcs_frame, msg, len);
-        if (len < T30_MAX_DIS_DTC_DCS_LEN)
-            memset(dcs_frame + len, 0, T30_MAX_DIS_DTC_DCS_LEN - len);
+    case 0:
+        s->step++;
+        if (send_nss_frame(s))
+            break;
+        /* Fall through */
+    case 1:
+        s->step++;
+        if (send_ident_frame(s, T30_TSI))
+            break;
+        /* Fall through */
+    case 2:
+        s->step++;
+        if (send_sub_frame(s))
+            break;
+        /* Fall through */
+    case 3:
+        s->step++;
+        if (send_sid_frame(s))
+            break;
+        /* Fall through */
+    case 4:
+        s->step++;
+        if (send_tsa_frame(s))
+            break;
+        /* Fall through */
+    case 5:
+        s->step++;
+        if (send_ira_frame(s))
+            break;
+        /* Fall through */
+    case 6:
+        s->step++;
+        prune_dcs(s);
+        send_frame(s, s->dcs_frame, s->dcs_len);
+        break;
+    case 7:
+        s->step++;
+        shut_down_hdlc_tx(s);
+        break;
+    default:
+        return -1;
     }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
 
-    s->octets_per_ecm_frame = test_ctrl_bit(dcs_frame, T30_DCS_BIT_64_OCTET_ECM_FRAMES)  ?  256  :  64;
-
-    s->x_resolution = -1;
-    s->y_resolution = -1;
-    s->current_page_resolution = 0;
-    x = -1;
-    if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T81_MODE)  ||  test_ctrl_bit(dcs_frame, T30_DCS_BIT_T43_MODE))
-    {
-        /* Gray scale or colour image */
-
-        /* Note 35 of Table 2/T.30 */
-        if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE))
-        {
-            /* We are going to work in full colour mode */
-        }
+static int send_cfr_sequence(t30_state_t *s, int start)
+{
+    /* (CSA) CFR */
+    /* CFR is usually a simple frame, but can become a sequence with Internet
+       FAXing. */
+    send_simple_frame(s, T30_CFR);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
 
-        if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_12BIT_COMPONENT))
-        {
-            /* We are going to work in 12 bit mode */
-        }
+static void disconnect(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;
+    set_phase(s, T30_PHASE_E);
+    set_state(s, T30_STATE_B);
+}
+/*- End of function --------------------------------------------------------*/
 
-        if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_NO_SUBSAMPLING))
-        {
-            //???? = T30_SUPPORT_COMPRESSION_T42_T81_SUBSAMPLING;
-        }
+static int set_min_scan_time_code(t30_state_t *s)
+{
+    /* Translation between the codes for the minimum scan times the other end needs,
+       and the codes for what we say will be used. We need 0 minimum. */
+    static const uint8_t translate_min_scan_time[3][8] =
+    {
+        {T30_MIN_SCAN_20MS, T30_MIN_SCAN_5MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_20MS, T30_MIN_SCAN_40MS, T30_MIN_SCAN_40MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_0MS}, /* normal */
+        {T30_MIN_SCAN_20MS, T30_MIN_SCAN_5MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_40MS, T30_MIN_SCAN_20MS, T30_MIN_SCAN_5MS,  T30_MIN_SCAN_0MS}, /* fine */
+        {T30_MIN_SCAN_10MS, T30_MIN_SCAN_5MS, T30_MIN_SCAN_5MS,  T30_MIN_SCAN_5MS,  T30_MIN_SCAN_20MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_5MS,  T30_MIN_SCAN_0MS}  /* superfine, when half fine time is selected */
+    };
+    /* Translation between the codes for the minimum scan time we will use, and milliseconds. */
+    static const int min_scan_times[8] =
+    {
+        20, 5, 10, 0, 40, 0, 0, 0
+    };
+    int min_bits_field;
 
-        if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_COLOUR_GRAY_1200_1200))
-        {
-            if ((s->supported_colour_resolutions & T4_SUPPORT_RESOLUTION_1200_1200))
-            {
-                s->x_resolution = T4_X_RESOLUTION_1200;
-                s->y_resolution = T4_Y_RESOLUTION_1200;
-                s->current_page_resolution = T4_RESOLUTION_1200_1200;
-                x = 5;
-            }
-        }
-        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_COLOUR_GRAY_600_600))
-        {
-            if ((s->supported_colour_resolutions & T4_SUPPORT_RESOLUTION_600_600))
-            {
-                s->x_resolution = T4_X_RESOLUTION_600;
-                s->y_resolution = T4_Y_RESOLUTION_600;
-                s->current_page_resolution = T4_RESOLUTION_600_600;
-                x = 4;
-            }
-        }
-        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_400_400))
-        {
-            if ((s->supported_colour_resolutions & T4_SUPPORT_RESOLUTION_400_400))
-            {
-                s->x_resolution = T4_X_RESOLUTION_400;
-                s->y_resolution = T4_Y_RESOLUTION_400;
-                s->current_page_resolution = T4_RESOLUTION_400_400;
-                x = 3;
-            }
-        }
-        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_300_300))
-        {
-            if ((s->supported_colour_resolutions & T4_SUPPORT_RESOLUTION_300_300))
-            {
-                s->x_resolution = T4_X_RESOLUTION_300;
-                s->y_resolution = T4_Y_RESOLUTION_300;
-                s->current_page_resolution = T4_RESOLUTION_300_300;
-                x = 2;
-            }
-        }
-        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_200_200))
-        {
-            if ((s->supported_colour_resolutions & T4_SUPPORT_RESOLUTION_200_200))
-            {
-                s->x_resolution = T4_X_RESOLUTION_200;
-                s->y_resolution = T4_Y_RESOLUTION_200;
-                s->current_page_resolution = T4_RESOLUTION_200_200;
-                x = 1;
-            }
-        }
-        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_COLOUR_GRAY_100_100))
-        {
-            if ((s->supported_colour_resolutions & T4_SUPPORT_RESOLUTION_100_100))
-            {
-                s->x_resolution = T4_X_RESOLUTION_100;
-                s->y_resolution = T4_Y_RESOLUTION_100;
-                s->current_page_resolution = T4_RESOLUTION_100_100;
-                x = 0;
-            }
-        }
-    }
+    /* Set the minimum scan time bits */
+    if (s->error_correcting_mode)
+        min_bits_field = T30_MIN_SCAN_0MS;
     else
-    {
-        /* Bi-level image */
-        if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_1200_1200))
-        {
-            if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_1200_1200))
-            {
-                s->x_resolution = T4_X_RESOLUTION_1200;
-                s->y_resolution = T4_Y_RESOLUTION_1200;
-                s->current_page_resolution = T4_RESOLUTION_1200_1200;
-                x = 5;
-            }
-        }
-        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_600_1200))
-        {
-            if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_600_1200))
-            {
-                s->x_resolution = T4_X_RESOLUTION_600;
-                s->y_resolution = T4_Y_RESOLUTION_1200;
-                s->current_page_resolution = T4_RESOLUTION_600_1200;
-                x = 4;
-            }
-        }
-        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_600_600))
-        {
-            if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_600_600))
-            {
-                s->x_resolution = T4_X_RESOLUTION_600;
-                s->y_resolution = T4_Y_RESOLUTION_600;
-                s->current_page_resolution = T4_RESOLUTION_600_600;
-                x = 4;
-            }
-        }
-        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_400_800))
-        {
-            if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_400_800))
-            {
-                s->x_resolution = T4_X_RESOLUTION_400;
-                s->y_resolution = T4_Y_RESOLUTION_800;
-                s->current_page_resolution = T4_RESOLUTION_400_800;
-                x = 3;
-            }
-        }
-        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_400_400))
-        {
-            if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_INCH_RESOLUTION))
-            {
-                if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_400_400))
-                {
-                    s->x_resolution = T4_X_RESOLUTION_400;
-                    s->y_resolution = T4_Y_RESOLUTION_400;
-                    s->current_page_resolution = T4_RESOLUTION_400_400;
-                    x = 3;
-                }
-            }
-            else
-            {
-                if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_R16_SUPERFINE))
-                {
-                    s->x_resolution = T4_X_RESOLUTION_R16;
-                    s->y_resolution = T4_Y_RESOLUTION_SUPERFINE;
-                    s->current_page_resolution = T4_RESOLUTION_R16_SUPERFINE;
-                    x = 3;
-                }
-            }
-        }
-        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_300_600))
-        {
-            if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_300_600))
-            {
-                s->x_resolution = T4_X_RESOLUTION_300;
-                s->y_resolution = T4_Y_RESOLUTION_600;
-                s->current_page_resolution = T4_RESOLUTION_300_600;
-                x = 2;
-            }
-        }
-        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_300_300))
-        {
-            if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_300_300))
-            {
-                s->x_resolution = T4_X_RESOLUTION_300;
-                s->y_resolution = T4_Y_RESOLUTION_300;
-                s->current_page_resolution = T4_RESOLUTION_300_300;
-                x = 2;
-            }
-        }
-        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_200_400))
-        {
-            if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_INCH_RESOLUTION))
-            {
-                if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_200_400))
-                {
-                    s->x_resolution = T4_X_RESOLUTION_200;
-                    s->y_resolution = T4_Y_RESOLUTION_400;
-                    s->current_page_resolution = T4_RESOLUTION_200_400;
-                    x = 1;
-                }
-            }
-            else
-            {
-                if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_R8_SUPERFINE))
-                {
-                    s->x_resolution = T4_X_RESOLUTION_R8;
-                    s->y_resolution = T4_Y_RESOLUTION_SUPERFINE;
-                    s->current_page_resolution = T4_RESOLUTION_R8_SUPERFINE;
-                    x = 1;
-                }
-            }
-        }
-        else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_200_200))
-        {
-            if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_INCH_RESOLUTION))
-            {
-                if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_200_200))
-                {
-                    s->x_resolution = T4_X_RESOLUTION_200;
-                    s->y_resolution = T4_Y_RESOLUTION_200;
-                    s->current_page_resolution = T4_RESOLUTION_200_200;
-                    x = 1;
-                }
-            }
-            else
-            {
-                if ((s->supported_bilevel_resolutions & T4_SUPPORT_RESOLUTION_R8_FINE))
-                {
-                    s->x_resolution = T4_X_RESOLUTION_R8;
-                    s->y_resolution = T4_Y_RESOLUTION_FINE;
-                    s->current_page_resolution = T4_RESOLUTION_R8_FINE;
-                    x = 1;
-                }
-            }
+        min_bits_field = (s->far_dis_dtc_frame[5] >> 4) & 7;
+    switch (s->y_resolution)
+    {
+    case T4_Y_RESOLUTION_SUPERFINE:
+        if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_400_CAPABLE))
+        {
+            s->min_scan_time_code = translate_min_scan_time[(test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_MIN_SCAN_TIME_HALVES))  ?  2  :  1][min_bits_field];
+            break;
         }
-        else
+        span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support super-fine resolution. Squashing image.\n");
+        /* Fall through */
+    case T4_Y_RESOLUTION_FINE:
+        if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_200_CAPABLE))
         {
-            if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_INCH_RESOLUTION))
-            {
-                s->x_resolution = T4_X_RESOLUTION_200;
-                s->y_resolution = T4_Y_RESOLUTION_100;
-                s->current_page_resolution = T4_RESOLUTION_200_100;
-                x = 1;
-            }
-            else
-            {
-                s->x_resolution = T4_X_RESOLUTION_R8;
-                s->y_resolution = T4_Y_RESOLUTION_STANDARD;
-                s->current_page_resolution = T4_RESOLUTION_R8_STANDARD;
-                x = 1;
-            }
+            s->min_scan_time_code = translate_min_scan_time[1][min_bits_field];
+            break;
         }
+        span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support fine resolution. Squashing image.\n");
+        /* Fall through */
+    default:
+    case T4_Y_RESOLUTION_STANDARD:
+        s->min_scan_time_code = translate_min_scan_time[0][min_bits_field];
+        break;
     }
+    if (!s->error_correcting_mode  &&  (s->iaf & T30_IAF_MODE_NO_FILL_BITS))
+        return 0;
+    return fallback_sequence[s->current_fallback].bit_rate*min_scan_times[s->min_scan_time_code]/1000;
+}
+/*- End of function --------------------------------------------------------*/
 
-    if (x < 0)
+static int start_sending_document(t30_state_t *s)
+{
+    int min_row_bits;
+
+    if (s->tx_file[0] == '\0')
     {
-        t30_set_status(s, T30_ERR_NORESSUPPORT);
+        /* There is nothing to send */
+        span_log(&s->logging, SPAN_LOG_FLOW, "No document to send\n");
         return -1;
     }
+    span_log(&s->logging, SPAN_LOG_FLOW, "Start sending document\n");
+    if (t4_tx_init(&s->t4.tx, s->tx_file, s->tx_start_page, s->tx_stop_page) == NULL)
+    {
+        span_log(&s->logging, SPAN_LOG_WARNING, "Cannot open source TIFF file '%s'\n", s->tx_file);
+        t30_set_status(s, T30_ERR_FILEERROR);
+        return -1;
+    }
+    s->operation_in_progress = OPERATION_IN_PROGRESS_T4_TX;
+    t4_tx_get_pages_in_file(&s->t4.tx);
+    t4_tx_set_tx_encoding(&s->t4.tx, s->line_encoding);
+    t4_tx_set_local_ident(&s->t4.tx, s->tx_info.ident);
+    t4_tx_set_header_info(&s->t4.tx, s->header_info);
+    if (s->use_own_tz)
+        t4_tx_set_header_tz(&s->t4.tx, &s->tz);
 
-    s->image_width = widths[x][dcs_frame[5] & (DISBIT2 | DISBIT1)];
-
-    /* Check which compression the far end has decided to use. */
-#if defined(SPANDSP_SUPPORT_T42)
-    if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T81_MODE))
+    if (tx_start_page(s))
     {
-        s->line_encoding = T4_COMPRESSION_T42_T81;
+        span_log(&s->logging, SPAN_LOG_WARNING, "Something seems to be wrong in the file\n");
+        t30_set_status(s, T30_ERR_FILEERROR);
+        return -1;
     }
-    else
-#endif
-#if defined(SPANDSP_SUPPORT_T43)
-    if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T43_MODE))
+
+    s->x_resolution = t4_tx_get_x_resolution(&s->t4.tx);
+    s->y_resolution = t4_tx_get_y_resolution(&s->t4.tx);
+    s->image_width = t4_tx_get_image_width(&s->t4.tx);
+    /* The minimum scan time to be used can't be evaluated until we know the Y resolution, and
+       must be evaluated before the minimum scan row bits can be evaluated. */
+    if ((min_row_bits = set_min_scan_time_code(s)) < 0)
     {
-        s->line_encoding = T4_COMPRESSION_T43;
+        terminate_operation_in_progress(s);
+        return -1;
     }
-    else
-#endif
-    if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T85_L0_MODE))
+    span_log(&s->logging, SPAN_LOG_FLOW, "Minimum bits per row will be %d\n", min_row_bits);
+    t4_tx_set_min_bits_per_row(&s->t4.tx, min_row_bits);
+
+    if (s->error_correcting_mode)
     {
-        s->line_encoding = T4_COMPRESSION_T85_L0;
+        if (get_partial_ecm_page(s) == 0)
+            span_log(&s->logging, SPAN_LOG_WARNING, "No image data to send\n");
     }
-    else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T85_MODE))
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int restart_sending_document(t30_state_t *s)
+{
+    t4_tx_restart_page(&s->t4.tx);
+    s->retries = 0;
+    s->ecm_block = 0;
+    send_dcs_sequence(s, TRUE);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int start_receiving_document(t30_state_t *s)
+{
+    if (s->rx_file[0] == '\0')
     {
-        s->line_encoding = T4_COMPRESSION_T85;
+        /* There is nothing to receive to */
+        span_log(&s->logging, SPAN_LOG_FLOW, "No document to receive\n");
+        return -1;
     }
-    else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T6_MODE))
+    span_log(&s->logging, SPAN_LOG_FLOW, "Start receiving document\n");
+    queue_phase(s, T30_PHASE_B_TX);
+    s->ecm_block = 0;
+    send_dis_or_dtc_sequence(s, TRUE);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void unexpected_non_final_frame(t30_state_t *s, const uint8_t *msg, int len)
+{
+    span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s frame in state %s\n", t30_frametype(msg[2]), state_names[s->state]);
+    if (s->current_status == T30_ERR_OK)
+        t30_set_status(s, T30_ERR_UNEXPECTED);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void unexpected_final_frame(t30_state_t *s, const uint8_t *msg, int len)
+{
+    span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s frame in state %s\n", t30_frametype(msg[2]), state_names[s->state]);
+    if (s->current_status == T30_ERR_OK)
+        t30_set_status(s, T30_ERR_UNEXPECTED);
+    send_dcn(s);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void unexpected_frame_length(t30_state_t *s, const uint8_t *msg, int len)
+{
+    span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s frame length - %d\n", t30_frametype(msg[0]), len);
+    if (s->current_status == T30_ERR_OK)
+        t30_set_status(s, T30_ERR_UNEXPECTED);
+    send_dcn(s);
+}
+/*- End of function --------------------------------------------------------*/
+
+static int process_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len)
+{
+    int new_status;
+
+    queue_phase(s, T30_PHASE_B_TX);
+    if (analyze_rx_dis_dtc(s, msg, len) < 0)
     {
+        send_dcn(s);
+        return -1;
+    }
+
+    /* Choose a compression scheme from amongst those mutually available */
+    if ((s->mutual_compressions & T30_SUPPORT_COMPRESSION_T85_L0))
+        s->line_encoding = T4_COMPRESSION_T85_L0;
+    else if ((s->mutual_compressions & T30_SUPPORT_COMPRESSION_T85))
+        s->line_encoding = T4_COMPRESSION_T85;
+    else if ((s->mutual_compressions & T30_SUPPORT_COMPRESSION_T6))
         s->line_encoding = T4_COMPRESSION_T6;
+    else if ((s->mutual_compressions & T30_SUPPORT_COMPRESSION_T4_2D))
+        s->line_encoding = T4_COMPRESSION_T4_2D;
+    else
+        s->line_encoding = T4_COMPRESSION_T4_1D;
+
+    span_log(&s->logging, SPAN_LOG_FLOW, "Choose compression %s (%d)\n", t4_encoding_to_str(s->line_encoding), s->line_encoding);
+
+    if (s->phase_b_handler)
+    {
+        new_status = s->phase_b_handler(s, s->phase_b_user_data, msg[2]);
+        if (new_status != T30_ERR_OK)
+        {
+            span_log(&s->logging, SPAN_LOG_FLOW, "Application rejected DIS/DTC - '%s'\n", t30_completion_code_to_str(new_status));
+            t30_set_status(s, new_status);
+            /* TODO: If FNV is allowed, process it here */
+            send_dcn(s);
+            return -1;
+        }
     }
-    else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_2D_MODE))
+    /* Try to send something */
+    if (s->tx_file[0])
     {
-        s->line_encoding = T4_COMPRESSION_T4_2D;
+        span_log(&s->logging, SPAN_LOG_FLOW, "Trying to send file '%s'\n", s->tx_file);
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_READY_TO_RECEIVE_FAX_DOCUMENT))
+        {
+            span_log(&s->logging, SPAN_LOG_FLOW, "%s far end cannot receive\n", t30_frametype(msg[2]));
+            t30_set_status(s, T30_ERR_RX_INCAPABLE);
+            send_dcn(s);
+            return -1;
+        }
+        if (start_sending_document(s))
+        {
+            send_dcn(s);
+            return -1;
+        }
+        if (build_dcs(s))
+        {
+            span_log(&s->logging, SPAN_LOG_FLOW, "The far end is incompatible\n", s->tx_file);
+            send_dcn(s);
+            return -1;
+        }
+        s->retries = 0;
+        send_dcs_sequence(s, TRUE);
+        return 0;
     }
-    else
+    span_log(&s->logging, SPAN_LOG_FLOW, "%s nothing to send\n", t30_frametype(msg[2]));
+    /* ... then try to receive something */
+    if (s->rx_file[0])
     {
-        s->line_encoding = T4_COMPRESSION_T4_1D;
+        span_log(&s->logging, SPAN_LOG_FLOW, "Trying to receive file '%s'\n", s->rx_file);
+        if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_READY_TO_TRANSMIT_FAX_DOCUMENT))
+        {
+            span_log(&s->logging, SPAN_LOG_FLOW, "%s far end cannot transmit\n", t30_frametype(msg[2]));
+            t30_set_status(s, T30_ERR_TX_INCAPABLE);
+            send_dcn(s);
+            return -1;
+        }
+        if (start_receiving_document(s))
+        {
+            send_dcn(s);
+            return -1;
+        }
+        if (set_dis_or_dtc(s))
+        {
+            t30_set_status(s, T30_ERR_INCOMPATIBLE);
+            send_dcn(s);
+            return -1;
+        }
+        s->retries = 0;
+        send_dis_or_dtc_sequence(s, TRUE);
+        return 0;
     }
-    span_log(&s->logging, SPAN_LOG_FLOW, "Far end selected compression %s (%d)\n", t4_encoding_to_str(s->line_encoding), s->line_encoding);
-    if (!test_ctrl_bit(dcs_frame, T30_DCS_BIT_RECEIVE_FAX_DOCUMENT))
-        span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Remote is not requesting receive in DCS\n");
+    span_log(&s->logging, SPAN_LOG_FLOW, "%s nothing to receive\n", t30_frametype(msg[2]));
+    /* There is nothing to do, or nothing we are able to do. */
+    send_dcn(s);
+    return -1;
+}
+/*- End of function --------------------------------------------------------*/
 
-    if ((s->current_fallback = find_fallback_entry(dcs_frame[4] & (DISBIT6 | DISBIT5 | DISBIT4 | DISBIT3))) < 0)
+static int process_rx_dcs(t30_state_t *s, const uint8_t *msg, int len)
+{
+    int new_status;
+
+    if (analyze_rx_dcs(s, msg, len) < 0)
     {
-        span_log(&s->logging, SPAN_LOG_FLOW, "Remote asked for a modem standard we do not support\n");
+        send_dcn(s);
         return -1;
     }
-    s->error_correcting_mode = (test_ctrl_bit(dcs_frame, T30_DCS_BIT_ECM_MODE) != 0);
 
     if (s->phase_b_handler)
     {
@@ -6657,7 +6682,6 @@ SPAN_DECLARE(int) t30_restart(t30_state_t *s)
     s->ppr_count = 0;
     s->ecm_progress = 0;
     s->receiver_not_ready_count = 0;
-    s->far_dis_dtc_len = 0;
     memset(&s->far_dis_dtc_frame, 0, sizeof(s->far_dis_dtc_frame));
     t30_build_dis_or_dtc(s);
     memset(&s->rx_info, 0, sizeof(s->rx_info));
index f2a2929fd1ff48ef9d4a7bc5cb78e48d0da9c2b1..2c5d6971adbcb2b5f8813a02689c55c56d7536af 100644 (file)
@@ -218,8 +218,14 @@ static int set_tiff_directory_info(t4_rx_state_t *s)
     case T4_COMPRESSION_T85_L0:
         output_compression = COMPRESSION_T85;
         break;
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        output_compression = COMPRESSION_T88;
+        break;
+#endif
 #if defined(SPANDSP_SUPPORT_T42)
     case T4_COMPRESSION_T42_T81:
+    case T4_COMPRESSION_SYCC_T81:
         output_compression = COMPRESSION_JPEG;
         bits_per_sample = 8;
         samples_per_pixel = 3;
@@ -233,6 +239,14 @@ static int set_tiff_directory_info(t4_rx_state_t *s)
         samples_per_pixel = 3;
         photometric = PHOTOMETRIC_ITULAB;
         break;
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        output_compression = COMPRESSION_T45;
+        bits_per_sample = 8;
+        samples_per_pixel = 3;
+        photometric = PHOTOMETRIC_ITULAB;
+        break;
 #endif
     }
 
@@ -347,6 +361,10 @@ static int set_tiff_directory_info(t4_rx_state_t *s)
     case T4_COMPRESSION_T85_L0:
         s->image_length = t85_decode_get_image_length(&s->decoder.t85);
         break;
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        break;
+#endif
     case T4_COMPRESSION_T42_T81:
         s->image_length = t42_decode_get_image_length(&s->decoder.t42);
         break;
@@ -354,6 +372,10 @@ static int set_tiff_directory_info(t4_rx_state_t *s)
     case T4_COMPRESSION_T43:
         s->image_length = t43_decode_get_image_length(&s->decoder.t43);
         break;
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        break;
 #endif
     }
     TIFFSetField(t->tiff_file, TIFFTAG_IMAGELENGTH, s->image_length);
@@ -392,19 +414,101 @@ static int row_read_handler(void *user_data, uint8_t row[], size_t len)
 }
 /*- End of function --------------------------------------------------------*/
 
-static int write_tiff_image(t4_rx_state_t *s)
+static int write_tiff_t85_image(t4_rx_state_t *s)
 {
-    t4_rx_tiff_state_t *t;
     uint8_t *buf;
     uint8_t *buf2;
     int buf_len;
     int len;
-    int len2;
+    int image_len;
     t85_encode_state_t t85;
+    packer_t packer;
+
+    /* We need to perform this compression here, as libtiff does not understand it. */
+    packer.buf = s->tiff.image_buffer;
+    packer.ptr = 0;
+    if (t85_encode_init(&t85, s->image_width, s->image_length, row_read_handler, &packer) == NULL)
+        return -1;
+    //if (t->output_encoding == T4_COMPRESSION_T85_L0)
+    //    t85_encode_set_options(&t85, 256, -1, -1);
+    buf = NULL;
+    buf_len = 0;
+    image_len = 0;
+    do
+    {
+        if (buf_len < image_len + 65536)
+        {
+            buf_len += 65536;
+            if ((buf2 = realloc(buf, buf_len)) == NULL)
+            {
+                if (buf)
+                    free(buf);
+                return -1;
+            }
+            buf = buf2;
+        }
+        len = t85_encode_get(&t85, &buf[image_len], buf_len - image_len);
+        image_len += len;
+    }
+    while (len > 0);
+    if (TIFFWriteRawStrip(s->tiff.tiff_file, 0, buf, image_len) < 0)
+    {
+        span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", s->tiff.file);
+        return -1;
+    }
+    t85_encode_release(&t85);
+    free(buf);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
 #if defined(SPANDSP_SUPPORT_T43)
+static int write_tiff_t43_image(t4_rx_state_t *s)
+{
+    uint8_t *buf;
+    uint8_t *buf2;
+    int buf_len;
+    int len;
+    int image_len;
     t43_encode_state_t t43;
-#endif
     packer_t packer;
+
+    packer.buf = s->tiff.image_buffer;
+    packer.ptr = 0;
+    if (t43_encode_init(&t43, s->image_width, s->image_length, row_read_handler, &packer) == NULL)
+        return -1;
+    buf = NULL;
+    buf_len = 0;
+    image_len = 0;
+    do
+    {
+        if (buf_len < image_len + 65536)
+        {
+            buf_len += 65536;
+            if ((buf2 = realloc(buf, buf_len)) == NULL)
+            {
+                if (buf)
+                    free(buf);
+                return -1;
+            }
+            buf = buf2;
+        }
+        len = t43_encode_get(&t43, &buf[image_len], buf_len - image_len);
+        image_len += len;
+    }
+    while (len > 0);
+    if (TIFFWriteRawStrip(s->tiff.tiff_file, 0, buf, image_len) < 0)
+        span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", s->tiff.file);
+    t43_encode_release(&t43);
+    free(buf);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+#endif
+
+static int write_tiff_image(t4_rx_state_t *s)
+{
+    t4_rx_tiff_state_t *t;
 #if defined(SPANDSP_SUPPORT_TIFF_FX)
     uint64_t offset;
 #endif
@@ -424,68 +528,28 @@ static int write_tiff_image(t4_rx_state_t *s)
     case T4_COMPRESSION_T85:
     case T4_COMPRESSION_T85_L0:
         /* We need to perform this compression here, as libtiff does not understand it. */
-        span_log(&s->logging, SPAN_LOG_WARNING, "%s: TODO need T.85 compression.\n", t->file);
-        buf_len = 0;
-        buf = NULL;
-        packer.buf = t->image_buffer;
-        packer.ptr = 0;
-        t85_encode_init(&t85, s->image_width, s->image_length, row_read_handler, &packer);
-        //if (t->output_encoding == T4_COMPRESSION_T85_L0)
-        //    t85_encode_set_options(&t85, 256, -1, -1);
-        len2 = 0;
-        do
-        {
-            if (buf_len < len2 + 50000)
-            {
-                buf_len += 50000;
-                if ((buf2 = realloc(buf, buf_len)) == NULL)
-                {
-                    if (buf)
-                        free(buf);
-                    return -1;
-                }
-                buf = buf2;
-            }
-            len = t85_encode_get(&t85, &buf[len2], 50000);
-            len2 += len;
-        }
-        while (len > 0);
-        if (TIFFWriteRawStrip(t->tiff_file, 0, buf, len2) < 0)
-            span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", t->file);
-        t85_encode_release(&t85);
-        free(buf);
+        if (write_tiff_t85_image(s) < 0)
+            return -1;
         break;
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        /* We need to perform this compression here, as libtiff does not understand it. */
+        if (write_tiff_t88_image(s) < 0)
+            return -1;
+        break;
+#endif
 #if defined(SPANDSP_SUPPORT_T43)
     case T4_COMPRESSION_T43:
         /* We need to perform this compression here, as libtiff does not understand it. */
-        span_log(&s->logging, SPAN_LOG_WARNING, "%s: TODO need T.43 compression.\n", t->file);
-        buf_len = 0;
-        buf = NULL;
-        packer.buf = t->image_buffer;
-        packer.ptr = 0;
-        t43_encode_init(&t43, s->image_width, s->image_length, row_read_handler, &packer);
-        len2 = 0;
-        do
-        {
-            if (buf_len < len2 + 50000)
-            {
-                buf_len += 50000;
-                if ((buf2 = realloc(buf, buf_len)) == NULL)
-                {
-                    if (buf)
-                        free(buf);
-                    return -1;
-                }
-                buf = buf2;
-            }
-            len = t43_encode_get(&t43, &buf[len2], 50000);
-            len2 += len;
-        }
-        while (len > 0);
-        if (TIFFWriteRawStrip(t->tiff_file, 0, buf, len2) < 0)
-            span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", t->file);
-        t43_encode_release(&t43);
-        free(buf);
+        if (write_tiff_t43_image(s) < 0)
+            return -1;
+        break;
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        /* We need to perform this compression here, as libtiff does not understand it. */
+        if (write_tiff_t45_image(s) < 0)
+            return -1;
         break;
 #endif
     default:
@@ -498,6 +562,8 @@ static int write_tiff_image(t4_rx_state_t *s)
     if (!TIFFWriteDirectory(t->tiff_file))
         span_log(&s->logging, SPAN_LOG_WARNING, "%s: Failed to write directory for page %d.\n", t->file, s->current_page);
 #if defined(SPANDSP_SUPPORT_TIFF_FX)
+    /* According to the TIFF/FX spec, a global parameters IFD should only be inserted into
+       the first page in the file */
     if (s->current_page == 0)
     {
         if (!TIFFCreateCustomDirectory(t->tiff_file, &tiff_fx_field_array))
@@ -597,11 +663,19 @@ SPAN_DECLARE(int) t4_rx_put(t4_rx_state_t *s, const uint8_t buf[], size_t len)
     case T4_COMPRESSION_T85:
     case T4_COMPRESSION_T85_L0:
         return t85_decode_put(&s->decoder.t85, buf, len);
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        break;
+#endif
     case T4_COMPRESSION_T42_T81:
         return t42_decode_put(&s->decoder.t42, buf, len);
 #if defined(SPANDSP_SUPPORT_T43)
     case T4_COMPRESSION_T43:
         return t43_decode_put(&s->decoder.t43, buf, len);
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        break;
 #endif
     }
     return T4_DECODE_OK;
@@ -686,10 +760,24 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding)
         }
         s->line_encoding = encoding;
         return 0;
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        switch (s->line_encoding)
+        {
+        case T4_COMPRESSION_T88:
+            break;
+        default:
+            break;
+        }
+        s->line_encoding = encoding;
+        return 0;
+#endif
     case T4_COMPRESSION_T42_T81:
+    case T4_COMPRESSION_SYCC_T81:
         switch (s->line_encoding)
         {
         case T4_COMPRESSION_T42_T81:
+        case T4_COMPRESSION_SYCC_T81:
             break;
         default:
             t42_decode_init(&s->decoder.t42, s->row_handler, s->row_handler_user_data);
@@ -717,6 +805,18 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding)
         }
         s->line_encoding = encoding;
         return 0;
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        switch (s->line_encoding)
+        {
+        case T4_COMPRESSION_T45:
+            break;
+        default:
+            break;
+        }
+        s->line_encoding = encoding;
+        return 0;
 #endif
     }
     return -1;
@@ -742,11 +842,19 @@ SPAN_DECLARE(int) t4_rx_set_row_write_handler(t4_rx_state_t *s, t4_row_write_han
     case T4_COMPRESSION_T85:
     case T4_COMPRESSION_T85_L0:
         return t85_decode_set_row_write_handler(&s->decoder.t85, handler, user_data);
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        break;
+#endif
     case T4_COMPRESSION_T42_T81:
         return t42_decode_set_row_write_handler(&s->decoder.t42, handler, user_data);
 #if defined(SPANDSP_SUPPORT_T43)
     case T4_COMPRESSION_T43:
         return t43_decode_set_row_write_handler(&s->decoder.t43, handler, user_data);
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        break;
 #endif
     }
     return -1;
@@ -789,6 +897,10 @@ SPAN_DECLARE(void) t4_rx_get_transfer_statistics(t4_rx_state_t *s, t4_stats_t *t
         t->image_length = t->length;
         t->line_image_size = t85_decode_get_compressed_image_size(&s->decoder.t85)/8;
         break;
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        break;
+#endif
     case T4_COMPRESSION_T42_T81:
         t->type = 0;
         t->width = t42_decode_get_image_width(&s->decoder.t42);
@@ -808,6 +920,10 @@ SPAN_DECLARE(void) t4_rx_get_transfer_statistics(t4_rx_state_t *s, t4_stats_t *t
         t->image_length = t->length;
         t->line_image_size = t43_decode_get_compressed_image_size(&s->decoder.t43)/8;
         break;
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        break;
 #endif
     }
 }
@@ -828,6 +944,10 @@ SPAN_DECLARE(int) t4_rx_start_page(t4_rx_state_t *s)
     case T4_COMPRESSION_T85_L0:
         t85_decode_restart(&s->decoder.t85);
         break;
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        break;
+#endif
     case T4_COMPRESSION_T42_T81:
         t42_decode_restart(&s->decoder.t42);
         break;
@@ -835,6 +955,10 @@ SPAN_DECLARE(int) t4_rx_start_page(t4_rx_state_t *s)
     case T4_COMPRESSION_T43:
         t43_decode_restart(&s->decoder.t43);
         break;
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        break;
 #endif
     }
     s->line_image_size = 0;
@@ -886,6 +1010,10 @@ SPAN_DECLARE(int) t4_rx_end_page(t4_rx_state_t *s)
         t85_decode_put(&s->decoder.t85, NULL, 0);
         length = t85_decode_get_image_length(&s->decoder.t85);
         break;
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        break;
+#endif
     case T4_COMPRESSION_T42_T81:
         t42_decode_put(&s->decoder.t42, NULL, 0);
         length = t42_decode_get_image_length(&s->decoder.t42);
@@ -895,6 +1023,10 @@ SPAN_DECLARE(int) t4_rx_end_page(t4_rx_state_t *s)
         t43_decode_put(&s->decoder.t43, NULL, 0);
         length = t43_decode_get_image_length(&s->decoder.t43);
         break;
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        break;
 #endif
     }
 
@@ -984,11 +1116,19 @@ SPAN_DECLARE(int) t4_rx_release(t4_rx_state_t *s)
     case T4_COMPRESSION_T85:
     case T4_COMPRESSION_T85_L0:
         return t85_decode_release(&s->decoder.t85);
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        break;
+#endif
     case T4_COMPRESSION_T42_T81:
         return t42_decode_release(&s->decoder.t42);
 #if defined(SPANDSP_SUPPORT_T43)
     case T4_COMPRESSION_T43:
         return t43_decode_release(&s->decoder.t43);
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        break;
 #endif
     }
     return -1;
index ff5b5fd349fd141133d6c04499366d68b221260c..6ad6821b9bbe26dab1272e1d2b89d652304e026d 100644 (file)
@@ -92,7 +92,7 @@ typedef struct
     int ptr;
     int row;
     int bit_mask;
-} t85_packer_t;
+} packer_t;
 
 typedef struct
 {
@@ -574,11 +574,11 @@ static int row_read(void *user_data, uint8_t buf[], size_t len)
 }
 /*- End of function --------------------------------------------------------*/
 
-static int t85_row_write_handler(void *user_data, const uint8_t buf[], size_t len)
+static int packing_row_write_handler(void *user_data, const uint8_t buf[], size_t len)
 {
-    t85_packer_t *s;
+    packer_t *s;
 
-    s = (t85_packer_t *) user_data;
+    s = (packer_t *) user_data;
     memcpy(&s->buf[s->ptr], buf, len);
     s->ptr += len;
     s->row++;
@@ -586,7 +586,7 @@ static int t85_row_write_handler(void *user_data, const uint8_t buf[], size_t le
 }
 /*- End of function --------------------------------------------------------*/
 
-static int t85_comment_handler(void *user_data, const uint8_t buf[], size_t len)
+static int embedded_comment_handler(void *user_data, const uint8_t buf[], size_t len)
 {
     t4_tx_state_t *s;
 
@@ -599,18 +599,235 @@ static int t85_comment_handler(void *user_data, const uint8_t buf[], size_t len)
 }
 /*- End of function --------------------------------------------------------*/
 
-static int read_tiff_image(t4_tx_state_t *s)
+static int read_tiff_t85_image(t4_tx_state_t *s)
 {
+    int biggest;
+    int num_strips;
     int total_len;
     int len;
-    int biggest;
     int i;
-    int num_strips;
     int result;
     uint8_t *t;
     uint8_t *raw_data;
     t85_decode_state_t t85;
-    t85_packer_t t85_pack;
+    packer_t pack;
+
+    /* Size up and allocate the buffer for the raw data */
+    num_strips = TIFFNumberOfStrips(s->tiff.tiff_file);
+    biggest = TIFFRawStripSize(s->tiff.tiff_file, 0);
+    for (i = 1;  i < num_strips;  i++)
+    {
+        len = TIFFRawStripSize(s->tiff.tiff_file, i);
+        if (len > biggest)
+            biggest = len;
+    }
+    if ((raw_data = malloc(biggest)) == NULL)
+        return -1;
+
+    s->tiff.image_size = s->image_length*((s->image_width + 7)/8);
+    if (s->tiff.image_size >= s->tiff.image_buffer_size)
+    {
+        if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL)
+            return -1;
+        s->tiff.image_buffer_size = s->tiff.image_size;
+        s->tiff.image_buffer = t;
+    }
+
+    pack.buf = s->tiff.image_buffer;
+    pack.ptr = 0;
+    pack.row = 0;
+    t85_decode_init(&t85, packing_row_write_handler, &pack);
+    t85_decode_set_comment_handler(&t85, 1000, embedded_comment_handler, s);
+
+    total_len = 0;
+    result = -1;
+    for (i = 0;  i < num_strips;  i++, total_len += len)
+    {
+        len = TIFFRawStripSize(s->tiff.tiff_file, i);
+        if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, raw_data, len)) < 0)
+        {
+            span_log(&s->logging, SPAN_LOG_WARNING, "%s: ReadRaw error.\n", s->tiff.file);
+            return -1;
+        }
+        result = t85_decode_put(&t85, raw_data, len);
+        if (result != T4_DECODE_MORE_DATA)
+            break;
+    }
+    if (result == T4_DECODE_MORE_DATA)
+        result = t85_decode_put(&t85, NULL, 0);
+
+    len = t85_decode_get_compressed_image_size(&t85);
+    span_log(&s->logging, SPAN_LOG_WARNING, "Compressed image is %d bytes, %d rows\n", len/8, s->image_length);
+    t85_decode_release(&t85);
+    free(raw_data);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+#if defined(SPANDSP_SUPPORT_T43)
+static int read_tiff_t43_image(t4_tx_state_t *s, uint8_t **buf)
+{
+    int num_strips;
+    int total_len;
+    int len;
+    int i;
+    int total_image_len;
+    int image_size;
+    logging_state_t *logging;
+    uint8_t *raw_data;
+    t43_decode_state_t t43;
+    packer_t pack;
+
+    num_strips = TIFFNumberOfStrips(s->tiff.tiff_file);
+    total_image_len = 0;
+    for (i = 0;  i < num_strips;  i++)
+        total_image_len += TIFFRawStripSize(s->tiff.tiff_file, i);
+    if ((raw_data = malloc(total_image_len)) == NULL)
+        return -1;
+
+    total_len = 0;
+    for (i = 0;  i < num_strips;  i++, total_len += len)
+    {
+        if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, &raw_data[total_len], total_image_len - total_len)) < 0)
+        {
+            span_log(&s->logging, SPAN_LOG_FLOW, "TIFF read error.\n");
+            return -1;
+        }
+    }
+
+    t43_decode_init(&t43, packing_row_write_handler, &pack);
+    t43_decode_set_comment_handler(&t43, 1000, embedded_comment_handler, NULL);
+    logging = t43_decode_get_logging_state(&t43);
+    span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
+
+    image_size = 3*s->image_length*s->image_width;
+    if ((*buf = malloc(image_size)) == NULL)
+        return -1;
+
+    pack.buf = *buf;
+    pack.ptr = 0;
+    pack.row = 0;
+    t43_decode_put(&t43, raw_data, total_len);
+    t43_decode_release(&t43);
+    free(raw_data);
+    return image_size;
+}
+/*- End of function --------------------------------------------------------*/
+#endif
+
+#if 0
+static int read_tiff_t42_t81_image(t4_tx_state_t *s, uint8_t **buf)
+{
+    int total_len;
+    int len;
+    int i;
+    int num_strips;
+    int total_image_len;
+    int image_size;
+    uint8_t *raw_data;
+    uint8_t *jpeg_table;
+    uint32_t jpeg_table_len;
+    uint32_t w;
+    uint32_t h;
+    tsize_t off;
+
+    num_strips = TIFFNumberOfStrips(s->tiff.tiff_file);
+    total_image_len = 0;
+    jpeg_table_len = 0;
+    if (TIFFGetField(s->tiff.tiff_file, TIFFTAG_JPEGTABLES, &jpeg_table_len, &jpeg_table))
+    {
+        total_image_len += (jpeg_table_len - 4);
+        span_log(&s->logging, SPAN_LOG_FLOW, "JPEG tables %u\n", jpeg_table_len);
+    }
+
+    for (i = 0;  i < num_strips;  i++)
+        total_image_len += TIFFRawStripSize(s->tiff.tiff_file, i);
+    if ((raw_data = malloc(total_image_len)) == NULL)
+        return -1;
+
+    total_len = 0;
+    if (jpeg_table_len > 0)
+        total_len += jpeg_table_len - 4;
+    for (i = 0;  i < num_strips;  i++, total_len += len)
+    {
+        if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, &raw_data[total_len], total_image_len - total_len)) < 0)
+        {
+            span_log(&s->logging, SPAN_LOG_FLOW, "TIFF read error.\n");
+            return -1;
+        }
+    }
+    if (jpeg_table_len > 0)
+        memcpy(raw_data, jpeg_table, jpeg_table_len - 2);
+
+    if (total_len != total_image_len)
+        span_log(&s->logging, SPAN_LOG_FLOW, "Size mismatch %d %d\n", (int) total_len, (int) total_image_len);
+
+    image_size = 3*s->image_length*s->image_width;
+    if ((*buf = malloc(image_size)) == NULL)
+        return -1;
+
+    off = 0;
+    if (!t42_itulab_to_srgb(&s->logging, &s->lab_params, *buf, &off, raw_data, total_image_len, &w, &h))
+    {
+        span_log(&s->logging, SPAN_LOG_FLOW, "Failed to convert from ITULAB.\n");
+        return -1;
+    }
+    free(raw_data);
+    return image_size;
+}
+/*- End of function --------------------------------------------------------*/
+#endif
+
+static int read_tiff_decompressed_image(t4_tx_state_t *s)
+{
+    int total_len;
+    int len;
+    int num_strips;
+    int i;
+    uint8_t *t;
+
+    /* Decode the whole image into a buffer */
+    /* Let libtiff handle the decompression */
+    s->tiff.image_size = s->image_length*TIFFScanlineSize(s->tiff.tiff_file);
+    if (s->tiff.image_size >= s->tiff.image_buffer_size)
+    {
+        if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL)
+            return -1;
+        s->tiff.image_buffer_size = s->tiff.image_size;
+        s->tiff.image_buffer = t;
+    }
+
+    /* Allow for the image being stored in multiple strips, although it is rare to find
+       a stripped image in a T.4 or T.6 encoded file. */
+    num_strips = TIFFNumberOfStrips(s->tiff.tiff_file);
+    for (i = 0, total_len = 0;  i < num_strips;  i++, total_len += len)
+    {
+        if ((len = TIFFReadEncodedStrip(s->tiff.tiff_file, i, &s->tiff.image_buffer[total_len], s->tiff.image_size - total_len)) < 0)
+        {
+            span_log(&s->logging, SPAN_LOG_WARNING, "%s: Read error.\n", s->tiff.file);
+            return -1;
+        }
+    }
+    /* We might need to flip all the bits, so 1 = black and 0 = white. */
+    if (s->tiff.photo_metric != PHOTOMETRIC_MINISWHITE)
+    {
+        span_log(&s->logging, SPAN_LOG_FLOW, "%s: Photometric needs swapping.\n", s->tiff.file);
+        for (i = 0;  i < s->tiff.image_size;  i++)
+            s->tiff.image_buffer[i] = ~s->tiff.image_buffer[i];
+        s->tiff.photo_metric = PHOTOMETRIC_MINISWHITE;
+    }
+    /* We might need to bit reverse each of the bytes of the image. */
+    if (s->tiff.fill_order != FILLORDER_LSB2MSB)
+        bit_reverse(s->tiff.image_buffer, s->tiff.image_buffer, s->tiff.image_size);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int read_tiff_image(t4_tx_state_t *s)
+{
+    int total_len;
+    int i;
+    uint8_t *t;
     image_translate_state_t *translator;
 
     if (s->tiff.image_type != T4_IMAGE_TYPE_BILEVEL)
@@ -666,89 +883,15 @@ static int read_tiff_image(t4_tx_state_t *s)
         case COMPRESSION_T85:
             /* Decode the whole image into a buffer */
             /* libtiff probably cannot decompress T.85, so we must handle it ourselves */
-            /* Size up and allocate the buffer for the raw data */
-            num_strips = TIFFNumberOfStrips(s->tiff.tiff_file);
-            biggest = TIFFRawStripSize(s->tiff.tiff_file, 0);
-            for (i = 1;  i < num_strips;  i++)
-            {
-                len = TIFFRawStripSize(s->tiff.tiff_file, i);
-                if (len > biggest)
-                    biggest = len;
-            }
-            if ((raw_data = malloc(biggest)) == NULL)
+            /* Decode the whole image into a buffer */
+            if (read_tiff_t85_image(s) < 0)
                 return -1;
-
-            s->tiff.image_size = s->image_length*((s->image_width + 7)/8);
-            if (s->tiff.image_size >= s->tiff.image_buffer_size)
-            {
-                if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL)
-                    return -1;
-                s->tiff.image_buffer_size = s->tiff.image_size;
-                s->tiff.image_buffer = t;
-            }
-
-            t85_pack.buf = s->tiff.image_buffer;
-            t85_pack.ptr = 0;
-            t85_pack.row = 0;
-            t85_decode_init(&t85, t85_row_write_handler, &t85_pack);
-            t85_decode_set_comment_handler(&t85, 1000, t85_comment_handler, s);
-
-            total_len = 0;
-            result = -1;
-            for (i = 0;  i < num_strips;  i++, total_len += len)
-            {
-                len = TIFFRawStripSize(s->tiff.tiff_file, i);
-                if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, raw_data, len)) < 0)
-                {
-                    span_log(&s->logging, SPAN_LOG_WARNING, "%s: ReadRaw error.\n", s->tiff.file);
-                    return -1;
-                }
-                result = t85_decode_put(&t85, raw_data, len);
-                if (result != T4_DECODE_MORE_DATA)
-                    break;
-            }
-            if (result == T4_DECODE_MORE_DATA)
-                result = t85_decode_put(&t85, NULL, 0);
-
-            len = t85_decode_get_compressed_image_size(&t85);
-            span_log(&s->logging, SPAN_LOG_WARNING, "Compressed image is %d bytes, %d rows\n", len/8, s->image_length);
-            t85_decode_release(&t85);
-            free(raw_data);
             break;
         default:
             /* Decode the whole image into a buffer */
             /* Let libtiff handle the decompression */
-            s->tiff.image_size = s->image_length*TIFFScanlineSize(s->tiff.tiff_file);
-            if (s->tiff.image_size >= s->tiff.image_buffer_size)
-            {
-                if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL)
-                    return -1;
-                s->tiff.image_buffer_size = s->tiff.image_size;
-                s->tiff.image_buffer = t;
-            }
-
-            /* Allow for the image being stored in multiple strips, although it is rare to find
-               a stripped image in a T.4 or T.6 encoded file. */
-            num_strips = TIFFNumberOfStrips(s->tiff.tiff_file);
-            for (i = 0, total_len = 0;  i < num_strips;  i++, total_len += len)
-            {
-                if ((len = TIFFReadEncodedStrip(s->tiff.tiff_file, i, &s->tiff.image_buffer[total_len], s->tiff.image_size - total_len)) < 0)
-                {
-                    span_log(&s->logging, SPAN_LOG_WARNING, "%s: Read error.\n", s->tiff.file);
-                    return -1;
-                }
-            }
-            /* We might need to flip all the bits, so 1 = black and 0 = white. */
-            if (s->tiff.photo_metric != PHOTOMETRIC_MINISWHITE)
-            {
-                span_log(&s->logging, SPAN_LOG_FLOW, "%s: Photometric needs swapping.\n", s->tiff.file);
-                for (i = 0;  i < s->tiff.image_size;  i++)
-                    s->tiff.image_buffer[i] = ~s->tiff.image_buffer[i];
-                s->tiff.photo_metric = PHOTOMETRIC_MINISWHITE;
-            }
-            /* We might need to bit reverse each of the bytes of the image. */
-            if (s->tiff.fill_order != FILLORDER_LSB2MSB)
-                bit_reverse(s->tiff.image_buffer, s->tiff.image_buffer, s->tiff.image_size);
+            if (read_tiff_decompressed_image(s) < 0)
+                return -1;
             break;
         }
     }
@@ -788,11 +931,20 @@ static int set_row_read_handler(t4_tx_state_t *s, t4_row_read_handler_t handler,
     case T4_COMPRESSION_T85:
     case T4_COMPRESSION_T85_L0:
         return t85_encode_set_row_read_handler(&s->encoder.t85, handler, user_data);
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        break;
+#endif
     case T4_COMPRESSION_T42_T81:
+    case T4_COMPRESSION_SYCC_T81:
         return t42_encode_set_row_read_handler(&s->encoder.t42, handler, user_data);
 #if defined(SPANDSP_SUPPORT_T43)
     case T4_COMPRESSION_T43:
         return t43_encode_set_row_read_handler(&s->encoder.t43, handler, user_data);
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        break;
 #endif
     }
     return -1;
@@ -971,10 +1123,24 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int encoding)
             }
             s->line_encoding = encoding;
             return s->line_encoding;
+#if defined(SPANDSP_SUPPORT_T88)
+        case T4_COMPRESSION_T88:
+            switch (s->line_encoding)
+            {
+            case T4_COMPRESSION_T88:
+                break;
+            default:
+                break;
+            }
+            s->line_encoding = encoding;
+            return s->line_encoding;
+#endif
         case T4_COMPRESSION_T42_T81:
+        case T4_COMPRESSION_SYCC_T81:
             switch (s->line_encoding)
             {
             case T4_COMPRESSION_T42_T81:
+            case T4_COMPRESSION_SYCC_T81:
                 break;
             default:
                 t42_encode_init(&s->encoder.t42, s->image_width, s->image_length, s->row_handler, s->row_handler_user_data);
@@ -994,6 +1160,18 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int encoding)
             }
             s->line_encoding = encoding;
             return s->line_encoding;
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+        case T4_COMPRESSION_T45:
+            switch (s->line_encoding)
+            {
+            case T4_COMPRESSION_T45:
+                break;
+            default:
+                break;
+            }
+            s->line_encoding = encoding;
+            return s->line_encoding;
 #endif
         }
     }
@@ -1028,13 +1206,22 @@ SPAN_DECLARE(void) t4_tx_set_image_width(t4_tx_state_t *s, int image_width)
     case T4_COMPRESSION_T85_L0:
         t85_encode_set_image_width(&s->encoder.t85, image_width);
         break;
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        break;
+#endif
     case T4_COMPRESSION_T42_T81:
+    case T4_COMPRESSION_SYCC_T81:
         t42_encode_set_image_width(&s->encoder.t42, image_width);
         break;
 #if defined(SPANDSP_SUPPORT_T43)
     case T4_COMPRESSION_T43:
         t43_encode_set_image_width(&s->encoder.t43, image_width);
         break;
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        break;
 #endif
     }
 }
@@ -1049,13 +1236,22 @@ static void t4_tx_set_image_length(t4_tx_state_t *s, int image_length)
     case T4_COMPRESSION_T85_L0:
         t85_encode_set_image_length(&s->encoder.t85, image_length);
         break;
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        break;
+#endif
     case T4_COMPRESSION_T42_T81:
+    case T4_COMPRESSION_SYCC_T81:
         t42_encode_set_image_length(&s->encoder.t42, image_length);
         break;
 #if defined(SPANDSP_SUPPORT_T43)
     case T4_COMPRESSION_T43:
         t43_encode_set_image_length(&s->encoder.t43, image_length);
         break;
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        break;
 #endif
     }
 }
@@ -1180,7 +1376,12 @@ SPAN_DECLARE(void) t4_tx_get_transfer_statistics(t4_tx_state_t *s, t4_stats_t *t
         t->length = t85_encode_get_image_length(&s->encoder.t85)/s->row_squashing_ratio;
         t->line_image_size = t85_encode_get_compressed_image_size(&s->encoder.t85)/8;
         break;
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        break;
+#endif
     case T4_COMPRESSION_T42_T81:
+    case T4_COMPRESSION_SYCC_T81:
         t->type = 0;
         t->width = t42_encode_get_image_width(&s->encoder.t42);
         t->length = t42_encode_get_image_length(&s->encoder.t42)/s->row_squashing_ratio;
@@ -1193,6 +1394,10 @@ SPAN_DECLARE(void) t4_tx_get_transfer_statistics(t4_tx_state_t *s, t4_stats_t *t
         t->length = t43_encode_get_image_length(&s->encoder.t43)/s->row_squashing_ratio;
         t->line_image_size = t43_encode_get_compressed_image_size(&s->encoder.t43)/8;
         break;
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        break;
 #endif
     }
 }
@@ -1209,11 +1414,20 @@ SPAN_DECLARE(int) t4_tx_image_complete(t4_tx_state_t *s)
     case T4_COMPRESSION_T85:
     case T4_COMPRESSION_T85_L0:
         return t85_encode_image_complete(&s->encoder.t85);
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        break;
+#endif
     case T4_COMPRESSION_T42_T81:
+    case T4_COMPRESSION_SYCC_T81:
         return t42_encode_image_complete(&s->encoder.t42);
 #if defined(SPANDSP_SUPPORT_T43)
     case T4_COMPRESSION_T43:
         return t43_encode_image_complete(&s->encoder.t43);
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        break;
 #endif
     }
     return SIG_STATUS_END_OF_DATA;
@@ -1238,11 +1452,20 @@ SPAN_DECLARE(int) t4_tx_get(t4_tx_state_t *s, uint8_t buf[], size_t max_len)
     case T4_COMPRESSION_T85:
     case T4_COMPRESSION_T85_L0:
         return t85_encode_get(&s->encoder.t85, buf, max_len);
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        break;
+#endif
     case T4_COMPRESSION_T42_T81:
+    case T4_COMPRESSION_SYCC_T81:
         return t42_encode_get(&s->encoder.t42, buf, max_len);
 #if defined(SPANDSP_SUPPORT_T43)
     case T4_COMPRESSION_T43:
         return t43_encode_get(&s->encoder.t43, buf, max_len);
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        break;
 #endif
     }
     return 0;
@@ -1278,13 +1501,22 @@ SPAN_DECLARE(int) t4_tx_start_page(t4_tx_state_t *s)
     case T4_COMPRESSION_T85_L0:
         t85_encode_restart(&s->encoder.t85, s->image_width, s->image_length);
         break;
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        break;
+#endif
     case T4_COMPRESSION_T42_T81:
+    case T4_COMPRESSION_SYCC_T81:
         t42_encode_restart(&s->encoder.t42, s->image_width, s->image_length);
         break;
 #if defined(SPANDSP_SUPPORT_T43)
     case T4_COMPRESSION_T43:
         t43_encode_restart(&s->encoder.t43, s->image_width, s->image_length);
         break;
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        break;
 #endif
     }
     /* If there is a page header, create that first */
@@ -1399,11 +1631,20 @@ SPAN_DECLARE(int) t4_tx_release(t4_tx_state_t *s)
     case T4_COMPRESSION_T85:
     case T4_COMPRESSION_T85_L0:
         return t85_encode_release(&s->encoder.t85);
+#if defined(SPANDSP_SUPPORT_T88)
+    case T4_COMPRESSION_T88:
+        break;
+#endif
     case T4_COMPRESSION_T42_T81:
+    case T4_COMPRESSION_SYCC_T81:
         return t42_encode_release(&s->encoder.t42);
 #if defined(SPANDSP_SUPPORT_T43)
     case T4_COMPRESSION_T43:
         return t43_encode_release(&s->encoder.t43);
+#endif
+#if defined(SPANDSP_SUPPORT_T45)
+    case T4_COMPRESSION_T45:
+        break;
 #endif
     }
     return -1;