Class2.c++
上传用户:weiyuanprp
上传日期:2020-05-20
资源大小:1169k
文件大小:30k
源码类别:

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: Class2.c++,v 1.25 2009/01/20 13:51:15 faxguy Exp $ */
  2. /*
  3.  * Copyright (c) 1990-1996 Sam Leffler
  4.  * Copyright (c) 1991-1996 Silicon Graphics, Inc.
  5.  * HylaFAX is a trademark of Silicon Graphics
  6.  *
  7.  * Permission to use, copy, modify, distribute, and sell this software and 
  8.  * its documentation for any purpose is hereby granted without fee, provided
  9.  * that (i) the above copyright notices and this permission notice appear in
  10.  * all copies of the software and related documentation, and (ii) the names of
  11.  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  12.  * publicity relating to the software without the specific, prior written
  13.  * permission of Sam Leffler and Silicon Graphics.
  14.  * 
  15.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  16.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  17.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  18.  * 
  19.  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  20.  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  21.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  22.  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  23.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  24.  * OF THIS SOFTWARE.
  25.  */
  26. #include "Class2.h"
  27. #include "ModemConfig.h"
  28. #include <ctype.h>
  29. Class2Modem::Class2Modem(FaxServer& s, const ModemConfig& c) : FaxModem(s,c)
  30. {
  31.     hangupCode[0] = '';
  32.     serviceType = 0; // must be set in derived class
  33.     useExtendedDF = false; // T.32 Amendment 1 extension for data format is detectable
  34.     useJP = false; // JP +FCC option support is detectable
  35.     imagefd = 0;
  36. }
  37. Class2Modem::~Class2Modem()
  38. {
  39. }
  40. /*
  41.  * Check if the modem is a Class 2 modem and, if so,
  42.  * configure it for use.  We try to confine a lot of
  43.  * the manufacturer-specific bogosity here and leave
  44.  * the remainder of the Class 2 support fairly generic.
  45.  */
  46. bool
  47. Class2Modem::setupModem(bool isSend)
  48. {
  49.     if (!selectBaudRate(conf.maxRate, conf.flowControl, conf.flowControl))
  50. return (false);
  51.     // Query service support information
  52.     fxStr s;
  53.     if (doQuery(conf.classQueryCmd, s, 5000) && FaxModem::parseRange(s, modemServices))
  54. traceBits(modemServices & SERVICE_ALL, serviceNames);
  55.     if ((modemServices & serviceType) == 0)
  56. return (false);
  57.     atCmd(classCmd);
  58.     /*
  59.      * Query manufacturer, model, and firmware revision.
  60.      * We use the manufacturer especially as a key to
  61.      * working around firmware bugs (yech!).
  62.      */
  63.     if (setupManufacturer(modemMfr)) {
  64. modemCapability("Mfr " | modemMfr);
  65. modemMfr.raisecase();
  66.     }
  67.     (void) setupModel(modemModel);
  68.     (void) setupRevision(modemRevision);
  69.     if (modemModel != "")
  70. modemCapability("Model " | modemModel);
  71.     if (modemRevision != "")
  72. modemCapability("Revision " | modemRevision);
  73.     /*
  74.      * Get modem capabilities and calculate best signalling
  75.      * rate, data formatting capabilities, etc. for use in
  76.      * T.30 negotiations.
  77.      */
  78.     fxStr t30parms;
  79.     if (!doQuery(dccQueryCmd, t30parms, 500)) {
  80. serverTrace("Error getting modem capabilities");
  81. return (false);
  82.     }
  83.     /*
  84.      * Syntax: (vr),(br),(wd),(ln),(df),(ec),(bf),(st)[,(jp)]
  85.      * where,
  86.      * vr vertical resolution
  87.      * br bit rate
  88.      * wd page width
  89.      * ln page length
  90.      * df data compression
  91.      * ec error correction
  92.      * bf binary file transfer
  93.      * st scan time/line
  94.      * jp JPEG support (optional)
  95.      */
  96.     if (!parseRange(t30parms, modemParams)) {
  97. serverTrace("Error parsing " | dccQueryCmd | " response: "
  98.     """ | t30parms | """);
  99. return (false);
  100.     }
  101.     /*
  102.      * The EC parameter varies between the Class 2 and Class 2.0 specs.
  103.      * And because modems don't all adhere to the respective spec, we must adjust
  104.      * modemParams based both on serviceType and on configuration to compensate.
  105.      */
  106.     if (conf.class2ECMType == ClassModem::ECMTYPE_CLASS20 ||
  107.        (conf.class2ECMType == ClassModem::ECMTYPE_UNSET && serviceType != SERVICE_CLASS2)) {
  108.     // The Class 2.0 spec wisely lumps both 64-byte and 256-byte ECM together
  109.     // because an ECM receiver must support both anyway per T.30-A.
  110.     modemParams.ec ^= BIT(EC_DISABLE); // don't adjust EC_DISABLE
  111.     modemParams.ec <<= 1; // simple adjustment
  112.     modemParams.ec |= BIT(EC_DISABLE); // reset EC_DISABLE
  113.     if (modemParams.ec & BIT(EC_ENABLE256))
  114. modemParams.ec |= BIT(EC_ENABLE64);
  115.     }
  116.     bool jbigsupported = false;
  117.     switch (conf.class2JBIGSupport) {
  118. case FaxModem::JBIG_FULL:
  119.     jbigsupported = true;
  120.     break;
  121. case FaxModem::JBIG_SEND:
  122.     jbigsupported = isSend;
  123.     break;
  124. case FaxModem::JBIG_RECV:
  125.     jbigsupported = !isSend;
  126.     break;
  127. }
  128.     if (!jbigsupported) {
  129. modemParams.df &= ~BIT(DF_JBIG);
  130.     }
  131.     traceModemParams();
  132.     /*
  133.      * Check to see if the modem supports copy quality checking.
  134.      * If the modem is capable, then enable it using the configured
  135.      * commands.  If the modem is incapable of doing copy quality
  136.      * checking, then the host will do the work.
  137.      *
  138.      * The recommendation for AT+FCQ varies significantly between
  139.      * the Class 2 and Class 2.0 specification.
  140.      *
  141.      * An assumption is made that Class2CQCmd, if configured, enables
  142.      * all available copy quality services (or disables them if none
  143.      * are available).
  144.      */
  145.     cqCmds = "";
  146.     sendCQ = 0;
  147.     if (serviceType == SERVICE_CLASS2) {
  148. /*
  149.  * The AT+FCQ=? response indicates which compression
  150.          * formats are copy-quality checked.
  151.  */
  152. if (doQuery(conf.class2CQQueryCmd, s) && FaxModem::parseRange(s, modemCQ)) {
  153.     if (modemCQ >>= 1)
  154. cqCmds = conf.class2CQCmd;
  155. } else
  156.     modemCQ = 0;
  157. static const char* whatCQ[4] = { "no", "1D", "2D", "1D+2D" };
  158. modemSupports("%s copy quality checking%s", whatCQ[modemCQ&3],
  159.     (modemCQ && cqCmds == "" ? " (but not enabled)" : ""));
  160.     } else { // SERVICE_CLASS20, SERVICE_CLASS21
  161. /*
  162.  * The AT+FCQ=? response indicates whether or not copy-quality
  163.          * checking and/or correction are supported.  Host CQ cannot
  164.  * be used if the modem performs copy-quality correction.
  165.  */
  166. cqCmds = conf.class2CQCmd;
  167. if (doQuery(conf.class2CQQueryCmd, s) && FaxModem::vparseRange(s, 0, 2, &modemCQ, &sendCQ)) {
  168.     modemCQ >>= 1;
  169.     sendCQ >>= 1;
  170. } else {
  171.     modemCQ = 0;
  172.     sendCQ = 0;
  173. }
  174. static const char* whatCQ[4] = { "no", "checking", "correction", "checking and correction" };
  175. if (modemCQ)
  176.     modemSupports("receiving copy quality %s", whatCQ[modemCQ&3]);
  177. else modemSupports("no receiving copy quality services");
  178. if (sendCQ)
  179.     modemSupports("sending copy quality %s%s", whatCQ[sendCQ&3],
  180. (sendCQ && cqCmds == "" ? " (but not enabled)" : ""));
  181. else modemSupports("no sending copy quality services");
  182.     }
  183.     /*
  184.      * In Class 2 we follow spec defaults and assume that if cqCmds is null 
  185.      * that CQ is not enabled.  In Class 2.0/2.1 we follow spec default and
  186.      * assume the opposite.  In order to know otherwise we'd need to make
  187.      * sense of AT+FCQ? and incorporate that in the messages above.
  188.      */
  189.     if (serviceType == SERVICE_CLASS2) {
  190. if (cqCmds == "") modemCQ = 0;
  191.     } else { // SERVICE_CLASS20, SERVICE_CLASS21
  192. if (cqCmds == "" && modemCQ) modemCQ = 1;
  193.     }
  194.     /*
  195.      * Deduce if modem supports T.class2-defined suport for
  196.      * subaddress, selective polling address, and passwords.
  197.      */
  198.     int sub = 0;
  199.     int sep = 0;
  200.     int pwd = 0;
  201.     // some modems don't support an AP-query command
  202.     if (strcasecmp(conf.class2APQueryCmd, "none") != 0) {
  203. if (doQuery(conf.class2APQueryCmd, s))
  204.     (void) vparseRange(s, 0, 3, &sub, &sep, &pwd);
  205.     }
  206.     if (sub & BIT(1)) {
  207. saCmd = conf.class2SACmd;
  208. modemSupports("subaddressing");
  209.     } else
  210. saCmd = "";
  211.     if (sep & BIT(1)) {
  212. paCmd = conf.class2PACmd;
  213. modemSupports("selective polling");
  214.     } else
  215. paCmd = "";
  216.     if (pwd & BIT(1)) {
  217. pwCmd = conf.class2PWCmd;
  218. modemSupports("passwords");
  219.     } else
  220. pwCmd = "";
  221.     if ((sub|sep|pwd) & BIT(1))
  222. apCmd = conf.class2APCmd;
  223.     /*
  224.      * Check if the modem supports polled reception of documents.
  225.      */
  226.     u_int t;
  227.     // some modems don't support an SPL command
  228.     if (strcasecmp(splCmd, "none") != 0) {
  229. if (doQuery(splCmd | "=?", s) && FaxModem::parseRange(s, t))
  230.     hasPolling = (t & BIT(1)) != 0;
  231.     }
  232.     /*
  233.      * Define the code to send to the modem to trigger the
  234.      * transfer of received Phase C data to the host.  Most
  235.      * modems based on SP-2388-A (Class 2) use DC1 while those
  236.      * based on SP-2388-B (Class 2.0) use DC2.  There are some
  237.      * exceptions (ZyXEL and modems based on Rockwell RC32ACL
  238.      * parts), but they are expected to set the appropriate
  239.      * value in the config file.
  240.      */
  241.     if (conf.class2RecvDataTrigger == "")
  242. recvDataTrigger = (serviceType == SERVICE_CLASS2 ? DC1 : DC2);
  243.     else
  244. recvDataTrigger = conf.class2RecvDataTrigger[0];
  245.     /*
  246.      * SP-2388-A (Class 2) specifies that the modem should send
  247.      * XON to the host when it is ready to received page data
  248.      * during a transmission.  This went away in SP-2388-B and in
  249.      * the final Class 2.0 spec.  Consequently we configure the
  250.      * modem either to ignore it (Class 2.0/2.1) or to use whatever
  251.      * is configured (it defaults to true).
  252.      */
  253.     if ((serviceType == SERVICE_CLASS20) || (serviceType == SERVICE_CLASS21))
  254. xmitWaitForXON = false;
  255.     else
  256. xmitWaitForXON = conf.class2XmitWaitForXON;
  257.     /*
  258.      * Most Class2 modems require recvFillOrder == MSB2LSB
  259.      * (Rockwell bug, which has become "standard" now).
  260.      *
  261.      * The only known exception is early Multitech modems,
  262.      * that follow original Class2 specs (LSB2MSB). They  report the
  263.      * manufacturer as "Multi-Tech Sys" or "Multi-Tech Systems".
  264.      *
  265.      * Other Class2 modems that requires recvFillOrder == LSB2MSB
  266.      * (if any) are expected to set the appropriate value in the
  267.      * config file.
  268.      */
  269.     if( conf.recvFillOrder == 0 &&  serviceType == SERVICE_CLASS2 ){
  270.         if ( modemMfr.find(0, "MULTI-TECH") >= modemMfr.length()  ){
  271.             // Not a Multitech
  272.             recvFillOrder = FILLORDER_MSB2LSB;
  273.         }
  274.     }
  275.     setupClass2Parameters(); // send parameters to the modem
  276.     return (true);
  277. }
  278. void
  279. Class2Modem::pokeConfig(bool isSend)
  280. {
  281. }
  282. /*
  283.  * Switch the modem to Class 2/2.0 and  insure parameters
  284.  * are set as we want them since some modems reset state
  285.  * on switching into Class 2/2.0.  Note that receive-related
  286.  * parameters are only set on modem reset because some
  287.  * commands, like setupAACmd, can reset the modem to Class
  288.  * 0.  This interface is used only for outbound use or when
  289.  * followed by setup of receive-specific parameters.
  290.  */
  291. bool
  292. Class2Modem::setupClass2Parameters(bool enableV34, bool enableV17)
  293. {
  294.     if (modemServices & serviceType) { // when resetting at startup
  295. setupFlowControl(flowControl); // flow control
  296. // some modems don't support a TBC command
  297. if (strcasecmp(tbcCmd, "none") != 0) {
  298.     atCmd(tbcCmd); // stream mode
  299. }
  300. atCmd(borCmd); // Phase B+C bit order
  301. /*
  302.  * Set Phase C data transfer timeout parameter.
  303.  * Note that we also have our own timeout parameter
  304.  * that should be at least as large (though it
  305.  * probably doesn't matter).  Some modem manufacturers
  306.  * do not support this command (or perhaps they
  307.  * hide it under a different name).
  308.  */
  309. if (strcasecmp(phctoCmd, "none") != 0) {
  310.     atCmd(phctoCmd);
  311. }
  312. (void) atCmd(cqCmds); // copy quality checking
  313. (void) atCmd(nrCmd); // negotiation reporting
  314. (void) atCmd(apCmd); // address&polling reporting
  315. (void) atCmd(pieCmd); // program interrupt enable
  316. // HDLC frame tracing
  317. // some modems don't support a BUG command
  318. if (getHDLCTracing() && strcasecmp(bugCmd, "none") != 0)
  319.     atCmd(bugCmd);
  320. /*
  321.  * Force the DCC so that we can override
  322.  * whatever the modem defaults are.
  323.  */
  324. setupDCC(enableV34, enableV17);
  325.     }
  326.     return (true);
  327. }
  328. /*
  329.  * Setup receive-specific parameters.
  330.  */
  331. bool
  332. Class2Modem::setupReceive()
  333. {
  334.     /*
  335.      * Try to setup byte-alignment of received EOL's.
  336.      * As always, this is problematic.  If the modem
  337.      * does not support this, but accepts the command
  338.      * (as many do!), then received facsimile will be
  339.      * incorrectly tagged as having byte-aligned EOL
  340.      * codes in them--not usually much of a problem.
  341.      */
  342.     if (conf.class2RELCmd != "" && atCmd(conf.class2RELCmd))
  343. group3opts |= GROUP3OPT_FILLBITS;
  344.     else
  345. group3opts &= ~GROUP3OPT_FILLBITS;
  346.     atCmd(crCmd); // enable receiving
  347.     /*
  348.      * Enable adaptive-answer support.  If we're configured,
  349.      * we'll act like getty and initiate a login session if
  350.      * we get a data connection.  Note that we do this last
  351.      * so that the modem can be left in a state other than
  352.      * +FCLASS=2 (e.g. Rockwell-based modems often need to be
  353.      * in Class 0).
  354.      */
  355.     return atCmd(conf.setupAACmd);
  356. }
  357. /*
  358.  * Send the modem any commands needed to force use of
  359.  * the specified flow control scheme.
  360.  */
  361. bool
  362. Class2Modem::setupFlowControl(FlowControl fc)
  363. {
  364.     switch (fc) {
  365.     case FLOW_NONE: return atCmd(noFlowCmd);
  366.     case FLOW_XONXOFF: return atCmd(softFlowCmd);
  367.     case FLOW_RTSCTS: return atCmd(hardFlowCmd);
  368.     }
  369.     return (true);
  370. }
  371. /*
  372.  * Setup DCC to reflect best capabilities of the server.
  373.  */
  374. bool
  375. Class2Modem::setupDCC(bool enableV34, bool enableV17)
  376. {
  377.     params.vr = getVRes();
  378.     params.br = enableV34 ? getBestSignallingRate() : fxmin((u_int) BR_14400, getBestSignallingRate());
  379.     params.br = enableV17 ? params.br : fxmin((u_int) BR_9600, getBestSignallingRate());
  380.     params.wd = getBestPageWidth();
  381.     params.ln = getBestPageLength();
  382.     params.df = useExtendedDF ? modemParams.df : getBestDataFormat();
  383.     params.ec = getBestECM();
  384.     params.bf = BF_DISABLE;
  385.     params.st = getBestScanlineTime();
  386.     params.jp = modemParams.jp;
  387.     return class2Cmd(dccCmd, params, true);
  388. }
  389. /*
  390.  * Parse a ``capabilities'' string from the modem and
  391.  * return the values through the params parameter.
  392.  */
  393. bool
  394. Class2Modem::parseClass2Capabilities(const char* cap, Class2Params& params, bool isDIS)
  395. {
  396.     /*
  397.      * Some modems report capabilities in hex values, others decimal.
  398.      */
  399.     fxStr notation;
  400.     if (conf.class2UseHex) {
  401. if (useJP) notation = "%X,%X,%X,%X,%X,%X,%X,%X,%X";
  402. else notation = "%X,%X,%X,%X,%X,%X,%X,%X";
  403.     } else {
  404. if (useJP) notation = "%d,%d,%d,%d,%d,%d,%d,%d,%d";
  405. else notation = "%d,%d,%d,%d,%d,%d,%d,%d";
  406.     }
  407.     int n = 0;
  408.     if (useJP) {
  409. n = sscanf(cap, notation,
  410.     &params.vr, &params.br, &params.wd, &params.ln,
  411.     &params.df, &params.ec, &params.bf, &params.st, &params.jp);
  412.     } else {
  413. n = sscanf(cap, notation,
  414.     &params.vr, &params.br, &params.wd, &params.ln,
  415.     &params.df, &params.ec, &params.bf, &params.st);
  416. params.jp = 0;
  417.     }
  418.     if ((useJP && n == 9) || (n == 8)) {
  419. if (useJP && (n == 8)) {
  420.     /*
  421.      * The modem was previously determined to support JP; however,
  422.      * this response does not include it.  So we just set it to zero.
  423.      */
  424.     params.jp = 0;
  425. }
  426. if (params.ec != EC_DISABLE && (conf.class2ECMType == ClassModem::ECMTYPE_CLASS20 ||
  427.    (conf.class2ECMType == ClassModem::ECMTYPE_UNSET && serviceType != SERVICE_CLASS2)))
  428.     params.ec += 1; // simple adjustment, drops EC_ENABLE64
  429. /*
  430.  * Clamp values to insure modem doesn't feed us
  431.  * nonsense; should log bogus stuff also.
  432.  */
  433. params.vr = params.vr & VR_ALL;
  434. params.br = fxmin(params.br, (u_int) BR_33600);
  435. params.wd = fxmin(params.wd, (u_int) WD_A3);
  436. params.ln = fxmin(params.ln, (u_int) LN_INF);
  437. if (useExtendedDF) {
  438.     /*
  439.      * The T.32-A1 DF extension presents us with a bitmap-like presentation
  440.      * similar to VR here... but leaves 2D-MMR = 3 for backwards-compatibility.
  441.      *
  442.      * 0 = 1D-MH, 1 = 2D-MR, 3 = 2D-MMR, 4 = JBIG-L0, 8 = JBIG
  443.      */
  444.     u_int dfscan = params.df;
  445.     if (isDIS) {
  446. params.df = BIT(DF_1DMH);
  447. if (dfscan & 0x1) params.df |= BIT(DF_2DMR);
  448. if (dfscan & 0x2) params.df |= BIT(DF_2DMMR); // don't require MR for MMR
  449. if (dfscan & 0x4) params.df |= BIT(DF_JBIG); // JBIG L0 is JBIG to us
  450. if (dfscan & 0x8) params.df |= BIT(DF_JBIG);
  451.     } else {
  452. // Dex 855 is known to indicate MMR in addition to JBIG when sending JBIG (0x7).
  453. // Thus only interpret DF=MMR when 0x3.
  454. params.df = DF_1DMH;
  455. if (dfscan == 0x3) params.df = DF_2DMMR;
  456. else if (dfscan & 0x1) params.df = DF_2DMR;
  457. else if (dfscan & 0x4) params.df = DF_JBIG; // JBIG L0 is JBIG to us
  458. else if (dfscan & 0x8) params.df = DF_JBIG;
  459.     }
  460. } else {
  461.     params.df = fxmin(params.df, (u_int) DF_2DMMR);
  462.     /*
  463.      * Table 21 T.32 does not match Table 2 T.30 very well in some aspects.
  464.      * Data format is one of those things.  When dealing with DIS we use DF 
  465.      * as a bitmap to suit T.30, but a T.32-following modem will only report 
  466.      * one supported receiver format (and not all of them).  Thus when 
  467.      * parsing T.32 DIS we must convert the modem response to a bitmap.  
  468.      * However, due to the incongruency between T.30 and T.32 the bitmap will 
  469.      * only contain the the reported format and the required format.
  470.      */
  471.     if (isDIS) {
  472. params.df = BIT(params.df) | BIT(DF_1DMH);
  473.     }
  474. }
  475. if (params.ec > EC_ECLFULL) // unknown, disable use
  476.     params.ec = EC_DISABLE;
  477. if (params.bf > BF_ENABLE)
  478.     params.bf = BF_DISABLE;
  479. params.st = fxmin(params.st, (u_int) ST_40MS);
  480. int jpscan = params.jp;
  481. params.jp = 0;
  482. if (isDIS) {
  483.     if (jpscan & 0x1) params.jp |= BIT(JP_GREY);
  484.     if (jpscan & 0x2) params.jp |= BIT(JP_COLOR);
  485. } else {
  486.     if (jpscan == 0x1) params.jp = JP_GREY;
  487.     else if (jpscan & 0x2) params.jp = JP_COLOR;
  488. }
  489. return (true);
  490.     } else {
  491. protoTrace("MODEM protocol botch, can not parse "%s"", cap);
  492. return (false);
  493.     }
  494. }
  495. /*
  496.  * Place the modem into the appropriate state
  497.  * for sending facsimile.
  498.  */
  499. bool
  500. Class2Modem::faxService(bool enableV34, bool enableV17)
  501. {
  502.     if (!enableV17 && conf.class2DisableV17Cmd != "" && !atCmd(conf.class2DisableV17Cmd)) return (false);
  503.     if (!enableV34 && conf.class2DisableV34Cmd != "" && !atCmd(conf.class2DisableV34Cmd)) return (false);
  504.     return setupClass2Parameters(enableV34, enableV17);
  505. }
  506. bool
  507. Class2Modem::setupRevision(fxStr& revision)
  508. {
  509.     if (FaxModem::setupRevision(revision)) {
  510. /*
  511.  * Cleanup ZyXEL response (argh), modem gives:
  512.  * +FREV? "U1496E   V 5.02 M    "
  513.  */
  514. if (modemMfr == "ZYXEL") {
  515.     u_int pos = modemRevision.next(0, ' ');
  516.     if (pos != modemRevision.length()) { // rev. has model 1st
  517. pos = modemRevision.skip(pos, ' ');
  518. modemRevision.remove(0, pos);
  519.     }
  520. }
  521. return (true);
  522.     } else
  523. return (false);
  524. }
  525. bool
  526. Class2Modem::setupModel(fxStr& model)
  527. {
  528.     if (FaxModem::setupModel(model)) {
  529. /*
  530.  * Cleanup ZyXEL response (argh), modem gives:
  531.  * +FMDL? "U1496E   V 5.02 M    "
  532.  */
  533. if (modemMfr == "ZYXEL")
  534.     modemModel.resize(modemModel.next(0, ' ')); // model is first word
  535. return (true);
  536.     } else
  537. return (false);
  538. }
  539. bool
  540. Class2Modem::supportsPolling() const
  541. {
  542.     return (hasPolling);
  543. }
  544. /*
  545.  * Strip any quote marks from a string.  This
  546.  * is used for received TSI+CSI strings passed
  547.  * to the server.
  548.  */
  549. fxStr
  550. Class2Modem::stripQuotes(const char* cp)
  551. {
  552.     fxStr s(cp);
  553.     u_int pos = s.next(0, '"');
  554.     while (pos != s.length()) {
  555. s.remove(pos,1);
  556. pos = s.next(0,'"');
  557.     }
  558.     return (s);
  559. }
  560. /*
  561.  * Construct the Calling Station Identifier (CSI) string
  562.  * for the modem.  We permit any ASCII printable characters
  563.  * in the string though the spec says otherwise.  The max
  564.  * length is 20 characters (per the spec), and we pad the
  565.  * string to that length to avoid buggy modems from putting
  566.  * garbage in the void.
  567.  */
  568. void
  569. Class2Modem::setLID(const fxStr& number)
  570. {
  571.     lid.resize(0);
  572.     for (u_int i = 0, n = number.length(); i < 20; i++) {
  573. if (i < n) {
  574.     char c = number[i];
  575.     if (isprint(c) || c == ' ')
  576. lid.append(c);
  577. } else
  578.     lid.append(' ');
  579.     }
  580.     class2Cmd(lidCmd, lid); // for DynamicConfig
  581. }
  582. /* 
  583.  * Modem manipulation support.
  584.  */
  585. /*
  586.  * Reset a Class 2 modem.
  587.  */
  588. bool
  589. Class2Modem::reset(long ms)
  590. {
  591.     return (FaxModem::reset(ms) && setupClass2Parameters());
  592. }
  593. /*
  594.  * Wait (carefully) for some response from the modem.
  595.  * In particular, beware of unsolicited hangup messages
  596.  * from the modem.  Some modems seem to not use the
  597.  * Class 2 required +FHNG response--and instead give
  598.  * us an unsolicited NO CARRIER message.  Isn't life
  599.  * wondeful?
  600.  */
  601. bool
  602. Class2Modem::waitFor(ATResponse wanted, long ms)
  603. {
  604.     for (;;) {
  605. ATResponse response = atResponse(rbuf, ms);
  606. if (response == wanted)
  607.     return (true);
  608. switch (response) {
  609. case AT_TIMEOUT:
  610. case AT_EMPTYLINE:
  611. case AT_ERROR:
  612. case AT_NOCARRIER:
  613. case AT_NODIALTONE:
  614. case AT_NOANSWER:
  615. case AT_RING:
  616.     modemTrace("MODEM %s", ATresponses[response]);
  617. case AT_OK:
  618.     return (false);
  619. case AT_FHNG:
  620.     // return hangup status, but try to wait for requested response
  621.     { char buf[1024]; (void) atResponse(buf, 2*1000); }
  622.     return (isNormalHangup());
  623. }
  624.     }
  625. }
  626. /*
  627.  * Interfaces for sending a Class 2 commands.
  628.  */
  629. /*
  630.  * Send <cmd>=<a0> and wait for response.
  631.  */
  632. bool
  633. Class2Modem::class2Cmd(const fxStr& cmd, int a0, ATResponse r, long ms)
  634. {
  635.     return atCmd(cmd | fxStr(a0, "=%u"), r, ms);
  636. }
  637. /*
  638.  * Send <cmd>=<t.30 parameters> and wait response.
  639.  */
  640. bool
  641. Class2Modem::class2Cmd(const fxStr& cmd, const Class2Params& p, bool isDCC, ATResponse r, long ms)
  642. {
  643.     bool ecm20 = false;
  644.     if (conf.class2ECMType == ClassModem::ECMTYPE_CLASS20 ||
  645.        (conf.class2ECMType == ClassModem::ECMTYPE_UNSET && serviceType != SERVICE_CLASS2))
  646. ecm20 = true;
  647.     return atCmd(cmd | "=" | p.cmd(conf.class2UseHex, ecm20, (isDCC && useExtendedDF), useJP), r, ms);
  648. }
  649. /*
  650.  * Send <cmd>="<s>" and wait for response.
  651.  */
  652. bool
  653. Class2Modem::class2Cmd(const fxStr& cmd, const fxStr& s, ATResponse r, long ms)
  654. {
  655.     return atCmd(cmd | "="" | s | """, r, ms); // XXX handle escapes
  656. }
  657. /*
  658.  * Parse a Class 2 parameter specification
  659.  * and return the resulting bit masks.
  660.  */
  661. bool
  662. Class2Modem::parseRange(const char* cp, Class2Params& p)
  663. {
  664.     /*
  665.      * VR, BF, and JP are already reported as bitmap
  666.      * values accoring to T.32 Table 21.
  667.      * In vparseRange(), VR:nargs=7, BF:nargs=1.
  668.      */
  669.     int masked = (1 << 7) + (1 << 1); // reversed, count-down style
  670.     if (!vparseRange(cp, masked, 8, &p.vr,&p.br,&p.wd,&p.ln,&p.df,&p.ec,&p.bf,&p.st))
  671. return (false);
  672.     p.vr &= VR_ALL;
  673.     p.br &= BR_ALL;
  674.     p.wd &= WD_ALL;
  675.     p.ln &= LN_ALL;
  676.     if ((p.df & 0x10) && (p.df & 0x100)) { // supports JBIG via T.32-A1 extension
  677. /*
  678.  * Old T.32 Table 21 does not provide for JBIG data formats.
  679.  * In amendment 1 the ITU has extended the DF parameter to include JBIG by 
  680.  * assigning "4" to JBIG L0 and "8" to JBIG.  The +FCC response for DF may  
  681.  * look like (00-0F) or possibly even (00-01,03-05,07-09,0B-0D,0F).  However, 
  682.  * in so doing the ITU has also changed the DF parameter (in +FIS and +FCS 
  683.  * responses) into a bitmap.  Yet, an +FCC response for DF of "(5DDD)" or "(0F)" 
  684.  * is not backwards-compatible with older T.32.  So the +FCC response is in 
  685.  * this backwards-compatible presentation (00-0F), and +FCS and +FIS responses 
  686.  * are not.
  687.  */
  688. useExtendedDF = true;
  689. p.df &= DF_ALL;
  690. p.df |= BIT(DF_JBIG);
  691.     } else
  692. p.df &= DF_ALL;
  693.     p.ec &= EC_ALL;
  694.     p.bf &= BF_ALL;
  695.     p.st &= ST_ALL;
  696.     /*
  697.      * As JP is optional we do a second, non-fatal parse for it.
  698.      */
  699.     int n;
  700.     masked = 1;
  701.     if (vparseRange(cp, masked, 9, &n,&n,&n,&n,&n,&n,&n,&n,&p.jp)) {
  702. useJP = true;
  703. if (conf.class2JPEGSupport) p.jp &= JP_ALL;
  704. else p.jp = 0;
  705.     } else
  706. p.jp = 0;
  707.     return true;
  708. }
  709. const char*
  710. Class2Modem::skipStatus(const char* s)
  711. {
  712.     const char* cp;
  713.     for (cp = s; *cp != '' && *cp != ':'; cp++)
  714. ;
  715.     return (*cp == ':' ? cp+1 : s);
  716. }
  717. /*
  718.  * Hangup codes are broken up according to:
  719.  *   2388/89
  720.  *   2388/90 and 2388-A
  721.  *   2388-B (Class 2.0)
  722.  * The table to search is based on the modem type deduced
  723.  * at modem configuration time.
  724.  *
  725.  * Note that we keep the codes as strings to avoid having
  726.  * to distinguish the decimal numbers of 2388-A from the
  727.  * hexadecimal renumbering done in 2388-B!
  728.  */
  729. static struct HangupCode {
  730.     const char* code[4]; // from 2388/89, 2388/90, 2388-A, 2388-B, and ours
  731.     const char* message; // what code means
  732. } hangupCodes[] = {
  733. // Call placement and termination
  734.     {{  "0",  "0",  "0", "E211" }, "Normal and proper end of connection" },
  735.     {{  "1",  "1",  "1", "E212" }, "Ring detect without successful handshake" },
  736.     {{  "2",  "2",  "2", "E213" }, "Call aborted,  from +FK or <CAN>" },
  737.     {{ NULL,  "3",  "3", "E214" }, "No loop current" },
  738.     {{ NULL, NULL,  "4", "E215" }, "Ringback detected, no answer (timeout)" },
  739.     {{ NULL, NULL,  "5", "E216" }, "Ringback detected, no answer without CED" },
  740. // Transmit Phase A & miscellaneous errors
  741.     {{ "10", "10", "10", "E217" }, "Unspecified Phase A error" },
  742.     {{ "11", "11", "11", "E218" }, "No answer (T.30 T1 timeout)" },
  743. // Transmit Phase B
  744.     {{ "20", "20", "20", "E219" }, "Unspecified Transmit Phase B error" },
  745.     {{ "21", "21", "21", "E220" }, "Remote cannot be polled" },
  746.     {{ "22", "22", "22", "E221" }, "COMREC error in transmit Phase B/got DCN" },
  747.     {{ "23", "23", "23", "E222" }, "COMREC invalid command received/no DIS or DTC" },
  748.     {{ "24", "24", "24", "E223" }, "RSPREC error/got DCN" },
  749.     {{ "25", "25", "25", "E224" }, "DCS sent 3 times without response" },
  750.     {{ "26", "26", "26", "E225" }, "DIS/DTC received 3 times; DCS not recognized" },
  751.     {{ "27", "27", "27", "E226" }, "Failure to train at 2400 bps or +FMINSP value" },
  752.     {{ "28", "28", "28", "E227" }, "RSPREC invalid response received" },
  753. // Transmit Phase C
  754.     {{ "30", "40", "40", "E228" }, "Unspecified Transmit Phase C error" },
  755.     {{ NULL, NULL, "41", "E229" }, "Unspecified Image format error" },
  756.     {{ NULL, NULL, "42", "E230" }, "Image conversion error" },
  757.     {{ "33", "43", "43", "E231" }, "DTE to DCE data underflow" },
  758.     {{ NULL, NULL, "44", "E232" }, "Unrecognized Transparent data command" },
  759.     {{ NULL, NULL, "45", "E233" }, "Image error, line length wrong" },
  760.     {{ NULL, NULL, "46", "E234" }, "Image error, page length wrong" },
  761.     {{ NULL, NULL, "47", "E235" }, "Image error, wrong compression code" },
  762. // Transmit Phase D
  763.     {{ "40", "50", "50", "E236" }, "Unspecified Transmit Phase D error, including"
  764.    " +FPHCTO timeout between data and +FET command" },
  765.     {{ "41", "51", "51", "E237" }, "RSPREC error/got DCN" },
  766.     {{ "42", "52", "52", "E238" }, "No response to MPS repeated 3 times" },
  767.     {{ "43", "53", "53", "E239" }, "Invalid response to MPS" },
  768.     {{ "44", "54", "54", "E240" }, "No response to EOP repeated 3 times" },
  769.     {{ "45", "55", "55", "E241" }, "Invalid response to EOP" },
  770.     {{ "46", "56", "56", "E242" }, "No response to EOM repeated 3 times" },
  771.     {{ "47", "57", "57", "E243" }, "Invalid response to EOM" },
  772.     {{ "48", "58", "58", "E244" }, "Unable to continue after PIN or PIP" },
  773. // Received Phase B
  774.     {{ "50", "70", "70", "E245" }, "Unspecified Receive Phase B error" },
  775.     {{ "51", "71", "71", "E246" }, "RSPREC error/got DCN" },
  776.     {{ "52", "72", "72", "E247" }, "COMREC error" },
  777.     {{ "53", "73", "73", "E248" }, "T.30 T2 timeout, expected page not received" },
  778.     {{ "54", "74", "74", "E249" }, "T.30 T1 timeout after EOM received" },
  779. // Receive Phase C
  780.     {{ "60", "90", "90", "E250" }, "Unspecified Phase C error, including too much delay"
  781.    " between TCF and +FDR command" },
  782.     {{ "61", "91", "91", "E251" }, "Missing EOL after 5 seconds (section 3.2/T.4)" },
  783.     {{ "63", "93", "93", "E252" }, "DCE to DTE buffer overflow" },
  784.     {{ "64", "94", "92", "E253" }, "Bad CRC or frame (ECM or BFT modes)" },
  785. // Receive Phase D
  786.     {{ "70","100", "A0", "E254" }, "Unspecified Phase D error" },
  787.     {{ "71","101", "A1", "E255" }, "RSPREC invalid response received" },
  788.     {{ "72","102", "A2", "E256" }, "COMREC invalid response received" },
  789.     {{ "73","103", "A3", "E257" }, "Unable to continue after PIN or PIP, no PRI-Q" },
  790. // Agere proprietary error codes
  791.     {{ NULL, NULL, "E0", "E258" }, "Command or signal 10 sec. timeout" },
  792. // Everex proprietary error codes (9/28/90)
  793.     {{ NULL,"128", NULL, "E259" }, "Cannot send: +FMINSP > remote's +FDIS(BR) code" },
  794.     {{ NULL,"129", NULL, "E260" }, "Cannot send: remote is V.29 only,"
  795.    " local DCE constrained to 2400 or 4800 bps" },
  796.     {{ NULL,"130", NULL, "E261" }, "Remote station cannot receive (DIS bit 10)" },
  797.     {{ NULL,"131", NULL, "E262" }, "+FK aborted or <CAN> aborted" },
  798.     {{ NULL,"132", NULL, "E263" }, "+Format conversion error in +FDT=DF,VR, WD,LN"
  799.    " Incompatible and inconvertable data format" },
  800.     {{ NULL,"133", NULL, "E264" }, "Remote cannot receive" },
  801.     {{ NULL,"134", NULL, "E265" }, "After +FDR, DCE waited more than 30 seconds for"
  802.    " XON from DTE after XOFF from DTE" },
  803.     {{ NULL,"135", NULL, "E266" }, "In Polling Phase B, remote cannot be polled" },
  804. };
  805. #define NCODES (sizeof (hangupCodes) / sizeof (hangupCodes[0]))
  806. /*
  807.  * Given a hangup code from a modem return a
  808.  * descriptive string.  We use strings here to
  809.  * avoid having to know what type of modem we're
  810.  * talking to (Rev A or Rev B or SP-2388).  This
  811.  * works right now becase the codes don't overlap
  812.  * (as strings).  Hopefully this'll continue to
  813.  * be true.
  814.  */
  815. const char*
  816. Class2Modem::hangupCause(const char* code, bool codetocode)
  817. {
  818.     for (u_int i = 0; i < NCODES; i++) {
  819. const HangupCode& c = hangupCodes[i];
  820. if ((c.code[1] != NULL && strcasecmp(code, c.code[1]) == 0) ||
  821.     (c.code[2] != NULL && strcasecmp(code, c.code[2]) == 0)) {
  822.     if (codetocode) {
  823. return (c.code[3]);
  824.     } else {
  825. return (c.message);
  826.     }
  827. }
  828.     }
  829.     if (codetocode) return ("E210");
  830.     return ("Unknown hangup code");
  831. }
  832. /*
  833.  * Process a hangup code string.
  834.  */
  835. void
  836. Class2Modem::processHangup(const char* cp)
  837. {
  838.     while (isspace(*cp)) // strip leading white space
  839. cp++;
  840.     while (*cp == '0' && cp[1] != '') // strip leading 0's
  841. cp++;
  842.     strncpy(hangupCode, cp, sizeof (hangupCode));
  843.     protoTrace("REMOTE HANGUP: %s (code %s)",
  844. hangupCause(hangupCode), hangupCode);
  845. }
  846. /*
  847.  * Does the current hangup code string indicate
  848.  * that the remote side hung up the phone in a
  849.  * "normal and proper way".
  850.  */
  851. bool
  852. Class2Modem::isNormalHangup()
  853. {
  854.     // normal hangup is "", "0", or "00"
  855.     return (hangupCode[0] == '' ||
  856. (hangupCode[0] == '0' &&
  857.  (hangupCode[1] == '0' || hangupCode[1] == '')));
  858. }
  859. #include "t.30.h"
  860. void
  861. Class2Modem::tracePPM(const char* dir, u_int ppm)
  862. {
  863.     static u_int ppm2fcf[8] = {
  864. FCF_MPS,
  865. FCF_EOM,
  866. FCF_EOP,
  867. 0,
  868. FCF_PRI_MPS,
  869. FCF_PRI_EOM,
  870. FCF_PRI_EOP,
  871. 0
  872.     };
  873.     FaxModem::traceFCF(dir, ppm2fcf[ppm&7]);
  874. }
  875. void
  876. Class2Modem::tracePPR(const char* dir, u_int ppr)
  877. {
  878.     static u_int ppr2fcf[8] = {
  879. 0,
  880. FCF_MCF,
  881. FCF_RTN,
  882. FCF_RTP,
  883. FCF_PIN,
  884. FCF_PIP,
  885. 0,
  886. 0
  887.     };
  888.     FaxModem::traceFCF(dir, ppr2fcf[ppr&7]);
  889. }