![]() |
Yesterday, I wrote: > Date: Wed, 30 Jul 1997 20:09:25 -0400 > From: "Jonathan I. Kamens" <jik@kamens.brookline.ma.us> > > The patch below fixes two problems: > > 1) My modem, a SupraExpress 56e, responds "0,1,1.0" to "at+fclass=?". > Unfortunately, hylafax was assuming that the only modem class with a > period in it is 2.0, which means that "1.0" gets incremented by the > parser to "2" and faxgetty thinks my modem supports class 2, which it > doesn't. I added a SERVICE_CLASS10 constant, and added "Class 1.0" to > the modem class names, and that seemed to solve the problem. > > 2) The documentation claims that the daemons send the soft reset > command to the modem, but I couldn't find any evidence that they do > so, either by reading and searching source code or by looking in the > server and session traces. I modified faxd/ClassModem.c++ to send the > soft reset command before the other reset commands. My patch was incorrect; it was treating "2.0" in the response to "at+fclass=?" as SERVICE_CLASS10, which is clearly wrong. Here's a new, somewhat more complex patch to replace the one I sent yesterday. It adds new functionality to the ClassModem::vparseRange function to allow a two-dimensional "mapping array" to be specified; I've explained the new functionality in a comment above vparseRange in the diff. --- util/class2.h 1997/07/30 16:39:44 1.1 +++ util/class2.h 1997/07/31 00:10:07 @@ -45,8 +45,9 @@ // service types returned by +fclass=? const int SERVICE_DATA = BIT(0); // data service const int SERVICE_CLASS1 = BIT(1); // class 1 interface -const int SERVICE_CLASS2 = BIT(2); // class 2 interface -const int SERVICE_CLASS20 = BIT(3); // class 2.0 interface +const int SERVICE_CLASS10 = BIT(2); // class 1.0 interface +const int SERVICE_CLASS2 = BIT(3); // class 2 interface +const int SERVICE_CLASS20 = BIT(4); // class 2.0 interface const int SERVICE_VOICE = BIT(8); // voice service (ZyXEL extension) const int SERVICE_ALL = BIT(9)-1; --- faxd/Class2.c++ 1997/07/31 14:56:25 1.1 +++ faxd/Class2.c++ 1997/07/31 15:32:29 @@ -134,7 +134,7 @@ int sep = 0; int pwd = 0; if (doQuery(conf.class2APQueryCmd, s)) - (void) vparseRange(s, 3, &sub, &sep, &pwd); + (void) vparseRange(s, 0, 0, 3, &sub, &sep, &pwd); if (sub & BIT(1)) { saCmd = conf.class2SACmd; modemSupports("subaddressing"); @@ -501,7 +501,7 @@ fxBool Class2Modem::parseRange(const char* cp, Class2Params& p) { - if (!vparseRange(cp, 8, &p.vr,&p.br,&p.wd,&p.ln,&p.df,&p.ec,&p.bf,&p.st)) + if (!vparseRange(cp, 0, 0, 8, &p.vr,&p.br,&p.wd,&p.ln,&p.df,&p.ec,&p.bf,&p.st)) return (FALSE); p.vr &= VR_ALL; p.br &= BR_ALL; --- faxd/ClassModem.c++ 1997/07/16 10:57:20 1.1 +++ faxd/ClassModem.c++ 1997/07/31 16:03:40 @@ -52,14 +52,27 @@ const char* ClassModem::serviceNames[9] = { "\"Data\"", // SERVICE_DATA "\"Class 1\"", // SERVICE_CLASS1 + "\"Class 1.0\"", // SERVICE_CLASS10 "\"Class 2\"", // SERVICE_CLASS2 - "\"Class 2.0\"", // SERVICE_CLASS20 (XXX 3) - "", // 4 + "\"Class 2.0\"", // SERVICE_CLASS20 "", // 5 "", // 6 "", // 7 "\"Voice\"", // SERVICE_VOICE }; +/* + Bits corresponding to various service classes. The first column is + for when there is no ".0", the second colum for when there is. + + If there end up being ".1", ".2", etc. classes later, this can be + extended by changing the number of columns in the array. +*/ +#define MAX_SERVICE_CLASS 2 +static const int serviceBits[3][MAX_VPARSE_POINT+2] = { + {SERVICE_DATA, SERVICE_DATA}, + {SERVICE_CLASS1, SERVICE_CLASS10}, + {SERVICE_CLASS2, SERVICE_CLASS20}, +}; const char* ClassModem::ATresponses[13] = { "Nothing", // AT_NOTHING "OK", // AT_OK @@ -129,7 +142,8 @@ */ // XXX: workaround yet another GCC bug (sigh) const fxStr& flow = conf.getFlowCmd(conf.flowControl); - resetCmds = "AT" + resetCmds = conf.softResetCmd + | "\nAT" | stripAT(conf.resetCmds) // prepend to insure our needs | stripAT(conf.echoOffCmd) | stripAT(conf.verboseResultsCmd) @@ -981,19 +995,68 @@ const char COMMA = ','; const char SPACE = ' '; +static int +parseInt(const char **in_ptr) +{ + const char *ptr = *in_ptr; + int val = 0; + + if (! isdigit(*ptr)) + return -1; + + do { + val = 10*val+(*ptr - '0'); + } while (isdigit(*++ptr)); + + *in_ptr = ptr; + return val; +} + +static void +addBit(const int major, const int point, const int max, + const int map_array[0][MAX_VPARSE_POINT+2], int * const mask) +{ + if (! max) { + if (major <= 31) + *mask |= 1<<major; + } + else if (major <= max && point <= MAX_VPARSE_POINT) + *mask |= map_array[major][point+1]; +} + /* - * Parse a Class 2 parameter range string. This is very - * forgiving because modem vendors do not exactly follow - * the syntax specified in the "standard". Try looking - * at some of the responses given by rev ~4.04 of the - * ZyXEL firmware (for example)! + * Parse a Class 2 parameter range string, possibly remapping bits + * with decimal numbers in them. This is very forgiving because modem + * vendors do not exactly follow the syntax specified in the + * "standard". Try looking at some of the responses given by rev + * ~4.04 of the ZyXEL firmware (for example)! * * NB: We accept alphanumeric items but don't return them * in the parsed range so that modems like the ZyXEL 2864 * that indicate they support ``Class Z'' are handled. + * + * When "max" is non-zero, then it should specify the height of the + * "map_array" array, which should contain in its slots the bits to + * set for each "a.b" pair. Column 0 in the array represents what bit + * to assign when there is no decimal part of a number; column 1 + * represents what bit to assign when the number of the decimal is + * "1", etc. For example, this array: + * + * { { BIT(0), BIT(1) }, + * { BIT(2), BIT(2) }, + * { BIT(3), BIT(4) } }; + * + * Will assign BIT(0) for the parameter "0", BIT(1) for "0.0", BIT(2) + * for either "1" or "1.0", BIT(3) for "2", and BIT(4) for "2.0". + * Bits whose decimal parts are larger than MAX_VPARSE_POINT or whose + * whole parts are larger than "max" are ignored. When no "max" and + * "map_array" are specified, the decimal parts of parameters are + * ignored. */ fxBool -ClassModem::vparseRange(const char* cp, int nargs ... ) +ClassModem::vparseRange(const char* cp, const int max, + const int map_array[][MAX_VPARSE_POINT+2], + int nargs ... ) { fxBool b = TRUE; va_list ap; @@ -1024,38 +1087,56 @@ b = FALSE; goto done; } - int v; + int v, vpoint; if (isdigit(cp[0])) { - v = 0; - do { - v = v*10 + (cp[0] - '0'); - } while (isdigit((++cp)[0])); + vpoint = -1; + if ((v = parseInt(&cp)) < 0) { + b = FALSE; + goto done; + } } else { v = -1; // XXX skip item below while (isalnum((++cp)[0])) ; } - int r = v; + int r = -1, rpoint = -1; + if (cp[0] == '.') { // <d.b> + cp++; + if ((vpoint = parseInt(&cp)) < 0) { + b = FALSE; + goto done; + } + } if (cp[0] == '-') { // <low>-<high> cp++; if (!isdigit(cp[0])) { b = FALSE; goto done; } - r = 0; - do { - r = r*10 + (cp[0] - '0'); - } while (isdigit((++cp)[0])); - } else if (cp[0] == '.') { // <d.b> - cp++; - while (isdigit(cp[0])) // XXX - cp++; - v++, r++; // XXX 2.0 -> 3 + if ((r = parseInt(&cp)) < 0) { + b = FALSE; + goto done; + } + rpoint = -1; + if (cp[0] == '.') { + cp++; + if ((rpoint = parseInt(&cp)) < 0) { + b = FALSE; + goto done; + } + } } if (v != -1) { // expand range or list - r = fxmin(r, 31); // clamp to valid range - for (; v <= r; v++) - mask |= 1<<v; + if (r == -1) { + r = v; + rpoint = vpoint; + } + for (; v <= r; v++) { + int max_point = (v == r) ? rpoint : MAX_VPARSE_POINT; + for (; vpoint <= max_point; vpoint++) + addBit(v, vpoint, max, map_array, &mask); + vpoint = -1; + } } if (acceptList && cp[0] == COMMA) // (<item>,<item>...) cp++; @@ -1078,7 +1159,7 @@ fxBool ClassModem::parseRange(const char* cp, u_int& a0) { - return vparseRange(cp, 1, &a0); + return vparseRange(cp, MAX_SERVICE_CLASS, serviceBits, 1, &a0); } void