]> git.ipfire.org Git - thirdparty/HylaFAX.git/commitdiff
Bug 77: add Class 1.0 and V.34-Fax/SuperG3 support
authorLee Howard <faxguy@howardsilvan.com>
Mon, 29 Mar 2004 18:57:41 +0000 (18:57 +0000)
committerLee Howard <faxguy@howardsilvan.com>
Mon, 29 Mar 2004 18:57:41 +0000 (18:57 +0000)
31 files changed:
config/Makefile.in
config/lucent
config/lucent-mt-10 [new file with mode: 0644]
distrules
etc/faxaddmodem.sh.in
etc/probemodem.sh.in
faxd/Class1.c++
faxd/Class1.h
faxd/Class10.c++ [new file with mode: 0644]
faxd/Class10.h [new file with mode: 0644]
faxd/Class1Ersatz.c++ [new file with mode: 0644]
faxd/Class1Ersatz.h [new file with mode: 0644]
faxd/Class1Recv.c++
faxd/Class1Send.c++
faxd/Class2.c++
faxd/Class2.h
faxd/ClassModem.c++
faxd/ClassModem.h
faxd/FaxMachineInfo.c++
faxd/FaxMachineInfo.h
faxd/FaxModem.h
faxd/FaxSend.c++
faxd/FaxSendStatus.h
faxd/FaxServer.c++
faxd/Makefile.in
faxd/ModemConfig.c++
faxd/ModemConfig.h
man/hylafax-config.4f
man/hylafax-info.4f
pkg/sproto.stub.in
util/class2.h

index 0f6a1599d7012fe172eec3c0a5989c9e070f2595..a33eb52282c7288a86740189ccbb2a588204696b 100644 (file)
@@ -74,6 +74,7 @@ CONFIGFILES=class1 \
        lucent-mt-2 \
        lucent-mt-20 \
        lucent-mt-21 \
+       lucent-mt-10 \
        moto-288 \
        mt-1432 \
        nuvo-voyager \
index 0c00bec8fdbb258e35aa613c94f717d10d4c8712..c19e233fafe84ff4ab377caa4bcf2cbb7990927f 100644 (file)
@@ -47,6 +47,7 @@
 # END-SERVER
 
 ModemType:             Class1          # use this to supply a hint
+ModemFlowControl:      rtscts          # many firmwares have broken software flow control
 Class1TCFRecvHack:     yes             # avoid +FCERROR before TCF
 # Class1MsgRecvHackCmd:        AT+FRS=1        # avoid +FCERROR before image
 Class1TMConnectDelay:  400             # counteract quick CONNECT response
diff --git a/config/lucent-mt-10 b/config/lucent-mt-10
new file mode 100644 (file)
index 0000000..0a61790
--- /dev/null
@@ -0,0 +1,35 @@
+# $Id$
+
+#
+# prototype config for MultiTech 5634-series modems using
+# the Lucent/Agere chipset supporting V.34-Fax.
+#
+
+# CONFIG:CLASS1.0:LT V.90 1.0 MT5634ZPX-PCI*:.*:.*: Manufacturer=MultiTech Model=MT5634ZPX-PCI
+# CONFIG:CLASS1.0:LT V.92 1.0 MT5634ZPX-PCI*:.*:.*: Manufacturer=MultiTech Model=MT5634ZPX-PCI-V92
+# CONFIG:CLASS1.0:LT V.90 1.0 MT5634ZBA*:.*:.*: Manufacturer=MultiTech Model=MT5634ZBA
+# CONFIG:CLASS1.0:LT V.92 1.0 MT5634ZBA*:.*:.*: Manufacturer=MultiTech Model=MT5634ZBA
+# CONFIG:CLASS1.0:LT V.90 1.0 ISI5634PCI*:.*:.*: Manufacturer=MultiTech Model=ISI5634PCI
+# CONFIG:CLASS1.0:LT V.92 1.0 ISI5634PCI*:.*:.*: Manufacturer=MultiTech Model=ISI5634PCI
+#
+# BEGIN-SERVER
+# END-SERVER
+
+ModemType:             Class1.0        # use this to supply a hint
+ModemRate:             57600           # must be at least as fast as the DCE-DCE communication
+ModemFlowControl:      rtscts          # many firmwares have broken software flow control
+Class1EnableV34Cmd:    AT+F34=14,1,2   # 33600-2400 primary, 2400-1200 control
+Class1TCFRecvHack:     yes             # avoid +FCERROR before TCF
+Class1TMConnectDelay:  400             # counteract quick CONNECT response
+
+# If your line supports Caller-ID, you may want to uncomment this...
+# QualifyCID:          etc/cid         # you must create this file
+# ModemResetCmds:      AT+VCID=1
+# CIDNumber:           "NMBR="
+# CIDName:             "NAME="
+
+# Or with the MT5634ZBA-DID you'll probably need something like...
+# QualifyCID:          etc/cid         # you must create this file
+# ModemResetCmds:      AT*DS1*DD0*DF1  # enable DTMF-DID
+# CIDNumber:           "DTMF"          # format of AT*DF1
+# CIDNumberAnswerLength:       4       # four DTMF-DID digits then ATA
index 54e0f94a7c610046fd6a5e646b8ae19fc429c931..701a86e38b2a951c50816517813843287596e1ae 100644 (file)
--- a/distrules
+++ b/distrules
@@ -216,6 +216,7 @@ HYLAFAXSRC=\
        config/lucent-mt-2                      \
        config/lucent-mt-20                     \
        config/lucent-mt-21                     \
+       config/lucent-mt-10                     \
        config/moto-288                         \
        config/nuvo-voyager                     \
        config/ppi-pm14400fxmt                  \
index bca454ee3b006019b7822d5097f364e1193d7951..3e67e51a07c6ff9173868776788ca703d5358eb5 100644 (file)
@@ -1053,7 +1053,7 @@ configureClass1Modem()
        MATCHSTR=CLASS1
        PROTOSTR=class1
     else echo "Hmm, this looks like a Class 1.0 modem."
-       MATCHSTR=CLASS10
+       MATCHSTR=CLASS1.0
        PROTOSTR=class1.0
     fi
 
@@ -1122,7 +1122,6 @@ Class 2 relies on the modem to perform the bulk of the fax protocol.
 Class 2.0 is similar to Class 2 but may include more features.
 Class 1.0 is similar to Class 1 but adds V.34-fax capability.
 Class 2.1 is similar to Class 2.0 but adds V.34-fax capability.
-    (V.34-fax is not yet supported by HylaFAX on Class 1.0 modems.)
       
 HylaFAX generally will have more features when using Class 1/1.0 than
 when using most modems' Class 2 or Class 2.0 implementations.  Generally
index 374363846f8d916d290789476af7c4ce1bffc8f7..ac274182fec49e6b627226e0399ab0cedf927945 100644 (file)
@@ -434,6 +434,26 @@ TryClass1Commands()
     Try "AT+FRS=?"
 }
 
+TryClass1dot0Commands()
+{
+    Try "AT+FCLASS=?"; Try "AT+FCLASS?"
+    Try "AT+FCLASS=0"; Try "$ATSTR"
+    Try "AT+FCLASS?"
+    Try "AT+FJUNK=?";  Try "AT+FJUNK?"
+    Try "AT+FAA=?";    Try "AT+FAA?"
+    Try "AT+FAE=?";    Try "AT+FAE?"
+    Try "AT+FTH=?"
+    Try "AT+FRH=?"
+    Try "AT+FTM=?"
+    Try "AT+FRM=?"
+    Try "AT+FTS=?"
+    Try "AT+FRS=?"
+    Try "AT+FAR=?"
+    Try "AT+FCL=?"
+    Try "AT+FIT=?"
+    Try "AT+F34=?"
+}
+
 TryCommonCommands()
 {
     for i in 0 1 2 3 4 5 6 7 8 9; do
@@ -472,7 +492,7 @@ class1dot0()
 {
     echo ""; echo "Class 1.0 stuff..."; echo ""
     ATSTR="AT+FCLASS=1.0"
-    TryClass1Commands
+    TryClass1dot0Commands
 }
 
 class2dot1()
index b5a1e16c8570f70a450882acdcbe8e91a56f243a..e4518e113d2a767c6fc54de456ba8286ed0dbf70 100644 (file)
@@ -125,7 +125,7 @@ Class1Modem::setupModem()
        traceBits(modemServices & SERVICE_ALL, serviceNames);
     if ((modemServices & SERVICE_CLASS1) == 0)
        return (false);
-    atCmd(conf.class1Cmd);
+    atCmd(classCmd);
 
     /*
      * Query manufacturer, model, and firmware revision.
@@ -157,6 +157,21 @@ Class1Modem::setupModem()
     for (i = 1; i < NCAPS; i++)
        if (xmitCaps[i].ok)
            modemParams.br |= BIT(xmitCaps[i].br);
+    nonV34br = modemParams.br;
+    if (conf.class1EnableV34Cmd != "" && conf.class1ECMSupport) {
+       // This is cosmetic, mostly, to state the modem supports V.34.
+       // We could query the modem but that would require another
+       // config option, so we just trust the enable command.
+       u_short pos = 0;
+       primaryV34Rate = 0;
+       const char* buf = conf.class1EnableV34Cmd;
+       while (buf[0] != '=') buf++;            // move to assignment
+       while (!isdigit(buf[0])) buf++;         // move to digits
+       do {
+           primaryV34Rate = primaryV34Rate*10 + (buf[0] - '0');
+       } while (isdigit((++buf)[0]));
+       modemParams.br |= BIT(primaryV34Rate) - 1;
+    }
     if (conf.class1ExtendedRes) {
        modemParams.vr = VR_ALL;
     } else {
@@ -220,6 +235,11 @@ Class1Modem::setupModem()
     frameRev = TIFFGetBitRevTable(conf.frameFillOrder == FILLORDER_LSB2MSB);
 
     setupClass1Parameters();
+    if (conf.class1EnableV34Cmd != "" && conf.class1ECMSupport) {
+       atCmd(conf.class1EnableV34Cmd);
+       gotEOT = false;
+    }
+    useV34 = false;    // only when V.8 handshaking is used
     return (true);
 }
 
@@ -230,7 +250,7 @@ bool
 Class1Modem::setupClass1Parameters()
 {
     if (modemServices & SERVICE_CLASS1) {
-       atCmd(conf.class1Cmd);
+       atCmd(classCmd);
        setupFlowControl(flowControl);
        atCmd(conf.setupAACmd);
     }
@@ -266,9 +286,14 @@ Class1Modem::setupFlowControl(FlowControl fc)
  * for sending/received facsimile.
  */
 bool
-Class1Modem::faxService()
+Class1Modem::faxService(bool enableV34)
 {
-    return (atCmd(conf.class1Cmd) && setupFlowControl(flowControl));
+    if (!atCmd(classCmd)) return (false);
+    if (conf.class1EnableV34Cmd != "" && enableV34)
+       atCmd(conf.class1EnableV34Cmd);
+    useV34 = false;    // only when V.8 handshaking is used
+    gotEOT = false;
+    return (setupFlowControl(flowControl));
 }
 
 /*
@@ -413,6 +438,7 @@ Class1Modem::sendClass1Data(const u_char* data, u_int cc,
 void
 Class1Modem::abortReceive()
 {
+    if (useV34) return;                        // nothing to do in V.34
     bool b = wasTimeout();
     char c = CAN;                      // anything other than DC1/DC3
     putModem(&c, 1, 1);
@@ -431,6 +457,145 @@ Class1Modem::abortReceive()
     setTimeout(b);                     // XXX putModem clobbers timeout state
 }
 
+/*
+ * Request a primary rate renegotiation.
+ */
+bool
+Class1Modem::renegotiatePrimary(bool constrain)
+{
+    u_char buf[4];
+    u_short size = 0;
+    buf[size++] = DLE;
+    if (constrain) {
+       // don't neotiate a faster rate
+       if (primaryV34Rate == 1) buf[size++] = 0x70;    // 2400 bit/s
+       else buf[size++] = primaryV34Rate + 0x6E;       // drop 2400 bit/s
+       buf[size++] = DLE;
+    }
+    buf[size++] = 0x6C;                                        // <DLE><pph>
+    if (!putModemData(buf, size)) return (false);
+    if (constrain)
+       protoTrace("Request primary rate renegotiation (limit %u bit/s).", (primaryV34Rate-1)*2400);
+    else
+       protoTrace("Request primary rate renegotiation.");
+    return (true);
+}
+
+/*
+ * Wait for a <DLE><ctrl> response per T.31-A1 B.8.4.
+ */
+bool
+Class1Modem::waitForDCEChannel(bool awaitctrl)
+{
+    time_t start = Sys::now();
+    int c;
+    fxStr garbage;
+    bool gotresponse = false;
+    gotRTNC = false;
+    do {
+       c = getModemChar(60000);
+       if (c == DLE) {
+           /*
+            * With V.34-faxing we expect <DLE><command>
+            * Refer to T.31-A1 Table B.1.  Except for EOT
+            * these are merely indicators and do not require
+            * action.
+            */
+           c = getModemChar(60000);
+           switch (c) {
+               case EOT:
+                   protoTrace("EOT received (end of transmission)");
+                   gotEOT = true;
+                   return (false);
+                   break;
+               case 0x69:
+                   protoTrace("Control channel retrain");
+                   // wait for the control channel to reappear
+                   // should we reset the timeout setting?
+                   waitForDCEChannel(true);
+                   gotRTNC = true;
+                   return (false);
+                   break;
+               case 0x6B:
+                   protoTrace("Primary channel selected");
+                   gotCTRL = false;
+                   continue;
+                   break;
+               case 0x6D:
+                   protoTrace("Control channel selected");
+                   gotCTRL = true;
+                   continue;
+                   break;
+               case 0x6E:                      // 1200 bit/s
+               case 0x6F:                      // 2400 bit/s
+                   // control rate indication
+                   if (controlV34Rate != (c - 0x6D)) {
+                       controlV34Rate = (c - 0x6D);
+                       protoTrace("Control channel rate now %u bit/s", controlV34Rate*1200);
+                   }
+                   if (awaitctrl) gotresponse = true;
+                   continue;
+                   break;
+               case 0x70:                      //  2400 bit/s
+               case 0x71:                      //  4800 bit/s
+               case 0x72:                      //  7200 bit/s
+               case 0x73:                      //  9600 bit/s
+               case 0x74:                      // 12000 bit/s
+               case 0x75:                      // 14400 bit/s
+               case 0x76:                      // 16800 bit/s
+               case 0x77:                      // 19200 bit/s
+               case 0x78:                      // 21600 bit/s
+               case 0x79:                      // 24000 bit/s
+               case 0x7A:                      // 26400 bit/s
+               case 0x7B:                      // 28800 bit/s
+               case 0x7C:                      // 31200 bit/s
+               case 0x7D:                      // 33600 bit/s
+                   // primary rate indication
+                   if (primaryV34Rate != (c - 0x6F)) {
+                       primaryV34Rate = (c - 0x6F);
+                       protoTrace("Primary channel rate now %u bit/s", primaryV34Rate*2400);
+                   }
+                   if (!awaitctrl) gotresponse = true;
+                   continue;
+                   break;
+               default:
+                   // unexpected <DLE><command>, deem as garbage
+                   garbage.append(DLE);
+                   garbage.append(c);
+                   break;
+           }
+       } else garbage.append(c);
+       fxStr rcpsignal;
+       rcpsignal.append(0xFF); rcpsignal.append(0x03); rcpsignal.append(0x86); rcpsignal.append(0x69);
+       rcpsignal.append(0xCB); rcpsignal.append(0x10); rcpsignal.append(0x03);
+       if (!gotCTRL && garbage == rcpsignal) {
+           // We anticipate getting "extra" RCP frames since we
+           // only look for one but usually we will see three.
+           garbage.cut(0, 7);
+       }
+    } while (!gotresponse && Sys::now()-start < 60);
+    if (getHDLCTracing() && garbage.length()) {
+       fxStr buf;
+       u_int j = 0;
+       for (u_int i = 0; i < garbage.length(); i++) {
+           if (j > 0)
+               buf.append(' ');
+           buf.append(fxStr(garbage[i] & 0xFF, "%2.2X"));
+           j++;
+           if (j > 19) {
+               protoTrace("--> [%u:%.*s]",
+                   j, buf.length(), (const char*) buf);
+               buf = "";
+               j = 0;
+           }
+       }
+       if (j)
+           protoTrace("--> [%u:%.*s]",
+               j, buf.length(), (const char*) buf);
+    }
+    return (gotresponse);
+}
+
 /*
  * Receive an HDLC frame. The frame itself must
  * be received within 3 seconds (per the spec).
@@ -461,9 +626,28 @@ Class1Modem::recvRawFrame(HDLCFrame& frame)
        c = getModemChar(0);
        if (c == 0xff || c == EOF)
            break;
-
+       if (useV34 && c == DLE) {
+           c = getModemChar(0);
+           switch (c) {
+               case EOT:
+                   protoTrace("EOT received (end of transmission)");
+                   gotEOT = true;
+                   return (false);
+                   break;
+               case 0x69:
+                   protoTrace("Control channel retrain");
+                   // wait for the control channel to reappear
+                   // should we reset the timeout setting?
+                   waitForDCEChannel(true);
+                   return (false);
+                   break;
+               default:
+                   // unexpected <DLE><command>, deem as garbage
+                   garbage.append(DLE);
+                   break;
+           }
+       }
        garbage.append(c);
-
        if ( garbage.length() >= 2 && garbage.tail(2) == "\r\n") {
            /*
             * CR+LF received before address field.
@@ -493,7 +677,7 @@ Class1Modem::recvRawFrame(HDLCFrame& frame)
        if (j)
            protoTrace("--> [%u:%.*s]",
                j, buf.length(), (const char*) buf);
-       }
+    }
 
     if (c == 0xff) {                   // address field received
        do {
@@ -501,6 +685,33 @@ Class1Modem::recvRawFrame(HDLCFrame& frame)
                c = getModemChar(0);
                if (c == ETX || c == EOF)
                    break;
+               if (useV34) {
+                   /*
+                    * T.31-A1 Table B.1
+                    * These indicate transparancy, shielding, or delimiting.
+                    */
+                   if (c == 0x07) {    // end of HDLC frame w/FCS error
+                       break;
+                   }
+                   switch (c) {
+                       case EOT:
+                           protoTrace("EOT received (end of transmission)");
+                           gotEOT = true;
+                           return (false);
+                           break;
+                       case DLE:       // <DLE><DLE> => <DLE>
+                           break;
+                       case SUB:       // <DLE><SUB> => <DLE><DLE>
+                           frame.put(frameRev[DLE]);
+                           break;
+                       case 0x51:      // <DLE><0x51> => <DC1>
+                           c = DC1;
+                           break;
+                       case 0x53:      // <DLE><0x53> => <DC3>
+                           c = 0x13;
+                           break;
+                   }
+               }
            }
            frame.put(frameRev[c]);
        } while ((c = getModemChar(0)) != EOF);
@@ -517,11 +728,15 @@ Class1Modem::recvRawFrame(HDLCFrame& frame)
      * response telling whether or not the FCS was
      * legitimate.
      */
-    if (!waitFor(AT_OK)) {
+    if (!useV34 && !waitFor(AT_OK)) {
        if (lastResponse == AT_ERROR)
            protoTrace("FCS error");
        return (false);
     }
+    if (useV34 && c == 0x07) {
+       protoTrace("FCS error");
+       return (false);
+    }
     if (frame.getFrameDataLength() < 1) {
        protoTrace("HDLC frame too short (%u bytes)", frame.getLength());
        return (false);
@@ -580,10 +795,74 @@ Class1Modem::syncECMFrame()
  * of "stuffed" zero-bits after five sequential one-bits between
  * flag sequences.  We assume this is called only after a
  * successfuly received complete flag sequence.
+ *
+ * Things are significantly more simple with V.34-fax ECM since
+ * there is no synchronization or RCP frames.  In this case we
+ * simply have to handle byte transparancy and shielding, looking
+ * for byte-aligned frame delimiters.
  */
 bool
 Class1Modem::recvECMFrame(HDLCFrame& frame)
 {
+    if (useV34) {
+       int c;
+       for (;;) {
+           c = getModemChar(60000);
+           if (wasTimeout()) {
+               return (false);
+           }
+           if (c == DLE) {
+               c = getModemChar(60000);
+               if (wasTimeout()) {
+                   return (false);
+               }
+               switch (c) {
+                   case DLE:
+                       break;
+                   case SUB:
+                       frame.put(frameRev[DLE]);
+                       break;
+                   case 0x51:
+                       c = 0x11;
+                       break;
+                   case 0x53:
+                       c = 0x13;
+                       break;
+                   case ETX:
+                       if (frame.getLength() > 0)
+                           traceHDLCFrame("-->", frame);
+                       if (frame.getLength() < 5) {            // RCP frame size
+                           protoTrace("HDLC frame too short (%u bytes)", frame.getLength());
+                           return (false);
+                       }
+                       if (frame[0] != 0xff) {
+                           protoTrace("HDLC frame with bad address field %#x", frame[0]);
+                           return (false);
+                       }
+                       if ((frame[1]&0xf7) != 0xc0) {
+                           protoTrace("HDLC frame with bad control field %#x", frame[1]);
+                           return (false);
+                       }
+                       return (true);
+                   case 0x07:
+                       protoTrace("FCS error");
+                       return (false);
+                   case 0x04:
+                       protoTrace("EOT received (end of transmission)");
+                       gotEOT = true;
+                       return (false);
+                   case 0x6D:
+                       protoTrace("Control channel selected");
+                       gotCTRL = true;
+                       return (false);
+                   default:
+                       protoTrace("got <DLE><%X>", c);
+                       break;
+               }
+           }
+           frame.put(frameRev[c]);
+       }
+    }
 
     int bit = getModemBit(0);
     u_short ones = 0;
@@ -749,7 +1028,7 @@ Class1Modem::sendRawFrame(HDLCFrame& frame)
     static u_char buf[2] = { DLE, ETX };
     return (putModemDLEData(frame, frame.getLength(), frameRev, 60*1000) &&
        putModem(buf, 2, 60*1000) &&
-       waitFor(frame.moreFrames() ? AT_CONNECT : AT_OK, 0));
+       (useV34 ? true : waitFor(frame.moreFrames() ? AT_CONNECT : AT_OK, 0)));
 }
 
 /*
@@ -826,8 +1105,8 @@ Class1Modem::transmitFrame(u_char fcf, bool lastFrame)
 {
     startTimeout(2550);                        // 3.0 - 15% = 2.55 secs
     bool frameSent =
-       atCmd(thCmd, AT_NOTHING) &&
-       atResponse(rbuf, 0) == AT_CONNECT &&
+       (useV34 ? true : atCmd(thCmd, AT_NOTHING)) &&
+       (useV34 ? true : atResponse(rbuf, 0) == AT_CONNECT) &&
        sendFrame(fcf, lastFrame);
     stopTimeout("sending HDLC frame");
     return (frameSent);
@@ -843,8 +1122,8 @@ Class1Modem::transmitFrame(u_char fcf, u_int dcs, u_int xinfo, bool lastFrame)
      */
     startTimeout(2550);                        // 3.0 - 15% = 2.55 secs
     bool frameSent =
-       atCmd(thCmd, AT_NOTHING) &&
-       atResponse(rbuf, 0) == AT_CONNECT &&
+       (useV34 ? true : atCmd(thCmd, AT_NOTHING)) &&
+       (useV34 ? true : atResponse(rbuf, 0) == AT_CONNECT) &&
        sendFrame(fcf, dcs, xinfo, lastFrame);
     stopTimeout("sending HDLC frame");
     return (frameSent);
@@ -855,8 +1134,8 @@ Class1Modem::transmitFrame(u_char fcf, const fxStr& tsi, bool lastFrame)
 {
     startTimeout(3000);                        // give more time than others
     bool frameSent =
-       atCmd(thCmd, AT_NOTHING) &&
-       atResponse(rbuf, 0) == AT_CONNECT &&
+       (useV34 ? true : atCmd(thCmd, AT_NOTHING)) &&
+       (useV34 ? true : atResponse(rbuf, 0) == AT_CONNECT) &&
        sendFrame(fcf, tsi, lastFrame);
     stopTimeout("sending HDLC frame");
     return (frameSent);
@@ -867,8 +1146,8 @@ Class1Modem::transmitFrame(u_char fcf, const u_char* code, const fxStr& nsf, boo
 {
     startTimeout(3000);                        // give more time than others
     bool frameSent =
-       atCmd(thCmd, AT_NOTHING) &&
-       atResponse(rbuf, 0) == AT_CONNECT &&
+       (useV34 ? true : atCmd(thCmd, AT_NOTHING)) &&
+       (useV34 ? true : atResponse(rbuf, 0) == AT_CONNECT) &&
        sendFrame(fcf, code, nsf, lastFrame);
     stopTimeout("sending HDLC frame");
     return (frameSent);
@@ -914,6 +1193,9 @@ Class1Modem::recvFrame(HDLCFrame& frame, long ms)
 {
     frame.reset();
     startTimeout(ms);
+    if (useV34) {
+       return recvRawFrame(frame);
+    }
     bool readPending = atCmd(rhCmd, AT_NOTHING);
     if (readPending && waitFor(AT_CONNECT,0)){
         stopTimeout("waiting for HDLC flags");
@@ -1012,6 +1294,30 @@ 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, "+F34:", 5)) {
+       /*
+        * V.8 handshaking was successful.  The rest of the
+        * session is governed by T.31 Amendment 1 Annex B.
+        * (This should only happen after ATA or ATD.)
+        *
+        * The +F34: response is interpreted according to T.31-A1 B.6.2.
+        */
+       buf += 5;                                       // skip "+F34:" prefix
+       primaryV34Rate = 0;
+       while (!isdigit(buf[0])) buf++;         // move to digits
+       do {
+           primaryV34Rate = primaryV34Rate*10 + (buf[0] - '0');
+        } while (isdigit((++buf)[0]));
+       controlV34Rate = 0;
+       while (!isdigit(buf[0])) buf++;         // move to digits
+       do {
+           controlV34Rate = controlV34Rate*10 + (buf[0] - '0');
+        } while (isdigit((++buf)[0]));
+       useV34 = true;
+       protoTrace("V.8 handshaking succeeded, V.34-Fax (SuperG3) capability enabled.");
+       protoTrace("Primary channel rate: %u bit/s, Control channel rate: %u bit/s.", primaryV34Rate*2400, controlV34Rate*1200);
+       modemParams.br |= BIT(primaryV34Rate) - 1;
+    }
     return (lastResponse);
 }
 
@@ -1110,7 +1416,8 @@ Class1Modem::modemDIS() const
 {
     // NB: DIS is in 24-bit format
     u_int fs = conf.class1ECMFrameSize == 64 ? DIS_FRAMESIZE : 0;
-    return (FaxModem::modemDIS() &~ DIS_SIGRATE) | (discap<<10) | DIS_XTNDFIELD | fs;
+    u_int v8 = conf.class1ECMSupport && conf.class1EnableV34Cmd != "" ? DIS_V8 : 0;
+    return (FaxModem::modemDIS() &~ DIS_SIGRATE) | (discap<<10) | DIS_XTNDFIELD | fs | v8;
 }
 
 /*
index 68b435b4000d5fa2e12630788d2d0e2813bf32bd..9b5fa89fb9d6b61f8570e388576fc961856c44b9 100644 (file)
@@ -49,11 +49,15 @@ class Class1Modem : public FaxModem {
 protected:
     fxStr      thCmd;                  // command for transmitting a frame
     fxStr      rhCmd;                  // command for receiving a frame
+    fxStr      classCmd;               // set class command
+    u_int      serviceType;            // modem service required
     u_int      dis;                    // current remote DIS
     u_int      xinfo;                  // current remote DIS extensions
     u_int      frameSize;              // size of image frames
     u_int      signalRcvd;             // last signal received in ECM protocol
+    u_int      nonV34br;               // modemParams.br without V.34
     bool       sentERR;                // whether or not ERR was sent
+    bool       hadV34Trouble;          // indicates failure due to V.34 restrictions
     const u_char* frameRev;            // HDLC frame bit reversal table
     fxStr      lid;                    // encoded local id string
     fxStr      pwd;                    // transmit password
@@ -96,6 +100,14 @@ protected:
     };
     static const char* modulationNames[6];
 
+// V.34 indicators
+    bool       useV34;         // whether or not V.8 handhaking was used
+    bool       gotEOT;         // V.34-fax heard EOT signal
+    bool       gotCTRL;        // current channel indicator
+    bool       gotRTNC;        // retrain control channel
+    u_short    primaryV34Rate; // rate indication for primary channel
+    u_short    controlV34Rate; // rate indication for control channel
+
 // modem setup stuff
     virtual bool setupModem();
     virtual bool setupClass1Parameters();
@@ -160,6 +172,8 @@ protected:
     bool       recvTCF(int br, HDLCFrame&, const u_char* bitrev, long ms);
     bool       recvRawFrame(HDLCFrame& frame);
     bool       recvECMFrame(HDLCFrame& frame);
+    bool        waitForDCEChannel(bool awaitctrl);
+    bool        renegotiatePrimary(bool constrain);
     bool       syncECMFrame();
     bool       recvPageECMData(TIFF* tif, const Class2Params& params, fxStr& emsg);
     void       blockData(u_int byte, bool flag);
@@ -202,7 +216,7 @@ public:
                    fxStr& emsg);
 
 // miscellaneous
-    bool       faxService();                   // switch to fax mode
+    bool       faxService(bool enableV34);     // switch to fax mode
     bool       reset(long ms);                 // reset modem
     void       setLID(const fxStr& number);    // set local id string
     bool       supportsPolling() const;        // modem capability
diff --git a/faxd/Class10.c++ b/faxd/Class10.c++
new file mode 100644 (file)
index 0000000..b42bd2e
--- /dev/null
@@ -0,0 +1,40 @@
+/*     $Id$ */
+/*
+ * Copyright (c) 2004 Lee Howard
+ * Copyright (c) 1994-1996 Sam Leffler
+ * Copyright (c) 1994-1996 Silicon Graphics, Inc.
+ * HylaFAX is a trademark of Silicon Graphics
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+#include "Class10.h"
+#include "ModemConfig.h"
+
+Class10Modem::Class10Modem(FaxServer& s, const ModemConfig& c) : Class1Modem(s,c)
+{
+    serviceType = SERVICE_CLASS10;
+    setupDefault(classCmd,     conf.class1Cmd,         "AT+FCLASS=1.0");
+
+}
+
+Class10Modem::~Class10Modem()
+{
+}
+
diff --git a/faxd/Class10.h b/faxd/Class10.h
new file mode 100644 (file)
index 0000000..6d9fed5
--- /dev/null
@@ -0,0 +1,39 @@
+/*     $Id$ */
+/*
+ * Copyright (c) 2004 Lee Howard
+ * Copyright (c) 1990-1996 Sam Leffler
+ * Copyright (c) 1991-1996 Silicon Graphics, Inc.
+ * HylaFAX is a trademark of Silicon Graphics
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+#ifndef _CLASS10_
+#define        _CLASS10_
+/*
+ * Class 1.0-style Modem Driver.
+ */
+#include "Class1.h"
+
+class Class10Modem : public Class1Modem {
+public:
+    Class10Modem(FaxServer&, const ModemConfig&);
+    virtual ~Class10Modem();
+};
+#endif /* _CLASS10_ */
diff --git a/faxd/Class1Ersatz.c++ b/faxd/Class1Ersatz.c++
new file mode 100644 (file)
index 0000000..f90e7ee
--- /dev/null
@@ -0,0 +1,40 @@
+/*     $Id$ */
+/*
+ * Copyright (c) 2004 Lee Howard
+ * Copyright (c) 1994-1996 Sam Leffler
+ * Copyright (c) 1994-1996 Silicon Graphics, Inc.
+ * HylaFAX is a trademark of Silicon Graphics
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+#include "Class1Ersatz.h"
+#include "ModemConfig.h"
+
+Class1ErsatzModem::Class1ErsatzModem(FaxServer& s, const ModemConfig& c) : Class1Modem(s,c)
+{
+    serviceType = SERVICE_CLASS1;
+    setupDefault(classCmd,     conf.class1Cmd,         "AT+FCLASS=1");
+
+}
+
+Class1ErsatzModem::~Class1ErsatzModem()
+{
+}
+
diff --git a/faxd/Class1Ersatz.h b/faxd/Class1Ersatz.h
new file mode 100644 (file)
index 0000000..30fa8f9
--- /dev/null
@@ -0,0 +1,39 @@
+/*     $Id$ */
+/*
+ * Copyright (c) 2004 Lee Howard
+ * Copyright (c) 1990-1996 Sam Leffler
+ * Copyright (c) 1991-1996 Silicon Graphics, Inc.
+ * HylaFAX is a trademark of Silicon Graphics
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+#ifndef _CLASS1ERSATZ_
+#define        _CLASS1ERSATZ_
+/*
+ * Class 1-style Modem Driver (non-V34-enabled).
+ */
+#include "Class1.h"
+
+class Class1ErsatzModem : public Class1Modem {
+public:
+    Class1ErsatzModem(FaxServer&, const ModemConfig&);
+    virtual ~Class1ErsatzModem();
+};
+#endif /* _CLASS1ERSATZ_ */
index 61c511fc54adeb2a8a88608909e312626eb9aca8..fe34c8577afde73a8fc93a3153740f10268c289a 100644 (file)
 CallType
 Class1Modem::answerCall(AnswerType type, fxStr& emsg, const char* number)
 {
+    // Reset modemParams.br to non-V.34 settings.  If V.8 handshaking
+    // succeeds, then it will be changed again.
+    modemParams.br = nonV34br;
+
     if (flowControl == FLOW_XONXOFF)
        setXONXOFF(FLOW_NONE, FLOW_NONE, ACT_FLUSH);
     return ClassModem::answerCall(type, emsg, number);
@@ -91,6 +95,8 @@ Class1Modem::recvBegin(fxStr& emsg)
     fxStr nsf;
     encodeNSF(nsf, HYLAFAX_VERSION);
 
+    if (useV34) waitForDCEChannel(true);       // expect control channel
+
     return FaxModem::recvBegin(emsg) && recvIdentification(
        0, fxStr::null,
        0, fxStr::null,
@@ -109,9 +115,11 @@ Class1Modem::recvEOMBegin(fxStr& emsg)
     /*
      * We must raise the transmission carrier to mimic the state following ATA.
      */
-    pause(conf.t2Timer);       // T.30 Fig 5.2B requires T2 to elapse
-    if (!(atCmd(thCmd, AT_NOTHING) && atResponse(rbuf, 0) == AT_CONNECT))
-       return (false);
+    if (!useV34) {
+       pause(conf.t2Timer);    // T.30 Fig 5.2B requires T2 to elapse
+       if (!(atCmd(thCmd, AT_NOTHING) && atResponse(rbuf, 0) == AT_CONNECT))
+           return (false);
+    }
     return Class1Modem::recvBegin(emsg);
 }
 
@@ -277,6 +285,10 @@ Class1Modem::recvDCSFrames(HDLCFrame& frame)
 bool
 Class1Modem::recvTraining()
 {
+    if (useV34) {
+       sendCFR = true;
+       return (true);
+    }
     /*
      * It is possible (and with some modems likely) that the sending
      * system has not yet dropped its V.21 carrier.  So we follow the
@@ -355,7 +367,15 @@ Class1Modem::recvTraining()
            protoTrace("RECV: reject TCF (zero run too short, min %u)", minrun);
            ok = false;
        }
-       (void) waitFor(AT_NOCARRIER);   // wait for message carrier to drop
+       /*
+        * We expect the message carrier to drop.  However, some senders will
+        * transmit garbage after we see <DLE><ETX> but before we see NO CARRIER.
+        */
+       time_t nocarrierstart = Sys::now();
+       bool gotnocarrier = false;
+       do {
+           gotnocarrier = waitFor(AT_NOCARRIER, 2*1000);
+       } while (!gotnocarrier && Sys::now() < (nocarrierstart + 5));
     }
     /*
      * Send training response; we follow the spec
@@ -388,8 +408,9 @@ Class1Modem::processDCSFrame(const HDLCFrame& frame)
     if (xinfo & DCSFRAME_64) frameSize = 64;
     else frameSize = 256;
     params.setFromDCS(dcs, xinfo);
+    if (useV34) params.br = primaryV34Rate-1;
+    else curcap = findSRCapability(dcs&DCS_SIGRATE, recvCaps);
     setDataTimeout(60, params.br);
-    curcap = findSRCapability(dcs&DCS_SIGRATE, recvCaps);
     recvDCS(params);                           // announce session params
 }
 
@@ -494,7 +515,10 @@ Class1Modem::recvPage(TIFF* tif, u_int& ppm, fxStr& emsg, const fxStr& id)
                         * The data was received correctly, wait
                         * for the modem to signal carrier drop.
                         */
-                       messageReceived = waitFor(AT_NOCARRIER, 2*1000);
+                       time_t nocarrierstart = Sys::now();
+                       do {
+                           messageReceived = waitFor(AT_NOCARRIER, 2*1000);
+                       } while (!messageReceived && Sys::now() < (nocarrierstart + 5));
                        if (messageReceived)
                            prevPage = true;
                        timer = conf.t1Timer;           // wait longer for PPM
@@ -627,7 +651,7 @@ Class1Modem::recvPage(TIFF* tif, u_int& ppm, fxStr& emsg, const fxStr& id)
                             * 5 seconds elapse.
                             */
                            // sendingHDLC =
-                           atCmd(thCmd, AT_CONNECT);
+                           if (!useV34) atCmd(thCmd, AT_CONNECT);
                        } else {
                            (void) transmitFrame(FCF_MCF|FCF_RCVR);
                        }
@@ -704,6 +728,7 @@ Class1Modem::recvPage(TIFF* tif, u_int& ppm, fxStr& emsg, const fxStr& id)
 void
 Class1Modem::abortPageRecv()
 {
+    if (useV34) return;                                // nothing to do in V.34
     char c = CAN;                              // anything other than DC1/DC3
     putModem(&c, 1, 1);
 }
@@ -739,29 +764,123 @@ Class1Modem::recvPageECMData(TIFF* tif, const Class2Params& params, fxStr& emsg)
            setInputBuffering(true);
            if (flowControl == FLOW_XONXOFF)
                (void) setXONXOFF(FLOW_NONE, FLOW_XONXOFF, ACT_FLUSH);
-           if (!atCmd(conf.class1MsgRecvHackCmd, AT_OK)) {
-               emsg = "Failure to receive silence.";
-               return (false);
-           }
-           fxStr rmCmd(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.";
-               if (conf.saveUnconfirmedPages && pagedataseen) {
-                   protoTrace("RECV keeping unconfirmed page");
-                   writeECMData(tif, block, (fcount * frameSize), params, (seq |= 2));
-                   prevPage = true;
+           if (!useV34) {
+               if (!atCmd(conf.class1MsgRecvHackCmd, AT_OK)) {
+                   emsg = "Failure to receive silence.";
+                   return (false);
+               }
+               fxStr rmCmd(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.";
+                   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 {
+               if (!gotEOT) {
+                   bool gotprimary = waitForDCEChannel(false);
+                   u_short rtnccnt = 0;
+                   while (!gotEOT && gotRTNC && rtnccnt++ < 3) {
+                       /*
+                        * Remote requested control channel retrain; the remote
+                        * didn't properly hear our last signal.  So now we have to
+                        * wait for a signal from the remote and then respond appropriately
+                        * to get us back in sync. DCS::CFR - PPS::PPR/MCF - EOR::ERR
+                        */
+                       if (flowControl == FLOW_XONXOFF)
+                           (void) setXONXOFF(FLOW_NONE, FLOW_NONE, ACT_DRAIN);
+                       setInputBuffering(false);
+                       HDLCFrame rtncframe(conf.class1FrameOverhead);
+                       if (recvFrame(rtncframe, conf.t2Timer)) {
+                           switch (rtncframe.getFCF()) {
+                               case FCF_DCS:
+                                   // hopefully it didn't change on us!
+                                   transmitFrame(FCF_CFR|FCF_RCVR);
+                                   break;
+                               case FCF_PPS:
+                                   tracePPM("RECV recv", rtncframe.getFCF());
+                                   if (rtncframe.getLength() > 5) {
+                                       tracePPM("RECV recv", rtncframe.getFCF2());
+                                       switch (rtncframe.getFCF2()) {
+                                           case 0:     // PPS-NULL
+                                           case FCF_EOM:
+                                           case FCF_MPS:
+                                           case FCF_EOP:
+                                           case FCF_PRI_EOM:
+                                           case FCF_PRI_MPS:
+                                           case FCF_PRI_EOP:
+                                               if (pprcnt) {
+                                                   sendFrame(FCF_PPR, fxStr(ppr, 32));
+                                                   tracePPR("RECV send", FCF_PPR);
+                                               } else {
+                                                   (void) transmitFrame(FCF_MCF|FCF_RCVR);
+                                                   tracePPR("RECV send", FCF_MCF);
+                                               }
+                                               break;
+                                       }
+                                   }
+                                   break;
+                               case FCF_EOR:
+                                   tracePPM("RECV recv", rtncframe.getFCF());
+                                   if (rtncframe.getLength() > 5) {
+                                       tracePPM("RECV recv", rtncframe.getFCF2());
+                                       switch (rtncframe.getFCF2()) {
+                                           case 0:     // PPS-NULL
+                                           case FCF_EOM:
+                                           case FCF_MPS:
+                                           case FCF_EOP:
+                                           case FCF_PRI_EOM:
+                                           case FCF_PRI_MPS:
+                                           case FCF_PRI_EOP:
+                                               (void) transmitFrame(FCF_ERR|FCF_RCVR);
+                                               tracePPR("RECV send", FCF_ERR);
+                                               break;
+                                       }
+                                   }
+                                   break;
+                           }
+                           setInputBuffering(true);
+                           if (flowControl == FLOW_XONXOFF)
+                               (void) setXONXOFF(FLOW_NONE, FLOW_XONXOFF, ACT_FLUSH);
+                           gotprimary = waitForDCEChannel(false);
+                       } else
+                           gotprimary = false;
+                   }
+                   if (!gotprimary) {
+                       emsg = "Failed to properly open V.34 primary channel.";
+                       protoTrace(emsg);
+                       if (conf.saveUnconfirmedPages && pagedataseen) {
+                           protoTrace("RECV keeping unconfirmed page");
+                           writeECMData(tif, block, (fcount * frameSize), params, (seq |= 2));
+                           prevPage = true;
+                       }
+                       free(block);
+                       return (false);
+                   }
+               } else {
+                   emsg = "Received premature V.34 termination.";
+                   protoTrace(emsg);
+                   if (conf.saveUnconfirmedPages && pagedataseen) {
+                       protoTrace("RECV keeping unconfirmed page");
+                       writeECMData(tif, block, (fcount * frameSize), params, (seq |= 2));
+                       prevPage = true;
+                   }
+                   free(block);
+                   return (false);
                }
-               free(block);
-               if (wasTimeout()) abortReceive();       // return to command mode
-               return (false);
            }
-           if (syncECMFrame()) {
+           if (useV34 || syncECMFrame()) {             // no synchronization needed w/V.34-fax
                time_t start = Sys::now();
                do {
                    frame.reset();
@@ -794,12 +913,45 @@ Class1Modem::recvPageECMData(TIFF* tif, const Class2Params& params, fxStr& emsg)
                        }
                    } else {
                        dataseen = true;        // assume that garbage was meant to be data
-                       syncECMFrame();
+                       if (!useV34) syncECMFrame();
+                       if (useV34 && (gotEOT || gotCTRL)) rcpcnt = 3;
                    }
                    // some senders don't send the requisite three RCP signals
                } while (rcpcnt == 0 && (unsigned) Sys::now()-start < 5*60);    // can't expect 50 ms of flags, some violate T.4 A.3.8
-               endECMBlock();                          // wait for <DLE><ETX>
-               (void) waitFor(AT_NOCARRIER);           // wait for message carrier to drop
+               if (useV34) {
+                   if (!gotEOT && !waitForDCEChannel(true)) {
+                       emsg = "Failed to properly open V.34 control channel.";
+                       protoTrace(emsg);
+                       if (conf.saveUnconfirmedPages && pagedataseen) {
+                           protoTrace("RECV keeping unconfirmed page");
+                           writeECMData(tif, block, (fcount * frameSize), params, (seq |= 2));
+                           prevPage = true;
+                       }
+                       free(block);
+                       return (false);
+                   }
+                   if (gotEOT) {
+                       emsg = "Received premature V.34 termination.";
+                       protoTrace(emsg);
+                       if (conf.saveUnconfirmedPages && pagedataseen) {
+                           protoTrace("RECV keeping unconfirmed page");
+                           writeECMData(tif, block, (fcount * frameSize), params, (seq |= 2));
+                           prevPage = true;
+                       }
+                       free(block);
+                       return (false);
+                   }
+               } else {
+                   endECMBlock();                              // wait for <DLE><ETX>
+               }
+               if (!useV34) {
+                   // wait for message carrier to drop
+                   time_t nocarrierstart = Sys::now();
+                   bool gotnocarrier = false;
+                   do {
+                       gotnocarrier = waitFor(AT_NOCARRIER, 2*1000);
+                   } while (!gotnocarrier && Sys::now() < (nocarrierstart + 5));
+               }
                if (flowControl == FLOW_XONXOFF)
                    (void) setXONXOFF(FLOW_NONE, FLOW_NONE, ACT_DRAIN);
                setInputBuffering(false);
@@ -832,7 +984,7 @@ Class1Modem::recvPageECMData(TIFF* tif, const Class2Params& params, fxStr& emsg)
                        }
 
                        // requisite pause before sending response (PPR/MCF)
-                       if (!atCmd(conf.class1SwitchingCmd, AT_OK)) {
+                       if (!useV34 && !atCmd(conf.class1SwitchingCmd, AT_OK)) {
                            emsg = "Failure to receive silence.";
                            if (conf.saveUnconfirmedPages && pagedataseen) {
                                protoTrace("RECV keeping unconfirmed page");
@@ -845,7 +997,7 @@ Class1Modem::recvPageECMData(TIFF* tif, const Class2Params& params, fxStr& emsg)
                        if (! blockgood) {
                            // inform the remote that one or more frames were invalid
 
-                           atCmd(thCmd, AT_CONNECT);
+                           if (!useV34) atCmd(thCmd, AT_CONNECT);
                            startTimeout(3000);
                            sendFrame(FCF_PPR, fxStr(ppr, 32));
                            stopTimeout("sending PPR frame");
@@ -862,6 +1014,17 @@ Class1Modem::recvPageECMData(TIFF* tif, const Class2Params& params, fxStr& emsg)
                                    u_int dcs;                  // possible bits 1-16 of DCS in FIF
                                    switch (rtnframe.getFCF()) {
                                        case FCF_CTC:
+                                           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);
+                                           }
                                            // use 16-bit FIF to alter speed, curcap
                                            dcs = rtnframe[3] | (rtnframe[4]<<8);
                                            curcap = findSRCapability(dcs&DCS_SIGRATE, recvCaps);
@@ -880,8 +1043,16 @@ Class1Modem::recvPageECMData(TIFF* tif, const Class2Params& params, fxStr& emsg)
                                            tracePPR("RECV send", FCF_CTR);
                                            break;
                                        case FCF_EOR:
-                                           blockgood = true;
                                            tracePPM("RECV recv", rtnframe.getFCF2());
+                                           /*
+                                            * It may be wise to disconnect here if MMR is being
+                                            * used because there will surely be image data loss.
+                                            * However, since the sender knows what the extent of
+                                            * the data loss will be, we'll naively assume that
+                                            * the sender knows what it's doing, and we'll
+                                            * proceed as instructed by it.
+                                            */
+                                           blockgood = true;
                                            switch (rtnframe.getFCF2()) {
                                                case 0:
                                                    // EOR-NULL partial page boundary
@@ -906,7 +1077,7 @@ Class1Modem::recvPageECMData(TIFF* tif, const Class2Params& params, fxStr& emsg)
                                                    return (false);
                                            }
                                            // requisite pause before sending response (ERR)
-                                           if (!atCmd(conf.class1SwitchingCmd, AT_OK)) {
+                                           if (!useV34 && !atCmd(conf.class1SwitchingCmd, AT_OK)) {
                                                emsg = "Failure to receive silence.";
                                                if (conf.saveUnconfirmedPages && pagedataseen) {
                                                    protoTrace("RECV keeping unconfirmed page");
@@ -1104,6 +1275,14 @@ Class1Modem::recvEnd(fxStr&)
        } while ((unsigned) Sys::now()-start < t1 &&
            (!frame.isOK() || frame.getFCF() == FCF_EOP));
     }
+    if (useV34) {
+       // terminate V.34 channel
+       u_char buf[2];
+       buf[0] = DLE; buf[1] = EOT;             // <DLE><EOT>
+       putModemData(buf, 2);
+       // T.31-A1 samples indicate an OK response, but anything is acceptable
+       waitFor(AT_OK, 60000);
+    }
     setInputBuffering(true);
     return (true);
 }
index d52f3e1b180f8a5cf43104bbf12954b14526c68e..27d80ef17a09c2ac6f0e689427a3054da921257b 100644 (file)
@@ -52,6 +52,10 @@ Class1Modem::sendSetup(FaxRequest& req, const Class2Params& dis, fxStr& emsg)
 CallStatus
 Class1Modem::dialResponse(fxStr& emsg)
 {
+    // This is as good a time as any, perhaps, to reset modemParams.br.
+    // If the call does V.8 handshaking, then it will be altered.
+    modemParams.br = nonV34br;
+
     int ntrys = 0;
     ATResponse r;
     do {
@@ -104,6 +108,8 @@ Class1Modem::getPrologue(Class2Params& params, bool& hasDoc, fxStr& emsg)
     time_t start = Sys::now();
     HDLCFrame frame(conf.class1FrameOverhead);
 
+    if (useV34)        waitForDCEChannel(true);                // expect control channel
+
     bool framerecvd = recvRawFrame(frame);
     for (;;) {
        if (framerecvd) {
@@ -261,8 +267,14 @@ Class1Modem::sendPhaseB(TIFF* tif, Class2Params& next, FaxMachineInfo& info,
        /*
         * Transmit the facsimile message/Phase C.
         */
-       if (!sendPage(tif, params, decodePageChop(pph, params), cmd, emsg))
+        hadV34Trouble = false;
+       if (!sendPage(tif, params, decodePageChop(pph, params), cmd, emsg)) {
+           if (hadV34Trouble) {
+               protoTrace("The destination appears to have trouble with V.34-Fax.");
+               return (send_v34fail);
+           }
            return (send_retry);        // a problem, disconnect
+       }
 
        int ncrp = 0;
 
@@ -442,10 +454,9 @@ Class1Modem::sendTCF(const Class2Params& params, u_int ms)
 bool
 Class1Modem::sendPrologue(u_int dcs, u_int dcs_xinfo, const fxStr& tsi)
 {
-    bool frameSent = (
-       atCmd(thCmd, AT_NOTHING) &&
-       atResponse(rbuf, 2550) == AT_CONNECT
-    );
+    bool frameSent;
+    if (useV34) frameSent = true;
+    else frameSent = (atCmd(thCmd, AT_NOTHING) && atResponse(rbuf, 2550) == AT_CONNECT);
     if (!frameSent)
        return (false);
     if (pwd != fxStr::null) {
@@ -524,51 +535,62 @@ Class1Modem::sendTraining(Class2Params& params, int tries, fxStr& emsg)
        frameSize = 64;
     } else
        frameSize = 256;
-    /*
-     * Select Class 1 capability: use params.br to hunt
-     * for the best signalling scheme acceptable to both
-     * local and remote (based on received DIS and modem
-     * capabilities gleaned at modem setup time).
-     */
-    if (!curcap)
-       curcap = findBRCapability(params.br, xmitCaps);
-    curcap++;
-    if (!dropToNextBR(params))
-       goto failed;
-    do {
+
+    if (!useV34) {
        /*
-        * Override the Class 2 parameter bit rate
-        * capability and use the signalling rate
-        * calculated from the modem's capabilities
-        * and the received DIS.  This is because
-        * the Class 2 state does not include the
-        * modulation technique (v.27, v.29, v.17, v.33).
-        *
-        * Technically, according to post-1994 revisions
-        * of T.30, V.33 should not be used except
-        * in the case where the remote announces
-        * specific V.33 support with the 1,1,1,0 rate
-        * bits set.  However, for the sake of versatility
-        * we'll not enforce this upon modems that support
-        * V.33 but not V.17 and will require the user
-        * to disable V.33 if it becomes problematic.  For
-        * modems that support both V.17 and V.33 the
-        * latter is never used.
-        */
-       params.br = curcap->br;
-       dcs = (dcs &~ DCS_SIGRATE) | curcap->sr;
-        /*
-        * Set the number of train attemps on the same
-        * modulation; having set it to 1 we immediately drop
-        * the speed if the training has been failed.
-        * This parameter is not specified by T.30
-        * (the algorith left implementation defined),
-        * so we choose the exact value according to our
-        * common sense.
+        * Select Class 1 capability: use params.br to hunt
+        * for the best signalling scheme acceptable to both
+        * local and remote (based on received DIS and modem
+        * capabilities gleaned at modem setup time).
         */
+       if (!curcap)
+           curcap = findBRCapability(params.br, xmitCaps);
+       curcap++;
+       if (!dropToNextBR(params))
+           goto failed;
+    }
+    do {
+       if (!useV34) {
+           /*
+            * Override the Class 2 parameter bit rate
+            * capability and use the signalling rate
+            * calculated from the modem's capabilities
+            * and the received DIS.  This is because
+            * the Class 2 state does not include the
+            * modulation technique (v.27, v.29, v.17, v.33).
+            *
+            * Technically, according to post-1994 revisions
+            * of T.30, V.33 should not be used except
+            * in the case where the remote announces
+            * specific V.33 support with the 1,1,1,0 rate
+            * bits set.  However, for the sake of versatility
+            * we'll not enforce this upon modems that support
+            * V.33 but not V.17 and will require the user
+            * to disable V.33 if it becomes problematic.  For
+            * modems that support both V.17 and V.33 the
+            * latter is never used.
+            */
+           params.br = curcap->br;
+           dcs = (dcs &~ DCS_SIGRATE) | curcap->sr;
+           /*
+            * Set the number of train attemps on the same
+            * modulation; having set it to 1 we immediately drop
+            * the speed if the training has been failed.
+            * This parameter is not specified by T.30
+            * (the algorith left implementation defined),
+            * so we choose the exact value according to our
+            * common sense.
+            */
+       } else {
+           /*
+            * T.30 Table 2 Note 33 says that when V.34-fax is used
+            * DCS bits 11-14 should be set to zero.
+            */
+           dcs = (dcs &~ DCS_SIGRATE);
+       }
        int t = 1;
        do {
-           protoTrace("SEND training at %s %s",
+           if (!useV34) protoTrace("SEND training at %s %s",
                modulationNames[curcap->mod],
                Class2Params::bitRateNames[curcap->br]);
            if (!sendPrologue(dcs, dcs_xinfo, lid)) {
@@ -579,38 +601,43 @@ Class1Modem::sendTraining(Class2Params& params, int tries, fxStr& emsg)
            }
 
            /*
-            * Delay before switching to high speed carrier
-            * to send the TCF data as required by T.30 chapter
-            * 5 note 3.
-            *
-            * Historically this delay was enforced by a pause,
-            * however, +FTS must be used.  See the notes preceding
-            * Class1PPMWaitCmd above.
+            * V.8 handshaking provides training for V.34-fax connections
             */
-           if (!atCmd(conf.class1TCFWaitCmd, AT_OK)) {
-               emsg = "Stop and wait failure (modem on hook)";
-               protoTrace(emsg);
-               return (send_retry);
-           }
+           if (!useV34) {
+               /*
+                * Delay before switching to high speed carrier
+                * to send the TCF data as required by T.30 chapter
+                * 5 note 3.
+                *
+                * Historically this delay was enforced by a pause,
+                * however, +FTS must be used.  See the notes preceding
+                * Class1PPMWaitCmd above.
+                */
+               if (!atCmd(conf.class1TCFWaitCmd, AT_OK)) {
+                   emsg = "Stop and wait failure (modem on hook)";
+                   protoTrace(emsg);
+                   return (send_retry);
+               }
 
-           if (!sendTCF(params, TCF_DURATION)) {
-               if (abortRequested())
-                   goto done;
-               protoTrace("Problem sending TCF data");
-           }
+               if (!sendTCF(params, TCF_DURATION)) {
+                   if (abortRequested())
+                       goto done;
+                   protoTrace("Problem sending TCF data");
+               }
 
-           /*
-            * Some modems may respond OK following TCF transmission
-            * so quickly that the carrier signal has not actually 
-            * dropped.  T.30 requires the receiver to wait 75 +/- 20 
-            * ms before sending a response.  Here we explicitly look for 
-            * that silence before looking for the low-speed carrier.  
-            * Doing this resolves "DIS/DTC received 3 times" errors 
-            * between USR modems and certain HP OfficeJets, in 
-            * particular.
-             */
-           if (conf.class1ResponseWaitCmd != "") {
-               atCmd(conf.class1ResponseWaitCmd, AT_OK);
+               /*
+                * Some modems may respond OK following TCF transmission
+                * so quickly that the carrier signal has not actually 
+                * dropped.  T.30 requires the receiver to wait 75 +/- 20 
+                * ms before sending a response.  Here we explicitly look for 
+                * that silence before looking for the low-speed carrier.  
+                * Doing this resolves "DIS/DTC received 3 times" errors 
+                * between USR modems and certain HP OfficeJets, in 
+                * particular.
+                */
+               if (conf.class1ResponseWaitCmd != "") {
+                   atCmd(conf.class1ResponseWaitCmd, AT_OK);
+               }
            }
 
            /*
@@ -630,11 +657,11 @@ Class1Modem::sendTraining(Class2Params& params, int tries, fxStr& emsg)
                        break;
                    }
                } while (frame.moreFrames() && recvFrame(frame, conf.t4Timer));
-           }
+           } 
            if (frame.isOK()) {
                switch (frame.getFCF()) {
                case FCF_CFR:           // training confirmed
-                   protoTrace("TRAINING succeeded");
+                   if (!useV34) protoTrace("TRAINING succeeded");
                    setDataTimeout(60, params.br);
                    return (true);
                case FCF_CRP:           // command repeat
@@ -676,11 +703,11 @@ Class1Modem::sendTraining(Class2Params& params, int tries, fxStr& emsg)
         * the signalling rate to the next lower rate supported
         * by the local & remote sides and try again.
         */
-    } while (dropToNextBR(params));
+    } while (!useV34 && dropToNextBR(params));
 failed:
     emsg = "Failure to train remote modem at 2400 bps or minimum speed";
 done:
-    protoTrace("TRAINING failed");
+    if (!useV34) protoTrace("TRAINING failed");
     return (false);
 }
 
@@ -739,6 +766,15 @@ Class1Modem::raiseToNextBR(Class2Params& params)
 void
 Class1Modem::blockData(u_int byte, bool flag)
 {
+    if (useV34) {
+       // With V.34-fax the DTE makes the stuffing
+       byte =  (((byte>>0)&1)<<7)|(((byte>>1)&1)<<6)|
+               (((byte>>2)&1)<<5)|(((byte>>3)&1)<<4)|
+               (((byte>>4)&1)<<3)|(((byte>>5)&1)<<2)|
+               (((byte>>6)&1)<<1)|(((byte>>7)&1)<<0);
+       ecmStuffedBlock[ecmStuffedBlockPos++] = byte;
+       return;
+    }
     for (u_int j = 8; j > 0; j--) {
        u_short bit = (byte & (1 << (j - 1))) != 0 ? 1 : 0;
        ecmByte |= (bit << ecmBitPos);
@@ -787,15 +823,17 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
        u_short pprcnt = 0;
        char ppr[32];                           // 256 bits
        for (u_int i = 0; i < 32; i++) ppr[i] = 0xff;
-       u_short badframes = 256, badframesbefore = 0;
+       u_short badframes = frameNumber, badframesbefore = 0;
 
        do {
            u_short fcount = 0;
            ecmStuffedBlockPos = 0;
 
-           // synchronize with 200 ms of 0x7e flags
-           for (u_int i = 0; i < params.transferSize(200); i++)
-               blockData(0x7e, true);
+           if (!useV34) {
+               // synchronize with 200 ms of 0x7e flags
+               for (u_int i = 0; i < params.transferSize(200); i++)
+                   blockData(0x7e, true);
+           }
 
            u_char* firstframe = (u_char*) malloc(frameSize + 6);
            fxAssert(firstframe != NULL, "ECM procedure error (frame duplication).");
@@ -812,7 +850,7 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                        ecmframe.put(ecmBlock[i]);
                        blockData(ecmBlock[i], false);
                    }
-                   int fcs1 = ecmframe.getCRC() >> 8;  // 1st byte FCS
+                   int fcs1 = ecmframe.getCRC() >> 8;          // 1st byte FCS
                    int fcs2 = ecmframe.getCRC() & 0xff;        // 2nd byte FCS
                    ecmframe.put(fcs1); ecmframe.put(fcs2);
                    blockData(fcs1, false);
@@ -820,8 +858,10 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                    traceHDLCFrame("<--", ecmframe);
                    protoTrace("SEND send frame number %u", fnum);
 
-                   // separate frames with a 0x7e flag
-                   blockData(0x7e, true);
+                   if (!useV34) {
+                       // separate frames with a 0x7e flag
+                       blockData(0x7e, true);
+                   }
 
                    if (firstframe[0] == 0x1) {
                        for (u_int i = 0; i < (frameSize + 6); i++) {
@@ -841,7 +881,7 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                ecmframe.put(firstframe, (frameSize + 6));
                traceHDLCFrame("<--", ecmframe);
                protoTrace("SEND send frame number %u", frameRev[firstframe[3]]);
-               blockData(0x7e, true);
+               if (!useV34) blockData(0x7e, true);
            }
            free(firstframe);
            HDLCFrame rcpframe(5);
@@ -852,41 +892,75 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                traceHDLCFrame("<--", rcpframe);
 
                // separate frames with a 0x7e flag
-               blockData(0x7e, true);
+               if (!useV34) blockData(0x7e, true);
            }
            // add one more flag to ensure one full flag gets transmitted before DLE+ETX
-           blockData(0x7e, true);
+           if (!useV34) blockData(0x7e, true);
 
            // start up the high-speed carrier...
            if (flowControl == FLOW_XONXOFF)   
                setXONXOFF(FLOW_XONXOFF, FLOW_NONE, ACT_FLUSH);
-           pause(conf.class1SendMsgDelay);             // T.30 5.3.2.4
-           fxStr tmCmd(curcap[HasShortTraining(curcap)].value, tmCmdFmt);
-           if (!atCmd(tmCmd, AT_CONNECT))
-               return (false);
-           pause(conf.class1TMConnectDelay);
+           if (!useV34) {
+               pause(conf.class1SendMsgDelay);         // T.30 5.3.2.4
+               fxStr tmCmd(curcap[HasShortTraining(curcap)].value, tmCmdFmt);
+               if (!atCmd(tmCmd, AT_CONNECT))
+                   return (false);
+               pause(conf.class1TMConnectDelay);
+           }
 
            // The block is assembled.  Transmit it, adding transparent DLEs.  End with DLE+ETX.
-           if (!putModemDLEData(ecmStuffedBlock, ecmStuffedBlockPos, bitrev, getDataTimeout())) {
-               return (false);
-           }
            u_char buf[2];
-           buf[0] = DLE; buf[1] = ETX;
-           if (!putModemData(buf, 2)) {
-               return (false);
+           if (useV34) {
+               // switch to primary channel
+               buf[0] = DLE; buf[1] = 0x6B;    // <DLE><pri>
+               if (!putModemData(buf, 2)) return (false);
+               // wait for the ready indicator, <DLE><pri><DLE><prate>
+               if (!waitForDCEChannel(false)) return (false);
            }
+           if (useV34) {
+               // we intentionally do not send the FCS bytes as the DCE regenerates them
+               // send fcount frames separated by <DLE><ETX>
+               buf[0] = DLE; buf[1] = ETX;
+               for (u_short v34frame = 0; v34frame < fcount; v34frame++) {
+                   if (!putModemDLEData(ecmStuffedBlock, frameSize + 4, bitrev, getDataTimeout()))
+                       return (false);
+                   if (!putModemData(buf, 2)) return (false);
+                   ecmStuffedBlock += (frameSize + 6);
+               }
+               // send 3 RCP frames separated by <DLE><ETX>
+               for (u_short v34frame = 0; v34frame < 3; v34frame++) {
+                   if (!putModemDLEData(ecmStuffedBlock, 3, bitrev, getDataTimeout()))
+                       return (false);
+                   if (!putModemData(buf, 2)) return (false);
+                   ecmStuffedBlock += 5;
+               }
+               ecmStuffedBlock -= ecmStuffedBlockPos;
+           } else {
+               if (!putModemDLEData(ecmStuffedBlock, ecmStuffedBlockPos, bitrev, getDataTimeout()))
+                   return (false);
+           }
+           if (useV34) {
+               // switch to control channel
+               buf[0] = DLE; buf[1] = 0x6D;    // <DLE><ctrl>
+               if (!putModemData(buf, 2)) return (false);
+               // wait for the ready indicator, <DLE><ctrl><DLE><crate>
+               if (!waitForDCEChannel(true)) return (false);
+           } else {
+               buf[0] = DLE; buf[1] = ETX;
+               if (!putModemData(buf, 2)) return (false);
 
-           // Wait for transmit buffer to empty.
-           ATResponse r;
-           while ((r = atResponse(rbuf, getDataTimeout())) == AT_OTHER);
-            if (!(r == AT_OK)) {
-               return (false);
+               // Wait for transmit buffer to empty.
+               ATResponse r;
+               while ((r = atResponse(rbuf, getDataTimeout())) == AT_OTHER);
+               if (!(r == AT_OK)) {
+                   return (false);
+               }
            }
 
            if (flowControl == FLOW_XONXOFF)
                setXONXOFF(FLOW_NONE, FLOW_NONE, ACT_DRAIN);
 
-           if (!atCmd(ppmcmd == FCF_MPS ? conf.class1PPMWaitCmd : conf.class1EOPWaitCmd, AT_OK)) {
+           if (!useV34 && !atCmd(ppmcmd == FCF_MPS ? conf.class1PPMWaitCmd : conf.class1EOPWaitCmd, AT_OK)) {
                emsg = "Stop and wait failure (modem on hook)";
                protoTrace(emsg);
                return (false);
@@ -916,7 +990,7 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
            /* get MCF/PPR/RNR */
            HDLCFrame pprframe(conf.class1FrameOverhead);
            do {
-               if (!atCmd(thCmd, AT_CONNECT))
+               if (!useV34 && !atCmd(thCmd, AT_CONNECT))
                    break;
                startTimeout(3000);
                sendFrame(FCF_PPS|FCF_SNDR, fxStr(pps, 4));
@@ -934,7 +1008,7 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                        gotppr = false;
                        crpcnt++;
                        ppscnt = 0;
-                       if (!atCmd(conf.class1SwitchingCmd, AT_OK)) {
+                       if (!useV34 && !atCmd(conf.class1SwitchingCmd, AT_OK)) {
                            emsg = "Failure to receive silence.";
                            protoTrace(emsg);
                            return (false);
@@ -943,7 +1017,7 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                }
            } while (!gotppr && (++ppscnt < 3) && (crpcnt < 3));
            if (gotppr) {
-               if (!atCmd(conf.class1SwitchingCmd, AT_OK)) {
+               if (!useV34 && !atCmd(conf.class1SwitchingCmd, AT_OK)) {
                    emsg = "Failure to receive silence.";
                    protoTrace(emsg);
                    return (false);
@@ -962,7 +1036,7 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                        u_short rrcnt = 0, crpcnt = 0;
                        bool gotmsg = false;
                        do {
-                           if (!atCmd(thCmd, AT_CONNECT))
+                           if (!useV34 && !atCmd(thCmd, AT_CONNECT))
                                break;
                            startTimeout(3000);
                            sendFrame(FCF_RR|FCF_SNDR);
@@ -975,7 +1049,7 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                                    gotmsg = false;
                                    crpcnt++;
                                    rrcnt = 0;
-                                   if (!atCmd(conf.class1SwitchingCmd, AT_OK)) {
+                                   if (!useV34 && !atCmd(conf.class1SwitchingCmd, AT_OK)) {
                                        emsg = "Failure to receive silence.";
                                        protoTrace(emsg);
                                        return (false);
@@ -994,7 +1068,7 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                                gotppr = true;
                                break;
                            case FCF_RNR:
-                               if (!atCmd(conf.class1SwitchingCmd, AT_OK)) {
+                               if (!useV34 && !atCmd(conf.class1SwitchingCmd, AT_OK)) {
                                    emsg = "Failure to receive silence.";
                                    protoTrace(emsg);
                                    return (false);
@@ -1031,6 +1105,15 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                                }
                            }
                        }
+                       if (useV34 && badframes && badframes >= (badframesbefore / 2) && pprcnt < 4) {
+                           /*
+                            * With V.34-Fax we cannot send CTC, but we can request 
+                            * to renegotiate the primary rate any time.  (T.31-A1 B.8.5)
+                            * In practice, if we do not constrain the rate then
+                            * we may likely speed up the rate; so we constrain it.
+                            */
+                           renegotiatePrimary(true);           // constrain
+                       }
                        if (pprcnt == 4) {
                            pprcnt = 0;
                            // Some receivers will ignorantly transmit PPR showing all frames good,
@@ -1039,7 +1122,8 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                                blockgood = true;
                                signalRcvd = FCF_MCF;
                            }
-                           if (conf.class1ECMDoCTC && (blockgood == false) && 
+                           // There is no CTC with V.34-fax (T.30 Annex F.3.4.5 Note 1).
+                           if (conf.class1ECMDoCTC && !useV34 && (blockgood == false) && 
                                !((curcap->br == 0) && (badframes >= badframesbefore))) {
                                // send ctc even at 2400 baud if we're getting somewhere
                                if (curcap->br != 0) {
@@ -1091,11 +1175,31 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                                    return (false);
                                }
                            } else {
+                               /*
+                                * At this point data corruption is inevitable if all data
+                                * frames have not been received correctly.  This is tolerable
+                                * with MH and MR data, as the effect of the corruption
+                                * will be limited, but with MMR data all data following the
+                                * corrupt frame will be meaningless: the page image will be
+                                * truncated on the receiver's end.  So if this is the case
+                                * we disconnect, and hopefully we try again successfully.  If
+                                * this happens repeatedly to the same destination, then
+                                * disabling MMR to this destination would be advisable.
+                                */
+                               if (blockgood == false && params.df == DF_2DMMR) {
+                                   if (useV34) {
+                                       // not using CTC seems to be a problem
+                                       hadV34Trouble = true;
+                                   }
+                                   emsg = "Failure to transmit clean MMR image data.";
+                                   protoTrace(emsg);
+                                   return (false);
+                               }
                                bool goterr = false;
                                u_short eorcnt = 0, crpcnt = 0;
                                HDLCFrame errframe(conf.class1FrameOverhead);
                                do {
-                                   if (!atCmd(thCmd, AT_CONNECT))
+                                   if (!useV34 && !atCmd(thCmd, AT_CONNECT))
                                        break;
                                    startTimeout(3000);
                                    sendFrame(FCF_EOR|FCF_SNDR, fxStr(pps, 1));
@@ -1108,7 +1212,7 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                                            goterr = false;
                                            crpcnt++;
                                            eorcnt = 0;
-                                           if (!atCmd(conf.class1SwitchingCmd, AT_OK)) {
+                                           if (!useV34 && !atCmd(conf.class1SwitchingCmd, AT_OK)) {
                                                emsg = "Failure to receive silence.";
                                                protoTrace(emsg);
                                                return (false);
@@ -1135,7 +1239,7 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                                        u_short rrcnt = 0, crpcnt = 0;
                                        bool gotmsg = false;
                                        do {
-                                           if (!atCmd(thCmd, AT_CONNECT))
+                                           if (!useV34 && !atCmd(thCmd, AT_CONNECT))
                                                break;
                                            startTimeout(3000);
                                            sendFrame(FCF_RR|FCF_SNDR);
@@ -1148,7 +1252,7 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                                                    gotmsg = false;
                                                    crpcnt++;
                                                    rrcnt = 0;
-                                                   if (!atCmd(conf.class1SwitchingCmd, AT_OK)) {
+                                                   if (!useV34 && !atCmd(conf.class1SwitchingCmd, AT_OK)) {
                                                        emsg = "Failure to receive silence.";
                                                        protoTrace(emsg);
                                                        return (false);
@@ -1161,12 +1265,12 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                                            protoTrace(emsg);
                                            return (false);
                                        }
-                                       switch (pprframe.getFCF()) {
+                                       switch (errframe.getFCF()) {
                                            case FCF_ERR:
                                                goterr = true;
                                                break;
                                            case FCF_RNR:
-                                               if (!atCmd(conf.class1SwitchingCmd, AT_OK)) {
+                                               if (!useV34 && !atCmd(conf.class1SwitchingCmd, AT_OK)) {
                                                    emsg = "Failure to receive silence.";
                                                    protoTrace(emsg);
                                                    return (false);
@@ -1610,6 +1714,14 @@ void
 Class1Modem::sendEnd()
 {
     transmitFrame(FCF_DCN|FCF_SNDR);           // disconnect
+    if (useV34) {
+       // terminate V.34 channel
+       u_char buf[2];
+       buf[0] = DLE; buf[1] = EOT;             // <DLE><EOT>
+       putModemData(buf, 2);
+       // T.31-A1 samples indicate an OK response, but anything is acceptable
+       (void) atResponse(rbuf, 60000);
+    }
     setInputBuffering(true);
 }
 
index ef5d5e106014f8776e315bb21438e0cd9e6ec255..181c6e09e36663b94d900557058a9cd75629b34b 100644 (file)
@@ -430,7 +430,7 @@ Class2Modem::parseClass2Capabilities(const char* cap, Class2Params& params)
  * for sending/received facsimile.
  */
 bool
-Class2Modem::faxService()
+Class2Modem::faxService(bool enableV34)
 {
     return setupClass2Parameters() && class2Cmd(lidCmd, lid);
 }
index eb459e6746d795e9779c208d2766671fa900a610..47b6116ceb8b3e1d889f04f0c1d55bb9e8253d79 100644 (file)
@@ -157,7 +157,7 @@ public:
                    fxStr& emsg);
 
 // miscellaneous
-    bool       faxService();                   // switch to fax mode
+    bool       faxService(bool enableV34);     // switch to fax mode
     bool       reset(long ms);                 // reset modem
     void       setLID(const fxStr& number);    // set local id string
     bool       supportsPolling() const;        // modem capability
index 31b52f5421c16d4a2f2e1e80c95d3002177bde05..5ea733dc4271cfa6a22428dfecdf3019dd67bda1 100644 (file)
@@ -61,7 +61,7 @@ const char* ClassModem::serviceNames[9] = {
     "",                                // 7
     "\"Voice\"",               // SERVICE_VOICE
 };
-const char* ClassModem::ATresponses[15] = {
+const char* ClassModem::ATresponses[16] = {
     "Nothing",                 // AT_NOTHING
     "OK",                      // AT_OK
     "Connection established",  // AT_CONNECT
@@ -75,6 +75,7 @@ const char* ClassModem::ATresponses[15] = {
     "<Empty line>",            // AT_EMPTYLINE
     "<Timeout>",               // AT_TIMEOUT
     "<dle+etx>",               // AT_DLEETX
+    "End of transmission",     // AT_DLEEOT
     "<xon>",                   // AT_XON
     "<Unknown response>"       // AT_OTHER
 };
@@ -158,6 +159,7 @@ ClassModem::isNoise(const char* s)
        "RRING",        // Telebit
        "RINGING",      // ZyXEL
        "+FHR:",        // Intel 144e
+       "+F34:",        // Class 1.0 V.34 report
     };
 #define        NNOISE  (sizeof (noiseMsgs) / sizeof (noiseMsgs[0]))
 
@@ -190,6 +192,8 @@ static const AnswerMsg answerMsgs[] = {
    ClassModem::AT_NOTHING, ClassModem::NOCARRIER, ClassModem::CALLTYPE_ERROR },
 { "+FHS:",     5,
    ClassModem::AT_NOTHING, ClassModem::NOCARRIER, ClassModem::CALLTYPE_ERROR },
+{ "OK",                2,
+   ClassModem::AT_NOTHING, ClassModem::NOCARRIER, ClassModem::CALLTYPE_ERROR },
 { "FAX",        3,
    ClassModem::AT_CONNECT, ClassModem::OK,       ClassModem::CALLTYPE_FAX },
 { "DATA",       4,
@@ -220,7 +224,7 @@ ClassModem::answerResponse(fxStr& emsg)
     do {
        r = atResponse(rbuf, conf.answerResponseTimeout);
 again:
-       if (r == AT_TIMEOUT)
+       if (r == AT_TIMEOUT || r == AT_DLEEOT)
            break;
        const AnswerMsg* am = findAnswer(rbuf);
        if (am != NULL) {
@@ -788,6 +792,8 @@ ClassModem::atResponse(char* buf, long ms)
        case '\020':
            if (streq(buf, "\020\003"))         // DLE/ETX
                lastResponse = AT_DLEETX;
+           if (streq(buf, "\020\004"))
+               lastResponse = AT_DLEEOT;       // DLE+EOT
            break;
        case '\021':
            if (streq(buf, "\021"))             // DC1 (XON)
index b479373bf3b065e6b4f6e844c899fd6a8656215f..9ddbf63e2815477d48becbdb4f5fb1b1a05c6d94 100644 (file)
@@ -178,8 +178,9 @@ public:
        AT_EMPTYLINE    = 10,   // empty line (0 characters received)
        AT_TIMEOUT      = 11,   // timeout waiting for response
        AT_DLEETX       = 12,   // dle/etx characters
-       AT_XON          = 13,   // xon character
-       AT_OTHER        = 14    // unknown response (not one of above)
+       AT_DLEEOT       = 13,   // dle+eot characters (end of transmission)
+       AT_XON          = 14,   // xon character
+       AT_OTHER        = 15    // unknown response (not one of above)
     };
 private:
     ModemServer& server;       // server for getting to device
@@ -203,7 +204,7 @@ protected:
 
     static const char* serviceNames[9];         // class 2 services
     static const char* callStatus[10];  // printable call status
-    static const char* ATresponses[15];
+    static const char* ATresponses[16];
 
     ClassModem(ModemServer&, const ModemConfig&);
 
index 9d2301537f81234b003d2038992a3ed90500ab56..88184b6920baeed9e4f63349c31413ee708b217e 100644 (file)
@@ -57,6 +57,7 @@ FaxMachineInfo::FaxMachineInfo(const FaxMachineInfo& other)
     supportsVRes = other.supportsVRes;
     supports2DEncoding = other.supports2DEncoding;
     supportsMMR = other.supportsMMR;
+    hasV34Trouble = other.hasV34Trouble;
     supportsPostScript = other.supportsPostScript;
     calledBefore = other.calledBefore;
     maxPageWidth = other.maxPageWidth;
@@ -102,6 +103,7 @@ FaxMachineInfo::resetConfig()
     supportsVRes = VR_FINE;            // normal and high res support
     supports2DEncoding = true;         // assume 2D-encoding support
     supportsMMR = true;                        // assume MMR support
+    hasV34Trouble = false;             // assume no problems
     supportsPostScript = false;                // no support for Adobe protocol
     calledBefore = false;              // never called before
     maxPageWidth = 2432;               // max required width
@@ -170,6 +172,7 @@ static const char* stnames[] =
 #define        LN      5
 #define        BR      6
 #define        ST      7
+#define V34    8
 
 #define        setLocked(b,ix) locked |= b<<ix
 
@@ -190,6 +193,9 @@ FaxMachineInfo::setConfigItem(const char* tag, const char* value)
     } else if (streq(tag, "supportsmmr")) {
        supportsMMR = getBoolean(value);
        setLocked(b, G4);
+    } else if (streq(tag, "hasv34trouble")) {
+       hasV34Trouble = getBoolean(value);
+       setLocked(b, V34);
     } else if (streq(tag, "supportspostscript")) {
        supportsPostScript = getBoolean(value);
        setLocked(b, PS);
@@ -254,6 +260,8 @@ void FaxMachineInfo::setSupports2DEncoding(bool b)
     { checkLock(G32D, supports2DEncoding, b); }
 void FaxMachineInfo::setSupportsMMR(bool b)
     { checkLock(G4, supportsMMR, b); }
+void FaxMachineInfo::setHasV34Trouble(bool b)
+    { checkLock(V34, hasV34Trouble, b); }
 void FaxMachineInfo::setSupportsPostScript(bool b)
     { checkLock(PS, supportsPostScript, b); }
 void FaxMachineInfo::setMaxPageWidthInPixels(int v)
@@ -347,6 +355,7 @@ FaxMachineInfo::writeConfig(fxStackBuffer& buf)
     putDecimal(buf, "supportsVRes", isLocked(VR), supportsVRes);
     putBoolean(buf, "supports2DEncoding", isLocked(G32D),supports2DEncoding);
     putBoolean(buf, "supportsMMR", isLocked(G4),supportsMMR);
+    putBoolean(buf, "hasV34Trouble", isLocked(V34),hasV34Trouble);
     putBoolean(buf, "supportsPostScript", isLocked(PS), supportsPostScript);
     putBoolean(buf, "calledBefore", false, calledBefore);
     putDecimal(buf, "maxPageWidth", isLocked(WD), maxPageWidth);
index 349b5b6e53a1f254d6dc7c1e254e8affe6b6fc3a..d7fc4b47456a1806867cb7c7aaf4dceb02256fc1 100644 (file)
@@ -52,6 +52,7 @@ private:
     u_short    supportsVRes;           // VR support bitmask
     bool       supports2DEncoding;     // handles Group 3 2D
     bool       supportsMMR;            // handles Group 4
+    bool       hasV34Trouble;          // has difficulty with V.34
     bool       supportsPostScript;     // handles Adobe NSF protocol
     bool       calledBefore;           // successfully called before
     u_short            maxPageWidth;           // max capable page width
@@ -91,6 +92,7 @@ public:
     u_short getSupportsVRes() const;
     bool getSupports2DEncoding() const;
     bool getSupportsMMR() const;
+    bool getHasV34Trouble() const;
     bool getSupportsPostScript() const;
     bool getCalledBefore() const;
     u_short getMaxPageWidthInPixels() const;
@@ -108,6 +110,7 @@ public:
     void setSupportsVRes(int);
     void setSupports2DEncoding(bool);
     void setSupportsMMR(bool);
+    void setHasV34Trouble(bool);
     void setSupportsPostScript(bool);
     void setCalledBefore(bool);
     void setMaxPageWidthInPixels(int);
@@ -135,6 +138,8 @@ inline bool FaxMachineInfo::getSupports2DEncoding() const
     { return supports2DEncoding; }
 inline bool FaxMachineInfo::getSupportsMMR() const
     { return supportsMMR; }
+inline bool FaxMachineInfo::getHasV34Trouble() const
+    { return hasV34Trouble; }
 inline bool FaxMachineInfo::getSupportsPostScript() const
     { return supportsPostScript; }
 inline bool FaxMachineInfo::getCalledBefore() const    
index 74ba014127976d0d26be7ce802ed11345b2066a2..904edb1fe845b6f0e40379c032a4ecab173939af 100644 (file)
@@ -218,7 +218,7 @@ public:
      * for doing things like keeping cover pages & documents in a
      * single T.30 document.
      */
-    virtual bool faxService() = 0;
+    virtual bool faxService(bool enableV34) = 0;
     virtual bool sendSetup(FaxRequest&, const Class2Params& dis, fxStr& emsg);
     virtual void sendBegin();
     virtual FaxSendStatus getPrologue(Class2Params&,
index b9c76d7b901bdd0282ae8a4e5baafd63aef7d68c..bcd6d8d8dc2672d99fa60e829cbc0ead85ea5fa6 100644 (file)
@@ -126,13 +126,26 @@ FaxServer::sendFax(FaxRequest& fax, FaxMachineInfo& clientInfo, const fxStr& num
 {
     connTime = 0;                              // indicate no connection
     fxStr notice;
+    /*
+     * Calculate initial page-related session parameters so
+     * that braindead Class 2 modems can constrain the modem
+     * before dialing the telephone, and so that Class 1.0
+     * modems know to not enable V.34 support if ECM is not
+     * being used.
+     */
+    Class2Params dis;
+    dis.decodePage(fax.pagehandling);
+    dis.br = fxmin(modem->getBestSignallingRate(), (u_int) fax.desiredbr);
+    dis.ec = (modem->supportsECM() ? fax.desiredec : EC_DISABLE);
+    dis.st = fxmax(modem->getBestScanlineTime(), (u_int) fax.desiredst);
+    dis.bf = BF_DISABLE;
     /*
      * Force the modem into the appropriate class
      * used to send facsimile.  We do this before
      * doing any fax-specific operations such as
      * requesting polling.
      */
-    if (!modem->faxService()) {
+    if (!modem->faxService(!clientInfo.getHasV34Trouble())) {
        sendFailed(fax, send_failed, "Unable to configure modem for fax use");
        return;
     }
@@ -147,17 +160,6 @@ FaxServer::sendFax(FaxRequest& fax, FaxMachineInfo& clientInfo, const fxStr& num
        sendFailed(fax, send_failed, notice);
        return;
     }
-    /*
-     * Calculate initial page-related session parameters so
-     * that braindead Class 2 modems can constrain the modem
-     * before dialing the telephone.
-     */
-    Class2Params dis;
-    dis.decodePage(fax.pagehandling);
-    dis.br = fxmin(modem->getBestSignallingRate(), (u_int) fax.desiredbr);
-    dis.ec = (modem->supportsECM() ? fax.desiredec : EC_DISABLE);
-    dis.st = fxmax(modem->getBestScanlineTime(), (u_int) fax.desiredst);
-    dis.bf = BF_DISABLE;
     if (!modem->sendSetup(fax, dis, notice)) {
        sendFailed(fax, send_failed, notice);
        return;
@@ -207,6 +209,13 @@ FaxServer::sendFax(FaxRequest& fax, FaxMachineInfo& clientInfo, const fxStr& num
                    traceProtocol("SEND file \"%s\"", (const char*) freq.item);
                    fileStart = pageStart = Sys::now();
                    if (!sendFaxPhaseB(fax, freq, clientInfo)) {
+                       /*
+                        * Prevent repeated V.34 errors.
+                        */ 
+                       if (fax.status == send_v34fail) {
+                           fax.status = send_retry;
+                           clientInfo.setHasV34Trouble(true);
+                       }
                        /*
                         * On protocol errors retry more quickly
                         * (there's no reason to wait is there?).
index 4a45ddddd4fcb6e8ee69495755451cc2f69d35d6..97b91ee329fafb6096bdaa8bc6794cdfcfcca416 100644 (file)
@@ -32,7 +32,8 @@ enum FaxSendStatus {
     send_retry,                        // waiting for retry
     send_failed,               // finished w/o success
     send_done,                 // completed successfully
-    send_reformat              // retry with reformatted documents
+    send_reformat,             // retry with reformatted documents
+    send_v34fail               // failed due to V.34-Fax trouble
 };
 #define        send_ok send_done
 #endif /* _FaxSendStatus_ */
index f92a433eb8fd399be1d65c324741b71be00bfc09..869af4ddace2e69ad591163b98893593e4aa9a67 100644 (file)
@@ -29,7 +29,8 @@
 #include "Sys.h"
 #include "FaxServer.h"
 #include "Class0.h"
-#include "Class1.h"
+#include "Class1Ersatz.h"
+#include "Class10.h"
 #include "Class2Ersatz.h"
 #include "Class20.h"
 #include "Class21.h"
@@ -120,7 +121,7 @@ FaxServer::deduceModem()
     h.raisecase();
     /*
      * Probe for modem using type, if specified; otherwise
-     * try Class 2.1, Class 2.0, Class 2, Class 1, and then Class 0 types.
+     * try Class 2.1, Class 2.0, Class 2, Class 1.0, Class 1, and then Class 0 types.
      */
     u_int modemServices = 0;    // fax classes to try (bitmask)
     ClassModem* modem;
@@ -150,6 +151,8 @@ FaxServer::deduceModem()
         modemServices |= SERVICE_CLASS20;
     else if (h == "CLASS2")
         modemServices |= SERVICE_CLASS2;
+    else if (h == "CLASS1.0")
+        modemServices |= SERVICE_CLASS10;
     else if (h == "CLASS1")
         modemServices |= SERVICE_CLASS1;
 
@@ -177,8 +180,16 @@ FaxServer::deduceModem()
            delete modem;
        }
     }
+    if (modemServices & SERVICE_CLASS10) {
+       modem = new Class10Modem(*this, *this);
+       if (modem) {
+           if (modem->setupModem())
+               return modem;
+           delete modem;
+       }
+    }
     if (modemServices & SERVICE_CLASS1) {
-       modem = new Class1Modem(*this, *this);
+       modem = new Class1ErsatzModem(*this, *this);
        if (modem) {
            if (modem->setupModem())
                return modem;
index 36f62bf063149398ff888cbe784d602a5559f7e5..f394d0bf633227ffabcac65e4829b2f16a3b380e 100644 (file)
@@ -47,6 +47,8 @@ C++FILES=ClassModem.c++ \
        FaxModem.c++ \
        Class0.c++ \
        Class1.c++ \
+       Class10.c++ \
+       Class1Ersatz.c++ \
        Class1Poll.c++ \
        Class1Recv.c++ \
        Class1Send.c++ \
@@ -100,6 +102,8 @@ MODEM_OBJS=ClassModem.o \
        FaxModem.o \
        Class0.o \
        Class1.o \
+       Class10.o \
+       Class1Ersatz.o \
        Class1Poll.o \
        Class1Recv.o \
        Class1Send.o \
index 9fbbdc0d86bc8784f970964f91aaf99c105ee54d..c49c8ac3846c697e51be63fd344a00e43bc48f1b 100644 (file)
@@ -105,7 +105,8 @@ static struct {
 { "modemrecvsuccesscmd",       &ModemConfig::recvSuccessCmd },
 { "modemclassquerycmd",                &ModemConfig::classQueryCmd,    "AT+FCLASS=?" },
 { "class0cmd",                 &ModemConfig::class0Cmd,        "AT+FCLASS=0" },
-{ "class1cmd",                 &ModemConfig::class1Cmd,        "AT+FCLASS=1" },
+{ "class1cmd",                 &ModemConfig::class1Cmd },
+{ "class1enablev34cmd",                &ModemConfig::class1EnableV34Cmd },
 { "class1nflocmd",             &ModemConfig::class1NFLOCmd },
 { "class1sflocmd",             &ModemConfig::class1SFLOCmd },
 { "class1hflocmd",             &ModemConfig::class1HFLOCmd },
index adc4624fb9a66f24c70e76978d8450f32aa47a99..fafad9c28fdaedf3c4b9a78329c92c111d9cd021 100644 (file)
@@ -117,6 +117,7 @@ public:
     u_int      pageDoneTimeout;        // page send/receive timeout (ms)
                                        // for class 1:
     fxStr      class1Cmd;              // cmd for setting Class 1
+    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
     fxStr      class1HFLOCmd;          // cmd to setup hardware flow control
index ea1a8c96f5eb51997616437955ec31fcc5f24442..79d752d1b0038a428543a63f19b29fe8abf84fd4 100644 (file)
@@ -259,33 +259,35 @@ FaxT4Timer        integer \s-13100\s+1    \s-1CCITT T.30 T4\s+1 timer (ms)
 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
-Class1ECMSupport       boolean \s-1Yes\s+1     Class 1: enable T.30-A ECM support
-Class1ECMDoCTC boolean \s-1Yes\s+1     Class 1: to perform CTC in ECM protocol
-Class1ECMFrameSize     integer \s-1256\s+1     Class 1: image frame size in ECM protocol
-Class1ExtendedRes      boolean \s-1Yes\s+1     Class 1: enable extended resolution support
-Class1HFLOCmd  string  \-      Class 1: command to set hardware flow control
-Class1FrameOverhead    integer \s-14\s+1       Class 1: extra bytes in a received \s-1HDLC\s+1 frame
-Class1NFLOCmd  string  \-      Class 1: command to set no flow control
-Class1RecvAbortOK      integer \s-1200\s+1     Class 1: max wait (ms) for ``\s-1OK\s+1'' after recv abort
-Class1RecvIdentTimer   integer \s-140000\s+1   Class 1: max wait (ms) for initial ident frame
-Class1SFLOCmd  string  \-      Class 1: command to set software flow control
-Class1PPMWaitCmd       string  \s-1AT+FTS=7\s+1        Class 1: command to stop and wait before PPM
-Class1ResponseWaitCmd  string  \-      Class 1: command to wait before TCF response
-Class1RMQueryCmd       string  \s-1AT+FRM=?\s+1        Class 1: command to query modem data reception rates
-Class1TCFWaitCmd       string  \s-1AT+FTS=7\s+1        Class 1: command to stop and wait before TCF
-Class1TMQueryCmd       string  \s-1AT+FTM=?\s+1        Class 1: command to query modem data transmission rates
-Class1EOPWaitCmd       string  \s-1AT+FTS=9\s+1        Class 1: command to stop and wait before EOP
-Class1MsgRecvHackCmd   string  \s-1""\s+1      Class 1: command to avoid +FCERROR before image data
-Class1TCFMaxNonZero    integer \s-110\s+1      Class 1: max% of non-zero data in good \s-1TCF\s+1
-Class1TCFMinRun        integer \s-11000\s+1    Class 1: minimum zero run in good \s-1TCF\s+1
-Class1TCFRecvHack      boolean \s-1No\s+1      Class 1: deliberately look for carrier loss before TCF
-Class1TCFRecvTimeout   integer \s-14500\s+1    Class 1: max wait (ms) for \s-1TCF\s+1
-Class1TCFResponseDelay integer \s-175\s+1      Class 1: delay between \s-1TCF\s+1 and ack/nak
-Class1TMConnectDelay   integer \s-10\s+1       Class 1: delay between +FTM CONNECT and data transmission
-Class1SendMsgDelay     integer \s-1200\s+1     Class 1: delay before sending image data
-Class1SwitchingCmd     string  \s-1AT+FRS=7\s+1        Class 1: command to ensure silence after HDLC reception
-Class1TrainingRecovery integer \s-11500\s+1    Class 1: delay after failed training
-Class1ValidateV21Frames        boolean \s-1No\s+1      Class 1: check FCS against received frames
+Class1Cmd      string  \s-1AT+FCLASS=1.0\s+1   Class 1.0: command to enter class 1
+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
+Class1ECMDoCTC boolean \s-1Yes\s+1     Class 1/1.0: to perform CTC in ECM protocol
+Class1ECMFrameSize     integer \s-1256\s+1     Class 1/1.0: image frame size in ECM protocol
+Class1ExtendedRes      boolean \s-1Yes\s+1     Class 1/1.0: enable extended resolution support
+Class1HFLOCmd  string  \-      Class 1/1.0: command to set hardware flow control
+Class1FrameOverhead    integer \s-14\s+1       Class 1/1.0: extra bytes in a received \s-1HDLC\s+1 frame
+Class1NFLOCmd  string  \-      Class 1/1.0: command to set no flow control
+Class1RecvAbortOK      integer \s-1200\s+1     Class 1/1.0: max wait (ms) for ``\s-1OK\s+1'' after recv abort
+Class1RecvIdentTimer   integer \s-140000\s+1   Class 1/1.0: max wait (ms) for initial ident frame
+Class1SFLOCmd  string  \-      Class 1/1.0: command to set software flow control
+Class1PPMWaitCmd       string  \s-1AT+FTS=7\s+1        Class 1/1.0: command to stop and wait before PPM
+Class1ResponseWaitCmd  string  \-      Class 1/1.0: command to wait before TCF response
+Class1RMQueryCmd       string  \s-1AT+FRM=?\s+1        Class 1/1.0: command to query modem data reception rates
+Class1TCFWaitCmd       string  \s-1AT+FTS=7\s+1        Class 1/1.0: command to stop and wait before TCF
+Class1TMQueryCmd       string  \s-1AT+FTM=?\s+1        Class 1/1.0: command to query modem data transmission rates
+Class1EOPWaitCmd       string  \s-1AT+FTS=9\s+1        Class 1/1.0: command to stop and wait before EOP
+Class1MsgRecvHackCmd   string  \s-1""\s+1      Class 1/1.0: command to avoid +FCERROR before image data
+Class1TCFMaxNonZero    integer \s-110\s+1      Class 1/1.0: max% of non-zero data in good \s-1TCF\s+1
+Class1TCFMinRun        integer \s-11000\s+1    Class 1/1.0: minimum zero run in good \s-1TCF\s+1
+Class1TCFRecvHack      boolean \s-1No\s+1      Class 1/1.0: deliberately look for carrier loss before TCF
+Class1TCFRecvTimeout   integer \s-14500\s+1    Class 1/1.0: max wait (ms) for \s-1TCF\s+1
+Class1TCFResponseDelay integer \s-175\s+1      Class 1/1.0: delay between \s-1TCF\s+1 and ack/nak
+Class1TMConnectDelay   integer \s-10\s+1       Class 1/1.0: delay between +FTM CONNECT and data transmission
+Class1SendMsgDelay     integer \s-1200\s+1     Class 1/1.0: delay before sending image data
+Class1SwitchingCmd     string  \s-1AT+FRS=7\s+1        Class 1/1.0: command to ensure silence after HDLC reception
+Class1TrainingRecovery integer \s-11500\s+1    Class 1/1.0: delay after failed training
+Class1ValidateV21Frames        boolean \s-1No\s+1      Class 1/1.0: check FCS against received frames
 .sp .5
 Class2Cmd      string  \s-1AT+FCLASS=2\s+1     Class 2: command to enter class 2/2.0
 Class2AbortCmd string  \s-1AT+FK\s+1   Class 2: command to abort active session
@@ -2034,6 +2036,10 @@ 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 Class1EnableV34Cmd
+The command to enable V.34-fax support with at least the desired
+maximum primary channel rate.
+.TP
 .B Class1ECMSupport
 Whether or not to support T.30-A error correction protocol.  Use of
 ECM will require 64 kilobytes of free memory per modem in active use.
index 83d3e816eb7e7b51c1e80d8fe60d091f007fc360..2be024ce2b79047c71d3d25e24ecb24332aa3ab8 100644 (file)
@@ -66,6 +66,7 @@ The following items are recorded:
 \fBTag Type    Description\fP
 calledBefore   boolean device has previously answered at this number
 dialFailures   number  count of consecutive failed dial operations
+hasV34Trouble  boolean there is difficulty sending to this destination with V.34
 lastDialFailure        string  reason for last failed dial operation
 lastSendFailure        string  reason for last failed send attempt
 maxPageWidth   number  maximum page width in pixels in normal resolution
index 51bcf9cfb139e9c3032d47edc71365615d362fd5..07279ce2345daf583cbcdc851c885fdb9b59c5af 100644 (file)
@@ -167,6 +167,7 @@ f none @SPOOL@/config/lucent-isa=../@SRCDIR@/config/lucent-isa 0444 @SYSUID@ @SY
 f none @SPOOL@/config/lucent-mt-2=../@SRCDIR@/config/lucent-mt-2 0444 @SYSUID@ @SYSGID@
 f none @SPOOL@/config/lucent-mt-20=../@SRCDIR@/config/lucent-mt-20 0444 @SYSUID@ @SYSGID@
 f none @SPOOL@/config/lucent-mt-21=../@SRCDIR@/config/lucent-mt-21 0444 @SYSUID@ @SYSGID@
+f none @SPOOL@/config/lucent-mt-10=../@SRCDIR@/config/lucent-mt-10 0444 @SYSUID@ @SYSGID@
 f none @SPOOL@/config/moto-288=../@SRCDIR@/config/moto-288 0444 @SYSUID@ @SYSGID@
 f none @SPOOL@/config/mt-1432=../@SRCDIR@/config/mt-1432 0444 @SYSUID@ @SYSGID@
 f none @SPOOL@/config/nuvo-voyager=../@SRCDIR@/config/nuvo-voyager 0444 @SYSUID@ @SYSGID@
index c9f0a759b57250cfb03e1787796733a33c32fcff..0cd92f52f3d8696dd9d234a9526902c48da1c219 100644 (file)
@@ -153,4 +153,5 @@ const int ETX = 3;          // <DLE><ETX> means end of transfer
 const int DC1 = 17;            // start data transfer (Class 2)
 const int DC2 = 18;            // start data transfer (Class 2.0 and ZyXEL)
 const int CAN = 24;            // abort data transfer
+const int EOT = 4;             // end transmission (Class 1.0)         
 #endif /* _class2_ */