lucent-mt-2 \
lucent-mt-20 \
lucent-mt-21 \
+ lucent-mt-10 \
moto-288 \
mt-1432 \
nuvo-voyager \
# 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
--- /dev/null
+# $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
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 \
MATCHSTR=CLASS1
PROTOSTR=class1
else echo "Hmm, this looks like a Class 1.0 modem."
- MATCHSTR=CLASS10
+ MATCHSTR=CLASS1.0
PROTOSTR=class1.0
fi
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
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
{
echo ""; echo "Class 1.0 stuff..."; echo ""
ATSTR="AT+FCLASS=1.0"
- TryClass1Commands
+ TryClass1dot0Commands
}
class2dot1()
traceBits(modemServices & SERVICE_ALL, serviceNames);
if ((modemServices & SERVICE_CLASS1) == 0)
return (false);
- atCmd(conf.class1Cmd);
+ atCmd(classCmd);
/*
* Query manufacturer, model, and firmware revision.
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 {
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);
}
Class1Modem::setupClass1Parameters()
{
if (modemServices & SERVICE_CLASS1) {
- atCmd(conf.class1Cmd);
+ atCmd(classCmd);
setupFlowControl(flowControl);
atCmd(conf.setupAACmd);
}
* 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));
}
/*
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);
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).
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.
if (j)
protoTrace("--> [%u:%.*s]",
j, buf.length(), (const char*) buf);
- }
+ }
if (c == 0xff) { // address field received
do {
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);
* 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);
* 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;
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)));
}
/*
{
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);
*/
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);
{
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);
{
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);
{
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");
{
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);
}
{
// 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;
}
/*
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
};
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();
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);
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
--- /dev/null
+/* $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()
+{
+}
+
--- /dev/null
+/* $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_ */
--- /dev/null
+/* $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()
+{
+}
+
--- /dev/null
+/* $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_ */
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);
fxStr nsf;
encodeNSF(nsf, HYLAFAX_VERSION);
+ if (useV34) waitForDCEChannel(true); // expect control channel
+
return FaxModem::recvBegin(emsg) && recvIdentification(
0, fxStr::null,
0, fxStr::null,
/*
* 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);
}
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
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
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
}
* 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
* 5 seconds elapse.
*/
// sendingHDLC =
- atCmd(thCmd, AT_CONNECT);
+ if (!useV34) atCmd(thCmd, AT_CONNECT);
} else {
(void) transmitFrame(FCF_MCF|FCF_RCVR);
}
void
Class1Modem::abortPageRecv()
{
+ if (useV34) return; // nothing to do in V.34
char c = CAN; // anything other than DC1/DC3
putModem(&c, 1, 1);
}
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();
}
} 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);
}
// 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");
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");
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);
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
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");
} 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);
}
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 {
time_t start = Sys::now();
HDLCFrame frame(conf.class1FrameOverhead);
+ if (useV34) waitForDCEChannel(true); // expect control channel
+
bool framerecvd = recvRawFrame(frame);
for (;;) {
if (framerecvd) {
/*
* 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;
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) {
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)) {
}
/*
- * 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);
+ }
}
/*
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
* 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);
}
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);
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).");
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);
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++) {
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);
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);
/* 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));
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);
}
} 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);
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);
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);
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);
}
}
}
+ 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,
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) {
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));
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);
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);
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);
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);
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);
}
* for sending/received facsimile.
*/
bool
-Class2Modem::faxService()
+Class2Modem::faxService(bool enableV34)
{
return setupClass2Parameters() && class2Cmd(lidCmd, lid);
}
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
"", // 7
"\"Voice\"", // SERVICE_VOICE
};
-const char* ClassModem::ATresponses[15] = {
+const char* ClassModem::ATresponses[16] = {
"Nothing", // AT_NOTHING
"OK", // AT_OK
"Connection established", // AT_CONNECT
"<Empty line>", // AT_EMPTYLINE
"<Timeout>", // AT_TIMEOUT
"<dle+etx>", // AT_DLEETX
+ "End of transmission", // AT_DLEEOT
"<xon>", // AT_XON
"<Unknown response>" // AT_OTHER
};
"RRING", // Telebit
"RINGING", // ZyXEL
"+FHR:", // Intel 144e
+ "+F34:", // Class 1.0 V.34 report
};
#define NNOISE (sizeof (noiseMsgs) / sizeof (noiseMsgs[0]))
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,
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) {
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)
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
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&);
supportsVRes = other.supportsVRes;
supports2DEncoding = other.supports2DEncoding;
supportsMMR = other.supportsMMR;
+ hasV34Trouble = other.hasV34Trouble;
supportsPostScript = other.supportsPostScript;
calledBefore = other.calledBefore;
maxPageWidth = other.maxPageWidth;
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
#define LN 5
#define BR 6
#define ST 7
+#define V34 8
#define setLocked(b,ix) locked |= b<<ix
} 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);
{ 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)
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);
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
u_short getSupportsVRes() const;
bool getSupports2DEncoding() const;
bool getSupportsMMR() const;
+ bool getHasV34Trouble() const;
bool getSupportsPostScript() const;
bool getCalledBefore() const;
u_short getMaxPageWidthInPixels() const;
void setSupportsVRes(int);
void setSupports2DEncoding(bool);
void setSupportsMMR(bool);
+ void setHasV34Trouble(bool);
void setSupportsPostScript(bool);
void setCalledBefore(bool);
void setMaxPageWidthInPixels(int);
{ 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
* 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&,
{
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;
}
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;
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?).
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_ */
#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"
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;
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;
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;
FaxModem.c++ \
Class0.c++ \
Class1.c++ \
+ Class10.c++ \
+ Class1Ersatz.c++ \
Class1Poll.c++ \
Class1Recv.c++ \
Class1Send.c++ \
FaxModem.o \
Class0.o \
Class1.o \
+ Class10.o \
+ Class1Ersatz.o \
Class1Poll.o \
Class1Recv.o \
Class1Send.o \
{ "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 },
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
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
.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.
\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
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@
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_ */