Modem.c++
上传用户:weiyuanprp
上传日期:2020-05-20
资源大小:1169k
文件大小:13k
- /* $Id: Modem.c++,v 1.9 2008/09/20 19:53:24 faxguy Exp $ */
- /*
- * 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.
- */
- #include "Sys.h"
- #include <errno.h>
- #include "Modem.h"
- #include "UUCPLock.h"
- #include "REDict.h"
- #include "TriggerRef.h"
- #include "Dispatcher.h"
- #include "config.h"
- REDict* ModemGroup::classes = NULL; // modem classes
- RE*
- ModemGroup::find(const char* name)
- {
- if (classes == NULL)
- return (NULL);
- const REPtr* re = classes->find(name);
- return (re ? (RE*) *(REPtr*) re : (RE*) NULL);
- }
- void
- ModemGroup::reset()
- {
- delete classes, classes = NULL;
- }
- void
- ModemGroup::set(const fxStr& name, RE* re)
- {
- if (classes == NULL)
- classes = new REDict;
- (*classes)[name] = re;
- }
- ModemLockWaitHandler::ModemLockWaitHandler(Modem& m) : modem(m) {}
- ModemLockWaitHandler::~ModemLockWaitHandler() {}
- void ModemLockWaitHandler::timerExpired(long, long)
- { faxQueueApp::instance().pollForModemLock(modem); }
- QLink Modem::list; // master list of known modems
- Modem::Modem(const fxStr& id)
- : fifoName(FAX_FIFO "." | id)
- , devID(id)
- , lockHandler(*this)
- {
- state = DOWN; // modem down until notified otherwise
- canpoll = true; // be optimistic
- fd = -1; // force open on first use
- priority = 255; // lowest priority
- insert(list); // place at end of master list
- lock = faxQueueApp::instance().getUUCPLock(faxApp::idToDev(id));
- }
- Modem::~Modem()
- {
- stopLockPolling();
- delete lock;
- if (fd >= 0)
- Sys::close(fd);
- remove();
- if (!triggers.isEmpty()) // purge trigger references
- TriggerRef::purge(triggers);
- }
- Modem*
- Modem::modemExists(const fxStr& id, bool notexempt)
- {
- for (ModemIter iter(list); iter.notDone(); iter++) {
- Modem& modem = iter;
- if (modem.devID == id) {
- if (notexempt && modem.getState() == Modem::EXEMPT) return (NULL);
- return (&modem);
- }
- }
- return (NULL);
- }
- /*
- * Given a modem device-id, return a reference
- * to a Modem instance. If no instance exists,
- * one is created and added to the list of known
- * modems.
- */
- Modem&
- Modem::getModemByID(const fxStr& id)
- {
- Modem* modem = modemExists(id);
- return *(modem ? modem : new Modem(id));
- }
- /*
- * Is the modem capable of handling the job.
- */
- bool
- Modem::isCapable(const Job& job) const
- {
- if (job.willpoll && !canpoll)
- return (false);
- if (job.pagewidth && !supportsPageWidthInMM(job.pagewidth))
- return (false);
- if (job.pagelength && !supportsPageLengthInMM(job.pagelength))
- return (false);
- if (job.resolution && !supportsVRes(job.resolution))
- return (false);
- return (true);
- }
- bool
- Modem::isInGroup(const fxStr& mgroup)
- {
- RE* c = ModemGroup::find(mgroup);
- if (c)
- return (c->Find(devID));
- return ((devID == mgroup));
- }
- /*
- * Find a modem that is capable of handling
- * work associated with the specified job.
- */
- Modem*
- Modem::findModem(const Job& job, bool ignorebusy)
- {
- RE* c = ModemGroup::find(job.device);
- if (c) {
- const fxStr& mdci = job.getJCI().getModem();
- RE* cdci = mdci != "" ? ModemGroup::find(mdci) : NULL;
- int loops = 2;
- /*
- * At first try to find modem strictly (suitable to job and destination rules)
- * Then try to find modem not strictly (suitable to job rules only)
- */
- for (int i = 0 ; i < loops ; i++) {
- /*
- * Job is assigned to a class of modems; search
- * the set of modems in the class according to
- * the order specified (if any order is specified).
- */
- for (ModemIter iter(list); iter.notDone(); iter++) {
- Modem& modem = iter;
- if (c->Find(modem.devID) && modem.isCapable(job)) {
- if (i == 0) {
- if (cdci) { // destination assigned to a class of modems
- if (!cdci->Find(modem.devID))
- continue;
- } else if (mdci != "") { // destination assigned to an explicit modem
- if (mdci != modem.devID)
- continue;
- }
- loops = 1; // there is a strictly suitable modem
- }
- if (modem.getState() != Modem::READY) {
- continue;
- }
- /*
- * Move modem to the end of the priority group
- */
- modem.remove();
- if (!list.isEmpty()) {
- ModemIter iter(list);
- for ( ; iter.notDone(); iter++) {
- if (iter.modem().priority > modem.priority)
- break;
- }
- modem.insert(iter.modem());
- } else
- modem.insert(list);
- return (&modem);
- }
- }
- }
- } else {
- /*
- * Job is assigned to an explicit modem or to an
- * invalid class or modem. Look for the modem
- * in the list of known modems.
- *
- * Here we deliberately return a modem that is EXEMPT
- * so that the caller can process accordingly.
- */
- for (ModemIter iter(list); iter.notDone(); iter++) {
- Modem& modem = iter;
- if (modem.getState() != Modem::READY && modem.getState() != Modem::EXEMPT)
- if (!(ignorebusy && modem.getState() == Modem::BUSY))
- continue;
- if (job.device != modem.devID)
- continue;
- return (modem.isCapable(job) ? &modem : (Modem*) NULL);
- }
- }
- return (NULL);
- }
- /*
- * Assign a modem for use by a job.
- *
- * ignorebusy tells us that the modem is already marked as busy.
- * In that case don't bother trying to lock. Just proceed as if
- * the lock had worked.
- */
- bool
- Modem::assign(Job& job, bool ignorebusy)
- {
- if (ignorebusy || lock->lock()) { // lock modem for use
- state = BUSY; // mark in use
- job.modem = this; // assign modem to job
- send("L", 2, false);
- return (true);
- } else {
- /*
- * Modem is locked for use by an outbound task.
- * This should only happen when operating in a
- * send-only environment--a modem is presumed
- * ready for use, only to discover when it's
- * actually assigned that it's really busy.
- * We mark the modem BUSY here so that if the
- * caller requests another modem we won't try
- * to re-assign it in findModem.
- */
- state = BUSY; // mark in use
- return (false);
- }
- }
- /*
- * Release a previously assigned modem.
- */
- void
- Modem::release()
- {
- lock->unlock();
- /*
- * We must mark the modem READY when releasing the lock
- * because we cannot depend on the faxgetty process
- * notifying us if/when the modem status changes. This
- * may result in overzealous scheduling of the modem, but
- * since sender apps are expected to stablize the modem
- * before starting work it shouldn't be too bad.
- *
- * We only mark the modem READY when it is BUSY. This
- * is to allow states DOWN and EXEMPT to persist.
- */
- if (state == BUSY) state = READY;
- }
- /*
- * UUCP lock file polling support. When a modem is not
- * monitored by a faxgetty process outbound modem usage
- * is ``discovered'' when we attempt to assign a modem
- * to a job. At that time we mark the modem BUSY and
- * kick off a polling procedure to watch for when the
- * lock file is removed; at which time we mark the modem
- * READY again and poke the scheduler in case jobs are
- * waiting for a modem to come ready again.
- */
- void
- Modem::startLockPolling(long sec)
- {
- Dispatcher::instance().startTimer(sec, 0, &lockHandler);
- }
- void
- Modem::stopLockPolling()
- {
- Dispatcher::instance().stopTimer(&lockHandler);
- }
- void
- Modem::setCapabilities(const char* s)
- {
- canpoll = (s[0] == 'P'); // P/p for polling/no polling
- char* tp;
- caps.decodeCaps((u_int) strtoul(s+1, &tp, 16));// fax capabilities
- if (tp && *tp == ':') { // modem priority
- u_int pri = (u_int) strtoul(tp+1, NULL, 16);
- if (pri != priority) {
- /*
- * Priority changed, move modem so that the list remains
- * sorted by priority (highest priority to lowest priority).
- */
- remove();
- priority = pri;
- if (!list.isEmpty()) {
- ModemIter iter(list);
- do {
- if (iter.modem().priority > pri)
- break;
- iter++;
- } while (iter.notDone());
- insert(iter.modem());
- } else
- insert(list);
- }
- }
- setState(READY); // XXX needed for static configuration
- }
- void Modem::setNumber(const char* cp) { number = cp; }
- void Modem::setCommID(const char* cp) { commid = cp; }
- void Modem::setState(ModemState s) { state = s; }
- /*
- * Return whether or not the modem supports the
- * specified page width. We perhaps should accept
- * page width when large page sizes are supported
- * (but then the caller would need to know in order
- * to pad the image to the appropriate width).
- */
- bool
- Modem::supportsPageWidthInMM(u_int w) const
- {
- if (w <= 218) // 1728 pixels + slop
- return caps.wd & BIT(WD_A4);
- else if (w <= 258) // 2048 pixels + slop
- return caps.wd & BIT(WD_B4);
- else if (w <= 306) // 2432 pixels + slop
- return caps.wd & BIT(WD_A3);
- else
- return false;
- }
- bool
- Modem::supportsPageWidthInPixels(u_int w) const
- {
- if (w <= 1744) // 1728 pixels + slop
- return caps.wd & BIT(WD_A4);
- else if (w <= 2064) // 2048 pixels + slop
- return caps.wd & BIT(WD_B4);
- else if (w <= 2448) // 2432 pixels + slop
- return caps.wd & BIT(WD_A3);
- else
- return false;
- }
- /*
- * Return whether or not the modem supports the
- * specified vertical resolution. Note that we're
- * rather tolerant because of potential precision
- * problems and general sloppiness on the part of
- * applications writing TIFF files.
- */
- bool
- Modem::supportsVRes(float res) const
- {
- if (75 <= res && res < 120)
- return (true); // all fax modems must support vr = 0
- else if (150 <= res && res < 250)
- return (caps.vr & VR_FINE || caps.vr & VR_200X200);
- else if (250 <= res && res < 350)
- return caps.vr & VR_300X300;
- else if (350 <= res && res < 500)
- return (caps.vr & VR_R8 || caps.vr & VR_200X400 || caps.vr & VR_R16);
- else
- return false;
- }
- /*
- * Return whether or not the modem supports the
- * specified VR setting.
- */
- bool
- Modem::supportsVR(u_int r) const
- {
- return caps.vr & r;
- }
- /*
- * Return whether or not the modem supports 2DMR.
- */
- bool
- Modem::supports2D() const
- {
- return caps.df & BIT(DF_2DMR);
- }
- /*
- * Return whether or not the modem supports 2DMMR.
- */
- bool
- Modem::supportsMMR() const
- {
- return caps.df & BIT(DF_2DMMR);
- }
- /*
- * Return whether or not the modem supports JBIG.
- */
- bool
- Modem::supportsJBIG() const
- {
- return caps.df & BIT(DF_JBIG);
- }
- /*
- * Return whether or not the modem supports the
- * specified page length. As above for vertical
- * resolution we're lenient in what we accept.
- */
- bool
- Modem::supportsPageLengthInMM(u_int l) const
- {
- // XXX probably need to be more forgiving with values
- if (270 < l && l <= 330)
- return caps.ln & (BIT(LN_A4)|BIT(LN_INF));
- else if (330 < l && l <= 390)
- return caps.ln & (BIT(LN_B4)|BIT(LN_INF));
- else
- return caps.ln & BIT(LN_INF);
- }
- /*
- * Broadcast a message to all known modems.
- */
- void
- Modem::broadcast(const fxStr& msg)
- {
- for (ModemIter iter(list); iter.notDone(); iter++) {
- /*
- * NB: We rarely send msgs, so for now close after each use.
- * +1 here is so the