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

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: Modem.c++,v 1.9 2008/09/20 19:53:24 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 "Sys.h"
  27. #include <errno.h>
  28. #include "Modem.h"
  29. #include "UUCPLock.h"
  30. #include "REDict.h"
  31. #include "TriggerRef.h"
  32. #include "Dispatcher.h"
  33. #include "config.h"
  34. REDict* ModemGroup::classes =  NULL; // modem classes
  35. RE*
  36. ModemGroup::find(const char* name)
  37. {
  38.     if (classes == NULL)
  39. return (NULL);
  40.     const REPtr* re = classes->find(name);
  41.     return (re ? (RE*) *(REPtr*) re : (RE*) NULL);
  42. }
  43. void
  44. ModemGroup::reset()
  45. {
  46.     delete classes, classes = NULL;
  47. }
  48. void
  49. ModemGroup::set(const fxStr& name, RE* re)
  50. {
  51.     if (classes == NULL)
  52. classes = new REDict;
  53.     (*classes)[name] = re;
  54. }
  55. ModemLockWaitHandler::ModemLockWaitHandler(Modem& m) : modem(m) {}
  56. ModemLockWaitHandler::~ModemLockWaitHandler() {}
  57. void ModemLockWaitHandler::timerExpired(long, long)
  58.     { faxQueueApp::instance().pollForModemLock(modem); }
  59. QLink Modem::list; // master list of known modems
  60. Modem::Modem(const fxStr& id)
  61.     : fifoName(FAX_FIFO "." | id)
  62.     , devID(id)
  63.     , lockHandler(*this)
  64. {
  65.     state = DOWN; // modem down until notified otherwise
  66.     canpoll = true; // be optimistic
  67.     fd = -1; // force open on first use
  68.     priority = 255; // lowest priority
  69.     insert(list); // place at end of master list
  70.     lock = faxQueueApp::instance().getUUCPLock(faxApp::idToDev(id));
  71. }
  72. Modem::~Modem()
  73. {
  74.     stopLockPolling();
  75.     delete lock;
  76.     if (fd >= 0)
  77. Sys::close(fd);
  78.     remove();
  79.     if (!triggers.isEmpty()) // purge trigger references
  80. TriggerRef::purge(triggers);
  81. }
  82. Modem*
  83. Modem::modemExists(const fxStr& id, bool notexempt)
  84. {
  85.     for (ModemIter iter(list); iter.notDone(); iter++) {
  86. Modem& modem = iter;
  87. if (modem.devID == id) {
  88.     if (notexempt && modem.getState() == Modem::EXEMPT) return (NULL);
  89.     return (&modem);
  90. }
  91.     }
  92.     return (NULL);
  93. }
  94. /*
  95.  * Given a modem device-id, return a reference
  96.  * to a Modem instance.  If no instance exists,
  97.  * one is created and added to the list of known
  98.  * modems.
  99.  */
  100. Modem&
  101. Modem::getModemByID(const fxStr& id)
  102. {
  103.     Modem* modem = modemExists(id);
  104.     return *(modem ? modem : new Modem(id));
  105. }
  106. /*
  107.  * Is the modem capable of handling the job.
  108.  */
  109. bool
  110. Modem::isCapable(const Job& job) const
  111. {
  112.     if (job.willpoll && !canpoll)
  113. return (false);
  114.     if (job.pagewidth && !supportsPageWidthInMM(job.pagewidth))
  115. return (false);
  116.     if (job.pagelength && !supportsPageLengthInMM(job.pagelength))
  117. return (false);
  118.     if (job.resolution && !supportsVRes(job.resolution))
  119. return (false);
  120.     return (true);
  121. }
  122. bool
  123. Modem::isInGroup(const fxStr& mgroup)
  124. {
  125.     RE* c = ModemGroup::find(mgroup);
  126.     if (c)
  127. return (c->Find(devID));
  128.     return ((devID == mgroup));
  129. }
  130. /*
  131.  * Find a modem that is capable of handling
  132.  * work associated with the specified job.
  133.  */
  134. Modem*
  135. Modem::findModem(const Job& job, bool ignorebusy)
  136. {
  137.     RE* c = ModemGroup::find(job.device);
  138.     if (c) {
  139. const fxStr& mdci = job.getJCI().getModem();
  140. RE* cdci = mdci != "" ? ModemGroup::find(mdci) : NULL;
  141. int loops = 2;
  142. /*
  143.  * At first try to find modem strictly (suitable to job and destination rules)
  144.  * Then try to find modem not strictly (suitable to job rules only)
  145.  */
  146. for (int i = 0 ; i < loops ; i++) {
  147.     /*
  148.      * Job is assigned to a class of modems; search
  149.      * the set of modems in the class according to
  150.      * the order specified (if any order is specified).
  151.      */
  152.     for (ModemIter iter(list); iter.notDone(); iter++) {
  153. Modem& modem = iter;
  154. if (c->Find(modem.devID) && modem.isCapable(job)) {
  155.     if (i == 0) {
  156. if (cdci) { // destination assigned to a class of modems
  157.     if (!cdci->Find(modem.devID))
  158. continue;
  159. } else if (mdci != "") { // destination assigned to an explicit modem
  160.     if (mdci != modem.devID)
  161. continue;
  162. }
  163. loops = 1; // there is a strictly suitable modem
  164.     }
  165.     if (modem.getState() != Modem::READY) {
  166. continue;
  167.     }
  168.     /*
  169.      * Move modem to the end of the priority group
  170.      */
  171.     modem.remove();
  172.     if (!list.isEmpty()) {
  173. ModemIter iter(list);
  174. for ( ; iter.notDone(); iter++) {
  175.     if (iter.modem().priority > modem.priority)
  176. break;
  177. }
  178. modem.insert(iter.modem());
  179.     } else
  180. modem.insert(list);
  181.     return (&modem);
  182. }
  183.     }
  184. }
  185.     } else {
  186. /*
  187.  * Job is assigned to an explicit modem or to an
  188.  * invalid class or modem.  Look for the modem
  189.  * in the list of known modems.
  190.  *
  191.  * Here we deliberately return a modem that is EXEMPT
  192.  * so that the caller can process accordingly.
  193.  */
  194. for (ModemIter iter(list); iter.notDone(); iter++) {
  195.     Modem& modem = iter;
  196.     if (modem.getState() != Modem::READY && modem.getState() != Modem::EXEMPT)
  197. if (!(ignorebusy && modem.getState() == Modem::BUSY))
  198.     continue;
  199.     if (job.device != modem.devID)
  200. continue;
  201.     return (modem.isCapable(job) ? &modem : (Modem*) NULL);
  202. }
  203.     }
  204.     return (NULL);
  205. }
  206. /*
  207.  * Assign a modem for use by a job.
  208.  *
  209.  * ignorebusy tells us that the modem is already marked as busy.
  210.  * In that case don't bother trying to lock.  Just proceed as if
  211.  * the lock had worked.
  212.  */
  213. bool
  214. Modem::assign(Job& job, bool ignorebusy)
  215. {
  216.     if (ignorebusy || lock->lock()) { // lock modem for use
  217. state = BUSY; // mark in use
  218. job.modem = this; // assign modem to job
  219. send("L", 2, false);
  220. return (true);
  221.     } else {
  222. /*
  223.  * Modem is locked for use by an outbound task.
  224.  * This should only happen when operating in a
  225.  * send-only environment--a modem is presumed
  226.  * ready for use, only to discover when it's
  227.  * actually assigned that it's really busy.
  228.  * We mark the modem BUSY here so that if the
  229.  * caller requests another modem we won't try
  230.  * to re-assign it in findModem.
  231.  */
  232. state = BUSY; // mark in use
  233. return (false);
  234.     }
  235. }
  236. /*
  237.  * Release a previously assigned modem.
  238.  */
  239. void
  240. Modem::release()
  241. {
  242.     lock->unlock();
  243.     /*
  244.      * We must mark the modem READY when releasing the lock
  245.      * because we cannot depend on the faxgetty process 
  246.      * notifying us if/when the modem status changes.  This
  247.      * may result in overzealous scheduling of the modem, but
  248.      * since sender apps are expected to stablize the modem
  249.      * before starting work it shouldn't be too bad.
  250.      *
  251.      * We only mark the modem READY when it is BUSY.  This
  252.      * is to allow states DOWN and EXEMPT to persist.
  253.      */
  254.     if (state == BUSY) state = READY;
  255. }
  256. /*
  257.  * UUCP lock file polling support.  When a modem is not
  258.  * monitored by a faxgetty process outbound modem usage
  259.  * is ``discovered'' when we attempt to assign a modem
  260.  * to a job.  At that time we mark the modem BUSY and
  261.  * kick off a polling procedure to watch for when the
  262.  * lock file is removed; at which time we mark the modem
  263.  * READY again and poke the scheduler in case jobs are
  264.  * waiting for a modem to come ready again.
  265.  */
  266. void
  267. Modem::startLockPolling(long sec)
  268. {
  269.     Dispatcher::instance().startTimer(sec, 0, &lockHandler);
  270. }
  271. void
  272. Modem::stopLockPolling()
  273. {
  274.     Dispatcher::instance().stopTimer(&lockHandler);
  275. }
  276. void
  277. Modem::setCapabilities(const char* s)
  278. {
  279.     canpoll = (s[0] == 'P'); // P/p for polling/no polling
  280.     char* tp;
  281.     caps.decodeCaps((u_int) strtoul(s+1, &tp, 16));// fax capabilities
  282.     if (tp && *tp == ':') { // modem priority
  283. u_int pri = (u_int) strtoul(tp+1, NULL, 16);
  284. if (pri != priority) {
  285.     /*
  286.      * Priority changed, move modem so that the list remains
  287.      * sorted by priority (highest priority to lowest priority).
  288.      */
  289.     remove();
  290.     priority = pri;
  291.     if (!list.isEmpty()) {
  292. ModemIter iter(list);
  293. do {
  294.     if (iter.modem().priority > pri)
  295. break;
  296.     iter++;
  297. } while (iter.notDone());
  298. insert(iter.modem());
  299.     } else
  300. insert(list);
  301. }
  302.     }
  303.     setState(READY); // XXX needed for static configuration
  304. }
  305. void Modem::setNumber(const char* cp) { number = cp; }
  306. void Modem::setCommID(const char* cp) { commid = cp; }
  307. void Modem::setState(ModemState s) { state = s; }
  308. /*
  309.  * Return whether or not the modem supports the
  310.  * specified page width.  We perhaps should accept
  311.  * page width when large page sizes are supported
  312.  * (but then the caller would need to know in order
  313.  * to pad the image to the appropriate width).
  314.  */
  315. bool
  316. Modem::supportsPageWidthInMM(u_int w) const
  317. {
  318.     if (w <= 218) // 1728 pixels + slop
  319. return caps.wd & BIT(WD_A4);
  320.     else if (w <= 258) // 2048 pixels + slop
  321. return caps.wd & BIT(WD_B4);
  322.     else if (w <= 306) // 2432 pixels + slop
  323. return caps.wd & BIT(WD_A3);
  324.     else
  325. return false;
  326. }
  327. bool
  328. Modem::supportsPageWidthInPixels(u_int w) const
  329. {
  330.     if (w <= 1744) // 1728 pixels + slop
  331. return caps.wd & BIT(WD_A4);
  332.     else if (w <= 2064) // 2048 pixels + slop
  333. return caps.wd & BIT(WD_B4);
  334.     else if (w <= 2448) // 2432 pixels + slop
  335. return caps.wd & BIT(WD_A3);
  336.     else
  337. return false;
  338. }
  339. /*
  340.  * Return whether or not the modem supports the
  341.  * specified vertical resolution.  Note that we're
  342.  * rather tolerant because of potential precision
  343.  * problems and general sloppiness on the part of
  344.  * applications writing TIFF files.
  345.  */
  346. bool
  347. Modem::supportsVRes(float res) const
  348. {
  349.     if (75 <= res && res < 120)
  350. return (true); // all fax modems must support vr = 0
  351.     else if (150 <= res && res < 250)
  352. return (caps.vr & VR_FINE || caps.vr & VR_200X200);
  353.     else if (250 <= res && res < 350)
  354. return caps.vr & VR_300X300;
  355.     else if (350 <= res && res < 500)
  356. return (caps.vr & VR_R8 || caps.vr & VR_200X400 || caps.vr & VR_R16);
  357.     else
  358. return false;
  359. }
  360. /*
  361.  * Return whether or not the modem supports the
  362.  * specified VR setting.
  363.  */
  364. bool
  365. Modem::supportsVR(u_int r) const
  366. {
  367.         return caps.vr & r;
  368. }
  369. /*
  370.  * Return whether or not the modem supports 2DMR.
  371.  */
  372. bool
  373. Modem::supports2D() const
  374. {
  375.     return caps.df & BIT(DF_2DMR);
  376. }
  377. /*
  378.  * Return whether or not the modem supports 2DMMR.
  379.  */
  380. bool
  381. Modem::supportsMMR() const
  382. {
  383.     return caps.df & BIT(DF_2DMMR);
  384. }
  385. /*
  386.  * Return whether or not the modem supports JBIG.
  387.  */
  388. bool
  389. Modem::supportsJBIG() const
  390. {
  391.     return caps.df & BIT(DF_JBIG);
  392. }
  393. /*
  394.  * Return whether or not the modem supports the
  395.  * specified page length.  As above for vertical
  396.  * resolution we're lenient in what we accept.
  397.  */
  398. bool
  399. Modem::supportsPageLengthInMM(u_int l) const
  400. {
  401.     // XXX probably need to be more forgiving with values
  402.     if (270 < l && l <= 330)
  403. return caps.ln & (BIT(LN_A4)|BIT(LN_INF));
  404.     else if (330 < l && l <= 390)
  405. return caps.ln & (BIT(LN_B4)|BIT(LN_INF));
  406.     else
  407. return caps.ln & BIT(LN_INF);
  408. }
  409. /*
  410.  * Broadcast a message to all known modems.
  411.  */
  412. void
  413. Modem::broadcast(const fxStr& msg)
  414. {
  415.     for (ModemIter iter(list); iter.notDone(); iter++) {
  416. /*
  417.  * NB: We rarely send msgs, so for now close after each use.
  418.  *     +1 here is so the  is included in the message.
  419.  */
  420. iter.modem().send(msg, msg.length()+1, false);
  421.     }
  422. }
  423. /*
  424.  * Send a message to the process managing a modem.
  425.  */
  426. bool
  427. Modem::send(const char* msg, u_int msgLen, bool cacheFd)
  428. {
  429.     bool retry = true;
  430. again:
  431.     if (fd < 0) {
  432. fd = Sys::open(fifoName, O_WRONLY|O_NDELAY);
  433. if (fd < 0) {
  434. #ifdef notdef
  435.     /*
  436.      * NB: We don't generate a message here because this
  437.      *     is expected when faxgetty is not running.
  438.      */
  439.     logError("MODEM " | devID | ": Cannot open FIFO: %m");
  440. #endif
  441.     return (false);
  442. }
  443.     }
  444.     int n = Sys::write(fd, msg, msgLen);
  445.     if (n == -1) {
  446. if (errno == EBADF && retry) { // cached descriptor bad, retry
  447.     retry = false;
  448.     Sys::close(fd), fd = -1;
  449.     goto again;
  450. }
  451. logError("MODEM " | devID | ": Cannot send msg "%.*s"", msgLen, msg);
  452.     }
  453.     if (!cacheFd)
  454. Sys::close(fd), fd = -1;
  455.     return ((unsigned)n == msgLen);
  456. }
  457. #include "StackBuffer.h"
  458. void
  459. Modem::encode(fxStackBuffer& buf) const
  460. {
  461.     buf.put(devID,  devID.length()+1);
  462.     buf.put(number, number.length()+1);
  463.     buf.put(commid, commid.length()+1);
  464.     switch (state) {
  465.     case DOWN: buf.put('D'); break;
  466.     case BUSY: buf.put('B'); break;
  467.     case READY: buf.put('R'); break;
  468.     }
  469.     buf.put(canpoll ? 'P' : 'p');
  470.     u_int ec = caps.encodeCaps();
  471.     buf.put((const char*) &ec, sizeof (u_int));
  472.     buf.put((const char*) &priority, sizeof (u_short));
  473. }
  474. void
  475. Modem::CLEANUP (void)
  476. {
  477.     ModemGroup::reset();
  478.     QLink* ql = list.next; 
  479.     while (ql != &list)
  480.     {
  481. Modem* m = (Modem*)ql;
  482. ql = ql->next;
  483. delete m;
  484.     }
  485. }