yate/libs/ysig/sigcall.cpp

1415 lines
37 KiB
C++

/**
* sigcall.cpp
* This file is part of the YATE Project http://YATE.null.ro
*
* Yet Another Signalling Stack - implements the support for SS7, ISDN and PSTN
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2006 Null Team
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "yatesig.h"
#include <stdlib.h>
#include <string.h>
using namespace TelEngine;
TokenDict SignallingCircuit::s_lockNames[] = {
{"localhw", LockLocalHWFail},
{"localmaint", LockLocalMaint},
{"localhwchanged", LockLocalHWFailChg},
{"localmaintchanged", LockLocalMaintChg},
{"remotehw", LockRemoteHWFail},
{"remotemaint", LockRemoteMaint},
{"remotehwchanged", LockRemoteHWFailChg},
{"remotemaintchanged", LockRemoteMaintChg},
{0,0},
};
/**
* SignallingCallControl
*/
SignallingCallControl::SignallingCallControl(const NamedList& params,
const char* msgPrefix)
: Mutex(true),
m_verifyEvent(false),
m_verifyTimer(0),
m_circuits(0),
m_strategy(SignallingCircuitGroup::Increment),
m_exiting(false),
m_dumper(0)
{
// Strategy
const char* strategy = params.getValue("strategy","increment");
m_strategy = SignallingCircuitGroup::str2strategy(strategy);
String restrict;
if (m_strategy != SignallingCircuitGroup::Random)
restrict = params.getValue("strategy-restrict");
if (!restrict.null())
if (restrict == "odd")
m_strategy |= SignallingCircuitGroup::OnlyOdd;
else if (restrict == "even")
m_strategy |= SignallingCircuitGroup::OnlyEven;
else if (restrict == "odd-fallback")
m_strategy |= SignallingCircuitGroup::OnlyOdd | SignallingCircuitGroup::Fallback;
else if (restrict == "even-fallback")
m_strategy |= SignallingCircuitGroup::OnlyEven | SignallingCircuitGroup::Fallback;
// Message prefix
m_msgPrefix = params.getValue("message-prefix",msgPrefix);
// Verify event timer
m_verifyTimer.interval(params,"verifyeventinterval",10,120,true,true);
m_verifyTimer.start();
}
SignallingCallControl::~SignallingCallControl()
{
attach((SignallingCircuitGroup*)0);
}
// Attach a signalling circuit group. Set its strategy
void SignallingCallControl::attach(SignallingCircuitGroup* circuits)
{
Lock lock(this);
// Don't attach if it's the same object
if (m_circuits == circuits)
return;
cleanup(circuits ? "circuit group attach" : "circuit group detach");
if (m_circuits && circuits)
Debug(DebugNote,
"SignallingCallControl. Replaced circuit group (%p) with (%p) [%p]",
m_circuits,circuits,this);
m_circuits = circuits;
if (m_circuits) {
Lock lock(m_circuits);
m_circuits->setStrategy(m_strategy);
}
}
// Reserve a circuit from a given list in attached group
bool SignallingCallControl::reserveCircuit(SignallingCircuit*& cic, const char* range,
int checkLock, const String* list, bool mandatory, bool reverseRestrict)
{
Lock lock(this);
releaseCircuit(cic);
if (!m_circuits)
return false;
if (list) {
int s = -1;
if (!mandatory && reverseRestrict) {
s = m_circuits->strategy();
// Use the opposite strategy restriction
if (s & SignallingCircuitGroup::OnlyEven)
s = (s & ~SignallingCircuitGroup::OnlyEven) | SignallingCircuitGroup::OnlyOdd;
else if (s & SignallingCircuitGroup::OnlyOdd)
s = (s & ~SignallingCircuitGroup::OnlyOdd) | SignallingCircuitGroup::OnlyEven;
}
cic = m_circuits->reserve(*list,mandatory,checkLock,s,m_circuits->findRange(range));
}
else
cic = m_circuits->reserve(checkLock,-1,m_circuits->findRange(range));
return (cic != 0);
}
// Release a given circuit
bool SignallingCallControl::releaseCircuit(SignallingCircuit*& cic, bool sync)
{
if (!cic)
return false;
bool ok = cic->status(SignallingCircuit::Idle,sync);
DDebug(DebugAll,"SignallingCallControl. Released circuit %u [%p]",cic->code(),this);
cic->deref();
cic = 0;
return ok;
}
bool SignallingCallControl::releaseCircuit(unsigned int code, bool sync)
{
Lock lock(this);
SignallingCircuit* cic = m_circuits ? m_circuits->find(code) : 0;
if (!cic)
return false;
return cic->status(SignallingCircuit::Idle,sync);
}
// Get events from calls
// Raise Disable event when no more calls and exiting
SignallingEvent* SignallingCallControl::getEvent(const Time& when)
{
lock();
ListIterator iter(m_calls);
for (;;) {
SignallingCall* call = static_cast<SignallingCall*>(iter.get());
// End of iteration?
if (!call)
break;
RefPointer<SignallingCall> callRef = call;
// Dead pointer?
if (!callRef)
continue;
unlock();
SignallingEvent* event = callRef->getEvent(when);
// Check if this call controller wants the event
if (event && !processEvent(event))
return event;
lock();
}
unlock();
// Get events from circuits not reserved
// TODO: Find a better way to parse circuit list to get events
Lock lckCtrl(this);
if (m_circuits) {
Lock lckCic(m_circuits);
for (ObjList* o = m_circuits->circuits().skipNull(); o; o = o->skipNext()) {
SignallingCircuit* cic = static_cast<SignallingCircuit*>(o->get());
if (cic->status() == SignallingCircuit::Reserved)
continue;
SignallingCircuitEvent* ev = cic->getEvent(when);
if (!ev)
continue;
SignallingEvent* event = processCircuitEvent(*ev);
TelEngine::destruct(ev);
if (event)
return event;
}
}
// Verify ?
if (m_verifyTimer.timeout(when.msecNow()) && m_verifyEvent) {
SignallingMessage* msg = new SignallingMessage;
SignallingEvent* event = new SignallingEvent(SignallingEvent::Verify,msg,this);
buildVerifyEvent(msg->params());
m_verifyTimer.start(when.msecNow());
return event;
}
// Terminate if exiting and no more calls
//TODO: Make sure we raise this event one time only
if (exiting() && !m_calls.skipNull())
return new SignallingEvent(SignallingEvent::Disable,0,this);
return 0;
}
void SignallingCallControl::setDumper(SignallingDumper* dumper)
{
Lock lock(this);
if (m_dumper == dumper)
return;
SignallingDumper* tmp = m_dumper;
m_dumper = dumper;
delete tmp;
XDebug(DebugAll,"SignallingCallControl. Data dumper set to (%p) [%p]",m_dumper,this);
}
// Clear call list
void SignallingCallControl::clearCalls()
{
Lock lock(this);
m_calls.clear();
}
// Remove a call from list
void SignallingCallControl::removeCall(SignallingCall* call, bool del)
{
if (!call)
return;
Lock lock(this);
if (m_calls.remove(call,del))
DDebug(DebugAll,
"SignallingCallControl. Call (%p) removed from queue. Deleted: %s [%p]",
call,String::boolText(del),this);
}
/**
* SignallingCall
*/
SignallingCall::SignallingCall(SignallingCallControl* controller, bool outgoing, bool signalOnly)
: m_callMutex(true),
m_lastEvent(0),
m_controller(controller),
m_outgoing(outgoing),
m_signalOnly(signalOnly),
m_inMsgMutex(true),
m_private(0)
{
}
SignallingCall::~SignallingCall()
{
m_inMsg.clear();
if (m_controller)
m_controller->removeCall(this,false);
}
// Event termination notification
void SignallingCall::eventTerminated(SignallingEvent* event)
{
Lock lock(m_callMutex);
if (!m_lastEvent || !event || m_lastEvent != event)
return;
XDebug(DebugAll,"SignallingCall. Event (%p,'%s') terminated [%p]",event,event->name(),this);
m_lastEvent = 0;
}
void SignallingCall::enqueue(SignallingMessage* msg)
{
if (!msg)
return;
Lock lock(m_inMsgMutex);
m_inMsg.append(msg);
XDebug(DebugAll,"SignallingCall. Enqueued message (%p,'%s') [%p]",
msg,msg->name(),this);
}
// Dequeue a received message
SignallingMessage* SignallingCall::dequeue(bool remove)
{
Lock lock(m_inMsgMutex);
ObjList* obj = m_inMsg.skipNull();
if (!obj)
return 0;
SignallingMessage* msg = static_cast<SignallingMessage*>(obj->get());
if (remove) {
m_inMsg.remove(msg,false);
XDebug(DebugAll,"SignallingCall. Dequeued message (%p,'%s') [%p]",
msg,msg->name(),this);
}
return msg;
}
/**
* SignallingEvent
*/
TokenDict SignallingEvent::s_types[] = {
{"Unknown", Unknown},
{"Generic", Generic},
{"NewCall", NewCall},
{"Accept", Accept},
{"Connect", Connect},
{"Complete", Complete},
{"Progress", Progress},
{"Ringing", Ringing},
{"Answer", Answer},
{"Transfer", Transfer},
{"Suspend", Suspend},
{"Resume", Resume},
{"Release", Release},
{"Info", Info},
{"Message", Message},
{"Facility", Facility},
{"Enable", Enable},
{"Disable", Disable},
{"Reset", Reset},
{"Verify", Verify},
{0,0}
};
SignallingEvent::SignallingEvent(Type type, SignallingMessage* message, SignallingCall* call)
: m_type(type), m_message(0), m_call(0), m_controller(0)
{
if (call && call->ref()) {
m_call = call;
m_controller = call->controller();
}
if (message && message->ref())
m_message = message;
}
SignallingEvent::SignallingEvent(Type type, SignallingMessage* message, SignallingCallControl* controller)
: m_type(type), m_message(0), m_call(0), m_controller(controller)
{
if (message && message->ref())
m_message = message;
}
SignallingEvent::~SignallingEvent()
{
m_controller = 0;
if (m_message)
m_message->deref();
if (m_call) {
m_call->eventTerminated(this);
m_call->deref();
}
}
/**
* SignallingCircuitEvent
*/
SignallingCircuitEvent::SignallingCircuitEvent(SignallingCircuit* cic, Type type, const char* name)
: NamedList(name),
m_circuit(0),
m_type(type)
{
XDebug(DebugAll,"SignallingCircuitEvent::SignallingCircuitEvent() [%p]",this);
if (cic && cic->ref())
m_circuit = cic;
}
SignallingCircuitEvent::~SignallingCircuitEvent()
{
if (m_circuit) {
m_circuit->eventTerminated(this);
m_circuit->deref();
}
XDebug(DebugAll,"SignallingCircuitEvent::~SignallingCircuitEvent() [%p]",this);
}
/**
* SignallingCircuit
*/
static TokenDict s_cicTypeDict[] = {
{"TDM", SignallingCircuit::TDM},
{"RTP", SignallingCircuit::RTP},
{"IAX", SignallingCircuit::IAX},
{"Unknown", SignallingCircuit::Unknown},
{"Local", SignallingCircuit::Local},
{0,0}
};
static TokenDict s_cicStatusDict[] = {
{"Missing", SignallingCircuit::Missing},
{"Disabled", SignallingCircuit::Disabled},
{"Idle", SignallingCircuit::Idle},
{"Reserved", SignallingCircuit::Reserved},
{"Starting", SignallingCircuit::Starting},
{"Stopping", SignallingCircuit::Stopping},
{"Connected", SignallingCircuit::Connected},
{0,0}
};
SignallingCircuit::SignallingCircuit(Type type, unsigned int code,
SignallingCircuitGroup* group, SignallingCircuitSpan* span)
: m_mutex(true),
m_group(group),
m_span(span),
m_code(code),
m_type(type),
m_status(Disabled),
m_lock(0),
m_lastEvent(0)
{
XDebug(m_group,DebugAll,"SignallingCircuit::SignallingCircuit [%p]",this);
}
SignallingCircuit::SignallingCircuit(Type type, unsigned int code, Status status,
SignallingCircuitGroup* group, SignallingCircuitSpan* span)
: m_mutex(true),
m_group(group),
m_span(span),
m_code(code),
m_type(type),
m_status(status),
m_lock(0),
m_lastEvent(0)
{
XDebug(m_group,DebugAll,"SignallingCircuit::SignallingCircuit [%p]",this);
}
SignallingCircuit::~SignallingCircuit()
{
clearEvents();
XDebug(m_group,DebugAll,"SignallingCircuit::~SignallingCircuit [%p]",this);
}
// Get first event from queue
SignallingCircuitEvent* SignallingCircuit::getEvent(const Time& when)
{
Lock lock(m_mutex);
if (m_lastEvent)
return 0;
ObjList* obj = m_events.skipNull();
if (!obj)
return 0;
m_lastEvent = static_cast<SignallingCircuitEvent*>(m_events.remove(obj->get(),false));
return m_lastEvent;
}
bool SignallingCircuit::sendEvent(SignallingCircuitEvent::Type type, NamedList* params)
{
XDebug(m_group,DebugStub,"SignallingCircuit::sendEvent(%u,%p) [%p]",type,params,this);
return false;
}
// Set/reset circuit flag(s)
inline bool cicFlag(SignallingCircuit* cic, bool set, int flag, int chgFlag, bool setChg)
{
if (chgFlag)
if (setChg)
cic->setLock(chgFlag);
else
cic->resetLock(chgFlag);
if (set == (0 != cic->locked(flag)))
return false;
if (set)
cic->setLock(flag);
else
cic->resetLock(flag);
return true;
}
// Set/reset HW failure lock flag
bool SignallingCircuit::hwLock(bool set, bool remote, bool changed, bool setChanged)
{
Lock lock(m_mutex);
int flag = remote ? LockRemoteHWFail : LockLocalHWFail;
int chgFlag = changed ? (remote ? LockRemoteHWFailChg : LockLocalHWFailChg) : 0;
return cicFlag(this,set,flag,chgFlag,setChanged);
}
// Set/reset maintenance lock flag
bool SignallingCircuit::maintLock(bool set, bool remote, bool changed, bool setChanged)
{
Lock lock(m_mutex);
int flag = remote ? LockRemoteMaint : LockLocalMaint;
int chgFlag = changed ? (remote ? LockRemoteMaintChg : LockLocalMaintChg) : 0;
return cicFlag(this,set,flag,chgFlag,setChanged);
}
// Add event to queue
void SignallingCircuit::addEvent(SignallingCircuitEvent* event)
{
if (!event)
return;
Lock lock(m_mutex);
m_events.append(event);
}
// Clear event queue
void SignallingCircuit::clearEvents()
{
Lock lock(m_mutex);
m_events.clear();
}
// Event termination notification
void SignallingCircuit::eventTerminated(SignallingCircuitEvent* event)
{
Lock lock(m_mutex);
if (event && m_lastEvent == event) {
XDebug(m_group,DebugAll,"Event (%p) '%s' terminated for cic=%u [%p]",
event,event->c_str(),code(),this);
m_lastEvent = 0;
}
}
// Get the text associated with a circuit type
const char* SignallingCircuit::lookupType(int type)
{
return lookup(type,s_cicTypeDict);
}
// Get the text associated with a circuit status
const char* SignallingCircuit::lookupStatus(int status)
{
return lookup(status,s_cicStatusDict);
}
/**
* SignallingCircuitRange
*/
SignallingCircuitRange::SignallingCircuitRange(const String& rangeStr, const char* name,
int strategy)
: String(name),
m_count(0),
m_last(0),
m_strategy(strategy),
m_used(0)
{
add(rangeStr);
}
// Add codes to this range from a string
bool SignallingCircuitRange::add(const String& rangeStr)
{
unsigned int n = 0;
unsigned int* p = SignallingUtils::parseUIntArray(rangeStr,0,(unsigned int)-1,n,true);
if (!p)
return false;
add(p,n);
delete[] p;
return true;
}
// Add an array of circuit codes to this rangevoid
void SignallingCircuitRange::add(unsigned int* codes, unsigned int len)
{
if (!(codes && len))
return;
m_range.append(codes,len*sizeof(int));
m_count += len;
updateLast();
}
// Remove a circuit code from this range
void SignallingCircuitRange::remove(unsigned int code)
{
unsigned int* d = (unsigned int*)range();
for (unsigned int i = 0; i < count(); i++)
if (d[i] == code)
d[i] = 0;
updateLast();
}
// Check if a circuit code is within this range
bool SignallingCircuitRange::find(unsigned int code)
{
if (!range())
return false;
for (unsigned int i = 0; i < count(); i++)
if (range()[i] == code)
return true;
return false;
}
// Update last circuit code
void SignallingCircuitRange::updateLast()
{
m_last = 0;
for (unsigned int i = 0; i < count(); i++)
if (m_last <= range()[i])
m_last = range()[i] + 1;
}
/**
* SignallingCircuitGroup
*/
TokenDict SignallingCircuitGroup::s_strategy[] = {
{"increment", Increment},
{"decrement", Decrement},
{"lowest", Lowest},
{"highest", Highest},
{"random", Random},
{0,0}
};
SignallingCircuitGroup::SignallingCircuitGroup(unsigned int base, int strategy, const char* name)
: SignallingComponent(name),
Mutex(true),
m_range(String::empty(),name,strategy),
m_base(base)
{
setName(name);
XDebug(this,DebugAll,"SignallingCircuitGroup::SignallingCircuitGroup() [%p]",this);
}
// Set circuits status to Missing. Clear circuit list
// Clear span list
SignallingCircuitGroup::~SignallingCircuitGroup()
{
clearAll();
XDebug(this,DebugAll,"SignallingCircuitGroup::~SignallingCircuitGroup() [%p]",this);
}
// Find a circuit by code
SignallingCircuit* SignallingCircuitGroup::find(unsigned int cic, bool local)
{
if (!local) {
if (cic < m_base)
return 0;
cic -= m_base;
}
Lock lock(this);
if (cic >= m_range.m_last)
return 0;
ObjList* l = m_circuits.skipNull();
for (; l; l = l->skipNext()) {
SignallingCircuit* c = static_cast<SignallingCircuit*>(l->get());
if (c->code() == cic)
return c;
}
return 0;
}
// Find a range of circuits owned by this group
SignallingCircuitRange* SignallingCircuitGroup::findRange(const char* name)
{
Lock lock(this);
ObjList* obj = m_ranges.find(name);
return obj ? static_cast<SignallingCircuitRange*>(obj->get()) : 0;
}
void SignallingCircuitGroup::getCicList(String& dest)
{
dest = "";
Lock lock(this);
for (ObjList* l = m_circuits.skipNull(); l; l = l->skipNext()) {
SignallingCircuit* c = static_cast<SignallingCircuit*>(l->get());
dest.append(String(c->code()),",");
}
}
// Insert a circuit if not already in the list
bool SignallingCircuitGroup::insert(SignallingCircuit* circuit)
{
if (!circuit)
return false;
Lock lock(this);
if (m_circuits.find(circuit) || find(circuit->code(),true))
return false;
circuit->m_group = this;
m_circuits.append(circuit);
m_range.add(circuit->code());
return true;
}
// Remove a circuit from list. Update maximum circuit code
void SignallingCircuitGroup::remove(SignallingCircuit* circuit)
{
if (!circuit)
return;
Lock lock(this);
if (!m_circuits.remove(circuit,false))
return;
circuit->m_group = 0;
m_range.remove(circuit->code());
// TODO: remove from all ranges
}
// Append a span to the list if not already there
bool SignallingCircuitGroup::insertSpan(SignallingCircuitSpan* span)
{
if (!span)
return false;
Lock lock(this);
if (!m_spans.find(span))
m_spans.append(span);
return true;
}
// Build and insert a range from circuits belonging to a given span
void SignallingCircuitGroup::insertRange(SignallingCircuitSpan* span, const char* name,
int strategy)
{
if (!span)
return;
if (!name)
name = span->id();
Lock lock(this);
String tmp;
for (ObjList* o = m_circuits.skipNull(); o; o = o->skipNext()) {
SignallingCircuit* c = static_cast<SignallingCircuit*>(o->get());
if (span == c->span())
tmp.append(String(c->code()),",");
}
lock.drop();
insertRange(tmp,name,strategy);
}
// Build and insert a range contained in a string
void SignallingCircuitGroup::insertRange(const String& range, const char* name,
int strategy)
{
Lock lock(this);
if (findRange(name))
return;
if (strategy < 0)
strategy = m_range.m_strategy;
m_ranges.append(new SignallingCircuitRange(range,name,strategy));
Debug(this,DebugAll,"Added range %s: %s [%p]",name,range.c_str(),this);
}
// Remove a span from list
void SignallingCircuitGroup::removeSpan(SignallingCircuitSpan* span, bool delCics, bool delSpan)
{
if (!span)
return;
Lock lock(this);
if (delCics)
removeSpanCircuits(span);
m_spans.remove(span,delSpan);
}
// Remove circuits belonging to a span
void SignallingCircuitGroup::removeSpanCircuits(SignallingCircuitSpan* span)
{
if (!span)
return;
Lock lock(this);
ListIterator iter(m_circuits);
for (GenObject* obj = 0; (obj = iter.get());) {
SignallingCircuit* c = static_cast<SignallingCircuit*>(obj);
if (span == c->span()) {
remove(c);
TelEngine::destruct(c);
}
}
}
// Get the status of a circuit given by its code
SignallingCircuit::Status SignallingCircuitGroup::status(unsigned int cic)
{
Lock lock(this);
SignallingCircuit* circuit = find(cic);
return circuit ? circuit->status() : SignallingCircuit::Missing;
}
// Change the status of a circuit given by its code
bool SignallingCircuitGroup::status(unsigned int cic, SignallingCircuit::Status newStat,
bool sync)
{
Lock lock(this);
SignallingCircuit* circuit = find(cic);
return circuit && circuit->status(newStat,sync);
}
inline void adjustParity(unsigned int& n, int strategy)
{
if ((strategy & SignallingCircuitGroup::OnlyEven) && (n & 1))
n &= ~1;
else if ((strategy & SignallingCircuitGroup::OnlyOdd) && !(n & 1))
n |= 1;
}
// Choose the next circuit code to check, depending on strategy
unsigned int SignallingCircuitGroup::advance(unsigned int n, int strategy,
SignallingCircuitRange& range)
{
// Increment by 2 when even or odd only circuits are requested
unsigned int delta = (strategy & (OnlyOdd|OnlyEven)) ? 2 : 1;
switch (strategy & 0xfff) {
case Increment:
case Lowest:
n += delta;
if (n >= range.m_last)
n = delta;
break;
case Decrement:
case Highest:
if (n >= delta)
n -= delta;
else {
n = range.m_last - 1;
adjustParity(n,strategy);
}
break;
default:
n = (n + 1) % range.m_last;
break;
}
return n;
}
// Reserve a circuit
SignallingCircuit* SignallingCircuitGroup::reserve(int checkLock, int strategy,
SignallingCircuitRange* range)
{
Lock lock(this);
if (!range)
range = &m_range;
if (range->m_last < 1)
return 0;
if (strategy < 0)
strategy = range->m_strategy;
int dir = 1;
unsigned int n = range->m_used;
// first adjust the last used channel number
switch (strategy & 0xfff) {
case Increment:
n = (n + 1) % range->m_last;
break;
case Decrement:
n = (n ? n : range->m_last) - 1;
dir = -1;
break;
case Lowest:
n = 0;
break;
case Highest:
n = range->m_last - 1;
dir = -1;
break;
default:
while ((range->m_last > 1) && (n == range->m_used))
n = ::random() % range->m_last;
}
// then go to the proper even/odd start circuit
adjustParity(n,strategy);
// remember where the scan started
unsigned int start = n;
// try at most how many channels we have, halve that if we only scan even or odd
unsigned int i = range->m_last;
if (strategy & (OnlyOdd|OnlyEven))
i = (i + 1) / 2;
while (i--) {
// Check if the circuit is within range
if (range->find(n)) {
SignallingCircuit* circuit = find(n,true);
if (circuit && !circuit->locked(checkLock) && circuit->reserve()) {
if (circuit->ref()) {
range->m_used = n;
return circuit;
}
release(circuit);
return 0;
}
}
n = advance(n,strategy,*range);
// if wrapped around bail out, don't scan again
if (n == start)
break;
}
lock.drop();
if (strategy & Fallback) {
if (strategy & OnlyEven) {
Debug(this,DebugNote,"No even circuits available, falling back to odd [%p]",this);
return reserve(checkLock,OnlyOdd | (strategy & 0xfff),range);
}
if (strategy & OnlyOdd) {
Debug(this,DebugNote,"No odd circuits available, falling back to even [%p]",this);
return reserve(checkLock,OnlyEven | (strategy & 0xfff),range);
}
}
return 0;
}
// Reserve a circuit from the given list
// Reserve another one if not found and not mandatory
SignallingCircuit* SignallingCircuitGroup::reserve(const String& list, bool mandatory,
int checkLock, int strategy, SignallingCircuitRange* range)
{
Lock lock(this);
if (!range)
range = &m_range;
// Check if any of the given circuits are free
while (true) {
if (list.null())
break;
ObjList* circuits = list.split(',',false);
if (!circuits)
break;
SignallingCircuit* circuit = 0;
for (ObjList* obj = circuits->skipNull(); obj; obj = obj->skipNext()) {
int code = (static_cast<String*>(obj->get()))->toInteger(-1);
if (code > 0 && range->find(code))
circuit = find(code,false);
if (circuit && !circuit->locked(checkLock) && circuit->reserve()) {
if (circuit->ref()) {
range->m_used = m_base + circuit->code();
break;
}
release(circuit);
}
circuit = 0;
}
TelEngine::destruct(circuits);
if (circuit)
return circuit;
break;
}
// Don't try to reserve another one if the given list is mandatory
if (mandatory)
return 0;
return reserve(checkLock,strategy,range);
}
// Clear data
void SignallingCircuitGroup::clearAll()
{
Lock lock(this);
// Remove spans and their circuits
ListIterator iter(m_spans);
for (GenObject* obj = 0; (obj = iter.get());)
removeSpan(static_cast<SignallingCircuitSpan*>(obj),true,true);
// Remove the rest of circuits. Reset circuits' group
// Some of them may continue to exists after clearing the list
for (ObjList* l = m_circuits.skipNull(); l; l = l->skipNext()) {
SignallingCircuit* c = static_cast<SignallingCircuit*>(l->get());
c->status(SignallingCircuit::Missing,true);
c->m_group = 0;
}
m_circuits.clear();
m_ranges.clear();
}
/**
* SignallingCircuitSpan
*/
SignallingCircuitSpan::SignallingCircuitSpan(const char* id, SignallingCircuitGroup* group)
: m_group(group),
m_id(id)
{
if (m_group)
m_group->insertSpan(this);
XDebug(DebugAll,"SignallingCircuitSpan::SignallingCircuitSpan() '%s' [%p]",id,this);
}
SignallingCircuitSpan::~SignallingCircuitSpan()
{
if (m_group)
m_group->removeSpan(this,true,false);
XDebug(DebugAll,"SignallingCircuitSpan::~SignallingCircuitSpan() '%s' [%p]",m_id.safe(),this);
}
/**
* AnalogLine
*/
const TokenDict* AnalogLine::typeNames()
{
static const TokenDict names[] = {
{"FXO", FXO},
{"FXS", FXS},
{"monitor", Monitor},
{0,0}
};
return names;
}
const TokenDict* AnalogLine::stateNames()
{
static const TokenDict names[] = {
{"OutOfService", OutOfService},
{"Idle", Idle},
{"Dialing", Dialing},
{"DialComplete", DialComplete},
{"Ringing", Ringing},
{"Answered", Answered},
{"CallEnded", CallEnded},
{"OutOfOrder", OutOfOrder},
{0,0}
};
return names;
}
const TokenDict* AnalogLine::csNames() {
static const TokenDict names[] = {
{"after", After},
{"before", Before},
{"none", NoCallSetup},
{0,0}
};
return names;
}
inline u_int64_t getValidInt(const NamedList& params, const char* param, int defVal)
{
int tmp = params.getIntValue(param,defVal);
return tmp >= 0 ? tmp : defVal;
}
// Reserve the line's circuit
AnalogLine::AnalogLine(AnalogLineGroup* grp, unsigned int cic, const NamedList& params)
: Mutex(true),
m_type(Unknown),
m_state(Idle),
m_inband(false),
m_echocancel(0),
m_acceptPulseDigit(true),
m_answerOnPolarity(false),
m_hangupOnPolarity(false),
m_polarityControl(false),
m_callSetup(NoCallSetup),
m_callSetupTimeout(0),
m_noRingTimeout(0),
m_alarmTimeout(0),
m_group(grp),
m_circuit(0),
m_private(0),
m_peer(0),
m_getPeerEvent(false)
{
// Check and set some data
const char* error = 0;
while (true) {
#define CHECK_DATA(test,sError) if (test) { error = sError; break; }
CHECK_DATA(!m_group,"circuit group is missing")
CHECK_DATA(m_group->findLine(cic),"circuit already allocated")
SignallingCircuit* circuit = m_group->find(cic);
if (circuit && circuit->ref())
m_circuit = circuit;
CHECK_DATA(!m_circuit,"circuit is missing")
break;
#undef CHECK_DATA
}
if (error) {
Debug(m_group,DebugNote,"Can't create analog line (cic=%u): %s",
cic,error);
return;
}
m_type = m_group->type();
m_address << m_group->toString() << "/" << m_circuit->code();
m_inband = params.getBoolValue("dtmfinband",false);
String tmp = params.getValue("echocancel");
if (tmp.isBoolean())
m_echocancel = tmp.toBoolean() ? 1 : -1;
m_answerOnPolarity = params.getBoolValue("answer-on-polarity",false);
m_hangupOnPolarity = params.getBoolValue("hangup-on-polarity",false);
m_polarityControl = params.getBoolValue("polaritycontrol",false);
m_callSetup = (CallSetupInfo)lookup(params.getValue("callsetup"),csNames(),After);
m_callSetupTimeout = getValidInt(params,"callsetup-timeout",2000);
m_noRingTimeout = getValidInt(params,"ring-timeout",10000);
m_alarmTimeout = getValidInt(params,"alarm-timeout",30000);
m_delayDial = getValidInt(params,"delaydial",2000);
DDebug(m_group,DebugAll,"AnalogLine() addr=%s type=%s [%p]",
address(),lookup(m_type,typeNames()),this);
if (!params.getBoolValue("out-of-service",false)) {
resetCircuit();
if (params.getBoolValue("connect",true))
connect(false);
}
else
enable(false,false);
}
AnalogLine::~AnalogLine()
{
DDebug(m_group,DebugAll,"~AnalogLine() addr=%s [%p]",address(),this);
}
// Remove old peer's peer. Set this line's peer
void AnalogLine::setPeer(AnalogLine* line, bool sync)
{
Lock lock(this);
if (line == this) {
Debug(m_group,DebugNote,"%s: Attempt to set peer to itself [%p]",
address(),this);
return;
}
if (line == m_peer) {
if (sync && m_peer) {
XDebug(m_group,DebugAll,"%s: Syncing with peer (%p) '%s' [%p]",
address(),m_peer,m_peer->address(),this);
m_peer->setPeer(this,false);
}
return;
}
AnalogLine* tmp = m_peer;
m_peer = 0;
if (tmp) {
DDebug(m_group,DebugAll,"%s: Removed peer (%p) '%s' [%p]",
address(),tmp,tmp->address(),this);
if (sync)
tmp->setPeer(0,false);
}
m_peer = line;
if (m_peer) {
DDebug(m_group,DebugAll,"%s: Peer set to (%p) '%s' [%p]",
address(),m_peer,m_peer->address(),this);
if (sync)
m_peer->setPeer(this,false);
}
}
// Reset the line circuit's echo canceller to line default echo canceller state
void AnalogLine::resetEcho(bool train)
{
if (!(m_circuit || m_echocancel))
return;
bool enable = (m_echocancel > 0);
m_circuit->setParam("echocancel",String::boolText(enable));
if (enable && train)
m_circuit->setParam("echotrain",String(""));
}
// Connect the line's circuit. Reset line echo canceller
bool AnalogLine::connect(bool sync)
{
Lock lock(this);
bool ok = m_circuit && m_circuit->connect();
resetEcho(true);
if (sync && ok && m_peer)
m_peer->connect(false);
return ok;
}
// Disconnect the line's circuit. Reset line echo canceller
bool AnalogLine::disconnect(bool sync)
{
Lock lock(this);
bool ok = m_circuit && m_circuit->disconnect();
resetEcho(false);
if (sync && ok && m_peer)
m_peer->disconnect(false);
return ok;
}
// Send an event through this line
bool AnalogLine::sendEvent(SignallingCircuitEvent::Type type, NamedList* params)
{
Lock lock(this);
if (state() == OutOfService)
return false;
if (m_inband &&
(type == SignallingCircuitEvent::Dtmf || type == SignallingCircuitEvent::PulseDigit))
return false;
return m_circuit && m_circuit->sendEvent(type,params);
}
// Get events from the line's circuit if not out of service
AnalogLineEvent* AnalogLine::getEvent(const Time& when)
{
Lock lock(this);
if (state() == OutOfService) {
checkTimeouts(when);
return 0;
}
SignallingCircuitEvent* event = m_circuit ? m_circuit->getEvent(when) : 0;
if (!event) {
checkTimeouts(when);
return 0;
}
if ((event->type() == SignallingCircuitEvent::PulseDigit ||
event->type() == SignallingCircuitEvent::PulseStart) &&
!m_acceptPulseDigit) {
DDebug(m_group,DebugInfo,"%s: ignoring pulse event '%s' [%p]",
address(),event->c_str(),this);
delete event;
return 0;
}
return new AnalogLineEvent(this,event);
}
// Alternate get events from this line or peer
AnalogLineEvent* AnalogLine::getMonitorEvent(const Time& when)
{
Lock lock(this);
m_getPeerEvent = !m_getPeerEvent;
AnalogLineEvent* event = 0;
if (m_getPeerEvent) {
event = getEvent(when);
if (!event && m_peer)
event = m_peer->getEvent(when);
}
else {
if (m_peer)
event = m_peer->getEvent(when);
if (!event)
event = getEvent(when);
}
return event;
}
// Change the line state if neither current or new state are OutOfService
bool AnalogLine::changeState(State newState, bool sync)
{
Lock lock(this);
bool ok = false;
while (true) {
if (m_state == newState || m_state == OutOfService || newState == OutOfService)
break;
if (newState != Idle && newState < m_state)
break;
DDebug(m_group,DebugInfo,"%s: changed state from %s to %s [%p]",
address(),lookup(m_state,stateNames()),
lookup(newState,stateNames()),this);
m_state = newState;
ok = true;
break;
}
if (sync && ok && m_peer)
m_peer->changeState(newState,false);
return true;
}
// Enable/disable line. Change circuit's state to Disabled/Reserved when
// entering/exiting the OutOfService state
bool AnalogLine::enable(bool ok, bool sync, bool connectNow)
{
Lock lock(this);
while (true) {
if (ok) {
if (m_state != OutOfService)
break;
Debug(m_group,DebugInfo,"%s: back in service [%p]",address(),this);
m_state = Idle;
if (m_circuit) {
m_circuit->status(SignallingCircuit::Reserved);
if (connectNow)
connect(false);
}
break;
}
// Disable
if (m_state == OutOfService)
break;
Debug(m_group,DebugNote,"%s: out of service [%p]",address(),this);
m_state = OutOfService;
disconnect(false);
if (m_circuit)
m_circuit->status(SignallingCircuit::Disabled);
break;
}
if (sync && m_peer)
m_peer->enable(ok,false,connectNow);
return true;
}
// Deref the circuit
void AnalogLine::destroyed()
{
lock();
disconnect(false);
if (m_circuit)
m_circuit->status(SignallingCircuit::Idle);
setPeer(0,true);
if (m_group)
m_group->removeLine(this);
TelEngine::destruct(m_circuit);
unlock();
RefObject::destroyed();
}
/**
* AnalogLineGroup
*/
// Construct an analog line group owning single lines
AnalogLineGroup::AnalogLineGroup(AnalogLine::Type type, const char* name, bool slave)
: SignallingCircuitGroup(0,SignallingCircuitGroup::Increment,name),
m_type(type),
m_fxo(0),
m_slave(false)
{
setName(name);
if (m_type == AnalogLine::FXO)
m_slave = slave;
XDebug(this,DebugAll,"AnalogLineGroup() [%p]",this);
}
// Constructs an FXS analog line monitor
AnalogLineGroup::AnalogLineGroup(const char* name, AnalogLineGroup* fxo)
: SignallingCircuitGroup(0,SignallingCircuitGroup::Increment,name),
m_type(AnalogLine::FXS),
m_fxo(fxo)
{
setName(name);
if (m_fxo)
m_fxo->debugChain(this);
else
Debug(this,DebugWarn,"Request to create monitor without fxo group [%p]",this);
XDebug(this,DebugAll,"AnalogLineGroup() monitor fxo=%p [%p]",m_fxo,this);
}
AnalogLineGroup::~AnalogLineGroup()
{
XDebug(this,DebugAll,"~AnalogLineGroup() [%p]",this);
}
// Append it to the list
bool AnalogLineGroup::appendLine(AnalogLine* line, bool destructOnFail)
{
if (!(line && line->type() == m_type && line->group() == this)) {
if (destructOnFail)
TelEngine::destruct(line);
return false;
}
Lock lock(this);
m_lines.append(line);
DDebug(this,DebugAll,"Added line (%p) %s [%p]",line,line->address(),this);
return true;
}
// Remove a line from the list and destruct it
void AnalogLineGroup::removeLine(unsigned int cic)
{
Lock lock(this);
AnalogLine* line = findLine(cic);
if (!line)
return;
removeLine(line);
TelEngine::destruct(line);
}
// Remove a line from the list without destroying it
void AnalogLineGroup::removeLine(AnalogLine* line)
{
if (!line)
return;
Lock lock(this);
if (m_lines.remove(line,false))
DDebug(this,DebugAll,"Removed line %p %s [%p]",line,line->address(),this);
}
// Find a line by its circuit
AnalogLine* AnalogLineGroup::findLine(unsigned int cic)
{
Lock lock(this);
for (ObjList* o = m_lines.skipNull(); o; o = o->skipNext()) {
AnalogLine* line = static_cast<AnalogLine*>(o->get());
if (line->circuit() && line->circuit()->code() == cic)
return line;
}
return 0;
}
// Find a line by its address
AnalogLine* AnalogLineGroup::findLine(const String& address)
{
Lock lock(this);
ObjList* tmp = m_lines.find(address);
return tmp ? static_cast<AnalogLine*>(tmp->get()) : 0;
}
// Iterate through the line list to get an event
AnalogLineEvent* AnalogLineGroup::getEvent(const Time& when)
{
lock();
ListIterator iter(m_lines);
for (;;) {
AnalogLine* line = static_cast<AnalogLine*>(iter.get());
// End of iteration?
if (!line)
break;
RefPointer<AnalogLine> lineRef = line;
// Dead pointer?
if (!lineRef)
continue;
unlock();
AnalogLineEvent* event = !fxo() ? lineRef->getEvent(when) : lineRef->getMonitorEvent(when);
if (event)
return event;
lock();
}
unlock();
return 0;
}
// Remove all spans and circuits. Release object
void AnalogLineGroup::destruct()
{
lock();
for (ObjList* o = m_lines.skipNull(); o; o = o->skipNext()) {
AnalogLine* line = static_cast<AnalogLine*>(o->get());
Lock lock(line);
line->m_group = 0;
}
m_lines.clear();
TelEngine::destruct(m_fxo);
unlock();
SignallingCircuitGroup::destruct();
}
/* vi: set ts=8 sw=4 sts=4 noet: */