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

trunk.cpp

// Copyright (C) 2001 Kai Germaschewski
// Copyright (C) 2002/2003 Peter Krapfl
//
// Version 1.2.0.2
//  
// 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 "driver.h"

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

char CapiTrunk::status[256];

CapiBuffer::CapiBuffer(unsigned int _len)
{
      len = _len;
      data = new unsigned char[_len];
}

CapiBuffer::~CapiBuffer()
{
      delete [] data;
}

void CapiBuffer::bitReverse(void)
{
      for (unsigned i = 0; i < len; i++) {
            unsigned char c1 = data[i];
            unsigned char c2 = 0;
            for (int j = 0; j <= 7; j++) 
                  if (c1 & (1 << j))
                        c2 |= 1 << (7-j);
            data[i] = c2;
      }
}

CapiBufferQueue::CapiBufferQueue()
{
      next = this;
      prev = this;
}

CapiBufferQueue::~CapiBufferQueue()
{
      purge();
}

void CapiBufferQueue::queueTail(CapiBuffer *buf)
{
      CapiBufferBase *_next, *_prev;

      enterMutex();
      _next = this;
      _prev = prev;
      buf->next = _next;
      buf->prev = _prev;
      _next->prev = buf;
      _prev->next = buf;
      qlen++;
      leaveMutex();
}

CapiBuffer *CapiBufferQueue::dequeue(void)
{
      CapiBufferBase *_next, *_prev, *result;

      enterMutex();
      
      _prev = this;
      _next = next;
      result = NULL;

      if (_next != _prev) {
            result = _next;
            _next = _next->next;
            _next->prev = _prev;
            _prev->next = _next;
            result->next = NULL;
            result->prev = NULL;
      }
      leaveMutex();

      return (CapiBuffer *) result;
}

void CapiBufferQueue::purge(void)
{
      CapiBuffer *buf;
      while (NULL != (buf = dequeue()))
            delete buf;
}

// ----------------------------------------------------------------------

CapiTrunk::CapiTrunk(unsigned b_channel, int c) :
      TimerPort(), Trunk(b_channel)
{
//    slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ << endl;

      TrunkEvent event;

      contr = c;
      joined = NULL;

      record_flag = false;
      flags.offhook = false;
      handler = &CapiTrunk::idleHandler;
      event.id = TRUNK_ENTER_STATE;
      postEvent(&event);

      plci=0;

      plci_handler = &CapiTrunk::p0Handler;
}

CapiTrunk::~CapiTrunk()
{
}

void CapiTrunk::setTimer(timeout_t time)
{
      char dummy;

      slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ 
                   << " " << time << endl;
      CapiDriver *drv = (CapiDriver *) driver;

      endTimer();
      TimerPort::setTimer(time);
      flags.timer = true;
      write(drv->evbuf[1], &dummy, 1);
}

void CapiTrunk::endTimer(void)
{
      slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ << endl;

      if(!flags.timer)
            return;
      flags.timer = false;
      TimerPort::endTimer();
}

void CapiTrunk::getName(char *buffer)
{
      sprintf(buffer, "CAPI20: Controller: %d B channel: %d", contr, id);
}

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

      slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ << endl;

      char buffer[33];
      getName(buffer);
      slog(Slog::levelDebug) << buffer << ": script exiting" << endl;

      handler = &CapiTrunk::hangupHandler;
}

bool CapiTrunk::postEvent(TrunkEvent *event)
{
      bool rtn = true;
      trunkhandler_t prior;
      
      if (event->id != 401)
            slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ << " " 
                         << event->id << endl;
            
      enterMutex();
      debug->debugEvent(this, event);

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

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

      rtn = true;
      switch (event->id) {
      case TRUNK_CALL_DETECT:
            ++rings;
            break;
      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_CALL_RELEASE:
      case TRUNK_STOP_DISCONNECT:
            if(flags.onexit)
                  break;
            slog(Slog::levelDebug) << "CAPI20: " << id << ": disconnecting" << endl;
            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_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_TIMER_EXPIRED:
            if (!trunkSignal(TRUNK_SIGNAL_TIMEOUT))
                  trunkSignal(TRUNK_SIGNAL_STEP);
            event->id = TRUNK_STOP_STATE;
            goto retry;
            break;
      case TRUNK_STOP_STATE:
            endTimer();
            handler = &CapiTrunk::stepHandler;
            break;
      default:
            rtn = false;
      }
      if(handler != prior) {
            event->id = TRUNK_ENTER_STATE;
            goto retry;
      }

 out:
      leaveMutex();
      return rtn;
}

unsigned long CapiTrunk::getIdleTime(void)
{
      slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ << endl;

      time_t now;

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

      return 0;
}

void CapiTrunk::setDTMFDetect(bool flag)
{
      slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ << endl;

      unsigned int err;
      CapiDriver *drv = (CapiDriver *) driver;
      _cmsg cmsg;

      if (!flags.offhook)
            return;

      if (flag) {
            err = FACILITY_REQ (&cmsg, drv->appl_id, drv->appl_id,
                            plci,
                            1,         // DMTF
                            (unsigned char *)
                            "\x08"     // length
                            "\x01\x00" // DTMF listen
                            "\x28\x00" // duration
                            "\x28\x00" // tone
                            "\x00"     // digits
                            "\x00");   // characteristics
            if (err != CapiNoError) {
                  slog(Slog::levelError) << "CAPI: FACILITY_REQ err = "
                               << err << endl;
                  return;
            }
            flags.dtmf = true;
      } else {
            if (!flags.dtmf)
                  return;

            err = FACILITY_REQ (&cmsg, drv->appl_id, drv->appl_id,
                            plci,
                            1,         // DMTF
                            (unsigned char *)
                            "\x08"     // length
                            "\x02\x00" // DTMF stop listen
                            "\x28\x00" // duration
                            "\x28\x00" // tone
                            "\x00"     // digits
                            "\x00");   // characteristics
            if (err != CapiNoError) {
                  slog(Slog::levelError) << "CAPI: FACILITY_REQ err = "
                               << err << endl;
            }
            flags.dtmf = false;
      }
}

// called in CapiDriver->thread context, may not block

bool CapiTrunk::recvCapiMsg(_cmsg *cmsg)
{
  //  slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ << endl;
      return (this->*plci_handler)(cmsg);
}

bool CapiTrunk::p0Handler(_cmsg *cmsg) {
  //  slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ << endl;

      TrunkEvent ev;

      if (IS_CONNECT_IND(cmsg)) {
            char script[256];
            int len;

            len = cmsg->CallingPartyNumber[0];
            if (len > 2) {
                  memcpy(script, &cmsg->CallingPartyNumber[3], len - 2);
                  script[len - 2] = 0;
                  setConst(SYM_CALLER, script);
            }
            len = cmsg->CalledPartyNumber[0];
            if (len > 1) {
                  memcpy(script, &cmsg->CalledPartyNumber[2], len - 1);
                  script[len - 1] = 0;
                  setConst(SYM_DIALED, script);
            }

            plci = cmsg->adr.adrPLCI;
            connect_ind_msg_id = cmsg->Messagenumber;
            plci_handler = &CapiTrunk::p2Handler;

            ev.id = TRUNK_CALL_DETECT;
            postEvent(&ev);
            return true;
      }
      if (IS_CONNECT_CONF(cmsg)) {
            return false;
      }

      slog(Slog::levelWarning) << "CAPI: " << __FUNCTION__ 
                     << " unhandled " << capi20_cmsg2str(cmsg)
                     << endl;
      return false;
}

bool CapiTrunk::p01Handler(_cmsg *cmsg)
{
  //  slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ << endl;

      TrunkEvent ev;

      if (IS_CONNECT_CONF(cmsg)) {
            if (cmsg->Messagenumber != connect_req_msg_id)
                  return false;
            
            connect_req_msg_id = 0;
            if (cmsg->Info != 0) {
                  plci_handler = &CapiTrunk::p0Handler;
                  ev.id = TRUNK_CALL_RELEASE;
                  return postEvent(&ev);
            }
            plci = cmsg->adr.adrPLCI;
            plci_handler = &CapiTrunk::p1Handler;
            return true;
      }
      slog(Slog::levelWarning) << "CAPI: " << __FUNCTION__ 
                     << " unhandled " << capi20_cmsg2str(cmsg)
                     << endl;
      return false;
}

bool CapiTrunk::p1Handler(_cmsg *cmsg)
{
  //  slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ << endl;

      TrunkEvent ev;
      unsigned int err;
      CapiDriver *drv = (CapiDriver *) driver;

      if (IS_CONNECT_ACTIVE_IND(cmsg)) {
            capi20_cmsg_answer(cmsg);
            capi20_put_cmsg(cmsg);
            plci_handler = &CapiTrunk::pActHandler;
            err = CONNECT_B3_REQ(cmsg, drv->appl_id, drv->msg_id++,
                             plci,
                             (unsigned char *)""); // NCPI
            if (err != CapiNoError) {
                  slog(Slog::levelError) << "CAPI20: CONNECT_B3_REQ err = "
                               << err << endl;
                  return true;
            }
            return true;
      }
      if (IS_DISCONNECT_IND(cmsg)) {
            capi20_cmsg_answer(cmsg);
            capi20_put_cmsg(cmsg);
            plci = 0;
            ncci = 0;
            plci_handler = &CapiTrunk::p0Handler;
            ev.id = TRUNK_CALL_RELEASE;
            postEvent(&ev);
            return true;
      }
      slog(Slog::levelWarning) << "CAPI20: " << __FUNCTION__ 
                     << " unhandled " << capi20_cmsg2str(cmsg)
                     << endl;
      return false;
}

bool CapiTrunk::p2Handler(_cmsg *cmsg)
{
  //  slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ << endl;

      TrunkEvent ev;

      if (IS_ALERT_CONF(cmsg)) {
            return true;
      }
      if (IS_DISCONNECT_IND(cmsg)) {
            capi20_cmsg_answer(cmsg);
            capi20_put_cmsg(cmsg);
            plci = 0;
            ncci = 0;
            plci_handler = &CapiTrunk::p0Handler;
            ev.id = TRUNK_CALL_RELEASE;
            postEvent(&ev);
            return true;
      }
      slog(Slog::levelWarning) << "CAPI20: " << __FUNCTION__ 
                     << " unhandled " << capi20_cmsg2str(cmsg)
                     << endl;
      return false;
}

bool CapiTrunk::p4Handler(_cmsg *cmsg)
{
  //  slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ << endl;

      TrunkEvent ev;

      if (IS_ALERT_CONF(cmsg)) {
            return true;
      }
      if (IS_CONNECT_ACTIVE_IND(cmsg)) {
            capi20_cmsg_answer(cmsg);
            capi20_put_cmsg(cmsg);
            plci_handler = &CapiTrunk::pActHandler;
            return true;
      }
      if (IS_DISCONNECT_IND(cmsg)) {
            capi20_cmsg_answer(cmsg);
            capi20_put_cmsg(cmsg);
            plci = 0;
            ncci = 0;
            plci_handler = &CapiTrunk::p0Handler;
            ev.id = TRUNK_CALL_RELEASE;
            postEvent(&ev);
            return true;
      }
      slog(Slog::levelWarning) << "CAPI20: " << __FUNCTION__ 
                     << " unhandled " << capi20_cmsg2str(cmsg)
                     << endl;
      return false;
}

bool CapiTrunk::pActHandler(_cmsg *cmsg)
{
      TrunkEvent ev;

      if (IS_DATA_B3_IND(cmsg) ||
          IS_DATA_B3_CONF(cmsg) ||
          IS_CONNECT_B3_ACTIVE_IND(cmsg) ||
          IS_DISCONNECT_B3_IND(cmsg)) {
            if (ncci != cmsg->adr.adrNCCI) {
                  //BUG();
                  goto err;
            }
            if (!ncci_handler) {
                  //BUG();
                  goto err;
            }
            return (this->*ncci_handler)(cmsg);
      }
      slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ << endl;

      if (IS_FACILITY_CONF(cmsg)) {
            if (cmsg->FacilitySelector != 1) // DTMF
                  goto err;
            if (cmsg->FacilityConfirmationParameter[0] != 2)
                  goto err;
            if (memcmp(&cmsg->FacilityConfirmationParameter[1],
                     "\x00\x00", 2))
                  goto err;
            return true;
      }
      if (IS_FACILITY_IND(cmsg)) {
            if (cmsg->FacilitySelector != 1) // DTMF
                  goto err;
            if (cmsg->FacilityIndicationParameter[0] != 1)
                  goto err;
            
            TrunkEvent ev;
            char p1 = tolower(cmsg->FacilityIndicationParameter[1]);
            char *d = strchr(digit, p1);
            if(!d)
                  return true;
            
            ev.id = TRUNK_DTMF_KEYUP;
            ev.parm.dtmf.digit = (d - digit);
            ev.parm.dtmf.duration = 0x32;
            postEvent(&ev);
            capi20_cmsg_answer(cmsg);
            capi20_put_cmsg(cmsg);
            return true;
      }
      if (IS_CONNECT_B3_IND(cmsg)) {
            ncci = cmsg->adr.adrNCCI;
            cmsg->Reject = 0;
            cmsg->NCPI = (unsigned char *)"";
            capi20_cmsg_answer(cmsg);
            capi20_put_cmsg(cmsg);
            ncci_handler = &CapiTrunk::n2Handler;
            return true;
      }
      if (IS_CONNECT_B3_CONF(cmsg)) {
            ncci = cmsg->adr.adrNCCI;
            ncci_handler = &CapiTrunk::n2Handler;
            return true;
      }
      if (IS_DISCONNECT_IND(cmsg)) {
            capi20_cmsg_answer(cmsg);
            capi20_put_cmsg(cmsg);
            plci = 0;
            ncci = 0;
            plci_handler = &CapiTrunk::p0Handler;
            ncci_handler = NULL;
            ev.id = TRUNK_CALL_RELEASE;
            postEvent(&ev);
            return true;
      }
 err:
      slog(Slog::levelWarning) << "CAPI20: " << __FUNCTION__ 
                     << " unhandled " << capi20_cmsg2str(cmsg)
                     << endl;
      return false;
}

bool CapiTrunk::p5Handler(_cmsg *cmsg)
{
  //  slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ << endl;

      TrunkEvent ev;

      if (IS_DATA_B3_IND(cmsg) ||
          IS_DATA_B3_CONF(cmsg) ||
          IS_CONNECT_B3_ACTIVE_IND(cmsg) ||
          IS_DISCONNECT_B3_IND(cmsg)) {
            if (ncci != cmsg->adr.adrNCCI) {
                  //BUG();
                  goto err;
            }
            if (!ncci_handler) {
                  //BUG();
                  goto err;
            }
            return (this->*ncci_handler)(cmsg);
      }

      if (IS_DISCONNECT_CONF(cmsg)) {
            return true;
      }
      if (IS_DISCONNECT_IND(cmsg)) {
            capi20_cmsg_answer(cmsg);
            capi20_put_cmsg(cmsg);
            plci = 0;
            plci_handler = &CapiTrunk::p0Handler;
            ncci_handler = NULL;
            ev.id = TRUNK_CALL_RELEASE;
            postEvent(&ev);
            return true;
      }
 err:
      slog(Slog::levelWarning) << "CAPI20: " << __FUNCTION__ 
                     << " unhandled " << capi20_cmsg2str(cmsg)
                     << endl;
      return false;
}

bool CapiTrunk::n2Handler(_cmsg *cmsg)
{
  //  slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ << endl;

      TrunkEvent ev;

      if (IS_CONNECT_B3_ACTIVE_IND(cmsg)) {
            capi20_cmsg_answer(cmsg);
            capi20_put_cmsg(cmsg);
            ncci_handler = &CapiTrunk::nActHandler;
            flags.offhook = true;
            ev.id = TRUNK_CALL_CONNECT;
            postEvent(&ev);
            return true;
      }
      slog(Slog::levelWarning) << "CAPI20: " << __FUNCTION__
                     << " unhandled " << capi20_cmsg2str(cmsg)
                     << endl;
      return false;
}

bool CapiTrunk::nActHandler(_cmsg *cmsg)
{
      CapiTrunk *trk;

      if (IS_DATA_B3_IND(cmsg)) {
            capi20_cmsg_answer(cmsg);
            capi20_put_cmsg(cmsg);

            trk = joined;
            if (trk && trk->ncci) {
                  CapiDriver *drv = (CapiDriver *) driver;

                  cmsg->Subcommand = CAPI_REQ;
                  cmsg->Messagenumber = drv->msg_id++;
                  cmsg->adr.adrNCCI = trk->ncci;
                  drv->enterMutex();
                  capi20_put_cmsg(cmsg);
                  drv->leaveMutex();
            } else if (record_flag) {
                  CapiBuffer *buf = new CapiBuffer(cmsg->DataLength);

                  memcpy(buf->data, cmsg->Data, cmsg->DataLength);

                  record_queue.queueTail(buf);
                  thread->post();
            }
            return true;
      }
      if (IS_DATA_B3_CONF(cmsg)) {
            --play_frames;
            if (thread)
                  thread->post();
            return true;
      }
      //    slog(Slog::levelDebug) << "CAPI20: " << __FUNCTION__ << endl;

      if (IS_DISCONNECT_B3_IND(cmsg)) {
            capi20_cmsg_answer(cmsg);
            capi20_put_cmsg(cmsg);
            ncci = 0;
            ncci_handler = NULL;
            flags.offhook = false;
            return true;
      }
// err:
      slog(Slog::levelWarning) << "CAPI20: " << __FUNCTION__
                     << " unhandled " << capi20_cmsg2str(cmsg)
                     << endl;
      return false;
}

bool CapiTrunk::scrJoin()
{
      Trunk *trunk = capiivr.getTrunkPort(atoi(getValue("-1")));

      if(!trunk) {
            error("join-no-port");
            return true;
      }

      data.join.trunk = trunk;
      data.join.wakeup = atoi(getValue("0")) * 1000;
      trunkStep(TRUNK_STEP_JOIN);
      return false;
}

bool CapiTrunk::scrWait()
{
      data.join.trunk = NULL;
      data.join.wakeup = atoi(getValue("0")) * 1000;
      trunkStep(TRUNK_STEP_JOIN);
      return false;
}

#ifdef      CCXX_NAMESPACES
};
#endif

Generated by  Doxygen 1.6.0   Back to index