From Lee:
| commit
cec8c45c447d6b25b3d5c2320e39c69e152958e1
| Author: Lee Howard <faxguy@howardsilvan.com>
| Date: Fri Jul 6 01:33:43 2007 +0000
|
| This implements support for +FDB=1 Phase C receive debugging for MultiTechs.
|
| There is more to come... send support needs to be done, for example.
and
| commit
ab579cecee024ef0ca50e6bc47ed789d9b177848
| Author: Lee Howard <faxguy@howardsilvan.com>
| Date: Wed Jul 11 00:27:25 2007 +0000
|
| This is the send-portion of the earlier +FDB=1 debugging support work.
|
| This implements ModemDoPhaseCDebug config option which will make HylaFAX
| query the modem during Phase C transmit to check for modem responses. This
| is done at that time so that they can be displayed at roughly the correct
| time in the log, rather than being buffered up for the end.
|
| This is left off by default, although in theory it should be harmless enabled
| all of the time. There is some potential risk if something goes wrong... like,
| say the modem shows NO CARRIER before the DTE signals <DLE><ETX> (i.e. I can
| envision a modem like iaxmodem doing this if there is a premature hangup). And,
| in that case the NO CARRIER response will get taken from the buffer and simply
| logged rather than interpreted... which means that we have some work to do
| in order to make this better... but this should be good for now.
and
| commit
bedb9087bb28ce6c58cd77b216b6e89c6b7910ca
| Author: Lee Howard <faxguy@howardsilvan.com>
| Date: Fri Jul 20 01:44:41 2007 +0000
|
| Final mod to support MultiTech +FDB DCE debugging.
Class1Modem::sendClass1Data(const u_char* data, u_int cc,
const u_char* bitrev, bool eod, long ms)
{
- bool ok = putModemDLEData(data, cc, bitrev, ms);
+ bool ok = putModemDLEData(data, cc, bitrev, ms, conf.doPhaseCDebug);
if (eod || abortRequested()) {
u_char buf[2];
buf[0] = DLE;
}
if (b == DLE) {
switch (b = getModemDataChar()) {
+ case 0x01: // +FDB=1 support
+ {
+ fxStr dbdata;
+ bool notdone = true;
+ do {
+ b = getModemDataChar();
+ if (b == DLE) {
+ b = getModemDataChar();
+ if (b == 0x04) {
+ notdone = false;
+ protoTrace("DCE DEBUG: %s", (const char*) dbdata);
+ } else {
+ dbdata.append(DLE);
+ }
+ }
+ if (b != '\0' && b != '\r' && b != '\n')
+ dbdata.append(b);
+ } while (notdone);
+ b = nextByte();
+ }
+ break;
case EOF: raiseEOF();
case ETX: raiseRTC(); // RTC
case DLE: break; // <DLE><DLE> -> <DLE>
bool
Class2Modem::recvPageData(TIFF* tif, Status& eresult)
{
+ // be careful about flushing here -- otherwise we can lose +FDB messages
if (flowControl == FLOW_XONXOFF)
- (void) setXONXOFF(FLOW_NONE, FLOW_XONXOFF, ACT_FLUSH);
+ (void) setXONXOFF(FLOW_NONE, FLOW_XONXOFF, ACT_DRAIN);
protoTrace("RECV: send trigger 0%o", recvDataTrigger&0377);
(void) putModem(&recvDataTrigger, 1); // initiate data transfer
}
beginTimedTransfer();
- rc = putModemDLEData(dp, (u_int) totdata, bitrev, getDataTimeout());
+ rc = putModemDLEData(dp, (u_int) totdata, bitrev, getDataTimeout(), conf.doPhaseCDebug);
endTimedTransfer();
protoTrace("SENT %u bytes of data", totdata);
}
"RINGING", // ZyXEL
"+FHR:", // Intel 144e
"+F34:", // Class 1.0 V.34 report
+ "+FDB:", // DCE debugging
"MESSAGE-WAITING", // voice-mail waiting, Conexant
};
#define NNOISE (sizeof (noiseMsgs) / sizeof (noiseMsgs[0]))
return (n);
}
int ClassModem::getModemBit(long ms) { return server.getModemBit(ms); }
-int ClassModem::getModemChar(long ms) { return server.getModemChar(ms); }
+int ClassModem::getModemChar(long ms, bool doquery) { return server.getModemChar(ms, doquery); }
int ClassModem::getModemDataChar() { return server.getModemChar(dataTimeout); }
int ClassModem::getLastByte() { return server.getLastByte(); }
bool ClassModem::didBlockEnd() { return server.didBlockEnd(); }
void ClassModem::resetBlock() { server.resetBlock(); }
bool
-ClassModem::putModemDLEData(const u_char* data, u_int cc, const u_char* bitrev, long ms)
+ClassModem::putModemDLEData(const u_char* data, u_int cc, const u_char* bitrev, long ms, bool doquery)
{
u_char dlebuf[2*1024];
while (cc > 0) {
return (false);
data += n;
cc -= n;
+ /*
+ * Fax is half-duplex. Thus, we shouldn't typically need to read
+ * data from the modem while we are transmitting. However, Phase C
+ * can be long, and things can go wrong, and it will be nice to
+ * see the timing of any messages that will come from the modem,
+ * instead of letting them buffer. Furthermore, advanced Class 2
+ * debugging will send us messages during Phase C. So in those
+ * cases when it will be useful, we query the modem during transmits.
+ *
+ * In the cases where things do go wrong, we'll need to begin
+ * building-in the intelligence to handle that here. But we have
+ * to wait and see what turns up in order to do that.
+ */
+ if (doquery) {
+ int c;
+ fxStr dbdata;
+ do {
+ c = getModemChar(0, true);
+ if (c != EOF && c != '\0' && c != '\r' && c != '\n')
+ dbdata.append(c);
+ else if (dbdata.length()) {
+ protoTrace("DCE DEBUG: %s", (const char*) dbdata);
+ dbdata = "";
+ }
+ } while (c != EOF);
+ }
}
return (true);
}
bool putModem(void* data, int n, long ms = 0);
bool putModemData(void* data, int n);
bool putModemDLEData(const u_char* data, u_int,
- const u_char* brev, long ms);
+ const u_char* brev, long ms, bool doquery = false);
bool putModemLine(const char* cp, long ms = 0);
int getModemBit(long ms = 0);
- int getModemChar(long ms = 0);
+ int getModemChar(long ms = 0, bool doquery = false);
int getModemDataChar();
int getLastByte();
bool didBlockEnd();
badPageHandling = FaxModem::BADPAGE_RTNSAVE; // send RTN but save the page
saveUnconfirmedPages = true; // keep unconfirmed pages
softRTFCC = true; // real-time fax comp. conv. (software)
+ doPhaseCDebug = false; // query modem for responses in Phase C transmit
noAnswerVoice = false; // answer voice calls
idConfig.resize(0);
noAnswerVoice = getBoolean(value);
else if (streq(tag, "modemsoftrtfcc"))
softRTFCC = getBoolean(value);
+ else if (streq(tag, "modemdophasecdebug"))
+ doPhaseCDebug = getBoolean(value);
else if (streq(tag, "saveunconfirmedpages"))
saveUnconfirmedPages = getBoolean(value);
else if (streq(tag, "distinctiverings"))
fxStr tagLineFontFile; // font file for imaging tag lines
u_int recvDataFormat; // received facsimile data format
bool useJobTagLine; // Use Job tagline or use conf taglineformat
+ bool doPhaseCDebug; // Query modem during Phase C for debugging info.
RTNHandling rtnHandling; // RTN signal handling method
BadPageHandling badPageHandling; // bad page (received) handling method
}
int
-ModemServer::getModemChar(long ms)
+ModemServer::getModemChar(long ms, bool isquery)
{
if (rcvNext >= rcvCC) {
int n = 0;
+ if (isquery) {
+ if (fcntl(modemFd, F_SETFL, fcntl(modemFd, F_GETFL, 0) | O_NONBLOCK)) {
+ traceStatus(FAXTRACE_MODEMCOM, "Can not set O_NONBLOCK: errno %u", errno);
+ return (EOF);
+ }
+ n = 5; // only read once
+ }
if (ms) startTimeout(ms);
do
rcvCC = Sys::read(modemFd, (char*) rcvBuf, sizeof (rcvBuf));
while (n++ < 5 && rcvCC == 0);
if (ms) stopTimeout("reading from modem");
+ if (isquery) {
+ if (fcntl(modemFd, F_SETFL, fcntl(modemFd, F_GETFL, 0) &~ O_NONBLOCK))
+ traceStatus(FAXTRACE_MODEMCOM, "Can not reset O_NONBLOCK: errno %u", errno);
+ }
if (rcvCC <= 0) {
if (rcvCC < 0) {
if (errno != EINTR)
- traceStatus(FAXTRACE_MODEMCOM,
- "MODEM READ ERROR: errno %u", errno);
+ if (!isquery || errno != EAGAIN)
+ traceStatus(FAXTRACE_MODEMCOM,
+ "MODEM READ ERROR: errno %u", errno);
}
return (EOF);
} else
void timerExpired(long, long);
void sendDLEETX();
int getModemLine(char buf[], u_int bufSize, long ms = 0);
- int getModemChar(long ms = 0);
+ int getModemChar(long ms = 0, bool isquery = false);
int getModemBit(long ms = 0);
int getLastByte();
bool didBlockEnd();
ModemCommaPauseTimeCmd string \s-1ATS8=2\s+1 command for setting time to pause for ``,'' in dialing string
ModemDialCmd string \s-1ATDT%s\s+1 command for dialing (%s for number to dial)
ModemDialResponseTimeout integer \s-1180000\s+1 dialing command timeout (ms)
+ModemDoPhaseCDebug boolean \s-1No\s+1 query modem responses during Phase C transmit
ModemDTRDropDelay integer \s-175\s+1 delay (ms) between DTR OFF and DTR ON
ModemEchoOffCmd string \s-1ATE0\s+1 command for disabling command echo
ModemFlowControl string \s-1XONXOFF\s+1 \s-1DTE-DCE\s+1 flow control scheme
This additional server-based timeout is provided to guard against
modems that can ``lock up'' when dialing the telephone.
.TP
+.B ModemDoPhaseCDebug
+Whether or not to query the modem for responses during image data
+transmission. Normally the modem should not produce any responses
+during Phase C data transmission. However, in some debugging
+scenarios (i.e. some Class 2.1 modems may show debugging information)
+it may be appropriate to query the modem for responses during
+the data transmission.
+.TP
.B ModemDTRDropDelay
The time, in milliseconds, to pause between placing
.SM DTR