]> git.ipfire.org Git - thirdparty/HylaFAX.git/commitdiff
Bug 626: add Class1AdaptRecvCmd support
authorLee Howard <faxguy@howardsilvan.com>
Mon, 28 Feb 2005 18:21:53 +0000 (18:21 +0000)
committerLee Howard <faxguy@howardsilvan.com>
Mon, 28 Feb 2005 18:21:53 +0000 (18:21 +0000)
CHANGES
faxd/Class1.c++
faxd/Class1.h
faxd/Class1Recv.c++
faxd/ModemConfig.c++
faxd/ModemConfig.h
man/hylafax-config.4f

diff --git a/CHANGES b/CHANGES
index bcf069cea94c356687e3c2059d755eab6909977b..3732980059510055d08416940a7236d657ff36e1 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,7 @@
 
 Changelog for HylaFAX
 
+* add Class1AdaptRecvCmd config option, AT+FAR=1 support, (28 Feb 2005)
 * fix DCS scanline-time error when sending to systems without
   ECM supporting "mixed" scanline-times (24 Feb 2005)
 * cause faxsend to log jobs in batches as separate commids (24 Feb 2005)
index 9d4d9d63ac8ee996422685ace874921f1eeeacae..14c8a8d7716540cb2677f1db2a7b9a6f5e21178a 100644 (file)
@@ -322,6 +322,9 @@ Class1Modem::ready(long ms)
     if (conf.class1EnableV34Cmd != "" && conf.class1ECMSupport)   
         if (!atCmd(conf.class1EnableV34Cmd))
            return (false);
+    if (conf.class1AdaptRecvCmd != "")
+       if (!atCmd(conf.class1AdaptRecvCmd))
+           return (false);
     return (FaxModem::ready(ms));
 }
 
@@ -1363,6 +1366,8 @@ Class1Modem::atResponse(char* buf, long ms)
 {
     if (FaxModem::atResponse(buf, ms) == AT_OTHER && strneq(buf, "+FCERROR", 8))
        lastResponse = AT_FCERROR;
+    if (lastResponse == AT_OTHER && strneq(buf, "+FRH:3", 6))
+       lastResponse = AT_FRH3;
     if (lastResponse == AT_OTHER && strneq(buf, "+F34:", 5)) {
        /*
         * V.8 handshaking was successful.  The rest of the
@@ -1413,6 +1418,7 @@ Class1Modem::waitFor(ATResponse wanted, long ms)
            /* fall thru... */
        case AT_OTHER:
        case AT_FCERROR:
+       case AT_FRH3:
        case AT_OK:
            return (false);
        }
index e3e7bfd58ca8d13edaa1655f4f837f434c2ab5e4..f2640110a60822224acdcbe98fd6425b897deb49 100644 (file)
@@ -138,12 +138,14 @@ protected:
     bool       recvTraining();
     bool       recvPPM(int& ppm, fxStr& emsg);
     bool       recvPageData(TIFF*, fxStr& emsg);
+    bool       raiseRecvCarrier(bool& dolongtrain, fxStr& emsg);
     void       recvData(TIFF*, u_char* buf, int n);
     void       processDCSFrame(const HDLCFrame& frame);
     void       abortPageRecv();
 // miscellaneous
     enum {                     // Class 1-specific AT responses
-       AT_FCERROR      = 100   // "+FCERROR"
+       AT_FCERROR      = 100,  // "+FCERROR"
+       AT_FRH3         = 101,  // "+FRH:3"
     };
     virtual ATResponse atResponse(char* buf, long ms = 30*1000);
     virtual bool waitFor(ATResponse wanted, long ms = 30*1000);
index db5dda2ed9546b670f8323bc64eb3b6157029b06..a89c76a910ad1c6077b2354e67e85ea1176d8f3a 100644 (file)
@@ -201,19 +201,29 @@ Class1Modem::recvIdentification(
                     * Verify a DCS command response and, if
                     * all is correct, receive phasing/training.
                     */
-                   if (!recvDCSFrames(frame)) {
-                       if (frame.getFCF() == FCF_DCN) {
-                           emsg = "RSPREC error/got DCN";
-                           recvdDCN = true;
+                   bool gotframe = true;
+                   while (gotframe) {
+                       if (!recvDCSFrames(frame)) {
+                           if (frame.getFCF() == FCF_DCN) {
+                               emsg = "RSPREC error/got DCN";
+                               recvdDCN = true;
                                return (false);
-                       } else                  // XXX DTC/DIS not handled
-                           emsg = "RSPREC invalid response received";
-                       break;
-                   }
-                   if (recvTraining()) {
-                       emsg = "";
-                       return (true);
+                           } else              // XXX DTC/DIS not handled
+                               emsg = "RSPREC invalid response received";
+                           break;
+                       }
+                       gotframe = false;
+                       if (recvTraining()) {
+                           emsg = "";
+                           return (true);
+                       } else {
+                           if (lastResponse == AT_FRH3 && waitFor(AT_CONNECT,0)) {
+                               gotframe = recvRawFrame(frame);
+                               lastResponse == AT_NOTHING;
+                           }
+                       }
                    }
+                   if (gotframe) break;        // where recvDCSFrames fails without DCN
                    emsg = "Failure to train modems";
                    /*
                     * Reset the timeout to insure the T1 timer is
@@ -384,6 +394,8 @@ Class1Modem::recvTraining()
        do {
            gotnocarrier = waitFor(AT_NOCARRIER, 2*1000);
        } while (!gotnocarrier && Sys::now() < (nocarrierstart + 5));
+    } else {
+       if (lastResponse == AT_FRH3) return (false);    // detected V.21 carrier
     }
     /*
      * Send training response; we follow the spec
@@ -532,6 +544,12 @@ Class1Modem::recvPage(TIFF* tif, u_int& ppm, fxStr& emsg, const fxStr& id)
                            prevPage = true;
                        timer = conf.t1Timer;           // wait longer for PPM
                    }
+               } else {
+                   if (rmResponse == AT_FRH3) {
+                       HDLCFrame frame(conf.class1FrameOverhead);
+                       if (waitFor(AT_CONNECT,0) && recvRawFrame(frame))
+                           signalRcvd = frame.getFCF();
+                   }
                }
            }
            if (signalRcvd != 0) {
@@ -539,7 +557,7 @@ Class1Modem::recvPage(TIFF* tif, u_int& ppm, fxStr& emsg, const fxStr& id)
                    (void) setXONXOFF(FLOW_NONE, FLOW_NONE, ACT_DRAIN);
                setInputBuffering(false);
            }
-           if (!messageReceived && rmResponse != AT_FCERROR) {
+           if (!messageReceived && rmResponse != AT_FCERROR && rmResponse != AT_FRH3) {
                if (rmResponse != AT_ERROR) {
                    /*
                     * One of many things may have happened:
@@ -613,14 +631,23 @@ Class1Modem::recvPage(TIFF* tif, u_int& ppm, fxStr& emsg, const fxStr& id)
            case FCF_NSS:
            case FCF_TSI:
            case FCF_DCS:
-               if (prevPage && !pageGood) recvResetPage(tif);
-               // look for high speed carrier only if training successful
-               messageReceived = !(
-                      FaxModem::recvBegin(emsg)
-                   && recvDCSFrames(frame)
-                   && recvTraining()
-               );
-               break;
+               {
+                   if (prevPage && !pageGood) recvResetPage(tif);
+                   // look for high speed carrier only if training successful
+                   messageReceived = !(FaxModem::recvBegin(emsg));
+                   bool gotframe = true;
+                   while (gotframe) {
+                       gotframe = false;
+                       if (!messageReceived) messageReceived = !(recvDCSFrames(frame));
+                       if (!messageReceived) messageReceived = !(recvTraining());
+                       if (messageReceived && lastResponse == AT_FRH3 && waitFor(AT_CONNECT,0)) {
+                           gotframe = recvRawFrame(frame);
+                           lastResponse = AT_NOTHING;
+                           messageReceived = false;
+                       }
+                   }
+                   break;
+               }
            case FCF_MPS:                       // MPS
            case FCF_EOM:                       // EOM
            case FCF_EOP:                       // EOP
@@ -810,6 +837,38 @@ Class1Modem::abortPageRecv()
     putModem(&c, 1, 1);
 }
 
+bool
+Class1Modem::raiseRecvCarrier(bool& dolongtrain, fxStr& emsg)
+{
+    if (!atCmd(conf.class1MsgRecvHackCmd, AT_OK)) {
+       emsg = "Failure to receive silence.";
+       return (false);
+    }
+    /*
+     * T.30 Section 5, Note 5 states that we must use long training
+     * on the first high-speed data message following CTR.
+     */
+    fxStr rmCmd;
+    if (dolongtrain) rmCmd = fxStr(curcap->value, rmCmdFmt);
+    else rmCmd = fxStr(curcap[HasShortTraining(curcap)].value, rmCmdFmt);
+    u_short attempts = 0;
+    lastResponse = AT_NOTHING;
+    while ((lastResponse == AT_NOTHING || lastResponse == AT_FCERROR) && attempts++ < 20) {
+       (void) atCmd(rmCmd, AT_NOTHING);
+       lastResponse = atResponse(rbuf, conf.t2Timer);
+    }
+    if (lastResponse == AT_FRH3 && waitFor(AT_CONNECT,0)) {
+       gotRTNC = true;
+       gotEOT = false;
+    }
+    if (lastResponse != AT_CONNECT && !gotRTNC) {
+       emsg = "Failed to properly detect high-speed data carrier.";
+       return (false);
+    }
+    dolongtrain = false;
+    return (true);
+}
+
 /*
  * Receive Phase C data in T.30-A ECM mode.
  */
@@ -842,25 +901,8 @@ Class1Modem::recvPageECMData(TIFF* tif, const Class2Params& params, fxStr& emsg)
            if (flowControl == FLOW_XONXOFF)
                (void) setXONXOFF(FLOW_NONE, FLOW_XONXOFF, ACT_FLUSH);
            if (!useV34) {
-               if (!atCmd(conf.class1MsgRecvHackCmd, AT_OK)) {
-                   emsg = "Failure to receive silence.";
-                   return (false);
-               }
-               /*
-                * T.30 Section 5, Note 5 states that we must use long training
-                * on the first high-speed data message following CTR.
-                */
-               fxStr rmCmd;
-               if (dolongtrain) rmCmd = fxStr(curcap->value, rmCmdFmt);
-               else rmCmd = fxStr(curcap[HasShortTraining(curcap)].value, rmCmdFmt);
-               u_short attempts = 0;
-               ATResponse response = AT_NOTHING;
-               while ((response == AT_NOTHING || response == AT_FCERROR) && attempts++ < 20) {
-                   (void) atCmd(rmCmd, AT_NOTHING);
-                   response = atResponse(rbuf, conf.t2Timer);
-               }
-               if (response != AT_CONNECT) {
-                   emsg = "Failed to properly detect high-speed data carrier.";
+               gotRTNC = false;
+               if (!raiseRecvCarrier(dolongtrain, emsg) && !gotRTNC) {
                    if (conf.saveUnconfirmedPages && pagedataseen) {
                        protoTrace("RECV keeping unconfirmed page");
                        writeECMData(tif, block, (fcount * frameSize), params, (seq |= 2));
@@ -870,10 +912,11 @@ Class1Modem::recvPageECMData(TIFF* tif, const Class2Params& params, fxStr& emsg)
                    if (wasTimeout()) abortReceive();   // return to command mode
                    return (false);
                }
-               dolongtrain = false;
-           } else {
+           }
+           if (useV34 || gotRTNC) {            // V.34 mode or if +FRH:3 in adaptive reception
                if (!gotEOT) {
-                   bool gotprimary = waitForDCEChannel(false);
+                   bool gotprimary;
+                   if (useV34) gotprimary = waitForDCEChannel(false);
                    u_short rtnccnt = 0;
                    while (!sendERR && !gotEOT && (gotRTNC || (ctrlFrameRcvd != fxStr::null)) && rtnccnt++ < 3) {
                        /*
@@ -888,17 +931,31 @@ Class1Modem::recvPageECMData(TIFF* tif, const Class2Params& params, fxStr& emsg)
                        setInputBuffering(false);
                        HDLCFrame rtncframe(conf.class1FrameOverhead);
                        bool gotrtncframe = false;
-                       if (ctrlFrameRcvd != fxStr::null) {
-                           gotrtncframe = true;
-                           for (u_int i = 0; i < ctrlFrameRcvd.length(); i++)
-                               rtncframe.put(frameRev[ctrlFrameRcvd[i] & 0xFF]);
-                           traceHDLCFrame("-->", rtncframe);
-                       } else
-                           gotrtncframe = recvFrame(rtncframe, conf.t2Timer);
+                       if (useV34) {
+                           if (ctrlFrameRcvd != fxStr::null) {
+                               gotrtncframe = true;
+                               for (u_int i = 0; i < ctrlFrameRcvd.length(); i++)
+                                   rtncframe.put(frameRev[ctrlFrameRcvd[i] & 0xFF]);
+                               traceHDLCFrame("-->", rtncframe);
+                           } else
+                               gotrtncframe = recvFrame(rtncframe, conf.t2Timer);
+                       } else {
+                           gotrtncframe = recvRawFrame(rtncframe);
+                       }
                        if (gotrtncframe) {
                            switch (rtncframe.getFCF()) {
                                case FCF_DCS:
                                    // hopefully it didn't change on us!
+                                   if (!useV34 && !atCmd(conf.class1SwitchingCmd, AT_OK)) {
+                                       emsg = "Failure to receive silence.";
+                                       if (conf.saveUnconfirmedPages && pagedataseen) {
+                                           protoTrace("RECV keeping unconfirmed page");
+                                           writeECMData(tif, block, (fcount * frameSize), params, (seq |= 2));
+                                           prevPage = true;
+                                       }
+                                       free(block);
+                                       return (false);
+                                   }
                                    transmitFrame(FCF_CFR|FCF_RCVR);
                                    break;
                                case FCF_PPS:
@@ -913,6 +970,16 @@ Class1Modem::recvPageECMData(TIFF* tif, const Class2Params& params, fxStr& emsg)
                                            case FCF_PRI_EOM:
                                            case FCF_PRI_MPS:
                                            case FCF_PRI_EOP:
+                                               if (!useV34 && !atCmd(conf.class1SwitchingCmd, AT_OK)) {
+                                                   emsg = "Failure to receive silence.";
+                                                   if (conf.saveUnconfirmedPages && pagedataseen) {
+                                                       protoTrace("RECV keeping unconfirmed page");
+                                                       writeECMData(tif, block, (fcount * frameSize), params, (seq |= 2));
+                                                       prevPage = true;
+                                                   }
+                                                   free(block);
+                                                   return (false);
+                                               }
                                                if (pprcnt) {
                                                    (void) transmitFrame(FCF_PPR, fxStr(ppr, 32));
                                                    tracePPR("RECV send", FCF_PPR);
@@ -947,6 +1014,16 @@ Class1Modem::recvPageECMData(TIFF* tif, const Class2Params& params, fxStr& emsg)
                                                    if (signalRcvd) lastblock = true;
                                                    sendERR = true;
                                                } else {
+                                                   if (!useV34 && !atCmd(conf.class1SwitchingCmd, AT_OK)) {
+                                                       emsg = "Failure to receive silence.";
+                                                       if (conf.saveUnconfirmedPages && pagedataseen) {
+                                                           protoTrace("RECV keeping unconfirmed page");
+                                                           writeECMData(tif, block, (fcount * frameSize), params, (seq |= 2));
+                                                           prevPage = true;
+                                                       }
+                                                       free(block);
+                                                       return (false);
+                                                   }
                                                    (void) transmitFrame(FCF_ERR|FCF_RCVR);
                                                    tracePPR("RECV send", FCF_ERR);
                                                }
@@ -954,9 +1031,48 @@ Class1Modem::recvPageECMData(TIFF* tif, const Class2Params& params, fxStr& emsg)
                                        }
                                    }
                                    break;
+                               case FCF_CTC:
+                                   {
+                                       u_int dcs;                      // possible bits 1-16 of DCS in FIF
+                                       tracePPM("RECV recv", rtncframe.getFCF());
+                                       if (useV34) {
+                                           // T.30 F.3.4.5 Note 1 does not permit CTC in V.34-fax
+                                           emsg = "Received invalid CTC signal in V.34-Fax.";
+                                           if (conf.saveUnconfirmedPages && pagedataseen) {
+                                               protoTrace("RECV keeping unconfirmed page");
+                                               writeECMData(tif, block, (fcount * frameSize), params, (seq |= 2));
+                                               prevPage = true;
+                                           }
+                                           free(block);
+                                           return (false);
+                                       }
+                                       /*
+                                        * See the other comments about T.30 A.1.3.  Some senders
+                                        * are habitually wrong in sending CTC at incorrect moments.
+                                        */
+                                       // use 16-bit FIF to alter speed, curcap
+                                       dcs = rtncframe[3] | (rtncframe[4]<<8);
+                                       curcap = findSRCapability(dcs&DCS_SIGRATE, recvCaps);
+                                       // requisite pause before sending response (CTR)
+                                       if (!atCmd(conf.class1SwitchingCmd, AT_OK)) {
+                                           emsg = "Failure to receive silence.";
+                                           if (conf.saveUnconfirmedPages && pagedataseen) {
+                                               protoTrace("RECV keeping unconfirmed page");
+                                               writeECMData(tif, block, (fcount * frameSize), params, (seq |= 2));
+                                               prevPage = true;
+                                           }
+                                           free(block);
+                                           return (false);
+                                       }
+                                       (void) transmitFrame(FCF_CTR|FCF_RCVR);
+                                       tracePPR("RECV send", FCF_CTR);
+                                       dolongtrain = true;
+                                       break;
+                                   }
                                case FCF_DCN:
                                    tracePPM("RECV recv", rtncframe.getFCF());
                                    gotEOT = true;
+                                   emsg = "COMREC received DCN";
                                    continue;
                                    break;
                            }
@@ -964,12 +1080,25 @@ Class1Modem::recvPageECMData(TIFF* tif, const Class2Params& params, fxStr& emsg)
                                setInputBuffering(true);
                                if (flowControl == FLOW_XONXOFF)
                                    (void) setXONXOFF(FLOW_NONE, FLOW_XONXOFF, ACT_FLUSH);
-                               gotprimary = waitForDCEChannel(false);
+                               if (useV34) gotprimary = waitForDCEChannel(false);
+                               else {
+                                   gotRTNC = false;
+                                   if (!raiseRecvCarrier(dolongtrain, emsg) && !gotRTNC) {
+                                       if (conf.saveUnconfirmedPages && pagedataseen) {
+                                           protoTrace("RECV keeping unconfirmed page");
+                                           writeECMData(tif, block, (fcount * frameSize), params, (seq |= 2));
+                                           prevPage = true;
+                                       }
+                                       free(block);
+                                       if (wasTimeout()) abortReceive();       // return to command mode
+                                       return (false);
+                                   }
+                               }
                            }
                        } else
                            gotprimary = false;
                    }
-                   if (!gotprimary && !sendERR) {
+                   if (!gotprimary && !sendERR && useV34) {
                        emsg = "Failed to properly open V.34 primary channel.";
                        protoTrace(emsg);
                        if (conf.saveUnconfirmedPages && pagedataseen) {
@@ -980,8 +1109,9 @@ Class1Modem::recvPageECMData(TIFF* tif, const Class2Params& params, fxStr& emsg)
                        free(block);
                        return (false);
                    }
-               } else {
-                   emsg = "Received premature V.34 termination.";
+               }
+               if (gotEOT) {           // intentionally not an else of the previous if
+                   if (useV34 && emsg == "") emsg = "Received premature V.34 termination.";
                    protoTrace(emsg);
                    if (conf.saveUnconfirmedPages && pagedataseen) {
                        protoTrace("RECV keeping unconfirmed page");
@@ -1450,7 +1580,7 @@ Class1Modem::recvEnd(fxStr&)
                    transmitFrame(FCF_DCN|FCF_RCVR);
                    break;
                }
-           } else if (!wasTimeout() && lastResponse != AT_FCERROR) {
+           } else if (!wasTimeout() && lastResponse != AT_FCERROR && lastResponse != AT_FRH3) {
                /*
                 * Beware of unexpected responses from the modem.  If
                 * we lose carrier, then we can loop here if we accept
index d7e53ebba8a5b2c11f2b62cfce1453ce32c54470..35e38dacea81435de4429a47f10ebcaf2a6100b1 100644 (file)
@@ -107,6 +107,7 @@ static struct {
 { "modemclassquerycmd",                &ModemConfig::classQueryCmd,    "AT+FCLASS=?" },
 { "class0cmd",                 &ModemConfig::class0Cmd,        "AT+FCLASS=0" },
 { "class1cmd",                 &ModemConfig::class1Cmd },
+{ "class1adaptrecvcmd",                &ModemConfig::class1AdaptRecvCmd },
 { "class1enablev34cmd",                &ModemConfig::class1EnableV34Cmd },
 { "class1nflocmd",             &ModemConfig::class1NFLOCmd },
 { "class1sflocmd",             &ModemConfig::class1SFLOCmd },
index dd5e43f4deeebd1106e8e20fd8db3d9ec7b860c0..90142bdb2eff4237961a3e218a26503f57e1aab7 100644 (file)
@@ -136,6 +136,7 @@ public:
     u_int      pageDoneTimeout;        // page send/receive timeout (ms)
                                        // for class 1:
     fxStr      class1Cmd;              // cmd for setting Class 1
+    fxStr      class1AdaptRecvCmd;     // cmd to enable adaptive reception control
     fxStr      class1EnableV34Cmd;     // cmd to enable V.34 support in Class 1.0
     fxStr      class1NFLOCmd;          // cmd to setup no flow control
     fxStr      class1SFLOCmd;          // cmd to setup software flow control
index a4d2c98d4bb8b3fd75956451b45b0fcfc780e31e..9feeed733fc93bfc1b4674dfece4c4d70c1616fa 100644 (file)
@@ -271,6 +271,7 @@ Class0Cmd   string  \s-1AT+FCLASS=0\s+1     Class 0: command to enter class 0
 .sp .5
 Class1Cmd      string  \s-1AT+FCLASS=1\s+1     Class 1: command to enter class 1
 Class1Cmd      string  \s-1AT+FCLASS=1.0\s+1   Class 1.0: command to enter class 1
+Class1AdaptRecvCmd     string  \-      Class 1/1.0: comman for adaptive reception support
 Class1EnableV34Cmd     string  \-      Class 1/1.0: command to enable V.34-fax support
 Class1ECMSupport       boolean \s-1Yes\s+1     Class 1/1.0: enable T.30-A ECM support
 Class1PersistentECM    boolean \s-1Yes\s+1     Class 1/1.0: to continue to correct while in ECM
@@ -2183,6 +2184,12 @@ of Class 1-style modems; they should not be changed lightly:
 .B Class1Cmd
 The command to set the modem into Class 1 operation.
 .TP
+.B Class1AdaptRecvCmd
+The command used to enable adaptive reception support (usually
+``AT+FAR=1'').  This feature is new in T.31, and many modems
+will not support it.  This feature may reduce the number of
+reception failures due to errors cascading from +FCERROR messages.
+.TP
 .B Class1EnableV34Cmd
 The command to enable V.34-fax support with at least the desired
 maximum primary channel rate.