Logo Search packages:      
Sourcecode: bayonne version File versions  Download package

trunk.cpp

// Copyright (C) 2000 Open Source Telecom Corporation.
//  
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software 
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#include <sys/ioctl.h>
#include "driver.h"

#ifdef      CCXX_NAMESPACES
namespace ost {
using namespace std;
#endif

char PhonedevTrunk::status[240];

PhonedevTrunk::PhonedevTrunk(int fd, int ts) :
Trunk(ts), TimerPort()
{
      TrunkEvent event;
      char *cp;
      struct phone_capability *cap;
      int ctrl;

      next = prev = NULL;
      service = NULL;
      handler = NULL;
      lastring = 0;

      dev = fd;

#ifdef      IXJ_PHONEDEV
      driver = ixj_driver;
#else
      driver = generic_driver;
#endif

#ifdef      POSIX_PHONEDEV
      ioctl(dev, PHONE_CAPABILITIES, &cap_count);
#else
      cap_count = ioctl(dev, PHONE_CAPABILITIES);
#endif
      cap_list = new struct phone_capability[cap_count];
#ifdef      POSIX_PHONEDEV
      ioctl(dev, PHONE_CAPABILITIES_LIST, &cap_list);
#else
      ioctl(dev, PHONE_CAPABILITIES_LIST, cap_list);
#endif

      cap = getCapability(vendor);
      if(cap)
            switch(cap->cap)
            {
#ifdef      IXJ_PHONEDEV
            case PHONE_VENDOR_IXJ:
                  driver = ixj_driver;
                  break;
#endif
            }


#ifdef      POSIX_PHONEDEV
      ctrl = phivr.getWinkDuration();
      ioctl(dev, PHONE_WINK_DURATION, &ctrl);
#else
      ioctl(dev, PHONE_WINK_DURATION, phivr.getWinkDuration());
#endif

      handler = &PhonedevTrunk::hangupHandler;
      event.id = TRUNK_ENTER_STATE;

      switch(driver)
      {
#ifdef      IXJ_PHONEDEV
      case ixj_driver:
            status[id] = 't';
#ifdef      POSIX_PHONEDEV
            ctrl = PORT_PSTN;
            ioctl(dev, IXJCTL_PORT, &ctrl);
#else
            ioctl(dev, IXJCTL_PORT, PORT_PSTN);
#endif

            cpatone[CPA_DIALTONE].filter = 0;
            cpatone[CPA_DIALTONE].freq = f440_480;
            cpatone[CPA_DIALTONE].enable = 0;
            ioctl(dev, IXJCTL_SET_FILTER, &cpatone[0]);
            
            slog(Slog::levelInfo) << "phone" << id << ": Quicknet driver started" << endl;
#ifdef      POSIX_PHONEDEV
            ioctl(dev, IXJCTL_PSTN_LINETEST, &ctrl);
#else
            ctrl = ioctl(dev, IXJCTL_PSTN_LINETEST);
#endif
            if(!ctrl)
            {
                  slog(Slog::levelWarning) << "phone" << id << ": linetest failure" << endl;
                  handler = &PhonedevTrunk::busyHandler;
            }
            break;
#endif
      default:
            slog(Slog::levelInfo) << "phone" << id << ": generic driver started" << endl;
      }
      service = phivr.getService();       
      service->attach(this);
      
      (this->*handler)(&event);
}

PhonedevTrunk::~PhonedevTrunk()
{
      int ctrl;

      handler = NULL;
      if(service)
            service->detach(this);

      endTimer();
      
      setHookState(false);
      switch(driver)
      {
#ifdef      IXJ_PHONEDEV
      case ixj_driver:
#ifdef      POSIX_PHONEDEV
            ctrl = PORT_POTS;
            ioctl(dev, IXJCTL_PORT, &ctrl);
#else
            ioctl(dev, IXJCTL_PORT, PORT_POTS);
#endif
            break;
#endif
      }
      close(dev);
      slog(Slog::levelInfo) << "phone" << id << ": device stopped" << endl;
}

void PhonedevTrunk::setEcho(aeclevel_t level)
{
      switch(driver)
      {
#ifdef      IXJ_PHONEDEV
      case ixj_driver:
            switch(level)
            {
            case ECHO_OFF:
                  ioctl(dev, IXJCTL_AEC_STOP);
                  break;
            case ECHO_LOW:
                  ioctl(dev, IXJCTL_AEC_START, AEC_LOW);
                  break;
            case ECHO_MEDIUM:
                  ioctl(dev, IXJCTL_AEC_START, AEC_MED);
                  break;
            case ECHO_HIGH:
                  ioctl(dev, IXJCTL_AEC_START, AEC_HIGH);
            }
            break;
#endif
      }
}

void PhonedevTrunk::setCPADetect(cpatone_t tone, bool mode)
{
      switch(driver)
      {
#ifdef      IXJ_PHONEDEV
      case ixj_driver:
            if(mode && cpatone[tone].enable)
                  break;
            if(!mode && !cpatone[tone].enable)
                  break;
            cpatone[tone].enable = mode;
            ioctl(dev, IXJCTL_SET_FILTER, &cpatone[tone]);
#endif
      }
}
void PhonedevTrunk::getName(char *buffer)
{
      sprintf(buffer, "phone%d", id);
}

void PhonedevTrunk::exit(void)
{
      if(!flags.onexit)
            if(redirect("::exit"))
            {
                  autoloop(false);
                  flags.onexit = true;
                  return;
            }

      handler = &PhonedevTrunk::hangupHandler;
}

bool PhonedevTrunk::postEvent(TrunkEvent *event)
{
      bool rtn = true;
      trunkhandler_t prior;

      enterMutex();
      switch(event->id)
      {
      case TRUNK_TIMER_EXIT:
            if(!exittimer)
                  rtn = false;
            exittimer = 0;
            break;
      case TRUNK_TIMER_SYNC:
            if(!synctimer)
                  rtn = false;
            synctimer = 0;
            break;
      case TRUNK_TIMER_EXPIRED:
            if(!getTimer())
                  rtn = false;
            break;
      case TRUNK_DTMF_KEYUP:
            if(flags.offhook)
                  time(&idletime);
            if(!flags.dtmf)
                  rtn = false;
            break;
      }     
      if(!rtn)
      {
            leaveMutex();
            return false;
      }

      if(!handler)
      {
            slog(Slog::levelWarning) << "phone" << id;
            slog() << ": no handler active; event=" << event->id << endl;
            leaveMutex();
            return false;
      }

retry:
      debug->debugEvent(this, event);
      prior = handler;
      rtn = (this->*handler)(event);
      if(rtn)
      {
            if(handler != prior)
            {
                  if(prior == &PhonedevTrunk::idleHandler)
                        setIdle(false);
                  event->id = TRUNK_ENTER_STATE;
                  goto retry;
            }
            leaveMutex();
            return true;
      }

      // default handler

      rtn = true;
      switch(event->id)
      {
      case TRUNK_RINGING_ON:
            ++rings;
            break;
      case TRUNK_ENTER_STATE:
            if(flags.offhook)
                  setDTMFDetect();
            else
                  setDTMFDetect(false);
            endTimer();
            break;
      case TRUNK_LINE_WINK:
            if(!flags.offhook)
                  break;
      case TRUNK_CPA_DIALTONE:
      case TRUNK_STOP_DISCONNECT:
            if(flags.onexit)
                  break;
            if(trunkSignal(TRUNK_SIGNAL_HANGUP))
            {
                  event->id = TRUNK_STOP_STATE;
                  goto retry;
            }
            break;
      case TRUNK_SEND_MESSAGE:
            if(recvEvent(event))
            {
                  event->id = TRUNK_STOP_STATE;
                  goto retry;
            }
            break;
      case TRUNK_TIMER_EXPIRED:
            trunkSignal(TRUNK_SIGNAL_TIMEOUT);
            event->id = TRUNK_STOP_STATE;
            goto retry;
            break;
        case TRUNK_TIMER_SYNC:
                if(trunkSignal(TRUNK_SIGNAL_TIME))
                {
                        event->id = TRUNK_STOP_STATE;
                        goto retry;
                }
                break;
        case TRUNK_TIMER_EXIT:
                if(trunkSignal(TRUNK_SIGNAL_TIME))
                        event->id = TRUNK_STOP_STATE;
                else
                        event->id = TRUNK_STOP_DISCONNECT;
                goto retry;

      case TRUNK_CHILD_EXIT:
            if(!isActive())
                  break;
            if(trunkSignal(TRUNK_SIGNAL_CHILD))
            {
                  event->id = TRUNK_STOP_STATE;
                  goto retry;
            }
            break;
      case TRUNK_DTMF_KEYUP:
            if(digits < MAX_DIGITS)
                  dtmf.bin.data[digits++] = digit[event->parm.dtmf.digit];
            dtmf.bin.data[digits] = 0;
            if(trunkSignal((trunksignal_t)(event->parm.dtmf.digit + TRUNK_SIGNAL_0)))
            {
                  event->id = TRUNK_STOP_STATE;
                  goto retry;
            }
            break;
      case TRUNK_EXIT_SHELL:
            tgi.pid = 0;
            break;
      case TRUNK_STOP_STATE:
            endTimer();
            handler = &PhonedevTrunk::stepHandler;
            break;
      case TRUNK_EXIT_STATE:
            break;
      case TRUNK_MAKE_BUSY:
            handler = &PhonedevTrunk::busyHandler;
            break;
      case TRUNK_MAKE_IDLE:
            handler = &PhonedevTrunk::hangupHandler;
            break;
      default:
            rtn = false;
      }
      if(handler != prior)
      {
            event->id = TRUNK_ENTER_STATE;
            goto retry;
      }
      leaveMutex();
      return rtn;
}

void PhonedevTrunk::getEvents(void)
{
      union telephony_exception exc;
      int digit;
      TrunkEvent event;
      cid_t cid;
      char buf[81];
      time_t now;
      int ringid;
      char *cp;
      int ctrl;

#ifdef      POSIX_PHONEDEV
      ioctl(dev, PHONE_EXCEPTION, &exc.bytes);
#else
      exc.bytes = ioctl(dev, PHONE_EXCEPTION);
#endif
      if(exc.bits.pstn_ring && rings < 2)
      {
            time(&now);
            if(now - lastring < 4)
            {
                  setSymbol(SYM_RINGID, 4);
                  cp = getSymbol(SYM_RINGID);
                  if(cp)
                        ringid = atoi(cp);
                  else
                        ringid = 0;
                  sprintf(buf, "%d", ++ringid);
                  setSymbol(SYM_RINGID, buf);
                  exc.bits.pstn_ring = 0;
//                exc.bits.caller_id = 1;
            }
      }
      if(exc.bits.pstn_ring)
      {
            event.id = TRUNK_RINGING_ON;
            cp = getSymbol(SYM_RINGID);
            if(cp)
                  event.parm.ring.digit = atoi(cp);
            else
                  event.parm.ring.digit = 0;
            event.parm.ring.duration = 1000;
            postEvent(&event);
            event.id = TRUNK_RINGING_OFF;
            postEvent(&event);
            time(&lastring);
      }
      if(exc.bits.pstn_wink)
      {
            event.id = TRUNK_LINE_WINK;
            event.parm.duration = 0;
            postEvent(&event);
      }
      if(exc.bits.hookstate)
      {
#ifdef      POSIX_PHONEDEV
            ioctl(dev, PHONE_HOOKSTATE, &ctrl);
#else
            ctrl = ioctl(dev, PHONE_HOOKSTATE);
#endif
            switch(ctrl)
            {
            case PSTN_ON_HOOK:
//                slog(Slog::levelDebug) << "onhook" << endl;
                  event.id = TRUNK_ON_HOOK;
                  postEvent(&event);
                  break;
            case PSTN_RINGING:
//                slog(Slog::levelDebug) << "ringing" << endl;
                  break;
            case PSTN_OFF_HOOK:
//                slog(Slog::levelDebug) << "offhook" << endl;
                  event,id = TRUNK_OFF_HOOK;
                  postEvent(&event);
                  break;
//          case PSTN_PULSE_DIAL:
//                slog(Slog::levelDebug) << "pulse dial" << endl;
            }
      }
      if(exc.bits.dtmf_ready)
      {
#ifdef      POSIX_PHONEDEV
            ioctl(dev, PHONE_GET_DTMF_ASCII, &digit);
#else
            digit = ioctl(dev, PHONE_GET_DTMF_ASCII);
#endif
            event.id = TRUNK_DTMF_KEYUP;
            switch(digit)
            {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                  event.parm.dtmf.digit = digit - '0';
                  break;
            case '*':
                  event.parm.dtmf.digit = 10;
                  break;
            case '#':
                  event.parm.dtmf.digit = 11;
                  break;
            }
            event.parm.dtmf.duration = 80;
            event.parm.dtmf.e1 = event.parm.dtmf.e2 = 0;
            postEvent(&event);
      }
      if(exc.bits.f0)
      {
            switch(driver)
            {
#ifdef      IXJ_PHONEDEV
            case ixj_driver:
#ifdef      POSIX_PHONEDEV
                  ctrl = 0;
                  ioctl(dev, IXJCTL_GET_FILTER_HIST, &ctrl);
#else
                  ctrl = ioctl(dev, IXJCTL_GET_FILTER_HIST, 0);
#endif
                  if(ctrl & 1)
                  {
                        event.id = TRUNK_CPA_DIALTONE;
                        postEvent(&event);
                  }
                  break;
#endif
            }
      }
      if(exc.bits.f1)
      {
            switch(driver)
            {
#ifdef      IXJ_PHONEDEV
            case ixj_driver:
#ifdef      POSIX_PHONEDEV
                  ctrl = 0;
                  ioctl(dev, IXJCTL_GET_FILTER_HIST, &ctrl);
#else
                  ctrl = ioctl(dev, IXJCTL_GET_FILTER_HIST, 0);
#endif
                  if(ctrl & 1)
                  {
                        event.id = TRUNK_CPA_BUSYTONE;
                        postEvent(&event);
                  }
                  break;
#endif
            }
      }
      if(exc.bits.caller_id && flags.dsp == DSP_MODE_CALLERID)
      {

            event.id = TRUNK_CALLER_ID;
            memset(&cid, 0, sizeof(cid));
            switch(driver)
            {
#ifdef IXJ_PHONEDEV
            case ixj_driver:
                  ioctl(dev, IXJCTL_CID, &cid.ixjcid);
                  strncpy(buf, cid.ixjcid.number, cid.ixjcid.numlen);
                  buf[cid.ixjcid.numlen] = 0;
                  if(cid.ixjcid.numlen)
                        setSymbol(SYM_CALLER, buf);
                  strncpy(buf, cid.ixjcid.name, cid.ixjcid.namelen);
                  buf[cid.ixjcid.namelen] = 0;
                  if(cid.ixjcid.namelen)
                        setSymbol(SYM_NAME, buf);
                  if(cid.ixjcid.numlen)
                        flags.dsp = DSP_MODE_INACTIVE;
                  break;
#endif
            }
      }           
}
            
void PhonedevTrunk::setTimer(timeout_t ptimer)
{
      TimerPort::setTimer(ptimer);
      if(!service->isThread())
            service->update();
}

void PhonedevTrunk::incTimer(timeout_t ptimer)
{
      TimerPort::incTimer(ptimer);
      service->update();
}

unsigned long PhonedevTrunk::getIdleTime(void)
{
      time_t now;

      time(&now);
      if(handler == &PhonedevTrunk::idleHandler)
            return now - idletime;

      return 0;
}

void PhonedevTrunk::setHookState(bool offhook)
{
      int ctrl;
      if(offhook == flags.offhook)
            return;

#ifdef      POSIX_PHONEDEV
      if(offhook)
            ctrl = PSTN_OFF_HOOK;
      else
            ctrl = PSTN_ON_HOOK;
      ioctl(dev, PHONE_PSTN_SET_STATE, &ctrl);
#else
      if(offhook)
            ioctl(dev, PHONE_PSTN_SET_STATE, PSTN_OFF_HOOK);
      else
            ioctl(dev, PHONE_PSTN_SET_STATE, PSTN_ON_HOOK);
#endif

      flags.offhook = offhook;
}

struct phone_capability *PhonedevTrunk::getCapability(phone_cap cid, int sub)
{
      int cap;

      for(cap = 0; cap < cap_count; ++cap)
      {
            if(cap_list[cap].captype != cid)
                  continue;

            if(sub == -1 || cap_list[cap].cap)
                  return &cap_list[cap];
      }
      return NULL;
}

#ifdef      CCXX_NAMESPACES
};
#endif

Generated by  Doxygen 1.6.0   Back to index