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

driver.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.
//
// As a special exception to the GNU General Public License, permission is
// granted for additional uses of the text contained in its release
// of Bayonne as noted here.
//
// This exception is that permission is hereby granted to link Bayonne 
// with the Aculab telephony libraries to produce a executable image
// without requiring Aculab's sources to be supplied in a free software
// license long as each source file so linked contains this exclusion
// and the unalrtered Aculab source files are also provided.
//
// This exception does not however invalidate any other reasons why
// the resulting executable file might be covered by the GNU General
// public license or invalidate the licensing requirements of any
// other component or library.
//
// This exception applies only to the code released by OST under the
// name Bayonne.  If you copy code from other releases into a copy of
// Bayonne, as the General Public License permits, the exception does not
// apply to the code that you add in this way.  To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own to Bayonne, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice, at which
// point the terms of your modification would be covered under the GPL
// as explicitly stated in "COPYING".

//
// General Aculab driver overview
// ------------------------------
//
// At this time, I only have Prosody PCI/T1/PRI cards to work with
// so some assumptions may be incorrect for other hardware produced
// by Aculab and supported by their drivers.
//
// Each 'port' on an Aculab card is a T1 interface.  Each T1/E1
// interface can support 24 (T1) or 30 (E1) simultaneous phone
// calls (channels).  Currently the highest density PCI Aculab card
// is the Prosody with a PM4 module which gives 4 ports, for a total
// capacity of 96 T1 or 120 E1 channels per board.  There can be multiple
// Prosody cards in one chassis.  I don't know what the limit is at
// this time, but I suspect it is limited by available PCI slots
// more than anything else.  My development system has 2 4-port
// cards installed, with room for 2 more.
//
// Because of the very high density involved with these cards,
// doing a simple thread-per-trunk(channel) mapping here in the
// driver rapidly gets out of hand as you add more cards.  Take
// a 16-port machine for example.  That would give 384(T1) or
// 480(E1) threads!  The machine will rapidly be running out of
// resources at that point.  Also, in the case of Compact-PCI systems,
// it's possible to cram the machine full of ports, which could
// easily double this number.
//
// To economize on resources, the main driver thread is responsible
// for receiving events from the Aculab driver and dispatching
// them to worker threads through a small queue class.  The optimal
// number of worker threads has yet to be determined, but I imagine
// one per port should suffice.
//
// There are 3 additional threads used by this driver:
//
// o An audio processing thread.  There is one of these per driver
//   and is responsible for feeding audio to/from the hardware
// o A dsp event monitor thread.  This pretty much only listens for
//   DTMF events at the moment.  There is one of these per driver.
// o A timer event thread.  Any time a timer is set, this thread
//   sits and waits for it to expire, posting an event when it does.
//
// See thread.c and atimer.c for more details.
//
// NOTE: The Aculab driver can operate in either state-driven
//       mode or event-driven.  For simplicity of the code, the
//       event driven model is being used.  At some point it may
//       be worth re-examining the issue to see if state-driven
//       provides any advantages.

#include "driver.h"

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

AculabConfig::AculabConfig() :
Keydata("/bayonne/aculab")
{
      static Keydata::Define defkeys[] = {
      {"prifirmware", "ni2_usr.p4r"},
      {"brifirmware", "ets_bnet.ram"},
      {"prosody", "/home/aculab/dtk111/speech/download/speech/sp30a.smf"},
      {"briconfig", ""},
      {"priconfig", ""},
      {"netformat", "alaw"},
      {"buffersize","256"},
      {NULL, NULL}};

      if(isFHS())
            load("/drivers/aculab");

      load(defkeys);
}

unsigned AculabConfig::getMaxPorts(void)
{
      const char *val = getLast("maxports");
      int v;
      if(!val)
            return MAXPORT;

      v = atoi(val);
      if(v > MAXPORT)
            return MAXPORT;

      return v;
}

AculabDriver::AculabDriver() :
Driver(), AculabConfig(), Thread(keythreads.priService())
{
      static Script::Define keywords[] = {
            {"join", (Method)&AculabTrunk::scrJoin, &ScriptCommand::chkHasArgs},
            {"wait", (Method)&AculabTrunk::scrWait, &ScriptCommand::chkHasArgs},
            {"sync", (Method)&AculabTrunk::scrMaxTime, &ScriptCommand::chkIgnore},
            {NULL, NULL, NULL}};

      running = false;
      port_count = 0;
      unsigned long mask;
      unsigned port, ts;
//    AculabTrunk *trunk;

#if 0
      int rc, x;
      char *mykey[128];
      rc=keytones.getIndex(mykey,127);

      /* Setup custom-defined tones */
      for (x=0; x < rc; x++) {
            if (!stricmp(mykey[x],"tones")) {
                  continue;
            }
            slog(Slog::levelDebug)<<"GETTING tone for KEY="<<mykey[x]<<endl;
            /*
             * TODO TODO - convert phTone into an Aculab custom tone
             * parameter and let DSP do tone generation...
             */
      }
#endif

      status = AculabTrunk::status;
      memset(status, ' ', sizeof(AculabTrunk::status));

      if(InitNetworks()) {
            if(InitProsody()) {
                  port_count = call_nports();
            }
      }

      if(port_count < 0)
            port_count = 0;

      if(!port_count) {
            slog(Slog::levelCritical) << "Aculab driver failure; no ports" << endl; 
            trunks = NULL;
            groups = NULL;
            imaps = NULL;
            omaps = NULL;
            qthreads = NULL;
            throw((Driver *)this);
      }

      qthreads = NULL;
      audiothread = NULL;

      imaps = new AculabTrunk *[port_count * MAXCHANNELS];
      omaps = new AculabTrunk *[port_count * MAXCHANNELS];
      ixmaps = new AculabTrunk *[MAX_CH_IDX];
      tsmap = new char[port_count * MAXTIMESLOTS];
      groups = new TrunkGroup *[port_count * MAXTIMESLOTS];
      trunks = new AculabTrunk *[port_count * MAXTIMESLOTS];

      memset(imaps, 0, sizeof(AculabTrunk *) * port_count * MAXCHANNELS);
      memset(omaps, 0, sizeof(AculabTrunk *) * port_count * MAXCHANNELS);
      memset(ixmaps, 0, sizeof(AculabTrunk *) * MAX_CH_IDX);
      memset(tsmap, 0, sizeof(char) * port_count * MAXTIMESLOTS);
      memset(groups, 0, sizeof(TrunkGroup *) * port_count * MAXTIMESLOTS);
      memset(trunks, 0, sizeof(AculabTrunk *) * port_count * MAXTIMESLOTS);
      memset(MVIP,0,sizeof(MVIP));

      mvipMutex=new Mutex();

      queue = new AculabFifo();

      call_signal_info(&siginfo[0]);

      /*
       * Determine what timeslots are available
       */
      for(port = 0; port < port_count; ++port) {
            slog(Slog::levelInfo) << "aculab("<<port<<",) signalling system: "
                        << siginfo[port].sigsys <<endl;
            mask = 1L;
            for(ts = 0; ts < MAXTIMESLOTS; ++ts) {
                  if(mask & siginfo[port].validvector) {
                        tsmap[port * MAXTIMESLOTS + ts] = 1;
                  }
                  mask <<=1L;
            }
      }                 

      ScriptCommand::load(keywords);

      slog(Slog::levelInfo) << "Aculab driver loaded; capacity="
                  << port_count * MAXCHANNELS << " channels" <<  endl;
}

AculabDriver::~AculabDriver()
{
      if(running)
            terminate();

      if(tsmap)
            delete[] tsmap;

      if(trunks)
            delete[] trunks;

      if(groups)
            delete[] groups;

      if(imaps)
            delete[] imaps;

      if(omaps)
            delete[] omaps;

}

interface_t AculabDriver::getInterface(unsigned port)
{
      if(port >= port_count)
            return INVALID_PORT;

      return interfaces[port];
}

void AculabDriver::setChannel(AculabTrunk *trunk)
{
      if(trunk->channel >= MAXCHANNELS)
            return;

      if (call_handle_2_io(trunk->handle) == 0) { /* Incoming call */
            imaps[trunk->port * MAXCHANNELS + trunk->channel] = trunk;
      }
      else {
            omaps[trunk->port * MAXCHANNELS + trunk->channel] = trunk;
      }
      if (trunk->dspChannel != -1) {
            ixmaps[sm_get_channel_ix(trunk->dspChannel)] = trunk;
      }
}

void AculabDriver::clrChannel(AculabTrunk *trunk)
{
      if(trunk->channel >= MAXCHANNELS)
            return;

      if (call_handle_2_io(trunk->handle) == 0) { /* Incoming call */
            imaps[trunk->port * MAXCHANNELS + trunk->channel] = NULL;
      }
      else {
            omaps[trunk->port * MAXCHANNELS + trunk->channel] = NULL;
      }
}

bool AculabDriver::getMVIPslot(int *stream, int *slot)
{
      int mvip_stream=0;
      int mvip_slot=0;

      mvipMutex->enterMutex();
      while(MVIP[(mvip_stream * MVIP_FD_STREAMS) + mvip_slot]) {
            if(mvip_slot==(MVIP_TIMESLOTS-1)) {
                  mvip_slot=0;
                  if(mvip_stream==(MVIP_FD_STREAMS-1)) {
                        return false;
                  }
                  else {
                        mvip_stream++;
                  }
            }
            else {
                  mvip_slot++;
            }
      }
      MVIP[(mvip_stream * MVIP_FD_STREAMS) + mvip_slot]=1;
      mvipMutex->leaveMutex();

      *stream=mvip_stream;
      *slot=mvip_slot;

      return true;
}


void AculabDriver::freeMVIPslot(int stream, int slot)
{
      mvipMutex->enterMutex();
      MVIP[(stream * MVIP_FD_STREAMS) + slot]=0;
      mvipMutex->leaveMutex();
}

bool AculabDriver::InitProsody(void)
{
      struct sm_download_parms sm_download_parms;
      int rc;
      int this_module;
      int total_modules = sm_get_modules();

      slog(Slog::levelInfo) << "Aculab loading " << total_modules
                  << " prosody modules" << endl;

      for(this_module = 0; this_module < total_modules; ++this_module) {
            if(sm_reset_module(this_module)) {
                  slog(Slog::levelError) << "Aculab module " << this_module
                              << " failed, exiting..." << endl;
                  return false;
            }
            sm_download_parms.module = this_module;
            sm_download_parms.id = 0;
            sm_download_parms.filename = (char *)getProsody();
            slog(Slog::levelInfo) << "Aculab module " << this_module
                        << " downloading '"<<sm_download_parms.filename
                        << "'..." << endl;
            rc=sm_download_fmw(&sm_download_parms);
            if (rc != 0) {
                  slog(Slog::levelError) << "Aculab module download failed: "<<rc<<", exiting..." << endl;
                  return false;
            }
      }
      return true;
}

bool AculabDriver::InitNetworks(void)
{
      int total_switches;
      unsigned this_port, total_ports;

//    struct sysinfo_xparms sysinfo;
      struct restart_xparms restart_xparms;

      int rc;

      pri_count = bri_count = 0;
      total_switches = sw_get_drvrs();
      if(total_switches < 1) {
            slog(Slog::levelError) << "Aculab Failure: no switch drivers" << endl;
            return false;
      }

      total_ports = call_nports();

      slog(Slog::levelInfo) << "Aculab: " << total_ports
                  << " network ports available" << endl;

      for(this_port = 0; this_port < getMaxPorts(); ++this_port) {
            interfaces[this_port] = INVALID_PORT;
      }

      for(this_port = 0; this_port < total_ports; ++this_port) {
            
            if(call_line(this_port) == L_BASIC_RATE) {
                  interfaces[this_port] = BRI_PORT;
                  ++bri_count;
                  restart_xparms.filenamep = (char *)getBriFirmware();
                  restart_xparms.config_stringp = (char *)getBriConfig();
            }
            else {
                  interfaces[this_port] = PRI_PORT;
                  ++pri_count;

                  char *firmware=(char *)getPriFirmware(this_port);
                  if (firmware == NULL) {
                        restart_xparms.filenamep = (char *)getPriFirmware();
                  }
                  else {
                        restart_xparms.filenamep = firmware;
                  }
                  restart_xparms.config_stringp = (char *)getPriConfig();
                  if (restart_xparms.config_stringp == NULL) {
                        restart_xparms.config_stringp="";
                  }
            }

            if(call_is_download(this_port)) {
                  slog(Slog::levelInfo) << "Aculab(" << this_port
                        << "): loading '" << restart_xparms.filenamep
                        << "'/'"<<restart_xparms.config_stringp
                        << "' to port  " << this_port << endl;

                  restart_xparms.net = this_port;
                  rc = call_restart_fmw(&restart_xparms);
                  if(rc) {
                        slog(Slog::levelError) << "Aculab(" << this_port
                              << "): firmware load failure: " << rc << endl;
                        interfaces[this_port] = FAILED_PORT;
                        return false;
                  }
            }
      }

      if(system_init()) {
            slog(Slog::levelError) << "Aculab: system init failure" << endl;
            return false;
      }
      return true;
}

int AculabDriver::start(void)
{
      int count = 0;
      unsigned port, ts, id;
      int n;

      if(active) {
            slog(Slog::levelError) << "driver already started" << endl;
            return 0;
      }

      slog(Slog::levelDebug) << "starting " << WORKER_THREADS << " worker threads..." << endl;

      queue->enable();

      qthreads = new AculabQueueThread *[WORKER_THREADS];

      for(port = 0; port < port_count; ++port) {
            for(ts = 0; ts < MAXTIMESLOTS; ++ts) {
                  id = port * MAXTIMESLOTS + ts;
                  if(!tsmap[port * MAXTIMESLOTS + ts])
                        continue;

                  trunks[id] = new AculabTrunk(port, ts);
                  ++count;
            }
      }

      /*
       * start the consumer threads - just one per port for now.
       */
      for (n=0; n < WORKER_THREADS; n++) {
            qthreads[n]=new AculabQueueThread(queue);
            qthreads[n]->start();
      }

      /*
       * Start the dsp event thread (must be done after all
       * trunks are initialized).
       */
      dspthread=new AculabDSPEventThread(queue, ixmaps);
      dspthread->start();

      /*
       * start the audio processing thread
       */
      audiothread=new AculabAudioThread(queue, ixmaps);
      audiothread->start();

      /*
       * start the layer-1 monitoring thread
       */
      l1thread=new AculabMonitorThread();
      l1thread->start();

      timer=new AculabTimer();
      timer->start();

      if (!running) {
            Thread::start();
      }
      active=true;

      slog(Slog::levelInfo) << "driver started..." << endl;
      return count;
}

void AculabDriver::stop(void)
{
      if(!active)
            return;

      if(trunks)
            memset(trunks, 0, sizeof(AculabTrunk *) * port_count * MAXTIMESLOTS);

      active = false;
      slog(Slog::levelInfo) << "driver stopping..." << endl;

      queue->disable();

      delete[] qthreads;
      qthreads = NULL;

      delete timer;
      timer=NULL;

      delete dspthread;
      dspthread=NULL;

      delete l1thread;
      l1thread=NULL;
}

Trunk *AculabDriver::getTrunkPort(int id)
{
      if(id < 0 || id >= (int)port_count * MAXTIMESLOTS)
            return NULL;

      if(!trunks)
            return NULL;

      return (Trunk *)trunks[id];
}

// this should dispatch events thru putEvent!
void AculabDriver::run(void)
{
      struct state_xparms event_xparms;
//    struct detail_xparms detail_xparms;
//    struct cause_xparms cause;
      unsigned port, channel;
      int rc;
      AculabTrunk *trunk;

      while(active) {
            event_xparms.handle = 0;
            event_xparms.timeout = 500L;

            rc=call_event(&event_xparms);
            if(rc != 0) {
                  slog(Slog::levelError) << "Aculab event failure, rc="<<rc<< endl;
                  Thread::sleep(100);
                  continue;
            }

            if(!event_xparms.handle)
                  continue;

            port = call_handle_2_port(event_xparms.handle);
            channel = call_handle_2_chan(event_xparms.handle);

            if(port >= MAXPORT)
                  continue;

            if(channel >= MAXCHANNELS)
                  continue;

            slog(Slog::levelDebug)<<"got aculab event "<<event_xparms.state<<
                        " on port "<<port<<",chan "<<channel<<endl;

            if (call_handle_2_io(event_xparms.handle) == 0) {
                  trunk = imaps[port * MAXCHANNELS + channel];
            }
            else {
                  trunk = omaps[port * MAXCHANNELS + channel];
            }
            if(!trunk) {
                  slog(Slog::levelWarning)<<": NO TRUNK FOR CHANNEL!"<<endl;
                  continue;
            }

            switch(event_xparms.state) {
            case EV_WAIT_FOR_INCOMING:
            case EV_WAIT_FOR_OUTGOING:
            case EV_OUTGOING_PROCEEDING:
                  break;
            case EV_DETAILS:
                  postEvent(trunk,TRUNK_CALL_INFO);
                  break;
            case EV_INCOMING_CALL_DET:
                  postEvent(trunk,TRUNK_CALL_DETECT);
                  break;
            case EV_CALL_CONNECTED:
                  postEvent(trunk,TRUNK_CALL_CONNECT);
                  break;
            case EV_PROGRESS:
            case EV_OUTGOING_RINGING:
                  postEvent(trunk,TRUNK_CPA_RINGBACK);
                  break;
            case EV_HOLD:
                  postEvent(trunk,TRUNK_CALL_HOLD);
                  break;
            case EV_HOLD_REJECT:
                  postEvent(trunk,TRUNK_CALL_NOHOLD);
                  break;
            case EV_TRANSFER_REJECT: /* Need better value here? */
                  postEvent(trunk,TRUNK_CALL_FAILURE);
                  break;
            case EV_IDLE:
                  postEvent(trunk,TRUNK_CALL_RELEASE);
                  break;
            default:
                  slog(Slog::levelInfo) << "Aculab("<<port<<","<<trunk->ts<<") "
                              << "Unknown event type received: "
                              << event_xparms.state << endl;
                  break;
            }
      }
}

/*
 * NOTE: This must _NOT_ block for very long!!!!
 * (or we will lose hardware events - driver doesn't queue events!)
 */
void AculabDriver::postEvent(AculabTrunk *trunk, trunkevent_t id)
{
      AculabTrunkEvent *aevent;

      aevent=new AculabTrunkEvent();
      aevent->trunk=trunk;
      aevent->event.id=id;
      queue->push(aevent);
}

/*
 * Dump out relavent information about an incoming call
 */
void AculabDriver::dumpCallDetail(struct detail_xparms *details)
{
      slog(Slog::levelInfo) << "  " << ((details->calltype == INCOMING) ? "INCOMING" : "OUTGOING")
                  << " call initiated" <<endl;

      slog(Slog::levelInfo) << "  sending_complete="<<details->sending_complete<<endl;
      slog(Slog::levelInfo) << "  destination_addr='"<<details->destination_addr<<"'"<<endl;
      slog(Slog::levelInfo) << "  originating_addr='"<<details->originating_addr<<"'"<<endl;
      slog(Slog::levelInfo) << "  connected_addr  ='"<<details->connected_addr<<"'"<<endl;
      slog(Slog::levelInfo) << "  feature_info    ="<<details->feature_information<<endl;
}


AculabDriver aculabivr;

#ifdef    CCXX_NAMESPACES
};
#endif

Generated by  Doxygen 1.6.0   Back to index