From ec08dd849af84fcdaea4e05a46d4af6e430f9dbf Mon Sep 17 00:00:00 2001 From: paulc Date: Mon, 2 Mar 2009 18:51:30 +0000 Subject: [PATCH] ISDN BRI support, most Andrei's (andrei@null.ro) work. Fixes and new features throughout the signalling engine. git-svn-id: http://voip.null.ro/svn/yate@2505 acf43c95-373e-0410-b603-e72c3f656dc1 --- conf.d/ysigchan.conf.sample | 2 + libs/ysig/dumper.cpp | 23 +- libs/ysig/engine.cpp | 19 +- libs/ysig/interface.cpp | 8 + libs/ysig/q921.cpp | 841 ++++++++++++++++++++++++++++-------- libs/ysig/q931.cpp | 545 ++++++++++++++++------- libs/ysig/sigcall.cpp | 12 +- libs/ysig/yatesig.h | 566 ++++++++++++++++++++---- modules/server/ysigchan.cpp | 80 +++- 9 files changed, 1639 insertions(+), 457 deletions(-) diff --git a/conf.d/ysigchan.conf.sample b/conf.d/ysigchan.conf.sample index ed8e8f61..4b46fb0f 100644 --- a/conf.d/ysigchan.conf.sample +++ b/conf.d/ysigchan.conf.sample @@ -22,6 +22,8 @@ ; type: string: Specify the link type ; Allowed values: ; ss7-isup: SS7 ISDN User Part over synchronous (HDLC) interface(s) +; isdn-bri-net: ISDN network side of a data link over basic rate HDLC interface(s) +; isdn-bri-cpe: ISDN CPE (user) side of a data link over basic rate HDLC interface(s) ; isdn-pri-net: ISDN network side of a data link over one or more primary HDLC interface(s) ; isdn-pri-cpe: ISDN CPE (user) side of a data link over one or more primary HDLC interface(s) ; isdn-pri-mon: ISDN monitor of one or more primary HDLC interface(s) diff --git a/libs/ysig/dumper.cpp b/libs/ysig/dumper.cpp index c1542cb6..0d2db52a 100644 --- a/libs/ysig/dumper.cpp +++ b/libs/ysig/dumper.cpp @@ -28,8 +28,8 @@ using namespace TelEngine; -SignallingDumper::SignallingDumper(Type type) - : m_type(type), m_output(0) +SignallingDumper::SignallingDumper(Type type, bool network) + : m_type(type), m_network(network), m_output(0) { } @@ -87,10 +87,15 @@ bool SignallingDumper::dump(void* buf, unsigned int len, bool sent, int link) case Q921: case Hdlc: { - // add LAPD pseudoheader + // add LAPD pseudoheader - see wiretap/libpcap.c hdr2.assign(0,16); unsigned char* ptr2 = (unsigned char*)hdr2.data(); - ptr2[6] = sent ? 1 : 0; + // packet type: outgoing 4, sniffed 3, incoming 0 + ptr2[0] = 0x00; + ptr2[1] = sent ? 0x04 : 0x00; + // address: are we the network side? + ptr2[6] = m_network ? 1 : 0; + // ETH_P_LAPD ptr2[14] = 0x00; ptr2[15] = 0x30; } @@ -149,20 +154,20 @@ void SignallingDumper::head() // Create a dumper from file SignallingDumper* SignallingDumper::create(DebugEnabler* dbg, const char* filename, Type type, - bool create, bool append) + bool network, bool create, bool append) { if (!filename) return 0; File* f = new File; if (f->openPath(filename,true,false,create,append,true)) - return SignallingDumper::create(f,type); + return SignallingDumper::create(f,type,network); Debug(dbg,DebugWarn,"Failed to create dumper '%s'",filename); delete f; return 0; } // Create a dumper from stream -SignallingDumper* SignallingDumper::create(Stream* stream, Type type, bool writeHeader) +SignallingDumper* SignallingDumper::create(Stream* stream, Type type, bool network, bool writeHeader) { if (!stream) return 0; @@ -170,7 +175,7 @@ SignallingDumper* SignallingDumper::create(Stream* stream, Type type, bool write delete stream; return 0; } - SignallingDumper* dumper = new SignallingDumper(type); + SignallingDumper* dumper = new SignallingDumper(type,network); dumper->setStream(stream,writeHeader); return dumper; } @@ -195,7 +200,7 @@ bool SignallingDumpable::setDumper(const String& name, bool create, bool append) type = SignallingDumper::Raw; else if (name.endsWith(".hex") || name.endsWith(".txt")) type = SignallingDumper::Hexa; - SignallingDumper* dumper = SignallingDumper::create(0,name,type,create,append); + SignallingDumper* dumper = SignallingDumper::create(0,name,type,m_dumpNet,create,append); if (dumper) setDumper(dumper); else diff --git a/libs/ysig/engine.cpp b/libs/ysig/engine.cpp index 93785a60..30655c0b 100644 --- a/libs/ysig/engine.cpp +++ b/libs/ysig/engine.cpp @@ -100,6 +100,13 @@ void* SignallingFactory::build(const String& type, const NamedList* name) } +SignallingComponent::SignallingComponent(const char* name) + : m_engine(0) +{ + DDebug(engine(),DebugAll,"Component '%s' created [%p]",name,this); + setName(name); +} + void SignallingComponent::setName(const char* name) { debugName(0); @@ -127,6 +134,16 @@ bool SignallingComponent::control(NamedList& params) return false; } +void SignallingComponent::engine(SignallingEngine* eng) +{ + if (eng == m_engine) + return; + if (eng) + eng->insert(this); + else + detach(); +} + void SignallingComponent::insert(SignallingComponent* component) { if (!component) @@ -395,7 +412,7 @@ static TokenDict s_dict_causeCCITT[] = { {"noanswer", 0x13}, // No answer from user (user alerted) {"rejected", 0x15}, // Call Rejected {"moved", 0x16}, // Number changed - {"non-sel-user-clearing", 0x1a}, // Non-selected user clearing + {"answered", 0x1a}, // Non-selected user clearing (answered elsewhere) {"offline", 0x1b}, // Destination out of order {"invalid-number", 0x1c}, // Invalid number format {"facility-rejected", 0x1d}, // Facility rejected diff --git a/libs/ysig/interface.cpp b/libs/ysig/interface.cpp index b37f46bc..f5bd22bb 100644 --- a/libs/ysig/interface.cpp +++ b/libs/ysig/interface.cpp @@ -99,6 +99,14 @@ bool SignallingInterface::notify(Notification event) } +YCLASSIMP(SignallingReceiver,SignallingComponent) + +SignallingReceiver::SignallingReceiver(const char* name) + : SignallingComponent(name), + m_ifaceMutex(true), m_interface(0) +{ +} + SignallingReceiver::~SignallingReceiver() { if (m_interface) diff --git a/libs/ysig/q921.cpp b/libs/ysig/q921.cpp index b5de3920..d1f6b411 100644 --- a/libs/ysig/q921.cpp +++ b/libs/ysig/q921.cpp @@ -24,6 +24,8 @@ #include "yatesig.h" +#include + using namespace TelEngine; @@ -40,6 +42,10 @@ using namespace TelEngine; static const char* s_linkSideNet = "NET"; static const char* s_linkSideCpe = "CPE"; +#define Q921_MANAGEMENT_TEI 15 // TEI management message descriptor (first byte). See Q.921 Table 8 +#define Q921_TEI_BROADCAST 127 // TEI value for broadcast and management procedures +#define Q921_SAPI_MANAGEMENT 63 // SAPI value for management procedures + inline const char* linkSide(bool net) { return net ? s_linkSideNet : s_linkSideCpe; @@ -47,7 +53,6 @@ inline const char* linkSide(bool net) // Drop frame reasons static const char* s_noState = "Not allowed in this state"; -static const char* s_noCfg = "Not allowed by configuration"; // Used to set or compare values that may wrap at 127 boundary class Modulo128 @@ -95,10 +100,12 @@ public: // **************************************************************************** // Constructor. Set data members. Print them -ISDNQ921::ISDNQ921(const NamedList& params, const char* name) - : ISDNLayer2(params,name), - SignallingReceiver(), - SignallingDumpable(SignallingDumper::Q921), +ISDNQ921::ISDNQ921(const NamedList& params, const char* name, ISDNQ921Management* mgmt, u_int8_t tei) + : SignallingComponent(name), + ISDNLayer2(params,name,tei), + SignallingReceiver(name), + SignallingDumpable(SignallingDumper::Q921,network()), + m_management(mgmt), m_remoteBusy(false), m_timerRecovery(false), m_rejectSent(false), @@ -107,7 +114,6 @@ ISDNQ921::ISDNQ921(const NamedList& params, const char* name) m_vs(0), m_va(0), m_vr(0), - m_layer(true), m_retransTimer(0), m_idleTimer(0), m_window(7), @@ -123,7 +129,10 @@ ISDNQ921::ISDNQ921(const NamedList& params, const char* name) m_errorSend(false), m_errorReceive(false) { - setName(params.getValue("debugname",name)); + if (mgmt && network()) + autoRestart(false); + if (!mgmt) + setName(params.getValue("debugname",name)); m_retransTimer.interval(params,"t200",1000,1000,false); m_idleTimer.interval(params,"t203",2000,10000,false); // Adjust idle timeout to data link side @@ -136,126 +145,128 @@ ISDNQ921::ISDNQ921(const NamedList& params, const char* name) if (debugAt(DebugInfo)) { String tmp; #ifdef DEBUG - tmp << " SAPI/TEI=" << (unsigned int)sapi() << "/" << (unsigned int)tei(); + tmp << " SAPI/TEI=" << (unsigned int)localSapi() << "/" << (unsigned int)localTei(); tmp << " auto-restart=" << String::boolText(autoRestart()); tmp << " max-user-data=" << (unsigned int)maxUserData(); tmp << " max-pending-frames: " << (unsigned int)m_window.maxVal(); tmp << " retrans/idle=" << (unsigned int)m_retransTimer.interval() << "/" << (unsigned int)m_idleTimer.interval(); - tmp << " allow-unack-data=" << String::boolText(allowUnack()); #endif - Debug(this,DebugInfo,"ISDN Data Link type=%s%s [%p]", + Debug(this,DebugAll,"ISDN Data Link type=%s%s [%p]", linkSide(network()),tmp.safe(),this); } - setDumper(params.getValue("layer2dump")); + if (!mgmt) + setDumper(params.getValue("layer2dump")); } // Destructor ISDNQ921::~ISDNQ921() { - Lock lock(m_layer); - ISDNLayer2::attach(0); + Lock lock(l2Mutex()); + ISDNLayer2::attach((ISDNLayer3*)0); SignallingReceiver::attach(0); cleanup(); - if (debugAt(DebugAll)) - Debug(this,DebugAll, - "ISDN Data Link destroyed. Frames: sent=%u (failed=%u) recv=%u rejected=%u dropped=%u. HW errors=%u [%p]", - (unsigned int)m_txFrames,(unsigned int)m_txFailFrames, - (unsigned int)m_rxFrames,(unsigned int)m_rxRejectedFrames, - (unsigned int)m_rxDroppedFrames,(unsigned int)m_hwErrors,this); + DDebug(this,DebugAll, + "ISDN Data Link destroyed. Frames: sent=%u (failed=%u) recv=%u rejected=%u dropped=%u. HW errors=%u [%p]", + (unsigned int)m_txFrames,(unsigned int)m_txFailFrames, + (unsigned int)m_rxFrames,(unsigned int)m_rxRejectedFrames, + (unsigned int)m_rxDroppedFrames,(unsigned int)m_hwErrors,this); } // Set or release 'multiple frame acknowledged' mode -bool ISDNQ921::multipleFrame(bool establish, bool force) +bool ISDNQ921::multipleFrame(u_int8_t tei, bool establish, bool force) { - Lock lock(m_layer); - // Check state. Don't do nothing in transition states - if (state() == WaitEstablish || state() == WaitRelease) + Lock lock(l2Mutex()); + // Check state. Don't do anything in transition states or if TEI changes + if ((localTei() != tei) || (state() == WaitEstablish) || (state() == WaitRelease)) return false; // The request wouldn't change our state and we are not forced to fulfill it if (!force && - ((establish && (state() == Established || state() == WaitEstablish)) || - (!establish && (state() == Released || state() == WaitRelease)))) + ((establish && (state() == Established)) || + (!establish && (state() == Released)))) return false; - XDebug(this,DebugAll,"Process '%s' request",establish ? "ESTABLISH" : "RELEASE"); - bool result; + XDebug(this,DebugAll,"Process '%s' request, TEI=%u",establish ? "ESTABLISH" : "RELEASE",tei); + bool result = true; if (establish) { reset(); result = sendUFrame(ISDNFrame::SABME,true,true); - changeState(WaitEstablish); + changeState(WaitEstablish,"multiple frame"); timer(true,false); } else { // Already disconnected: Just notify Layer 3 if (state() == Released) { lock.drop(); - multipleFrameReleased(true,false); + if (m_management) + m_management->multipleFrameReleased(tei,true,false,this); + else + multipleFrameReleased(tei,true,false); return true; } reset(); result = sendUFrame(ISDNFrame::DISC,true,true); - changeState(WaitRelease); + changeState(WaitRelease,"multiple frame"); timer(true,false); } return result; } // Send data through the HDLC interface -bool ISDNQ921::sendData(const DataBlock& data, bool ack) +bool ISDNQ921::sendData(const DataBlock& data, u_int8_t tei, bool ack) { - Lock lock(m_layer); - if (!(data.length() && teiAssigned())) + if (data.null()) return false; + Lock lock(l2Mutex()); if (ack) { - if (state() == Released || m_window.full()) + if (localTei() != tei || !teiAssigned() || state() == Released || m_window.full()) return false; // Enqueue and send outgoing data - ISDNFrame* f = new ISDNFrame(true,network(),sapi(),tei(),false,data); + ISDNFrame* f = new ISDNFrame(true,network(),localSapi(),localTei(),false,data); // Update frame send seq number. Inc our send seq number and window counter f->update(&m_vs,0); Modulo128::inc(m_vs); m_window.inc(); // Append and try to send frame m_outFrames.append(f); - DDebug(this,DebugAll, - "Enqueued data frame (%p). Sequence number: %u",f,f->ns()); + XDebug(this,DebugAll,"Enqueued data frame (%p). Sequence number: %u",f,f->ns()); sendOutgoingData(); return true; } // Unacknowledged data request - if (!allowUnack()) + if (tei != Q921_TEI_BROADCAST) { + Debug(this,DebugInfo,"Not sending unacknowledged data with TEI %u [%p]",tei,this); return false; + } // P/F bit is always false for UI frames. See Q.921 5.2.2 - ISDNFrame* f = new ISDNFrame(false,network(),sapi(),tei(),false,data); + ISDNFrame* f = new ISDNFrame(false,network(),localSapi(),localTei(),false,data); bool result = sendFrame(f); - f->deref(); + TelEngine::destruct(f); return result; } // Send DISC. Reset data void ISDNQ921::cleanup() { - Lock lock(m_layer); + Lock lock(l2Mutex()); DDebug(this,DebugAll,"Cleanup in state '%s'",stateName(state())); // Don't send DISC if we are disconnected or waiting to become disconnected if (state() == Established) sendUFrame(ISDNFrame::DISC,true,true); reset(); - changeState(Released); + changeState(Released,"cleanup"); } -void* ISDNQ921::getObject(const String& name) const -{ - if (name == "ISDNQ921") - return (void*)this; - return 0; -} +YCLASSIMP2(ISDNQ921,ISDNLayer2,SignallingReceiver) // Method called periodically to check timeouts // Re-sync with remote peer if necessary void ISDNQ921::timerTick(const Time& when) { - Lock lock(m_layer); + // If possible return early without locking + if (state() == Released) + return; + Lock lock(l2Mutex()); + // Check state again after locking, to be sure it didn't change if (state() == Released) return; // T200 not started @@ -279,13 +290,12 @@ void ISDNQ921::timerTick(const Time& when) // Q.921 5.6.7: Timeout // Done all retransmissions ? if (m_n200.full()) { - DDebug(this,DebugNote,"Timeout. Link is down"); reset(); - changeState(Released); + changeState(Released,"timeout"); lock.drop(); - multipleFrameReleased(false,true); + multipleFrameReleased(localTei(),false,true); if (autoRestart()) - multipleFrame(true,false); + multipleFrame(localTei(),true,false); return; } // Waiting to establish/release ? @@ -317,27 +327,31 @@ void ISDNQ921::timerTick(const Time& when) // Parse data. Validate received frame and process it bool ISDNQ921::receivedPacket(const DataBlock& packet) { - if (!packet.length()) - return false; - Lock lock(m_layer); - XDebug(this,DebugAll,"Received packet (Length: %u)",packet.length()); - ISDNFrame* frame = ISDNFrame::parse(packet,this); - if (!frame) { - if (!m_errorReceive) - Debug(this,DebugNote,"Received short data (Length: %u)",packet.length()); - m_errorReceive = true; + ISDNFrame* f = parsePacket(packet); + if (!f) { + if (!m_errorReceive) { + m_errorReceive = true; + Debug(this,DebugNote,"Received invalid packet with length %u [%p]",packet.length(),this); + } return false; } m_errorReceive = false; // Print & dump if (debugAt(DebugInfo) && m_printFrames) { String tmp; - frame->toString(tmp,m_extendedDebug); - Debug(this,DebugInfo,"Received frame (%p):%s",frame,tmp.c_str()); + f->toString(tmp,m_extendedDebug); + Debug(this,DebugInfo,"Received frame (%p):%s",f,tmp.c_str()); } - if (frame->type() < ISDNFrame::Invalid) - dump(frame->buffer(),false); - // Accept + if (f->type() < ISDNFrame::Invalid) + dump(f->buffer(),false); + return receivedFrame(f); +} + +bool ISDNQ921::receivedFrame(ISDNFrame* frame) +{ + if (!frame) + return false; + Lock lock(l2Mutex()); bool reject = false; // Not accepted: // If not rejected, for out of range sequence number send @@ -355,22 +369,22 @@ bool ISDNQ921::receivedPacket(const DataBlock& packet) else sendSFrame(ISDNFrame::RR,false,frame->poll()); } - frame->deref(); + TelEngine::destruct(frame); return true; } // Unrecoverable error: re-establish - Debug(this,DebugNote,"Rejected frame (%p): %s. Reason: '%s'. Restarting", - frame,frame->name(),ISDNFrame::typeName(frame->error())); - frame->deref(); + Debug(this,DebugNote,"Rejected %s frame %p, reason: '%s'. Restarting", + frame->name(),frame,ISDNFrame::typeName(frame->error())); + TelEngine::destruct(frame); reset(); - changeState(WaitEstablish); + changeState(WaitEstablish,"received frame"); sendUFrame(ISDNFrame::SABME,true,true); timer(true,false); return true; } // Process - XDebug(this,DebugAll,"Process frame (%p): '%s' in state '%s'", - frame,frame->name(),ISDNLayer2::stateName(state())); + XDebug(this,DebugAll,"Process %s frame %p in state '%s'", + frame->name(),frame,ISDNLayer2::stateName(state())); bool chgState = false, confirmation = false; State newState; if (frame->category() == ISDNFrame::Data) { @@ -379,7 +393,7 @@ bool ISDNQ921::receivedPacket(const DataBlock& packet) DataBlock tmp; frame->getData(tmp); lock.drop(); - receiveData(tmp,ack); + receiveData(tmp,localTei()); } frame->deref(); return true; @@ -397,21 +411,27 @@ bool ISDNQ921::receivedPacket(const DataBlock& packet) } else chgState = processUFrame(frame,newState,confirmation); - frame->deref(); + TelEngine::destruct(frame); // Change state ? if (!chgState) return true; reset(); - changeState(newState); + changeState(newState,"received frame"); switch (newState) { case Established: timer(false,true); lock.drop(); - multipleFrameEstablished(confirmation,false); + if (m_management) + m_management->multipleFrameEstablished(localTei(),confirmation,false,this); + else + multipleFrameEstablished(localTei(),confirmation,false); break; case Released: lock.drop(); - multipleFrameReleased(confirmation,false); + if (m_management) + m_management->multipleFrameReleased(localTei(),confirmation,false,this); + else + multipleFrameReleased(localTei(),confirmation,false); break; case WaitEstablish: sendUFrame(ISDNFrame::SABME,true,true); @@ -428,7 +448,7 @@ bool ISDNQ921::receivedPacket(const DataBlock& packet) // Process a notification generated by the attached interface bool ISDNQ921::notify(SignallingInterface::Notification event) { - Lock lock(m_layer); + Lock lock(l2Mutex()); if (event != SignallingInterface::LinkUp) m_hwErrors++; else { @@ -440,16 +460,20 @@ bool ISDNQ921::notify(SignallingInterface::Notification event) Debug(this,DebugWarn,"Received notification %u: '%s'", event,lookup(event,SignallingInterface::s_notifName)); reset(); - changeState(Released); + changeState(Released,"interface down"); lock.drop(); - multipleFrameReleased(false,false); + multipleFrameReleased(localTei(),false,false); + if (m_management && !network()) { + teiAssigned(false); + setRi(0); + } if (autoRestart()) - multipleFrame(true,false); + multipleFrame(localTei(),true,false); return true; } #ifdef DEBUG if (!(m_hwErrors % 250)) - DDebug(this,DebugNote,"Received notification %u: '%s'. Total=%u", + Debug(this,DebugNote,"Received notification %u: '%s'. Total=%u", event,lookup(event,SignallingInterface::s_notifName,"Undefined"),m_hwErrors); #endif return true; @@ -458,8 +482,8 @@ bool ISDNQ921::notify(SignallingInterface::Notification event) // Reset data void ISDNQ921::reset() { - Lock lock(m_layer); - XDebug(this,DebugAll,"Reset"); + Lock lock(l2Mutex()); + XDebug(this,DebugAll,"Reset, total frames: %d [%p]",m_outFrames.count(),this); m_remoteBusy = false; m_timerRecovery = false; m_rejectSent = false; @@ -487,7 +511,7 @@ bool ISDNQ921::ackOutgoingFrames(const ISDNFrame* frame) break; } ack = true; - DDebug(this,DebugAll, + XDebug(this,DebugAll, "Remove acknowledged data frame (%p). Sequence number: %u",f,f->ns()); m_window.dec(); m_outFrames.remove(f,true); @@ -508,24 +532,14 @@ bool ISDNQ921::ackOutgoingFrames(const ISDNFrame* frame) // Ack pending outgoing data and confirm (by sending any pending data or an RR confirmation) bool ISDNQ921::processDataFrame(const ISDNFrame* frame, bool ack) { - const char* reason = 0; - // State or configuration allow receiving data ? - if (ack) { - if (state() != Established) - reason = s_noState; - } - else { - if (!allowUnack()) - reason = s_noCfg; - } - if (reason) { - dropFrame(frame,reason); - return false; - } - // Done for unacknowledged (UI frame) data + // Always accept UI if (!ack) return true; - // Acknoledged data + // Acknowledged data: accept only when established + if (state() != Established) { + dropFrame(frame,s_noState); + return false; + } m_rejectSent = false; m_remoteBusy = false; m_vr = frame->ns(); @@ -553,6 +567,9 @@ bool ISDNQ921::processDataFrame(const ISDNFrame* frame, bool ack) // RNR Adjust send frame counter if necessary bool ISDNQ921::processSFrame(const ISDNFrame* frame) { + if (!frame) + return false; + Lock lock(l2Mutex()); if (state() != Established) { dropFrame(frame,s_noState); return false; @@ -735,7 +752,7 @@ bool ISDNQ921::acceptFrame(ISDNFrame* frame, bool& reject) // Check frame only if it's not already invalid for (; frame->error() < ISDNFrame::Invalid;) { // Check SAPI/TEI - if (frame->sapi() != sapi() || frame->tei() != tei()) { + if (frame->sapi() != localSapi() || frame->tei() != localTei()) { frame->m_error = ISDNFrame::ErrInvalidAddress; break; } @@ -816,10 +833,10 @@ bool ISDNQ921::sendUFrame(ISDNFrame::Type type, bool command, bool pf, } // Create and send frame // U frames don't have an N(R) control data - ISDNFrame* f = new ISDNFrame(type,command,network(),sapi(),tei(),pf); + ISDNFrame* f = new ISDNFrame(type,command,network(),localSapi(),localTei(),pf); f->sent(retrans); bool result = sendFrame(f); - f->deref(); + TelEngine::destruct(f); return result; } @@ -831,9 +848,9 @@ bool ISDNQ921::sendSFrame(ISDNFrame::Type type, bool command, bool pf) type == ISDNFrame::REJ)) return false; // Create and send frame - ISDNFrame* f = new ISDNFrame(type,command,network(),sapi(),tei(),pf,m_vr); + ISDNFrame* f = new ISDNFrame(type,command,network(),localSapi(),localTei(),pf,m_vr); bool result = sendFrame(f); - f->deref(); + TelEngine::destruct(f); return result; } @@ -848,14 +865,14 @@ bool ISDNQ921::sendFrame(const ISDNFrame* frame) return false; } // Print frame - if (debugAt(DebugInfo) && m_printFrames && !m_errorSend) { + if (debugAt(DebugInfo) && m_printFrames && !m_errorSend && frame->type() != ISDNFrame::UI) { String tmp; frame->toString(tmp,m_extendedDebug); Debug(this,DebugInfo,"Sending frame (%p):%s", frame,tmp.c_str()); } - bool result = SignallingReceiver::transmitPacket(frame->buffer(), - false,SignallingInterface::Q921); + bool result = m_management ? m_management->sendFrame(frame,this) : + SignallingReceiver::transmitPacket(frame->buffer(),false,SignallingInterface::Q921); // Dump frame if no error and we have a dumper if (result) { m_txFrames++; @@ -948,15 +965,431 @@ void ISDNQ921::timer(bool start, bool t203, u_int64_t time) } } +/** + * ISDNQ921Management + */ +// Constructor +ISDNQ921Management::ISDNQ921Management(const NamedList& params, const char* name, bool net) + : ISDNLayer2(params,name), + SignallingReceiver(name), + SignallingDumpable(SignallingDumper::Q921,network()), + m_teiManTimer(0), m_teiTimer(0) +{ + String baseName = params.getValue("debugname",name); + setName(baseName); + m_network = net; + m_teiManTimer.interval(params,"t202",2500,2600,false); + m_teiTimer.interval(params,"t201",1000,5000,false); + setDumper(params.getValue("layer2dump")); + bool set0 = true; + if (baseName.endsWith("Management")) { + baseName = baseName.substr(0,baseName.length()-10); + set0 = false; + } + // If we are NET create one ISDNQ921 for each possible TEI + for (int i = 0; i < 127; i++) { + if (network() || (i == 0)) { + String qName = baseName; + if (set0 || (i != 0)) + qName << "-" << i; + m_layer2[i] = new ISDNQ921(params,qName,this,i); + m_layer2[i]->ISDNLayer2::attach(this); + } + else + m_layer2[i] = 0; + } + if (!network()) { + m_layer2[0]->teiAssigned(false); + m_teiManTimer.start(); + } +} + +ISDNQ921Management::~ISDNQ921Management() +{ + Lock lock(l2Mutex()); + ISDNLayer2::attach((ISDNLayer3*)0); + SignallingReceiver::attach(0); + for (int i = 0; i < 127; i++) + TelEngine::destruct(m_layer2[i]); +} + +YCLASSIMP3(ISDNQ921Management,ISDNLayer2,ISDNLayer3,SignallingReceiver) + +void ISDNQ921Management::engine(SignallingEngine* eng) +{ + SignallingComponent::engine(eng); + for (int i = 0; i < 127; i++) + if (m_layer2[i]) + m_layer2[i]->engine(eng); +} + +void ISDNQ921Management::cleanup() +{ + Lock lock(l2Mutex()); + for (int i = 0;i < 127; i++) + if (m_layer2[i]) + m_layer2[i]->cleanup(); +} + +bool ISDNQ921Management::multipleFrame(u_int8_t tei, bool establish, bool force) +{ + if (tei >= 127) + return false; + m_sapi = Q921_SAPI_MANAGEMENT; + l2Mutex().lock(); + RefPointer q921 = m_layer2[network() ? tei : 0]; + l2Mutex().unlock(); + return q921 && q921->multipleFrame(tei,establish,force); +} + +bool ISDNQ921Management::sendFrame(const ISDNFrame* frame, const ISDNQ921* q921) +{ + if (!frame) + return false; + Lock lock(l2Mutex()); + if (SignallingReceiver::transmitPacket(frame->buffer(),false,SignallingInterface::Q921)) { + dump(frame->buffer(),true); + return true; + } + return false; +} + +bool ISDNQ921Management::sendData(const DataBlock& data, u_int8_t tei, bool ack) +{ + if (tei > Q921_TEI_BROADCAST) + return false; + if (tei == Q921_TEI_BROADCAST) + ack = false; + int auxTei = tei; + + Lock lock(l2Mutex()); + if (!network()) { + if (m_layer2[0] && m_layer2[0]->teiAssigned()) + auxTei = 0; + else + return false; + } + if (ack) + return m_layer2[auxTei] && m_layer2[auxTei]->sendData(data,tei,true); + + // P/F bit is always false for UI frames. See Q.921 5.2.2 + ISDNFrame* f = new ISDNFrame(false,network(),0,tei,false,data); + bool ok = sendFrame(f); + lock.drop(); + TelEngine::destruct(f); + return ok; +} + +void ISDNQ921Management::multipleFrameEstablished(u_int8_t tei, bool confirm, bool timeout, ISDNLayer2* layer2) +{ + m_layer3Mutex.lock(); + RefPointer l3 = m_layer3; + m_layer3Mutex.unlock(); + if (l3) + l3->multipleFrameEstablished(tei,confirm,timeout,layer2); + else + Debug(this,DebugNote,"'Established' notification. No Layer 3 attached"); +} + +void ISDNQ921Management::multipleFrameReleased(u_int8_t tei, bool confirm, bool timeout, ISDNLayer2* layer2) +{ + m_layer3Mutex.lock(); + RefPointer l3 = m_layer3; + m_layer3Mutex.unlock(); + if (l3) + l3->multipleFrameReleased(tei,confirm,timeout,layer2); + else + Debug(this,DebugNote,"'Released' notification. No Layer 3 attached"); +} + +void ISDNQ921Management::dataLinkState(u_int8_t tei, bool cmd, bool value, ISDNLayer2* layer2) +{ + m_layer3Mutex.lock(); + RefPointer l3 = m_layer3; + m_layer3Mutex.unlock(); + if (l3) + l3->dataLinkState(tei,cmd,value,layer2); + else + Debug(this,DebugNote,"Data link notification. No Layer 3 attached"); +} + +void ISDNQ921Management::receiveData(const DataBlock& data, u_int8_t tei, ISDNLayer2* layer2) +{ + m_layer3Mutex.lock(); + RefPointer l3 = m_layer3; + m_layer3Mutex.unlock(); + if (!network()) { + l2Mutex().lock(); + if (m_layer2[0]) + tei = m_layer2[0]->localTei(); + l2Mutex().unlock(); + } + if (l3) + l3->receiveData(data,tei,layer2); + else + Debug(this,DebugNote,"Data received. No Layer 3 attached"); +} + +// Process a Signalling Packet received by the interface +bool ISDNQ921Management::receivedPacket(const DataBlock& packet) +{ + Lock lock(l2Mutex()); + ISDNFrame* frame = parsePacket(packet); + if (!frame) + return false; + if (frame->type() < ISDNFrame::Invalid) + dump(frame->buffer(),false); + // Non UI frame (even invalid): send it to the appropriate Layer 2 + if (frame->type() != ISDNFrame::UI) { + if (network()) { + if (m_layer2[frame->tei()] && m_layer2[frame->tei()]->m_ri) { + lock.drop(); + return m_layer2[frame->tei()]->receivedFrame(frame); + } + sendTeiManagement(ISDNFrame::TeiRemove,0,frame->tei()); + lock.drop(); + TelEngine::destruct(frame); + return false; + } + else if (m_layer2[0] && m_layer2[0]->m_ri && m_layer2[0]->localTei() == frame->tei()) { + lock.drop(); + return m_layer2[0]->receivedFrame(frame); + } + return false; + } + if (!processTeiManagement(frame)) { + DataBlock tmp; + frame->getData(tmp); + u_int8_t tei = frame->tei(); + TelEngine::destruct(frame); + receiveData(tmp,tei,m_layer2[0]); + return true; + } + // FIXME + TelEngine::destruct(frame); + return true; +} + +// Periodically called method to take care of timers +void ISDNQ921Management::timerTick(const Time& when) +{ + if (network()) { + if (m_teiTimer.started() && m_teiTimer.timeout(when.msec())) { + for (u_int8_t i = 0; i < 127; i++) { + if (m_layer2[i] && !m_layer2[i]->m_checked) { + m_layer2[i]->setRi(0); + m_layer2[i]->teiAssigned(false); + multipleFrameReleased(i,false,true,this); + } + } + m_teiTimer.stop(); + } + } + else if (m_layer2[0]) { + if (m_layer2[0]->teiAssigned()) + m_teiManTimer.stop(); + else if (!m_teiManTimer.started()) + m_teiManTimer.start(); + else if (m_teiManTimer.timeout(when.msec())) { + m_teiManTimer.stop(); + u_int16_t ri = m_layer2[0]->m_ri; + while (!ri) + ri = ::random() & 0xffff; + m_layer2[0]->m_tei = 0; + m_layer2[0]->setRi(ri); + sendTeiManagement(ISDNFrame::TeiReq,ri,Q921_TEI_BROADCAST); + } + } +} + +// Forward interface notifications to controlled Q.921 +bool ISDNQ921Management::notify(SignallingInterface::Notification event) +{ + DDebug(this,DebugInfo,"Received notification %u: '%s'", + event,lookup(event,SignallingInterface::s_notifName)); + for (u_int8_t i = 0; i < 127; i++) + if (m_layer2[i]) + m_layer2[i]->notify(event); + return true; +} + +// Process TEI management frames according to their type +bool ISDNQ921Management::processTeiManagement(ISDNFrame* frame) +{ + if (!frame) + return false; + if (!frame->checkTeiManagement()) + return false; + DataBlock data; + frame->getData(data); + u_int8_t ai = ISDNFrame::getAi(data); + u_int16_t ri = ISDNFrame::getRi(data); + u_int8_t type = ISDNFrame::getType(data); + XDebug(this,DebugAll,"Management frame type=0x%02X ri=%u ai=%u",type,ri,ai); + switch (type) { + case ISDNFrame::TeiReq: + processTeiRequest(ri,ai,frame->poll()); + break; + case ISDNFrame::TeiRemove: + processTeiRemove(ai); + break; + case ISDNFrame::TeiCheckReq: + processTeiCheckRequest(ai,frame->poll()); + break; + case ISDNFrame::TeiAssigned: + processTeiAssigned(ri,ai); + break; + case ISDNFrame::TeiDenied: + processTeiDenied(ri); + break; + case ISDNFrame::TeiCheckRsp: + processTeiCheckResponse(ri,ai); + break; + case ISDNFrame::TeiVerify: + processTeiVerify(ai,frame->poll()); + break; + default: + Debug(this,DebugNote,"Unknown management frame type 0x%02X",type); + } + return true; +} + +// Build and send a TEI management frame +bool ISDNQ921Management::sendTeiManagement(ISDNFrame::TeiManagement type, + u_int16_t ri, u_int8_t ai, u_int8_t tei, bool pf) +{ + DataBlock data; + if (!ISDNFrame::buildTeiManagement(data,type,ri,ai)) { + Debug(this,DebugNote,"Could not build TEI management frame"); + return false; + } + ISDNFrame* frame = new ISDNFrame(false,network(), + Q921_SAPI_MANAGEMENT,tei,pf,data); + bool ok = sendFrame(frame); + TelEngine::destruct(frame); + return ok; +} + +// We are NET, a CPE has requested a TEI assignment +void ISDNQ921Management::processTeiRequest(u_int16_t ri, u_int8_t ai, bool pf) +{ + if (!network() || !ri) + return; + if (ai < 127 && m_layer2[ai] && m_layer2[ai]->m_ri == ri) { + // TEI already assigned to same reference number, confirm it + sendTeiManagement(ISDNFrame::TeiAssigned,ri,ai,Q921_TEI_BROADCAST,pf); + return; + } + u_int8_t i; + for (i = 0; i < 127; i++) { + if (!m_layer2[i]) + continue; + if (m_layer2[i]->m_ri == ri) { + // Reference number already used for different TEI + sendTeiManagement(ISDNFrame::TeiDenied,ri,ai,Q921_TEI_BROADCAST,pf); + return; + } + } + for (i = 64; i < 127; i++) { + if (m_layer2[i]->m_ri != 0) + continue; + // Found a free dynamic TEI slot, assign to given reference number + if (sendTeiManagement(ISDNFrame::TeiAssigned,ri,i,Q921_TEI_BROADCAST,pf)) { + m_layer2[i]->setRi(ri); + m_layer2[i]->reset(); + } + return; + } + // All dynamic TEI slots are in use, deny new request + sendTeiManagement(ISDNFrame::TeiDenied,ri,Q921_TEI_BROADCAST,pf); + m_teiTimer.stop(); + // Mark all dynamic TEI slots as not checked and ask them to check + for (i = 64; i < 127; i++) + if (m_layer2[i]) + m_layer2[i]->m_checked = false; + sendTeiManagement(ISDNFrame::TeiCheckReq,0,Q921_TEI_BROADCAST); + m_teiTimer.start(); +} + +// We are CPE, NET asked us to remove our TEI +void ISDNQ921Management::processTeiRemove(u_int8_t ai) +{ + if (network()) + return; + u_int8_t tei = m_layer2[0]->localTei(); + if ((ai == tei) || (ai == Q921_TEI_BROADCAST && tei >= 64)) { + Debug(this,((tei < 64) ? DebugMild : DebugInfo),"Removing our TEI %u",tei); + m_layer2[0]->teiAssigned(false); + m_layer2[0]->setRi(0); + multipleFrameReleased(ai,false,false,this); + m_teiManTimer.start(); + } +} + +// We are CPE, NET is checking our TEI +void ISDNQ921Management::processTeiCheckRequest(u_int8_t ai, bool pf) +{ + if (network()) + return; + if (m_layer2[0]->m_ri && ((ai == Q921_TEI_BROADCAST) || (ai == m_layer2[0]->localTei()))) + sendTeiManagement(ISDNFrame::TeiCheckRsp,m_layer2[0]->m_ri,ai,Q921_TEI_BROADCAST,pf); +} + +// We are NET and received a TEI check response to our request +void ISDNQ921Management::processTeiCheckResponse(u_int16_t ri, u_int8_t ai) +{ + if (!network()) + return; + if ((ai >= 127) || !m_layer2[ai]) + return; + if (m_layer2[ai]->m_ri == ri) + m_layer2[ai]->m_checked = true; + else if (sendTeiManagement(ISDNFrame::TeiRemove,ri,ai)) + m_layer2[ai]->setRi(0); +} + +// We are CPE and the NET assigned a TEI, possibly to us +void ISDNQ921Management::processTeiAssigned(u_int16_t ri, u_int8_t ai) +{ + if (network()) + return; + if (m_layer2[0]->m_ri != ri) + return; + m_teiManTimer.stop(); + m_layer2[0]->m_tei = ai; + m_layer2[0]->teiAssigned(true); + multipleFrame(ai,true,true); +} + +// We are CPE and the NET denied assigning a TEI, possibly to us +void ISDNQ921Management::processTeiDenied(u_int16_t ri) +{ + if (network()) + return; + if (m_layer2[0]->m_ri != ri) + return; + m_layer2[0]->setRi(0); + m_teiManTimer.start(); +} + +// We are NET, a CPE is asking to be verified +void ISDNQ921Management::processTeiVerify(u_int8_t ai, bool pf) +{ + if (!network()) + return; + if ((ai < 127) && m_layer2[ai] && m_layer2[ai]->m_ri) + sendTeiManagement(ISDNFrame::TeiCheckReq,0,ai,Q921_TEI_BROADCAST,pf); +} + + /** * ISDNQ921Passive */ // Constructor. Set data members. Print them ISDNQ921Passive::ISDNQ921Passive(const NamedList& params, const char* name) : ISDNLayer2(params,name), - SignallingReceiver(), - SignallingDumpable(SignallingDumper::Q921), - m_layer(true), + SignallingReceiver(name), + SignallingDumpable(SignallingDumper::Q921,network()), m_checkLinkSide(false), m_idleTimer(0), m_lastFrame(255), @@ -973,7 +1406,7 @@ ISDNQ921Passive::ISDNQ921Passive(const NamedList& params, const char* name) m_checkLinkSide = detectType(); setDebug(params.getBoolValue("print-frames",false), params.getBoolValue("extended-debug",false)); - Debug(this,DebugInfo, + DDebug(this,DebugInfo, "ISDN Passive Data Link type=%s autodetect=%s idle-timeout=%u [%p]", linkSide(network()),String::boolText(detectType()), (unsigned int)m_idleTimer.interval(),this); @@ -986,37 +1419,30 @@ ISDNQ921Passive::ISDNQ921Passive(const NamedList& params, const char* name) // Destructor ISDNQ921Passive::~ISDNQ921Passive() { - Lock lock(m_layer); + Lock lock(l2Mutex()); ISDNLayer2::attach(0); SignallingReceiver::attach(0); cleanup(); - if (debugAt(DebugAll)) - Debug(this,DebugAll, - "ISDN Passive Data Link destroyed. Frames: recv=%u rejected=%u dropped=%u. HW errors=%u [%p]", - (unsigned int)m_rxFrames,(unsigned int)m_rxRejectedFrames, - (unsigned int)m_rxDroppedFrames,(unsigned int)m_hwErrors,this); + DDebug(this,DebugAll, + "ISDN Passive Data Link destroyed. Frames: recv=%u rejected=%u dropped=%u. HW errors=%u [%p]", + (unsigned int)m_rxFrames,(unsigned int)m_rxRejectedFrames, + (unsigned int)m_rxDroppedFrames,(unsigned int)m_hwErrors,this); } // Reset data void ISDNQ921Passive::cleanup() { - Lock lock(m_layer); + Lock lock(l2Mutex()); m_idleTimer.start(); } -// Get data members pointers -void* ISDNQ921Passive::getObject(const String& name) const -{ - if (name == "ISDNQ921Passive") - return (void*)this; - return 0; -} +YCLASSIMP2(ISDNQ921Passive,ISDNLayer2,SignallingReceiver) // Called periodically by the engine to check timeouts // Check idle timer. Notify upper layer on timeout void ISDNQ921Passive::timerTick(const Time& when) { - Lock lock(m_layer); + Lock lock(l2Mutex()); if (!m_idleTimer.timeout(when.msec())) return; // Timeout. Notify layer 3. Restart timer @@ -1031,12 +1457,12 @@ bool ISDNQ921Passive::receivedPacket(const DataBlock& packet) { if (!packet.length()) return false; - Lock lock(m_layer); + Lock lock(l2Mutex()); XDebug(this,DebugAll,"Received packet (Length: %u)",packet.length()); - ISDNFrame* frame = ISDNFrame::parse(packet,this); + ISDNFrame* frame = parsePacket(packet); if (!frame) { if (!m_errorReceive) - Debug(this,DebugNote,"Received short data (Length: %u)",packet.length()); + Debug(this,DebugNote,"Received invalid frame (Length: %u)",packet.length()); m_errorReceive = true; return false; } @@ -1062,20 +1488,20 @@ bool ISDNQ921Passive::receivedPacket(const DataBlock& packet) DataBlock tmp; frame->getData(tmp); m_lastFrame = frame->ns(); - receiveData(tmp,frame->type() == ISDNFrame::I); + receiveData(tmp,localTei()); } } else - dataLinkState(cmd,value); + dataLinkState(localTei(),cmd,value); } - frame->deref(); + TelEngine::destruct(frame); return true; } // Process a notification generated by the attached interface bool ISDNQ921Passive::notify(SignallingInterface::Notification event) { - Lock lock(m_layer); + Lock lock(l2Mutex()); if (event != SignallingInterface::LinkUp) m_hwErrors++; else { @@ -1105,7 +1531,7 @@ bool ISDNQ921Passive::acceptFrame(ISDNFrame* frame, bool& cmd, bool& value) if (frame->error() >= ISDNFrame::Invalid) return dropFrame(frame); // Check SAPI/TEI - if (frame->sapi() != sapi() || frame->tei() != tei()) + if (frame->sapi() != localSapi() || frame->tei() != localTei()) return dropFrame(frame,ISDNFrame::typeName(ISDNFrame::ErrInvalidAddress)); // Valid UI/I if (frame->category() == ISDNFrame::Data) @@ -1168,31 +1594,30 @@ TokenDict ISDNLayer2::m_states[] = { {0,0} }; -ISDNLayer2::ISDNLayer2(const NamedList& params, const char* name) +ISDNLayer2::ISDNLayer2(const NamedList& params, const char* name, u_int8_t tei) : SignallingComponent(name), m_layer3(0), - m_interfaceMutex(true), + m_layerMutex(true), m_layer3Mutex(true), m_state(Released), m_network(false), m_detectType(false), m_sapi(0), m_tei(0), + m_ri(0), + m_checked(false), m_teiAssigned(false), - m_allowUnack(false), m_autoRestart(true), m_maxUserData(260) { - setName(params.getValue("debugname",name)); - XDebug(this,DebugAll,"ISDNLayer2"); + XDebug(this,DebugAll,"ISDNLayer2 '%s' comp=%p [%p]",name,static_cast(this),this); m_network = params.getBoolValue("network",false); m_detectType = params.getBoolValue("detect",false); int tmp = params.getIntValue("sapi",0); - m_sapi = (tmp >= 0 && tmp <= 63) ? tmp : 0; - tmp = params.getIntValue("tei",0); - m_tei = (tmp >= 0 && tmp <= 127) ? tmp : 0; + m_sapi = (tmp >= 0 && tmp <= Q921_SAPI_MANAGEMENT) ? tmp : 0; + tmp = params.getIntValue("tei",tei); + m_tei = (tmp >= 0 && tmp < Q921_TEI_BROADCAST) ? tmp : 0; teiAssigned(true); - m_allowUnack = params.getBoolValue("allow-unack",false); m_autoRestart = params.getBoolValue("auto-restart",true); m_maxUserData = params.getIntValue("maxuserdata",260); if (!m_maxUserData) @@ -1223,43 +1648,68 @@ void ISDNLayer2::attach(ISDNLayer3* layer3) name = tmp->toString().safe(); tmp->attach(0); } - Debug(this,DebugAll,"Detached L3 (%p,'%s') [%p]",tmp,name,this); + DDebug(this,DebugAll,"Detached L3 (%p,'%s') [%p]",tmp,name,this); } if (!layer3) return; - Debug(this,DebugAll,"Attached L3 (%p,'%s') [%p]",layer3,layer3->toString().safe(),this); + DDebug(this,DebugAll,"Attached L3 (%p,'%s') [%p]",layer3,layer3->toString().safe(),this); insert(layer3); layer3->attach(this); } +// Parse a received packet, create a frame from it +ISDNFrame* ISDNLayer2::parsePacket(const DataBlock& packet) +{ + if (packet.null()) + return 0; + Lock lock(m_layerMutex); + ISDNFrame* frame = ISDNFrame::parse(packet,this); +#ifdef XDEBUG + if (frame) { + if (debugAt(DebugAll)) { + String tmp; + frame->toString(tmp,true); + Debug(this,DebugInfo,"Parsed frame (%p):%s",frame,tmp.c_str()); + } + } + else + Debug(this,DebugWarn,"Packet with length %u invalid [%p]",packet.length(),this); +#endif + return frame; +} + // Indication/confirmation of 'multiple frame acknowledged' mode established -void ISDNLayer2::multipleFrameEstablished(bool confirmation, bool timeout) -{ - Lock lock(m_layer3Mutex); - if (m_layer3) - m_layer3->multipleFrameEstablished(confirmation,timeout,this); - else - Debug(this,DebugNote,"'Established' notification. No Layer 3 attached"); -} - -// Indication/confirmation of 'multiple frame acknowledged' mode released -void ISDNLayer2::multipleFrameReleased(bool confirmation, bool timeout) -{ - Lock lock(m_layer3Mutex); - if (m_layer3) - m_layer3->multipleFrameReleased(confirmation,timeout,this); - else - Debug(this,DebugNote,"'Released' notification. No Layer 3 attached"); -} - -// Data link state change command/response -void ISDNLayer2::dataLinkState(bool cmd, bool value) +void ISDNLayer2::multipleFrameEstablished(u_int8_t tei, bool confirmation, bool timeout) { m_layer3Mutex.lock(); RefPointer tmp = m_layer3; m_layer3Mutex.unlock(); if (tmp) - tmp->dataLinkState(cmd,value,this); + tmp->multipleFrameEstablished(tei,confirmation,timeout,this); + else + Debug(this,DebugNote,"'Established' notification. No Layer 3 attached"); +} + +// Indication/confirmation of 'multiple frame acknowledged' mode released +void ISDNLayer2::multipleFrameReleased(u_int8_t tei, bool confirmation, bool timeout) +{ + m_layer3Mutex.lock(); + RefPointer tmp = m_layer3; + m_layer3Mutex.unlock(); + if (tmp) + tmp->multipleFrameReleased(tei,confirmation,timeout,this); + else + Debug(this,DebugNote,"'Released' notification. No Layer 3 attached"); +} + +// Data link state change command/response +void ISDNLayer2::dataLinkState(u_int8_t tei, bool cmd, bool value) +{ + m_layer3Mutex.lock(); + RefPointer tmp = m_layer3; + m_layer3Mutex.unlock(); + if (tmp) + tmp->dataLinkState(tei,cmd,value,this); else Debug(this,DebugNote,"Data link notification. No Layer 3 attached"); } @@ -1277,13 +1727,13 @@ void ISDNLayer2::idleTimeout() } // Indication of received data -void ISDNLayer2::receiveData(const DataBlock& data, bool ack) +void ISDNLayer2::receiveData(const DataBlock& data, u_int8_t tei) { m_layer3Mutex.lock(); RefPointer tmp = m_layer3; m_layer3Mutex.unlock(); if (tmp) - tmp->receiveData(data,ack,this); + tmp->receiveData(data,tei,this); else Debug(this,DebugNote,"Data received. No Layer 3 attached"); } @@ -1291,34 +1741,35 @@ void ISDNLayer2::receiveData(const DataBlock& data, bool ack) // Change TEI ASSIGNED state void ISDNLayer2::teiAssigned(bool status) { - Lock lock(m_interfaceMutex); + Lock lock(m_layerMutex); if (m_teiAssigned == status) return; m_teiAssigned = status; - XDebug(this,DebugAll,"%s 'TEI assigned' state", + DDebug(this,DebugAll,"%s 'TEI assigned' state", m_teiAssigned ? "Enter" : "Exit from"); if (!m_teiAssigned) cleanup(); } // Change the data link status while in TEI ASSIGNED state -void ISDNLayer2::changeState(State newState) +void ISDNLayer2::changeState(State newState, const char* reason) { - Lock lock(m_interfaceMutex); - if (!m_teiAssigned) - return; + Lock lock(m_layerMutex); if (m_state == newState) return; - DDebug(this,DebugInfo,"Changing state from '%s' to '%s'", - stateName(m_state),stateName(newState)); + if (!m_teiAssigned && (newState != Released)) + return; + DDebug(this,DebugInfo,"Changing state from '%s' to '%s'%s%s%s", + stateName(m_state),stateName(newState), + (reason ? " (" : ""),c_safe(reason),(reason ? ")" : "")); m_state = newState; } // Change the interface type bool ISDNLayer2::changeType() { - Lock lock(m_interfaceMutex); - DDebug(this,DebugNote,"Interface type changed from '%s' to '%s'", + Lock lock(m_layerMutex); + Debug(this,DebugNote,"Interface type changed from '%s' to '%s'", linkSide(m_network),linkSide(!m_network)); m_network = !m_network; return true; @@ -1683,4 +2134,28 @@ ISDNFrame* ISDNFrame::parse(const DataBlock& data, ISDNLayer2* receiver) return frame; } +// Get the Reference number from a frame data block +u_int16_t ISDNFrame::getRi(const DataBlock& data) +{ + int i = data.at(2); + if (i < 0) + return 0; + return (u_int16_t)((data.at(1) << 8) | i); +} + +// Build a TEI management message buffer +bool ISDNFrame::buildTeiManagement(DataBlock& data, u_int8_t type, u_int16_t ri, u_int8_t ai) +{ + u_int8_t d[5] = { Q921_MANAGEMENT_TEI, (ri >> 8) & 0xff, ri & 0xff, type, (ai << 1) | 1 }; + data.assign(d,5); + return true; +} + +// Check if a message buffer holds a TEI management frame +bool ISDNFrame::checkTeiManagement() const +{ + const u_int8_t* d = m_buffer.data(m_headerLength); + return (d && (type() == UI) && (m_dataLength >= 5) && (d[0] == Q921_MANAGEMENT_TEI)); +} + /* vi: set ts=8 sw=4 sts=4 noet: */ diff --git a/libs/ysig/q931.cpp b/libs/ysig/q931.cpp index b89f98fa..7b5ab77d 100644 --- a/libs/ysig/q931.cpp +++ b/libs/ysig/q931.cpp @@ -294,6 +294,8 @@ private: bool encodeSignal(ISDNQ931IE* ie, DataBlock& buffer); bool encodeRestart(ISDNQ931IE* ie, DataBlock& buffer); bool encodeSendComplete(ISDNQ931IE* ie, DataBlock& buffer); + bool encodeHighLayerCap(ISDNQ931IE* ie, DataBlock& buffer); + bool encodeUserUser(ISDNQ931IE* ie, DataBlock& buffer); ISDNQ931ParserData* m_settings; // Settings ISDNQ931Message* m_msg; // Current encoded/decoded message @@ -306,8 +308,8 @@ private: /** * ISDNQ931IEData */ -ISDNQ931IEData::ISDNQ931IEData() - : m_bri(false), +ISDNQ931IEData::ISDNQ931IEData(bool bri) + : m_bri(bri), m_channelMandatory(true), m_channelByNumber(true) { @@ -365,17 +367,23 @@ bool ISDNQ931IEData::processChannelID(ISDNQ931Message* msg, bool add, ISDNQ931IE* ie = msg->getIE(ISDNQ931IE::ChannelID); m_channels = ""; if (!ie) { - m_bri = m_channelMandatory = m_channelByNumber = false; + m_channelMandatory = m_channelByNumber = false; return false; } - m_bri = ie->getBoolValue("interface-bri"); - // Done for basic rate interface - if (m_bri) - return true; + m_bri = ie->getBoolValue("interface-bri",m_bri); m_channelMandatory = ie->getBoolValue("channel-exclusive"); m_channelByNumber = ie->getBoolValue("channel-by-number"); m_channelType = ie->getValue("type"); m_channelSelect = ie->getValue("channel-select"); + if (m_bri && m_channelSelect) { + m_channelByNumber = true; + if (m_channelSelect == "b1") + m_channels = "1"; + else if (m_channelSelect == "b2") + m_channels = "2"; + else + return false; + } // ChannelID IE may repeat if channel is given by number if (m_channelByNumber) for (; ie; ie = msg->getIE(ISDNQ931IE::ChannelID,ie)) @@ -623,6 +631,7 @@ bool ISDNQ931State::checkStateRecv(int type, bool* retrans) case ConnectReq: case IncomingProceeding: case Active: + case OverlapSend: return true; default: ; } @@ -669,6 +678,7 @@ bool ISDNQ931State::checkStateSend(int type) case ConnectReq: case IncomingProceeding: case Active: + case OverlapSend: return true; default: ; } @@ -692,23 +702,32 @@ bool ISDNQ931State::checkStateSend(int type) #define Q931_CALL_ID this->outgoing(),this->callRef() ISDNQ931Call::ISDNQ931Call(ISDNQ931* controller, bool outgoing, - u_int32_t callRef, u_int8_t callRefLen) + u_int32_t callRef, u_int8_t callRefLen, u_int8_t tei) : SignallingCall(controller,outgoing), m_callRef(callRef), m_callRefLen(callRefLen), + m_tei(tei), m_circuit(0), + m_overlap(false), m_circuitChange(false), m_channelIDSent(false), m_rspBearerCaps(false), + m_net(false), + m_data(controller && !controller->primaryRate()), m_discTimer(0), m_relTimer(0), m_conTimer(0), + m_overlapSendTimer(0), + m_overlapRecvTimer(0), + m_retransSetupTimer(0), m_terminate(false), m_destroy(false), m_destroyed(false) { - Debug(q931(),DebugAll,"Call(%u,%u) direction=%s [%p]", - Q931_CALL_ID,(outgoing ? "outgoing" : "incoming"),this); + Debug(q931(),DebugAll,"Call(%u,%u) direction=%s TEI=%u [%p]", + Q931_CALL_ID,(outgoing ? "outgoing" : "incoming"),tei,this); + for (u_int8_t i = 0; i < 127; i++) + m_broadcast[i] = false; if (!controller) { Debug(DebugWarn,"ISDNQ931Call(%u,%u). No call controller. Terminate [%p]", Q931_CALL_ID,this); @@ -716,10 +735,14 @@ ISDNQ931Call::ISDNQ931Call(ISDNQ931* controller, bool outgoing, m_data.m_reason = "temporary-failure"; return; } + m_net = q931() && q931()->m_q921 && q931()->m_q921->network(); // Init timers q931()->setInterval(m_discTimer,305); q931()->setInterval(m_relTimer,308); q931()->setInterval(m_conTimer,313); + m_overlapSendTimer.interval(10000); + m_overlapRecvTimer.interval(20000); + m_retransSetupTimer.interval(1000); if (outgoing) reserveCircuit(); } @@ -773,9 +796,16 @@ bool ISDNQ931Call::sendEvent(SignallingEvent* event) retVal = sendAlerting(event->message()); break; case SignallingEvent::Accept: + if (m_overlap) { + sendSetupAck(); + m_overlap = false; + break; + } + changeState(CallPresent); retVal = sendCallProceeding(event->message()); break; case SignallingEvent::Answer: + changeState(CallPresent); retVal = sendConnect(event->message()); break; case SignallingEvent::Release: @@ -878,19 +908,19 @@ SignallingEvent* ISDNQ931Call::getEvent(const Time& when) sendSuspendRej("service-not-implemented",0); break; case ISDNQ931Message::Resume: - q931()->sendStatus(this,"no-call-suspended"); + q931()->sendStatus(this,"no-call-suspended",callTei()); break; case ISDNQ931Message::SuspendAck: case ISDNQ931Message::SuspendRej: case ISDNQ931Message::ResumeAck: case ISDNQ931Message::ResumeRej: - q931()->sendStatus(this,"wrong-state-message"); + q931()->sendStatus(this,"wrong-state-message",callTei()); break; default: DDebug(q931(),DebugNote, "Call(%u,%u). Received unknown/not implemented message '%s'. Sending status [%p]", Q931_CALL_ID,msg->name(),this); - q931()->sendStatus(this,"unknown-message"); + q931()->sendStatus(this,"unknown-message",callTei()); // Fall through to destruct the message and check timeouts } TelEngine::destruct(msg); @@ -933,7 +963,7 @@ void ISDNQ931Call::dataLinkState(bool up) state() == ISDNQ931Call::OverlapRecv) { setTerminate(true,"temporary-failure"); } - q931()->sendStatus(this,"normal"); + q931()->sendStatus(this,"normal",callTei()); } // Process termination flags or requests (messages) @@ -960,11 +990,11 @@ SignallingEvent* ISDNQ931Call::processTerminate(ISDNQ931Message* msg) } if (complete) return releaseComplete(); - sendRelease(); + sendRelease("normal-clearing"); return 0; } -// Check message timeout for Connect, Disconnect, Release +// Check message timeout for Connect, Disconnect, Release, Setup SignallingEvent* ISDNQ931Call::checkTimeout(u_int64_t time) { #define CALL_TIMEOUT_DEBUG(info) \ @@ -995,6 +1025,19 @@ SignallingEvent* ISDNQ931Call::checkTimeout(u_int64_t time) m_data.m_reason = reason; sendDisconnect(0); break; + case CallInitiated: + if (!m_retransSetupTimer.timeout(time)) + break; + CALL_TIMEOUT_DEBUG("Setup") + m_retransSetupTimer.stop(); + m_data.m_reason = reason; + return releaseComplete(reason); + case OverlapSend: + if (!m_overlapSendTimer.timeout(time)) { + m_overlapSendTimer.stop(); + m_overlapSendTimer.start(); + } + break; default: ; } return 0; @@ -1017,7 +1060,7 @@ bool ISDNQ931Call::checkMsgRecv(ISDNQ931Message* msg, bool status) "Call(%u,%u). Received '%s'. Invalid in state '%s'. Drop [%p]", Q931_CALL_ID,msg->name(),stateName(state()),this); if (status && state() != Null) - q931()->sendStatus(this,"wrong-state-message"); + q931()->sendStatus(this,"wrong-state-message",callTei()); } return false; } @@ -1064,6 +1107,7 @@ SignallingEvent* ISDNQ931Call::processMsgCallProceeding(ISDNQ931Message* msg) // IE: BearerCaps, ChannelID, Progress, Display, DateTime, Signal, LoLayerCompat, HiLayerCompat SignallingEvent* ISDNQ931Call::processMsgConnect(ISDNQ931Message* msg) { + m_retransSetupTimer.stop(); if (!checkMsgRecv(msg,true)) return 0; if (m_data.processChannelID(msg,false) && !reserveCircuit()) @@ -1121,6 +1165,7 @@ SignallingEvent* ISDNQ931Call::processMsgDisconnect(ISDNQ931Message* msg) // IE: SendComplete, Display, Keypad, Signal, CalledNo SignallingEvent* ISDNQ931Call::processMsgInfo(ISDNQ931Message* msg) { + m_lastEvent = checkTimeout(10000); // Check complete bool complete = (0 != msg->getIE(ISDNQ931IE::SendComplete)); msg->params().addParam("complete",String::boolText(complete)); @@ -1160,13 +1205,15 @@ SignallingEvent* ISDNQ931Call::processMsgProgress(ISDNQ931Message* msg) msg->params().setParam("reason",m_data.m_reason); if (m_data.processDisplay(msg,false)) msg->params().setParam("callername",m_data.m_display); - return new SignallingEvent(SignallingEvent::Progress,msg,this);; + return new SignallingEvent(SignallingEvent::Progress,msg,this); } // Process RELEASE and RELEASE COMPLETE. See Q.931 3.1.9/3.1.10 // IE: Cause, Display, Signal SignallingEvent* ISDNQ931Call::processMsgRelease(ISDNQ931Message* msg) { + if (!msg) + return 0; m_discTimer.stop(); m_relTimer.stop(); m_conTimer.stop(); @@ -1206,24 +1253,25 @@ SignallingEvent* ISDNQ931Call::processMsgSetup(ISDNQ931Message* msg) Q931_CALL_ID,m_data.m_transferMode.c_str(),this); return errorWrongIE(msg,ISDNQ931IE::BearerCaps,true); } - // *** ChannelID. Mandatory - if (!msg->getIE(ISDNQ931IE::ChannelID)) + // *** ChannelID. Mandatory on PRI + if (msg->getIE(ISDNQ931IE::ChannelID)) + m_data.processChannelID(msg,false); + else if (q931() && q931()->primaryRate()) return errorNoIE(msg,ISDNQ931IE::ChannelID,true); - m_data.processChannelID(msg,false); - // Check if channel contains valid BRI flag - // TODO: Support BRI: check consistency with q931()->primaryRate() - if (m_data.m_bri) { + // Check if channel contains valid PRI/BRI flag + if (q931() && (m_data.m_bri == q931()->primaryRate())) { Debug(q931(),DebugWarn, - "Call(%u,%u). Invalid interface (BRI not implemented). Releasing call [%p]", + "Call(%u,%u). Invalid interface type. Releasing call [%p]", Q931_CALL_ID,this); return errorWrongIE(msg,ISDNQ931IE::ChannelID,true); } // Get a circuit from controller - if (!reserveCircuit()) - return releaseComplete(); - m_circuit->updateFormat(m_data.m_format,0); + if (reserveCircuit()) + m_circuit->updateFormat(m_data.m_format,0); + else if (q931() && q931()->primaryRate()) + return releaseComplete("congestion"); // *** CalledNo /CallingNo - m_data.processCalledNo(msg,false); + m_overlap = !m_data.processCalledNo(msg,false); m_data.processCallingNo(msg,false); // *** Display m_data.processDisplay(msg,false); @@ -1238,6 +1286,7 @@ SignallingEvent* ISDNQ931Call::processMsgSetup(ISDNQ931Message* msg) msg->params().setParam("callerscreening",m_data.m_callerScreening); msg->params().setParam("callednumtype",m_data.m_calledType); msg->params().setParam("callednumplan",m_data.m_calledPlan); + msg->params().setParam("overlapped",String::boolText(m_overlap)); return new SignallingEvent(SignallingEvent::NewCall,msg,this); } @@ -1364,7 +1413,7 @@ SignallingEvent* ISDNQ931Call::processMsgStatus(ISDNQ931Message* msg) // IE: Display SignallingEvent* ISDNQ931Call::processMsgStatusEnquiry(ISDNQ931Message* msg) { - q931()->sendStatus(this,"status-enquiry-rsp"); + q931()->sendStatus(this,"status-enquiry-rsp",callTei()); return 0; } @@ -1395,10 +1444,19 @@ bool ISDNQ931Call::sendAlerting(SignallingMessage* sigMsg) m_rspBearerCaps = false; } if (!m_channelIDSent) { - m_data.processChannelID(msg,true); + if (!q931()->primaryRate()) { + m_data.m_channelType = "B"; + if (m_circuit) + m_data.m_channelSelect = lookup(m_circuit->code(),Q931Parser::s_dict_channelIDSelect_BRI); + if (!m_data.m_channelSelect) { + TelEngine::destruct(msg); + return sendReleaseComplete("congestion"); + } + } + m_data.processChannelID(msg,true,&q931()->parserData()); m_channelIDSent = true; } - return q931()->sendMessage(msg); + return q931()->sendMessage(msg,callTei()); } // Send CALL PROCEEDING. See Q.931 3.1.2 @@ -1417,7 +1475,7 @@ bool ISDNQ931Call::sendCallProceeding(SignallingMessage* sigMsg) m_data.processChannelID(msg,true); m_channelIDSent = true; } - return q931()->sendMessage(msg); + return q931()->sendMessage(msg,callTei()); } // Send CONNECT. See Q.931 3.1.3 @@ -1437,6 +1495,11 @@ bool ISDNQ931Call::sendConnect(SignallingMessage* sigMsg) m_rspBearerCaps = false; } if (!m_channelIDSent) { + if (!q931()->primaryRate()) { + m_data.m_channelType = "B"; + m_data.m_channelByNumber = true; + m_data.m_channelSelect = lookup(m_circuit->code(),Q931Parser::s_dict_channelIDSelect_BRI); + } m_data.processChannelID(msg,true,&q931()->parserData()); m_channelIDSent = true; } @@ -1446,7 +1509,7 @@ bool ISDNQ931Call::sendConnect(SignallingMessage* sigMsg) m_data.processProgress(msg,true,&q931()->parserData()); } m_conTimer.start(); - return q931()->sendMessage(msg); + return q931()->sendMessage(msg,callTei()); } // Send CONNECT ACK. See Q.931 3.1.4 @@ -1464,7 +1527,7 @@ bool ISDNQ931Call::sendConnectAck(SignallingMessage* sigMsg) } else m_data.m_progress = ""; - return q931()->sendMessage(msg); + return q931()->sendMessage(msg,callTei()); } // Send DISCONNECT. See Q.931 3.1.5 @@ -1479,7 +1542,7 @@ bool ISDNQ931Call::sendDisconnect(SignallingMessage* sigMsg) m_data.processCause(msg,true); changeState(DisconnectReq); m_discTimer.start(); - return q931()->sendMessage(msg); + return q931()->sendMessage(msg,callTei()); } // Send INFORMATION. See Q.931 3.1.6 @@ -1499,7 +1562,7 @@ bool ISDNQ931Call::sendInfo(SignallingMessage* sigMsg) const char* tone = sigMsg->params().getValue("tone"); if (tone) msg->appendIEValue(ISDNQ931IE::Keypad,"keypad",tone); - return q931()->sendMessage(msg); + return q931()->sendMessage(msg,callTei()); } // Send PROGRESS. See Q.931 3.1.8 @@ -1515,7 +1578,7 @@ bool ISDNQ931Call::sendProgress(SignallingMessage* sigMsg) } ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::Progress,this); m_data.processProgress(msg,true); - return q931()->sendMessage(msg); + return q931()->sendMessage(msg,callTei()); } // Send RELEASE. See Q.931 3.1.9 @@ -1532,22 +1595,30 @@ bool ISDNQ931Call::sendRelease(const char* reason, SignallingMessage* sigMsg) m_terminate = true; changeState(ReleaseReq); m_relTimer.start(); - return q931()->sendRelease(this,true,m_data.m_reason); + return q931()->sendRelease(this,true,m_data.m_reason,callTei()); } // Send RELEASE COMPLETE. See Q.931 3.1.10 // IE: Cause, Display, Signal -bool ISDNQ931Call::sendReleaseComplete(const char* reason, const char* diag) +bool ISDNQ931Call::sendReleaseComplete(const char* reason, const char* diag, u_int8_t tei) { m_relTimer.stop(); - if (state() == Null) + if ((state() == Null) && (0 == tei)) return false; if (reason) m_data.m_reason = reason; m_terminate = m_destroy = true; changeState(Null); q931()->releaseCircuit(m_circuit); - return q931()->sendRelease(this,false,m_data.m_reason,diag); + if (callTei() >= 127) { + for (u_int8_t i = 0; i < 127; i++) + if (m_broadcast[i]) + return q931()->sendRelease(this,false,m_data.m_reason,i,diag); + return true; + } + if (0 == tei) + tei = callTei(); + return q931()->sendRelease(this,false,m_data.m_reason,tei,diag); } // Send SETUP. See Q.931 3.1.14 @@ -1575,14 +1646,30 @@ bool ISDNQ931Call::sendSetup(SignallingMessage* sigMsg) // ChannelID if (!m_circuit) break; - m_circuit->updateFormat(m_data.m_format,0); - m_data.m_bri = false; - m_data.m_channelMandatory = false; - m_data.m_channelByNumber = true; - m_data.m_channelType = "B"; - m_data.m_channelSelect = "present"; - m_data.m_channels = m_circuit->code(); - m_data.processChannelID(msg,true); + if (m_net || q931()->primaryRate()) { + // Reserving a circuit attempted only on PRI or if we are NET + if (!reserveCircuit()) { + m_data.m_reason = "network-busy"; + break; + } + m_circuit->updateFormat(m_data.m_format,0); + m_data.m_channelMandatory = false; + m_data.m_channelByNumber = true; + m_data.m_channelType = "B"; + if (m_data.m_bri) { + if (m_circuit->code() > 0 && m_circuit->code() < 3) + m_data.m_channelSelect = lookup(m_circuit->code(),Q931Parser::s_dict_channelIDSelect_BRI); + if (!m_data.m_channelSelect) { + m_data.m_reason = "network-busy"; + break; + } + } + else { + m_data.m_channelSelect = "present"; + m_data.m_channels = m_circuit->code(); + } + m_data.processChannelID(msg,true); + } // Progress indicator m_data.m_progress = sigMsg->params().getValue("call-progress"); m_data.processProgress(msg,true,&q931()->parserData()); @@ -1603,7 +1690,11 @@ bool ISDNQ931Call::sendSetup(SignallingMessage* sigMsg) m_data.processCalledNo(msg,true); // Send changeState(CallInitiated); - if (q931()->sendMessage(msg,&m_data.m_reason)) + if (m_net && !q931()->primaryRate()) { + m_tei = 127; + m_retransSetupTimer.start(); + } + if (q931()->sendMessage(msg,callTei(),&m_data.m_reason)) return true; msg = 0; break; @@ -1621,7 +1712,26 @@ bool ISDNQ931Call::sendSuspendRej(const char* reason, SignallingMessage* sigMsg) reason = sigMsg->params().getValue("reason"); ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::SuspendRej,this); msg->appendIEValue(ISDNQ931IE::Cause,0,reason); - return q931()->sendMessage(msg); + return q931()->sendMessage(msg,callTei()); +} + +bool ISDNQ931Call::sendSetupAck() +{ + MSG_CHECK_SEND(ISDNQ931Message::SetupAck) + ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::SetupAck,this); + if (!m_channelIDSent) { + m_data.m_channelType = "B"; + if (m_circuit) + m_data.m_channelSelect = lookup(m_circuit->code(),Q931Parser::s_dict_channelIDSelect_BRI); + if (!m_data.m_channelSelect) { + Debug(q931(),DebugNote,"Call(%u,%u). No voice channel available [%p]", + Q931_CALL_ID,this); + return sendReleaseComplete("congestion"); + } + m_data.processChannelID(msg,true,&q931()->parserData()); + m_channelIDSent = true; + } + return q931()->sendMessage(msg,callTei()); } SignallingEvent* ISDNQ931Call::releaseComplete(const char* reason, const char* diag) @@ -1680,9 +1790,16 @@ bool ISDNQ931Call::reserveCircuit() m_circuitChange = false; bool anyCircuit = false; while (true) { - // For incoming calls we reserve the circuit only one time (at SETUP) - if (!outgoing()) + // For incoming PRI calls we reserve the circuit only one time (at SETUP) + if (!outgoing()) { + // Check if we are a BRI NET and we should assign any channel + int briChan = lookup(m_data.m_channelSelect,Q931Parser::s_dict_channelIDSelect_BRI,3); + if (m_net && (briChan == 3) && !q931()->primaryRate()) + anyCircuit = true; + else + m_data.m_channels = briChan; break; + } // Outgoing calls if (!m_data.m_channelByNumber) { m_data.m_reason = "service-not-implemented"; @@ -1690,7 +1807,7 @@ bool ISDNQ931Call::reserveCircuit() } // Check if we don't have a circuit reserved if (!m_circuit) { - anyCircuit = true; + anyCircuit = m_net || q931()->primaryRate(); break; } // Check the received circuit if any @@ -1709,13 +1826,18 @@ bool ISDNQ931Call::reserveCircuit() q931()->reserveCircuit(m_circuit,0,-1,&m_data.m_channels,m_data.m_channelMandatory,true); if (m_circuit) { m_data.m_channels = m_circuit->code(); - if (!m_circuit->connect(m_data.m_format)) + if (!m_circuit->connect(m_data.m_format) && !m_net && (state() != ISDNQ931State::CallPresent)) { Debug(q931(),DebugNote, "Call(%u,%u). Failed to connect circuit [%p]",Q931_CALL_ID,this); + return false; + } + DDebug(q931(),DebugInfo,"Call(%u,%u). Connected to circuit %u [%p]", + Q931_CALL_ID,m_circuit->code(),this); return true; } DDebug(q931(),DebugNote, - "Call(%u,%u). Can't reserve circuit [%p]",Q931_CALL_ID,this); + "Call(%u,%u). Can't reserve%s circuit [%p]", + Q931_CALL_ID,(anyCircuit ? " any" : ""),this); m_data.m_reason = anyCircuit ? "congestion" : "channel-unacceptable"; return false; } @@ -2194,11 +2316,13 @@ TokenDict ISDNQ931::s_swType[] = { {0,0} }; +YCLASSIMP(ISDNQ931,ISDNLayer3) + ISDNQ931::ISDNQ931(const NamedList& params, const char* name) - : SignallingCallControl(params,"isdn."), + : SignallingComponent(name), + SignallingCallControl(params,"isdn."), SignallingDumpable(SignallingDumper::Q931), ISDNLayer3(name), - m_layer(true), m_q921(0), m_q921Up(false), m_primaryRate(true), @@ -2226,7 +2350,6 @@ ISDNQ931::ISDNQ931(const NamedList& params, const char* name) { setName(params.getValue("debugname",name)); m_parserData.m_dbg = this; - //m_primaryRate = params.getBoolValue("pri",true); m_callRefLen = params.getIntValue("callreflen",2); if (m_callRefLen < 1 || m_callRefLen > 4) m_callRefLen = 2; @@ -2240,6 +2363,7 @@ ISDNQ931::ISDNQ931(const NamedList& params, const char* name) m_callDiscTimer.interval(params,"t305",0,5000,false); m_callRelTimer.interval(params,"t308",0,5000,false); m_callConTimer.interval(params,"t313",0,5000,false); + m_cpeNumber = params.getValue("number"); m_numPlan = params.getValue("numplan"); if (0xffff == lookup(m_numPlan,Q931Parser::s_dict_numPlan,0xffff)) m_numPlan = "unknown"; @@ -2303,16 +2427,27 @@ ISDNQ931::~ISDNQ931() DDebug(this,DebugAll,"ISDN Call Controller destroyed [%p]",this); } +// Check if layer 2 may be up +bool ISDNQ931::q921Up() const +{ + if (!m_q921) + return false; + if (m_q921Up) + return true; + // Assume BRI NET is always up + return !primaryRate() && m_q921->network(); +} + // Send a message to layer 2 -bool ISDNQ931::sendMessage(ISDNQ931Message* msg, String* reason) +bool ISDNQ931::sendMessage(ISDNQ931Message* msg, u_int8_t tei, String* reason) { if (!msg) { if (reason) *reason = "wrong-message"; return false; } - Lock lock(m_layer); - if (!(m_q921 && m_q921Up)) { + Lock lock(l3Mutex()); + if (!q921Up()) { if (!m_flagQ921Invalid) Debug(this,DebugNote, "Refusing to send message. Layer 2 is missing or down"); @@ -2344,7 +2479,7 @@ bool ISDNQ931::sendMessage(ISDNQ931Message* msg, String* reason) for (; obj; obj = obj->skipNext()) { DataBlock* buffer = static_cast(obj->get()); dump(*buffer,true); - if (!m_q921->sendData(*buffer,true)) { + if (!m_q921->sendData(*buffer,tei,true)) { if (reason) *reason = "net-out-of-order"; return false; @@ -2353,18 +2488,18 @@ bool ISDNQ931::sendMessage(ISDNQ931Message* msg, String* reason) return true; } -// Data link up notificatin from layer 2 +// Data link up notification from layer 2 // Notify calls -void ISDNQ931::multipleFrameEstablished(bool confirmation, bool timeout, ISDNLayer2* layer2) +void ISDNQ931::multipleFrameEstablished(u_int8_t tei, bool confirmation, bool timeout, ISDNLayer2* layer2) { - m_layer.lock(); + l3Mutex().lock(); m_q921Up = true; - DDebug(this,DebugNote,"'Established' %s", - confirmation ? "confirmation" :"indication"); + DDebug(this,DebugNote,"'Established' %s TEI %u", + confirmation ? "confirmation" :"indication",tei); endReceiveSegment("Data link is up"); m_l2DownTimer.stop(); m_flagQ921Down = false; - m_layer.unlock(); + l3Mutex().unlock(); if (confirmation) return; // Notify calls @@ -2373,23 +2508,23 @@ void ISDNQ931::multipleFrameEstablished(bool confirmation, bool timeout, ISDNLay (static_cast(obj->get()))->dataLinkState(true); } -// Data link down notificatin from layer 2 +// Data link down notification from layer 2 // Notify calls -void ISDNQ931::multipleFrameReleased(bool confirmation, bool timeout, ISDNLayer2* layer2) +void ISDNQ931::multipleFrameReleased(u_int8_t tei, bool confirmation, bool timeout, ISDNLayer2* layer2) { - Lock lockLayer(m_layer); + Lock lockLayer(l3Mutex()); m_q921Up = false; - DDebug(this,DebugNote,"'Released' %s. Timeout: %s", - confirmation ? "confirmation" :"indication",String::boolText(timeout)); + DDebug(this,DebugNote,"'Released' %s TEI %u. Timeout: %s", + confirmation ? "confirmation" :"indication",tei,String::boolText(timeout)); endReceiveSegment("Data link is down"); // Re-establish if layer 2 doesn't have an automatically re-establish procedure if (m_q921 && !m_q921->autoRestart()) { DDebug(this,DebugNote,"Re-establish layer 2."); - m_q921->multipleFrame(true,false); + m_q921->multipleFrame(tei,true,false); } if (confirmation) return; - if (!m_l2DownTimer.started()) { + if (primaryRate() && !m_l2DownTimer.started()) { XDebug(this,DebugAll,"Starting T309 (layer 2 down)"); m_l2DownTimer.start(); } @@ -2402,61 +2537,115 @@ void ISDNQ931::multipleFrameReleased(bool confirmation, bool timeout, ISDNLayer2 // Receive and parse data from layer 2 // Process the message -void ISDNQ931::receiveData(const DataBlock& data, bool ack, ISDNLayer2* layer2) +void ISDNQ931::receiveData(const DataBlock& data, u_int8_t tei, ISDNLayer2* layer2) { - XDebug(this,DebugAll,"Received data. Length: %u",data.length()); - if (!ack) { - Debug(this,DebugNote,"Received unacknowledged data. Drop"); - return; - } - Lock lock(m_layer); + XDebug(this,DebugAll,"Received data. Length: %u, TEI: %u",data.length(),tei); + Lock lock(l3Mutex()); ISDNQ931Message* msg = getMsg(data); if (!msg) return; // Dummy call reference if (msg->dummyCallRef()) { - sendStatus("service-not-implemented",0); + sendStatus("service-not-implemented",0,tei); TelEngine::destruct(msg); return; } // Global call reference or a message that should have a dummy call reference if (!msg->callRef() || msg->type() == ISDNQ931Message::Restart || msg->type() == ISDNQ931Message::RestartAck) { - processGlobalMsg(msg); + processGlobalMsg(msg,tei); TelEngine::destruct(msg); return; } + bool doMore = true; // This is an incoming message: // if initiator is true, the message is for an incoming call - ISDNQ931Call* call = findCall(msg->callRef(),!msg->initiator()); - while (true) { + ISDNQ931Call* call = findCall(msg->callRef(),!msg->initiator(),tei); + if (call && (call->callTei() == 127) && (call->callRef() == msg->callRef())) { + // Call was or still is Point-to-Multipoint + int i; + switch (msg->type()) { + case ISDNQ931Message::Disconnect: + case ISDNQ931Message::ReleaseComplete: + if ((tei < 127) && call->m_broadcast[tei]) + call->m_broadcast[tei] = false; + else + doMore = false; + if (call->m_retransSetupTimer.timeout()) { + call->m_retransSetupTimer.stop(); + for (i = 0; i < 127; i++) { + if (call->m_broadcast[i]) { + doMore = false; + break; + } + } + } + if ((msg->type() != ISDNQ931Message::ReleaseComplete) && !doMore) + sendRelease(false,msg->callRefLen(),msg->callRef(), + tei,!msg->initiator()); + break; + case ISDNQ931Message::Connect: + if (tei >= 127) + break; + call->m_tei = tei; + call->m_broadcast[tei] = false; + // All other pending calls are to be aborted + for (i = 0; i < 127; i++) { + if (call->m_broadcast[i]) { + sendRelease(true,msg->callRefLen(),msg->callRef(), + i,!msg->initiator(),"answered"); + call->m_broadcast[i] = false; + break; + } + } + break; + default: + if (tei < 127) + call->m_broadcast[tei] = true; + } + } + while (doMore) { if (call) { - if (msg->type() != ISDNQ931Message::Setup) { + if (msg->type() != ISDNQ931Message::Setup && + (call->callTei() == 127 || call->callTei() == tei)) { call->enqueue(msg); msg = 0; } - else - sendRelease(false,msg->callRefLen(),msg->callRef(),!msg->initiator(), - "invalid-callref"); + else if (msg->type() != ISDNQ931Message::ReleaseComplete) { + sendRelease((msg->type() != ISDNQ931Message::Release), + msg->callRefLen(),msg->callRef(),tei, + !msg->initiator(),"invalid-callref"); + } break; } // Check if it is a new incoming call if (msg->initiator() && msg->type() == ISDNQ931Message::Setup) { + if (!primaryRate() && m_cpeNumber && m_q921 && !m_q921->network()) { + // We are a BRI CPE with a number - check the called party field + ISDNQ931IE* ie = msg->getIE(ISDNQ931IE::CalledNo); + if (ie) { + const char* number = ie->getValue("number"); + if (number && (m_cpeNumber != number)) { + DDebug(this,DebugInfo,"Setup was for '%s', not us.",number); + break; + } + } + } // Accept new calls only if no channel is restarting and not exiting String reason; if (acceptNewCall(false,reason)) { - call = new ISDNQ931Call(this,false,msg->callRef(),msg->callRefLen()); + call = new ISDNQ931Call(this,false,msg->callRef(),msg->callRefLen(),tei); m_calls.append(call); call->enqueue(msg); msg = 0; call = 0; } else - sendRelease(false,msg->callRefLen(),msg->callRef(), + sendRelease(false,msg->callRefLen(),msg->callRef(),tei, !msg->initiator(),reason); break; } - processInvalidMsg(msg); + processInvalidMsg(msg,tei); break; } TelEngine::destruct(call); @@ -2467,18 +2656,20 @@ void ISDNQ931::receiveData(const DataBlock& data, bool ack, ISDNLayer2* layer2) // Update some data from the attached object void ISDNQ931::attach(ISDNLayer2* q921) { - Lock lock(m_layer); + Lock lock(l3Mutex()); if (m_q921 == q921) return; cleanup(q921 ? "layer 2 attach" : "layer 2 detach"); ISDNLayer2* tmp = m_q921; m_q921 = q921; if (m_q921) { - ISDNQ921* q = static_cast(m_q921->getObject("ISDNQ921")); + ISDNQ921* q = YOBJECT(ISDNQ921,m_q921); // Adjust timers from the new lower layer // Add 1000 ms to minimum value to allow the lower layer to re-establish // the data link before we make a retransmission if (q) { + m_primaryRate = true; + m_data.m_bri = false; u_int64_t min = q->dataTimeout(); if (m_callDiscTimer.interval() <= min) m_callDiscTimer.interval(min + 1000); @@ -2490,14 +2681,21 @@ void ISDNQ931::attach(ISDNLayer2* q921) m_l2DownTimer.interval(min + 1000); if (m_syncCicTimer.interval() <= min) m_syncCicTimer.interval(min + 1000); + // Adjust some parser flags + if (m_parserData.m_flagsOrig == EuroIsdnE1 && !q->network()) + m_parserData.m_flags |= NoDisplayIE; + if (m_parserData.m_flagsOrig != QSIG && !q->network()) + m_parserData.m_flags |= NoActiveOnConnect; + } + else if (YOBJECT(ISDNQ921Management,m_q921)) { + m_primaryRate = false; + m_data.m_bri = true; + m_callRefLen = 1; + m_callRefMask = 0x7f; + m_callRef &= m_callRefMask; } // Adjust parser data message length limit m_parserData.m_maxMsgLen = m_q921->maxUserData(); - // Adjust some parser flags - if (m_parserData.m_flagsOrig == EuroIsdnE1 && !q->network()) - m_parserData.m_flags |= NoDisplayIE; - if (m_parserData.m_flagsOrig != QSIG && !q->network()) - m_parserData.m_flags |= NoActiveOnConnect; } else { // Reset parser data if no layer 2 @@ -2516,7 +2714,8 @@ void ISDNQ931::attach(ISDNLayer2* q921) if (!q921) return; Debug(this,DebugAll,"Attached L2 '%s' (%p,'%s') [%p]", - (q921->network()?"NET":"CPE"),q921,q921->toString().safe(),this); + (q921->network() ? "NET" : "CPE"), + q921,q921->toString().safe(),this); insert(q921); q921->attach(this); } @@ -2528,7 +2727,7 @@ SignallingCall* ISDNQ931::call(SignallingMessage* msg, String& reason) reason = "invalid-parameter"; return 0; } - Lock lock(m_layer); + Lock lock(l3Mutex()); if (!acceptNewCall(true,reason)) { TelEngine::destruct(msg); return 0; @@ -2578,17 +2777,10 @@ void ISDNQ931::setInterval(SignallingTimer& timer, int id) } } -void* ISDNQ931::getObject(const String& name) const -{ - if (name == "ISDNQ931") - return (void*)this; - return SignallingComponent::getObject(name); -} - // Check timeouts for segmented messages, layer 2 down state, restart circuits void ISDNQ931::timerTick(const Time& when) { - Lock lock(m_layer); + Lock lock(l3Mutex()); // Check segmented message if (m_recvSgmTimer.timeout(when.msec())) endReceiveSegment("timeout"); @@ -2627,14 +2819,17 @@ void ISDNQ931::timerTick(const Time& when) } // Find a call by call reference and direction -ISDNQ931Call* ISDNQ931::findCall(u_int32_t callRef, bool outgoing) +ISDNQ931Call* ISDNQ931::findCall(u_int32_t callRef, bool outgoing, u_int8_t tei) { Lock lock(this); ObjList* obj = m_calls.skipNull(); for (; obj; obj = obj->skipNext()) { ISDNQ931Call* call = static_cast(obj->get()); - if (callRef == call->callRef() && outgoing == call->outgoing()) + if (callRef == call->callRef() && outgoing == call->outgoing()) { + if (!primaryRate() && (call->callTei() != tei) && (call->callTei() != 127)) + return 0; return (call->ref() ? call : 0); + } } return 0; } @@ -2685,7 +2880,7 @@ void ISDNQ931::terminateCalls(ObjList* list, const char* reason) // Check if new calls are acceptable bool ISDNQ931::acceptNewCall(bool outgoing, String& reason) { - if (exiting() || !m_q921 || !m_q921Up) { + if (exiting() || !q921Up()) { Debug(this,DebugInfo,"Denying %s call request, reason: %s.", outgoing ? "outgoing" : "incoming", exiting() ? "exiting" : "link down"); @@ -2710,7 +2905,7 @@ static inline ISDNQ931Message* dropSegMsg(ISDNQ931* q931, ISDNQ931Message* msg, // Create a message from it. Validate it. Process segmented messages ISDNQ931Message* ISDNQ931::getMsg(const DataBlock& data) { - Lock lock(m_layer); + Lock lock(l3Mutex()); DataBlock segData; ISDNQ931Message* msg = ISDNQ931Message::parse(m_parserData,data,&segData); if (!msg) @@ -2780,12 +2975,12 @@ ISDNQ931Message* ISDNQ931::getMsg(const DataBlock& data) // Check call identification if (m_segmented->initiator() != msg->initiator() || m_segmented->callRef() != msg->callRef()) { - dropSegMsg(this,msg,"Invalid call identification");; + dropSegMsg(this,msg,"Invalid call identification"); return endReceiveSegment("Segment with invalid call identification"); } // Check segment parameters if (first || m_remaining <= remaining || m_remaining - remaining != 1) { - dropSegMsg(this,msg,"Invalid Segmented IE parameters");; + dropSegMsg(this,msg,"Invalid Segmented IE parameters"); return endReceiveSegment("Segment with invalid parameters"); } TelEngine::destruct(msg); @@ -2801,7 +2996,7 @@ ISDNQ931Message* ISDNQ931::getMsg(const DataBlock& data) // Terminate receiving segmented message ISDNQ931Message* ISDNQ931::endReceiveSegment(const char* reason) { - Lock lock(m_layer); + Lock lock(l3Mutex()); m_recvSgmTimer.stop(); if (!m_segmented) return 0; @@ -2827,7 +3022,7 @@ ISDNQ931Message* ISDNQ931::endReceiveSegment(const char* reason) } // Process messages with global call reference and messages that should have it -void ISDNQ931::processGlobalMsg(ISDNQ931Message* msg) +void ISDNQ931::processGlobalMsg(ISDNQ931Message* msg, u_int8_t tei) { if (!msg) return; @@ -2840,7 +3035,7 @@ void ISDNQ931::processGlobalMsg(ISDNQ931Message* msg) Debug(this,DebugNote, "Dropping (%p): '%s' without global call reference", msg,msg->name()); - sendStatus("invalid-message",m_callRefLen); + sendStatus("invalid-message",m_callRefLen,tei); return; #else DDebug(this,DebugNote,"(%p): '%s' without global call reference", @@ -2848,7 +3043,7 @@ void ISDNQ931::processGlobalMsg(ISDNQ931Message* msg) #endif } if (msg->type() == ISDNQ931Message::Restart) { - processMsgRestart(msg); + processMsgRestart(msg,tei); return; } if (m_restartCic) { @@ -2861,14 +3056,14 @@ void ISDNQ931::processGlobalMsg(ISDNQ931Message* msg) msg->name(),tmp.c_str(),m_restartCic->code()); } else - sendStatus("wrong-state-message",m_callRefLen); + sendStatus("wrong-state-message",m_callRefLen,tei); return; case ISDNQ931Message::Status: break; default: Debug(this,DebugNote,"Dropping (%p): '%s' with global call reference", msg,msg->name()); - sendStatus("invalid-callref",m_callRefLen); + sendStatus("invalid-callref",m_callRefLen,tei); return; } // Message is a STATUS one @@ -2881,7 +3076,7 @@ void ISDNQ931::processGlobalMsg(ISDNQ931Message* msg) // Process restart requests // See Q.931 5.5 -void ISDNQ931::processMsgRestart(ISDNQ931Message* msg) +void ISDNQ931::processMsgRestart(ISDNQ931Message* msg, u_int8_t tei) { m_data.processRestart(msg,false); m_data.processChannelID(msg,false); @@ -2962,7 +3157,7 @@ void ISDNQ931::processMsgRestart(ISDNQ931Message* msg) false,0,m_callRefLen); m->append(msg->removeIE(ISDNQ931IE::ChannelID)); m->append(msg->removeIE(ISDNQ931IE::Restart)); - sendMessage(m); + sendMessage(m,tei); return; } @@ -2973,16 +3168,16 @@ void ISDNQ931::processMsgRestart(ISDNQ931Message* msg) "Invalid '%s' request class=%s circuits=%s reason='%s' diagnostic=%s", msg->name(),m_data.m_restart.c_str(),m_data.m_channels.c_str(), m_data.m_reason.c_str(),diagnostic.c_str()); - sendStatus(m_data.m_reason,m_callRefLen,0,false,ISDNQ931Call::Null,0,diagnostic); + sendStatus(m_data.m_reason,m_callRefLen,tei,0,false,ISDNQ931Call::Null,0,diagnostic); } // Process messages with invalid call reference. See Q.931 5.8 -void ISDNQ931::processInvalidMsg(ISDNQ931Message* msg) +void ISDNQ931::processInvalidMsg(ISDNQ931Message* msg, u_int8_t tei) { if (!msg) return; - DDebug(this,DebugNote,"Received (%p): '%s' with invalid call reference [%p]", - msg,msg->name(),this); + DDebug(this,DebugNote,"Received (%p): '%s' with invalid call reference %u [%p]", + msg,msg->name(),msg->callRef(),this); switch (msg->type()) { case ISDNQ931Message::Resume: case ISDNQ931Message::Setup: @@ -2990,7 +3185,7 @@ void ISDNQ931::processInvalidMsg(ISDNQ931Message* msg) break; case ISDNQ931Message::Release: sendRelease(false,msg->callRefLen(),msg->callRef(), - !msg->initiator(),"invalid-callref"); + tei,!msg->initiator(),"invalid-callref"); break; case ISDNQ931Message::Status: // Assume our call state to be Null. See Q.931 5.8.11 @@ -2999,16 +3194,16 @@ void ISDNQ931::processInvalidMsg(ISDNQ931Message* msg) String s = msg->getIEValue(ISDNQ931IE::CallState,"state"); if (s != ISDNQ931Call::stateName(ISDNQ931Call::Null)) sendRelease(false,msg->callRefLen(),msg->callRef(), - !msg->initiator(),"wrong-state-message"); + tei,!msg->initiator(),"wrong-state-message"); } break; case ISDNQ931Message::StatusEnquiry: sendStatus("status-enquiry-rsp",msg->callRefLen(),msg->callRef(), - !msg->initiator(),ISDNQ931Call::Null); + tei,!msg->initiator(),ISDNQ931Call::Null); break; default: sendRelease(true,msg->callRefLen(),msg->callRef(), - !msg->initiator(),"invalid-callref"); + tei,!msg->initiator(),"invalid-callref"); return; } } @@ -3017,8 +3212,10 @@ void ISDNQ931::processInvalidMsg(ISDNQ931Message* msg) // Start counting the restart interval if no circuit reserved void ISDNQ931::sendRestart(u_int64_t time, bool retrans) { - Lock lock(m_layer); + Lock lock(l3Mutex()); m_syncCicTimer.stop(); + if (!primaryRate()) + return; if (m_restartCic) { if (!retrans) return; @@ -3053,14 +3250,14 @@ void ISDNQ931::sendRestart(u_int64_t time, bool retrans) msg->appendSafe(ie); msg->appendIEValue(ISDNQ931IE::Restart,"class","channels"); m_syncCicTimer.start(time ? time : Time::msecNow()); - sendMessage(msg); + sendMessage(msg,0); } // End our restart requests // Release reserved circuit. Continue restarting circuits if requested void ISDNQ931::endRestart(bool restart, u_int64_t time, bool timeout) { - Lock lock(m_layer); + Lock lock(l3Mutex()); m_syncCicTimer.stop(); m_syncCicCounter.reset(); if (m_restartCic) { @@ -3083,11 +3280,13 @@ void ISDNQ931::endRestart(bool restart, u_int64_t time, bool timeout) // Send STATUS. See Q.931 3.1.16 // IE: Cause, CallState, Display bool ISDNQ931::sendStatus(const char* cause, u_int8_t callRefLen, u_int32_t callRef, - bool initiator, ISDNQ931Call::State state, const char* display, + u_int8_t tei, bool initiator, ISDNQ931Call::State state, const char* display, const char* diagnostic) { + if (!primaryRate()) + return false; // Create message - ISDNQ931Message* msg; + ISDNQ931Message* msg = 0; if (callRefLen) msg = new ISDNQ931Message(ISDNQ931Message::Status,initiator,callRef,callRefLen); else @@ -3104,27 +3303,29 @@ bool ISDNQ931::sendStatus(const char* cause, u_int8_t callRefLen, u_int32_t call msg->appendIEValue(ISDNQ931IE::CallState,"state",ISDNQ931Call::stateName(state)); if (display) msg->appendIEValue(ISDNQ931IE::Display,"display",display); - return sendMessage(msg); + return sendMessage(msg,tei); } // Send RELEASE (See Q.931 3.1.9) or RELEASE COMPLETE (See Q.931 3.1.10) // IE: Cause, Display, Signal bool ISDNQ931::sendRelease(bool release, u_int8_t callRefLen, u_int32_t callRef, - bool initiator, const char* cause, const char* diag, + u_int8_t tei, bool initiator, const char* cause, const char* diag, const char* display, const char* signal) { // Create message ISDNQ931Message::Type t = release ? ISDNQ931Message::Release : ISDNQ931Message::ReleaseComplete; ISDNQ931Message* msg = new ISDNQ931Message(t,initiator,callRef,callRefLen); // Add IEs - ISDNQ931IE* ie = msg->appendIEValue(ISDNQ931IE::Cause,0,cause); - if (diag) - ie->addParamPrefix("diagnostic",diag); + if (cause) { + ISDNQ931IE* ie = msg->appendIEValue(ISDNQ931IE::Cause,0,cause); + if (diag) + ie->addParamPrefix("diagnostic",diag); + } if (display) msg->appendIEValue(ISDNQ931IE::Display,"display",display); if (signal) msg->appendIEValue(ISDNQ931IE::Signal,"signal",signal); - return sendMessage(msg); + return sendMessage(msg,tei); } /** @@ -3133,7 +3334,6 @@ bool ISDNQ931::sendRelease(bool release, u_int8_t callRefLen, u_int32_t callRef, ISDNQ931Monitor::ISDNQ931Monitor(const NamedList& params, const char* name) : SignallingCallControl(params,"isdn."), ISDNLayer3(name), - m_layer(true), m_q921Net(0), m_q921Cpe(0), m_cicNet(0), @@ -3164,7 +3364,7 @@ ISDNQ931Monitor::~ISDNQ931Monitor() } // Notification from layer 2 of data link set/release command or response -void ISDNQ931Monitor::dataLinkState(bool cmd, bool value, ISDNLayer2* layer2) +void ISDNQ931Monitor::dataLinkState(u_int8_t tei, bool cmd, bool value, ISDNLayer2* layer2) { #ifdef DEBUG if (debugAt(DebugInfo)) { @@ -3189,13 +3389,9 @@ void ISDNQ931Monitor::idleTimeout(ISDNLayer2* layer2) } // Receive data -void ISDNQ931Monitor::receiveData(const DataBlock& data, bool ack, ISDNLayer2* layer2) +void ISDNQ931Monitor::receiveData(const DataBlock& data, u_int8_t tei, ISDNLayer2* layer2) { - XDebug(this,DebugAll,"Received data. Length: %u",data.length()); - if (!ack) { - Debug(this,DebugNote,"Received unacknowledged data. Drop"); - return; - } + XDebug(this,DebugAll,"Received data. Length: %u, TEI: %u",data.length(),tei); //TODO: Implement segmentation ISDNQ931Message* msg = ISDNQ931Message::parse(m_parserData,data,0); if (!msg) @@ -3252,7 +3448,7 @@ void ISDNQ931Monitor::receiveData(const DataBlock& data, bool ack, ISDNLayer2* l // Attach ISDN Q.921 pasive transport that monitors one side of the link void ISDNQ931Monitor::attach(ISDNQ921Passive* q921, bool net) { - Lock lock(m_layer); + Lock lock(l3Mutex()); ISDNQ921Passive* which = net ? m_q921Net : m_q921Cpe; if (which == q921) return; @@ -3267,7 +3463,7 @@ void ISDNQ931Monitor::attach(ISDNQ921Passive* q921, bool net) const char* name = 0; if (engine() && engine()->find(tmp)) { name = tmp->toString().safe(); - tmp->ISDNLayer2::attach(0); + static_cast(tmp)->attach((ISDNLayer3*)0); } Debug(this,DebugAll,"Detached L2 %s (%p,'%s') [%p]",net?"NET":"CPE",tmp,name,this); } @@ -3282,7 +3478,7 @@ void ISDNQ931Monitor::attach(ISDNQ921Passive* q921, bool net) // Attach a circuit group to this call controller void ISDNQ931Monitor::attach(SignallingCircuitGroup* circuits, bool net) { - Lock lock(m_layer); + Lock lock(l3Mutex()); // Don't attach if it's the same object SignallingCircuitGroup* tmp = net ? m_cicNet : m_cicCpe; if (tmp == circuits) @@ -3320,7 +3516,7 @@ void ISDNQ931Monitor::timerTick(const Time& when) bool ISDNQ931Monitor::reserveCircuit(unsigned int code, bool netInit, SignallingCircuit** caller, SignallingCircuit** called) { - Lock lock(m_layer); + Lock lock(l3Mutex()); if (!(m_cicNet && m_cicCpe)) return false; String cic = code; @@ -3342,7 +3538,7 @@ bool ISDNQ931Monitor::reserveCircuit(unsigned int code, bool netInit, // Release a circuit from both groups bool ISDNQ931Monitor::releaseCircuit(SignallingCircuit* circuit) { - Lock lock(m_layer); + Lock lock(l3Mutex()); if (!circuit) return false; if (m_cicNet == circuit->group()) @@ -4466,7 +4662,7 @@ bool Q931Parser::encodeIE(ISDNQ931IE* ie, DataBlock& buffer) } return false; } - case ISDNQ931IE::Display: return encodeDisplay(ie,buffer);; + case ISDNQ931IE::Display: return encodeDisplay(ie,buffer); case ISDNQ931IE::CallingNo: return encodeCallingNo(ie,buffer); case ISDNQ931IE::CalledNo: return encodeCalledNo(ie,buffer); case ISDNQ931IE::CallState: return encodeCallState(ie,buffer); @@ -4477,6 +4673,8 @@ bool Q931Parser::encodeIE(ISDNQ931IE* ie, DataBlock& buffer) case ISDNQ931IE::Signal: return encodeSignal(ie,buffer); case ISDNQ931IE::Restart: return encodeRestart(ie,buffer); case ISDNQ931IE::SendComplete: return encodeSendComplete(ie,buffer); + case ISDNQ931IE::HiLayerCompat: return encodeHighLayerCap(ie,buffer); + case ISDNQ931IE::UserUser: return encodeUserUser(ie,buffer); } Debug(m_settings->m_dbg,DebugMild,"Encoding not implemented for IE '%s' [%p]", ie->c_str(),m_msg); @@ -5670,4 +5868,33 @@ bool Q931Parser::encodeSendComplete(ISDNQ931IE* ie, DataBlock& buffer) return true; } +bool Q931Parser::encodeHighLayerCap(ISDNQ931IE* ie, DataBlock& buffer) +{ + // **coding standard ** + //octet 1:information element identifier 7d + // 2:the length of contents + // 3:bit -8 extension set to 1 + // -7-6 coding standad + // -5-4-3 interpretation + // -2-1 presentation method of protocol profile + // 4:bit -8 extension set to 0 + // -7-1 high layer caracteristics identification + + // TODO: implement it! + u_int8_t tmp[4]; + tmp[0]=0x7d; tmp[1]=0x02; tmp[2]=0x91; tmp[3]=0x81; + buffer.assign(tmp,sizeof(tmp)); + return true; +} + +bool Q931Parser::encodeUserUser(ISDNQ931IE* ie, DataBlock& buffer) +{ + // TODO: implement it! + u_int8_t tmp[10]; + tmp[0]=0x7e;tmp[1]=0x08;tmp[2]=0x04;tmp[3]=0x30;tmp[4]=0x39; + tmp[5]=0x32;tmp[6]=0x21;tmp[7]=0x30;tmp[8]=0x39;tmp[9]=0x32; + buffer.assign(tmp,sizeof(tmp)); + return true; +} + /* vi: set ts=8 sw=4 sts=4 noet: */ diff --git a/libs/ysig/sigcall.cpp b/libs/ysig/sigcall.cpp index cc7cc533..5b4edc6a 100644 --- a/libs/ysig/sigcall.cpp +++ b/libs/ysig/sigcall.cpp @@ -209,8 +209,9 @@ SignallingEvent* SignallingCallControl::getEvent(const Time& when) // Clear call list void SignallingCallControl::clearCalls() { - Lock lock(this); + lock(); m_calls.clear(); + unlock(); } // Remove a call from list @@ -218,11 +219,12 @@ void SignallingCallControl::removeCall(SignallingCall* call, bool del) { if (!call) return; - Lock lock(this); + lock(); if (m_calls.remove(call,del)) DDebug(DebugAll, - "SignallingCallControl. Call (%p) removed from queue. Deleted: %s [%p]", - call,String::boolText(del),this); + "SignallingCallControl. Call (%p) removed%s from queue [%p]", + call,(del ? " and deleted" : ""),this); + unlock(); } @@ -242,9 +244,11 @@ SignallingCall::SignallingCall(SignallingCallControl* controller, bool outgoing, SignallingCall::~SignallingCall() { + m_callMutex.lock(); m_inMsg.clear(); if (m_controller) m_controller->removeCall(this,false); + m_callMutex.unlock(); } // Event termination notification diff --git a/libs/ysig/yatesig.h b/libs/ysig/yatesig.h index d6b0de00..535e2c2a 100644 --- a/libs/ysig/yatesig.h +++ b/libs/ysig/yatesig.h @@ -115,6 +115,7 @@ class ISDNLayer3; // Abstract ISDN layer 3 (Q.931) messag class ISDNFrame; // An ISDN Q.921 frame class ISDNQ921; // ISDN Q.921 implementation on top of a hardware interface class ISDNQ921Passive; // Stateless ISDN Q.921 implementation on top of a hardware interface +class ISDNQ921Management; // ISDN Layer 2 BRI TEI management or PRI with D-channel(s) backup class ISDNIUA; // SIGTRAN ISDN Q.921 User Adaptation Layer class ISDNQ931IE; // A Q.931 ISDN Layer 3 message Information Element class ISDNQ931Message; // A Q.931 ISDN Layer 3 message @@ -170,8 +171,9 @@ public: /** * Constructor * @param type Type of the output desired + * @param network True if we are the network side of the link */ - SignallingDumper(Type type = Hexa); + SignallingDumper(Type type = Hexa, bool network = false); /** * Destructor, closes the output @@ -185,6 +187,13 @@ public: inline Type type() const { return m_type; } + /** + * Get the network side flag + * @return True if we are the network side + */ + inline bool network() const + { return m_network; } + /** * Check if the dumper is active * @return True if the object will actually send data to something @@ -228,25 +237,28 @@ public: * @param dbg DebugEnabler requesting the operation (used for debug message on failure) * @param filename The file name to use * @param type The dumper type + * @param network True to create a network side dumper * @param create True to create the file if doesn't exist * @param append Append to an existing file. If false and the file already exists, it will be truncated * @return SignallingDumper pointer on success, 0 on failure */ static SignallingDumper* create(DebugEnabler* dbg, const char* filename, Type type, - bool create = true, bool append = false); + bool network = false, bool create = true, bool append = false); /** * Create a dumper from an already existing stream * @param stream Stream to use for output, will be owned by dumper * @param type The dumper type + * @param network True to create a network side dumper * @param writeHeader True to write the header (if any) at start of stream * @return SignallingDumper pointer on success, 0 on failure */ - static SignallingDumper* create(Stream* stream, Type type, bool writeHeader = true); + static SignallingDumper* create(Stream* stream, Type type, bool network = false, bool writeHeader = true); private: void head(); Type m_type; + bool m_network; Stream* m_output; }; @@ -267,9 +279,10 @@ protected: /** * Constructor * @param type Default type of the data dumper + * @param network True if we are the network side of the link */ - inline SignallingDumpable(SignallingDumper::Type type) - : m_type(type), m_dumper(0) + inline SignallingDumpable(SignallingDumper::Type type, bool network = false) + : m_type(type), m_dumpNet(network), m_dumper(0) { } /** @@ -293,6 +306,13 @@ protected: inline bool dump(const DataBlock& data, bool sent = false, int link = 0) { return dump(data.data(),data.length(),sent,link); } + /** + * Set the dump network side flag + * @param network True to dump as network side, false othervise + */ + inline void setDumpNetwork(bool network) + { m_dumpNet = network; } + /** * Set or remove the data dumper * @param dumper Pointer to the data dumper object, 0 to remove @@ -318,6 +338,7 @@ protected: private: SignallingDumper::Type m_type; + bool m_dumpNet; SignallingDumper* m_dumper; }; @@ -557,6 +578,13 @@ public: */ virtual bool control(NamedList& params); + /** + * Set the @ref TelEngine::SignallingEngine that manages this component + * and any subcomponent of it + * @param eng Pointer to the engine that will manage this component + */ + virtual void engine(SignallingEngine* eng); + /** * Get the @ref TelEngine::SignallingEngine that manages this component * @return Pointer to engine or NULL if not managed by an engine @@ -569,9 +597,7 @@ protected: * Constructor with a default empty component name * @param name Name of this component */ - inline SignallingComponent(const char* name = 0) - : m_engine(0), m_name(name) - { } + SignallingComponent(const char* name = 0); /** * This method is called to clean up and destroy the object after the @@ -2143,16 +2169,22 @@ class YSIG_API SignallingReceiver : virtual public SignallingComponent public: /** * Constructor + * @param name Name of the component to create */ - inline SignallingReceiver() - : m_ifaceMutex(true), m_interface(0) - {} + SignallingReceiver(const char* name = 0); /** * Destructor, stops the interface and detaches from it */ virtual ~SignallingReceiver(); + /** + * Get a pointer to this object or other data + * @param name Object name + * @return The requested pointer or 0 if not exists + */ + virtual void* getObject(const String& name) const; + /** * Attach a hardware interface to the data link. Detach from the old one if valid * @param iface Pointer to interface to attach @@ -5830,6 +5862,7 @@ protected: */ class YSIG_API ISDNLayer2 : virtual public SignallingComponent { + friend class ISDNQ921Management; public: /** * Layer states if it has a TEI assigned @@ -5871,14 +5904,14 @@ public: * Get the SAPI (Service Access Point Identifier) of this interface * @return The SAPI (Service Access Point Identifier) of this interface */ - inline u_int8_t sapi() const + inline u_int8_t localSapi() const { return m_sapi; } /** * Get the TEI (Terminal Endpoint Identifier) of this interface * @return The TEI (Terminal Endpoint Identifier) of this interface */ - inline u_int8_t tei() const + inline u_int8_t localTei() const { return m_tei; } /** @@ -5895,13 +5928,6 @@ public: inline bool teiAssigned() const { return m_teiAssigned; } - /** - * Check if unacknowledged data is allowed to pass through this interface - * @return True if unacknowledged data is allowed to pass through this interface - */ - inline bool allowUnack() const - { return m_allowUnack; } - /** * Check if this interface will automatically re-establish when released * @return The auto restart flag @@ -5912,22 +5938,24 @@ public: /** * Implements Q.921 DL-ESTABLISH and DL-RELEASE request primitives * Descendants must implement this method to fullfill the request + * @param tei This layer TEI (Terminal Endpoint Identifier) * @param establish True to establish. False to release * @param force True to establish even if we already are in this mode. This * parameter is ignored if establish is false * @return True if the request was accepted */ - virtual bool multipleFrame(bool establish, bool force) + virtual bool multipleFrame(u_int8_t tei, bool establish, bool force) { return false; } /** * Implements Q.921 DL-DATA and DL-UNIT DATA request primitives * Descendants must implement this method to fullfill the request * @param data Data to send + * @param tei This layer TEI * @param ack True to send an acknowledged frame, false to send an unacknowledged one * @return True if the request was accepted */ - virtual bool sendData(const DataBlock& data, bool ack) + virtual bool sendData(const DataBlock& data, u_int8_t tei, bool ack) { return false; } /** @@ -5958,37 +5986,48 @@ protected: * Initialize this interface and the component * @param params Layer's parameters * @param name Optional name of the component + * @param tei Value of TEI for this layer */ - ISDNLayer2(const NamedList& params, const char* name = 0); + ISDNLayer2(const NamedList& params, const char* name = 0, u_int8_t tei = 0); + + /** + * Retrieve the layer's mutex + * @return Reference to the Layer 2 mutex + */ + inline Mutex& l2Mutex() + { return m_layerMutex; } /** * Implements Q.921 DL-ESTABLISH indication/confirmation primitive * of 'multiple frame acknowledged' mode established + * @param tei The TEI requested * @param confirm True if this is a confirmation of a previous request. * False if it is an indication of state change on remote request * @param timeout True if the reason is a timeout. */ - void multipleFrameEstablished(bool confirm, bool timeout); + void multipleFrameEstablished(u_int8_t tei, bool confirm, bool timeout); /** * Implements Q.921 DL-RELEASE indication/confirmation primitive * of 'multiple frame acknowledged' mode released + * @param tei The TEI released * @param confirm True if this is a confirmation of a previous request. * False if it is an indication of state change on remote request * @param timeout True if the reason is a timeout. */ - void multipleFrameReleased(bool confirm, bool timeout); + void multipleFrameReleased(u_int8_t tei, bool confirm, bool timeout); /** * Notify layer 3 of data link set/release command or response * Used for stateless layer 2 + * @param tei The TEI of this layer * @param cmd True if received a command, false if received a response * @param value The value of the notification * If 'cmd' is true (command), the value is true if a request to establish data link was received * or false if received a request to release data link * If 'cmd' is false (response), the value is the response */ - void dataLinkState(bool cmd, bool value); + void dataLinkState(u_int8_t tei, bool cmd, bool value); /** * Notify layer 3 of data link idle timeout @@ -6000,9 +6039,9 @@ protected: * Implements Q.921 DL-DATA and DL-UNIT DATA indication primitives * Receive data from remote peer * @param data Received data - * @param ack True if data is an acknowledged frame, false if it is an unacknowledged one + * @param tei The TEI for which the data was received */ - void receiveData(const DataBlock& data, bool ack); + void receiveData(const DataBlock& data, u_int8_t tei); /** * Set TEI assigned status. Print a debug message. If status is false calls cleanup() @@ -6015,8 +6054,9 @@ protected: * Set the state * Descendants are responsable for multiple frame status management * @param newState The new state + * @param reason Reason of state change, NULL if unspecified */ - void changeState(State newState); + void changeState(State newState, const char* reason = 0); /** * Change the interface type @@ -6024,19 +6064,42 @@ protected: */ bool changeType(); + /** + * Set the automatically re-establish when released flag + * @param restart The new value of the auto restart flag + */ + inline void autoRestart(bool restart) + { m_autoRestart = restart; } + + /** + * Set the Reference Identifier used in management procedures + * @param ri The new reference number + */ + inline void setRi(u_int16_t ri) + { m_ri = ri; } + + /** + * Parse a received packet + * @param packet The packet received + * @return Pointer to a newly created frame, NULL if an error occured + */ + ISDNFrame* parsePacket(const DataBlock& packet); + private: ISDNLayer3* m_layer3; // The attached Layer 3 interface - Mutex m_interfaceMutex; // Interface operations lock + Mutex m_layerMutex; // Layer 2 operations mutex Mutex m_layer3Mutex; // Control m_layer3 operations State m_state; // Layer's state bool m_network; // Network/CPE type of the interface bool m_detectType; // Detect interface type u_int8_t m_sapi; // SAPI value u_int8_t m_tei; // TEI value + u_int16_t m_ri; // Reference number + bool m_checked; // Flag to indicate if the layer was checked bool m_teiAssigned; // The TEI status - bool m_allowUnack; // Allow unacknowledged data to pass through this interface bool m_autoRestart; // True to restart when released u_int32_t m_maxUserData; // Maximum length of user data transported trough this layer + unsigned int m_teiRefNumber; // The Reference Number (Ri) carried by a TEI management frame static TokenDict m_states[]; // Keep the string associated with each state }; @@ -6050,28 +6113,31 @@ public: /** * Implements Q.921 DL-ESTABLISH indication/confirmation primitive: * 'multiple frame acknowledged' mode established + * @param tei The TEI of the frame * @param confirm True if this is a confirmation of a previous request. * False if it is an indication of state change on remote request * @param timeout True if the reason is a timeout * @param layer2 Pointer to the notifier */ - virtual void multipleFrameEstablished(bool confirm, bool timeout, ISDNLayer2* layer2) + virtual void multipleFrameEstablished(u_int8_t tei, bool confirm, bool timeout, ISDNLayer2* layer2) {} /** * Implements Q.921 DL-RELEASE indication/confirmation primitive: * 'multiple frame acknowledged' mode released + * @param tei The TEI of the frame * @param confirm True if this is a confirmation of a previous request. * False if it is an indication of state change on remote request * @param timeout True if the reason is a timeout. * @param layer2 Pointer to the notifier */ - virtual void multipleFrameReleased(bool confirm, bool timeout, ISDNLayer2* layer2) + virtual void multipleFrameReleased(u_int8_t tei, bool confirm, bool timeout, ISDNLayer2* layer2) {} /** * Notification from layer 2 of data link set/release command or response * Used for stateless layer 2 + * @param tei The TEI of the command or response * @param cmd True if received a command, false if received a response * @param value The value of the notification * If 'cmd' is true (command), the value is true if a request to establish data link was received @@ -6079,7 +6145,7 @@ public: * If 'cmd' is false (response), the value is the response * @param layer2 Pointer to the notifier */ - virtual void dataLinkState(bool cmd, bool value, ISDNLayer2* layer2) + virtual void dataLinkState(u_int8_t tei, bool cmd, bool value, ISDNLayer2* layer2) {} /** @@ -6094,11 +6160,10 @@ public: * Implements Q.921 DL-DATA and DL-UNIT DATA indication primitives * Receive data from remote peer * @param data Received data - * @param ack True if data is an acknowledged frame, - * false if it is an unacknowledged one + * @param tei The TEI of the received frame * @param layer2 Pointer to the sender */ - virtual void receiveData(const DataBlock& data, bool ack, ISDNLayer2* layer2) = 0; + virtual void receiveData(const DataBlock& data, u_int8_t tei, ISDNLayer2* layer2) = 0; /** * Attach an ISDN Q.921 Layer 2 @@ -6107,6 +6172,13 @@ public: virtual void attach(ISDNLayer2* layer2) {} + /** + * Detach an ISDN Q.921 Layer 2 + * @param layer2 Pointer to the Q.921 Layer 2 to detach + */ + virtual void detach(ISDNLayer2* layer2) + {} + protected: /** * Constructor @@ -6114,8 +6186,18 @@ protected: * @param name Name of this component */ inline ISDNLayer3(const char* name = 0) - : SignallingComponent(name) + : SignallingComponent(name), m_layerMutex(true) {} + + /** + * Retrieve the layer's mutex + * @return Reference to the Layer 3 mutex + */ + inline Mutex& l3Mutex() + { return m_layerMutex; } + +private: + Mutex m_layerMutex; // Layer 2 operations mutex }; /** @@ -6125,6 +6207,7 @@ protected: class YSIG_API ISDNFrame : public RefObject { friend class ISDNQ921; + friend class ISDNQ921Management; public: /** * Frame type according to Q.921 3.6 @@ -6154,6 +6237,19 @@ public: ErrInvalidCR = 109, // Error: Invalid command/response flag }; + /** + * Codes used for TEI management procedures (Q.921 Table 8) + */ + enum TeiManagement { + TeiReq = 1, // TEI request (user to network) + TeiAssigned = 2, // TEI assigned (network to user) + TeiDenied = 3, // TEI denied (network to user) + TeiCheckReq = 4, // TEI check request (network to user) + TeiCheckRsp = 5, // TEI check response (user to network) + TeiRemove = 6, // TEI remove (network to user) + TeiVerify = 7 // TEI verify (user to network) + }; + /** * Frame category */ @@ -6187,7 +6283,7 @@ public: * Get the category of this frame * @return The category of this frame as enumeration */ - inline Category category() + inline Category category() const { return m_category; } /** @@ -6294,6 +6390,36 @@ public: */ void toString(String& dest, bool extendedDebug) const; + /** + * Check if the received frame is a TEI management frame + * @return True if the frame is a TEI management one, false if it's not + */ + bool checkTeiManagement() const; + + /** + * Get reference number from frame + * @param data The data block which contains it + * @return Reference number + */ + static u_int16_t getRi(const DataBlock& data); + + /** + * Get frame message type + * @param data The data block which contains it + * @return Message type + */ + inline static u_int8_t getType(const DataBlock& data) + { return static_cast(data.at(3,0)); } + + /** + * Get action indicator + * @param data Data block which contains it + * @return Action indicator value + */ + inline static u_int8_t getAi(const DataBlock& data) + { return static_cast(data.at(4,0) >> 1); } + + /** * Parse a received data block * @param data Data to parse @@ -6302,6 +6428,17 @@ public: */ static ISDNFrame* parse(const DataBlock& data, ISDNLayer2* receiver); + /** + * Build a TEI management message buffer + * @param data Destination buffer to fill + * @param type The message type + * @param ri The reference number + * @param ai The action indicator + * @return True on succes + */ + static bool buildTeiManagement(DataBlock& data, u_int8_t type, u_int16_t ri, + u_int8_t ai); + /** * Get the command bit value for a given side of a data link * @param network True for the network side, @@ -6309,7 +6446,7 @@ public: * @return The appropriate command bit value */ static inline bool commandBit(bool network) - { return network ? true : false; } + { return network; } /** * Get the response bit value for a given side of a data link @@ -6318,19 +6455,16 @@ public: * @return The appropriate response bit value */ static inline bool responseBit(bool network) - { return network ? false : true; } + { return !network; } /** * Get the command/response type from C/R bit value and sender type * @param cr The value of the C/R bit - * @param senderNetwork True if the sender is the network side of the data link + * @param sentByNetwork True if the sender is the network side of the data link * @return True if it is a command */ - static inline bool isCommand(u_int8_t cr, bool senderNetwork) { - if (cr) - return senderNetwork ? true : false; - return senderNetwork ? false : true; - } + static inline bool isCommand(u_int8_t cr, bool sentByNetwork) + { return cr ? sentByNetwork : !sentByNetwork; } /** * Get the text associated with the given frame type @@ -6411,14 +6545,17 @@ private: */ class YSIG_API ISDNQ921 : public ISDNLayer2, public SignallingReceiver, public SignallingDumpable { + friend class ISDNQ921Management; public: /** * Constructor * Initialize this object and the component * @param params Layer's and @ref TelEngine::ISDNLayer2 parameters * @param name Name of this component + * @param mgmt TEI management component + * @param tei Value of TEI for this component */ - ISDNQ921(const NamedList& params, const char* name = 0); + ISDNQ921(const NamedList& params, const char* name = 0, ISDNQ921Management* mgmt = 0, u_int8_t tei = 0); /** * Destructor @@ -6436,22 +6573,31 @@ public: * Implements Q.921 DL-ESTABLISH and DL-RELEASE request primitives * If accepted, the primitive is enqueued for further processing * This method is thread safe + * @param tei This layer's TEI * @param establish True to establish. False to release * @param force True to establish even if we already are in this mode. This * parameter is ignored if establish is false * @return True if the request was accepted */ - virtual bool multipleFrame(bool establish, bool force); + virtual bool multipleFrame(u_int8_t tei, bool establish, bool force); /** * Implements Q.921 DL-DATA and DL-UNIT DATA request primitives * Send data through the HDLC interface * This method is thread safe * @param data Data to send + * @param tei The TEI to send with the data frane * @param ack True to send an acknowledged frame, false to send an unacknowledged one * @return False if the request was not accepted or send operation failed */ - virtual bool sendData(const DataBlock& data, bool ack); + virtual bool sendData(const DataBlock& data, u_int8_t tei, bool ack); + + /** + * Send a SABME frame to reset the layer + * @return True if a SABME frame was sent + */ + inline bool sendSabme() + { return sendUFrame(ISDNFrame::SABME,true,true); } /** * Emergency release. @@ -6482,7 +6628,7 @@ protected: * Detach links. Disposes memory */ virtual void destroyed() { - ISDNLayer2::attach(0); + ISDNLayer2::attach((ISDNLayer3*)0); SignallingReceiver::attach(0); SignallingComponent::destroyed(); } @@ -6502,6 +6648,13 @@ protected: */ virtual bool receivedPacket(const DataBlock& packet); + /** + * Process the frame received + * @param frame Pointer to frame to process + * @return True if the frame was processed + */ + bool receivedFrame(ISDNFrame* frame); + /** * Process a notification generated by the attached interface * This method is thread safe @@ -6566,6 +6719,7 @@ private: // @param time Current time if known void timer(bool start, bool t203, u_int64_t time = 0); + ISDNQ921Management* m_management; // TEI management component // State variables bool m_remoteBusy; // Remote peer is busy: don't send any I frames bool m_timerRecovery; // T200 expired @@ -6575,8 +6729,6 @@ private: u_int8_t m_vs; // Sequence number of the next transmitted I frame u_int8_t m_va; // Last ack'd I frame by remote peer u_int8_t m_vr; // Expected I frame sequence number - // Lock - Mutex m_layer; // Lock all layer operations // Timers and counters SignallingTimer m_retransTimer; // T200: Retransmission interval SignallingTimer m_idleTimer; // T203: Channel idle interval @@ -6599,6 +6751,231 @@ private: bool m_errorReceive; // Receive error }; +/** + * This class is intended to be used as a proxy between an ISDN Layer 3 and multiple + * Layer 2 objects sharing the same signalling interface. + * It is used for BRI TEI management or PRI with D-channel backup. + * It also keeps a list of ISDN Layer 2 object(s) used for the designated purpose + * @short ISDN Layer 2 BRI TEI management or PRI with D-channel(s) backup + */ +class YSIG_API ISDNQ921Management : public ISDNLayer2, public ISDNLayer3, public SignallingReceiver, public SignallingDumpable +{ +public: + /** + * Constructor - initialize this Layer 2 and the component + * @param params Layer's parameters + * @param name Optional name of the component + * @param net True if managing the network side of Q.921 + */ + ISDNQ921Management(const NamedList& params, const char* name = 0, bool net = true); + + /** + * Destructor + */ + virtual ~ISDNQ921Management(); + + + /** + * Get an object from this management + * @return The requested object or 0 + */ + virtual void* getObject(const String& name) const; + + /** + * Set the engine for this management and all Layer 2 children + * @param eng Pointer to the engine that will manage this mangement + */ + virtual void engine(SignallingEngine* eng); + + /** + * Implements Q.921 DL-ESTABLISH and DL-RELEASE request primitives + * @param tei This layer TEI (-1 to apply it to all targets this object may have attached) + * @param establish True to establish. False to release + * @param force True to establish even if we already are established. + * @return True if the request was accepted + */ + virtual bool multipleFrame(u_int8_t tei, bool establish, bool force); + + /** + * Implements Q.921 DL-DATA and DL-UNIT DATA request primitives + * @param data Data to send + * @param tei This layer TEI (-1 for broadcast) + * @param ack True to send an acknowledged frame, false to send an unacknowledged one + * @return True if the request was accepted + */ + virtual bool sendData(const DataBlock& data, u_int8_t tei, bool ack); + + /** + * Implements Q.921 send frame to the interface + * @param frame The frame to be sent + * @param q921 Pointer to the Q.921 that sends the frame, if any + * @return True if the frame was sent + */ + bool sendFrame(const ISDNFrame* frame, const ISDNQ921* q921 = 0); + + + /** + * Emergency release. + * Cleanup all Layer 2 objects attached to this Management + */ + virtual void cleanup(); + + /** + * Implements Q.921 DL-ESTABLISH indication/confirmation primitive: + * 'multiple frame acknowledged' mode established + * @param tei This layer TEI + * @param confirm True if this is a confirmation of a previous request, + * false if it is an indication of state change on remote request + * @param timeout True if the reason is a timeout + * @param layer2 Pointer to the notifier + */ + virtual void multipleFrameEstablished(u_int8_t tei, bool confirm, bool timeout, ISDNLayer2* layer2); + + /** + * Implements Q.921 DL-RELEASE indication/confirmation primitive: + * 'multiple frame acknowledged' mode released + * @param tei This layer TEI + * @param confirm True if this is a confirmation of a previous request, + * false if it is an indication of state change on remote request + * @param timeout True if the reason is a timeout. + * @param layer2 Pointer to the notifier + */ + virtual void multipleFrameReleased(u_int8_t tei, bool confirm, bool timeout, ISDNLayer2* layer2); + + /** + * Notification from layer 2 of data link set/release command or response + * Used for stateless layer 2 + * @param tei This layer TEI + * @param cmd True if received a command, false if received a response + * @param value The value of the notification + * If 'cmd' is true (command), the value is true if a request to establish data link was received + * or false if received a request to release data link + * If 'cmd' is false (response), the value is the response + * @param layer2 Pointer to the notifier + */ + virtual void dataLinkState(u_int8_t tei, bool cmd, bool value, ISDNLayer2* layer2); + + /** + * Implements Q.921 DL-DATA and DL-UNIT DATA indication primitives + * Receive data from an encapsulated Layer 2 and send it to the attached Layer 3 + * @param data Received data + * @param tei The TEI as received in the packet + * @param layer2 Pointer to the sender + */ + virtual void receiveData(const DataBlock& data, u_int8_t tei, ISDNLayer2* layer2); + +protected: + /** + * Method called periodically to check timeouts + * This method is thread safe + * @param when Time to use as computing base for events and timeouts + */ + virtual void timerTick(const Time& when); + + /** + * Process a Signalling Packet received by the interface. + * Parse the data and send all non-UI frames to the appropriate Layer 2. + * Process UI frames + * @return True if message was successfully processed + */ + virtual bool receivedPacket(const DataBlock& packet); + + /** + * Process a notification generated by the attached interface + * @param event Notification event reported by the interface + * @return True if notification was processed + */ + virtual bool notify(SignallingInterface::Notification event); + + /** + * Process UI frames carrying TEI management messages + * @param frame The parsed frame + * @return False if the frame is not a TEI management one, true otherwise + */ + bool processTeiManagement(ISDNFrame* frame); + + /** + * Send a TEI management frame + * @param type Type of the frame to send + * @param ri Reference number to send in frame + * @param ai Action indicator to send in frame + * @param tei The TEI to send the frame to, default use broadcast + * @param pf The Poll/Final bit to set in the frame + * @return True if frame was sent successfully + */ + bool sendTeiManagement(ISDNFrame::TeiManagement type, u_int16_t ri, u_int8_t ai, u_int8_t tei = 127, bool pf = false); + + /** + * Process TEI request message and send back to TE: + * TEI Assigned message if the request succeeded; + * TEI Denied message with the received reference number if the + * reference number is already in use; + * TEI Denied message with the reference number set to 127 if + * there is no TEI value available. + * @param ri The reference number + * @param ai Action indicator + * @param pf The Poll/Final bit in the incoming frame + */ + void processTeiRequest(u_int16_t ri, u_int8_t ai, bool pf); + + /** + * Process Tei remove message removing the tei(s) contained by ai + * @param ai Contains the TEI value to remove or 127 to remove all TEI values + */ + void processTeiRemove(u_int8_t ai); + + /** + * Process TEI Check Request message and send to the NET a message with + * the TEI and the asociated reference number + * @param ai Contains the TEI value to check or 127 to check all TEI values + * @param pf The Poll/Final bit in the incoming frame + */ + void processTeiCheckRequest(u_int8_t ai, bool pf); + + /** + * Process TEI Check Response message and set the check flag to true to know that we have a response for that TEI + * @param ri The associated reference number to the ai + * @param ai The TEI value as received in the answer + */ + void processTeiCheckResponse(u_int16_t ri, u_int8_t ai); + + /** + * Process TEI Assigned message + * @param ri The reference number assigned to the ai + * @param ai The TEI value assigned + */ + void processTeiAssigned(u_int16_t ri, u_int8_t ai); + + /** + * Process TEI Denied message + * @param ri The reference number of the denied request + */ + void processTeiDenied(u_int16_t ri); + + /** + * Process TEI verify + * @param ai The TEI value of the message initiator + * @param pf The Poll/Final bit in the incoming frame + */ + void processTeiVerify(u_int8_t ai, bool pf); + + /** + * Send TEI request message + * @param tei TEI value to assign + */ + void sendTeiReq(u_int8_t tei); + + /** + * Send a TEI remove frame + */ + void sendTeiRemove(); + +private: + ISDNQ921* m_layer2[127]; // The list of Layer 2 objects attached to this Layer 3 + SignallingTimer m_teiManTimer; // T202 + SignallingTimer m_teiTimer; // T201 +}; + /** * Q.921 ISDN Layer 2 pasive (stateless) implementation on top of a hardware HDLC interface * @short Stateless pasive ISDN Q.921 implementation on top of a hardware interface @@ -6686,7 +7063,6 @@ private: // Show debug message. Count dropped frames bool dropFrame(const ISDNFrame* frame, const char* reason = 0); - Mutex m_layer; // Lock all layer operations bool m_checkLinkSide; // Check if this is the correct side of the data link SignallingTimer m_idleTimer; // Channel idle interval u_int8_t m_lastFrame; // Transmitter send number of the last received frame @@ -7093,7 +7469,7 @@ class YSIG_API ISDNQ931IEData friend class ISDNQ931Monitor; private: // Constructor - ISDNQ931IEData(); + ISDNQ931IEData(bool bri = false); // Process received IEs // If add is true, append an IE to the message // If add is false, extract data from message. Set data to default values if IE is missing @@ -7247,6 +7623,13 @@ public: inline u_int32_t callRefLen() const { return m_callRefLen; } + /** + * Get the Terminal Equipment Indicator for this call + * @return Value of TEI used in this call + */ + inline u_int8_t callTei() const + { return m_tei; } + /** * Get the circuit this call had reserved * @return The circuit reserved by this call @@ -7301,7 +7684,7 @@ protected: * @param callRefLen The call reference length in bytes */ ISDNQ931Call(ISDNQ931* controller, bool outgoing, u_int32_t callRef, - u_int8_t callRefLen); + u_int8_t callRefLen, u_int8_t tei = 0); /** * Send RELEASE COMPLETE if not in Null state. @@ -7356,9 +7739,10 @@ private: bool sendInfo(SignallingMessage* sigMsg); bool sendProgress(SignallingMessage* sigMsg); bool sendRelease(const char* reason = 0, SignallingMessage* sigMsg = 0); - bool sendReleaseComplete(const char* reason = 0, const char* diag = 0); + bool sendReleaseComplete(const char* reason = 0, const char* diag = 0, u_int8_t tei = 0); bool sendSetup(SignallingMessage* sigMsg); bool sendSuspendRej(const char* reason = 0, SignallingMessage* sigMsg = 0); + bool sendSetupAck(); // Errors on processing received messages // Missing mandatory IE // @param release True to send release complete and generate a release event @@ -7374,16 +7758,23 @@ private: // Call data u_int32_t m_callRef; // Call reference u_int32_t m_callRefLen; // Call reference length + u_int8_t m_tei; // TEI used for the call SignallingCircuit* m_circuit; // Circuit reserved for this call + bool m_overlap; // Call is using overlapped sending bool m_circuitChange; // True if circuit changed bool m_channelIDSent; // Incoming calls: ChannelID IE already sent bool m_rspBearerCaps; // Incoming calls: Send BearerCaps IE in the first response + bool m_net; // Flag indicating call is sent by a NT ISDNQ931IEData m_data; // Data to process IEs ObjList m_inMsg; // Incoming message queue + bool m_broadcast[127]; // TEIs that answered to PTMP SETUP // Timers SignallingTimer m_discTimer; // T305: sending DISCONNECT SignallingTimer m_relTimer; // T308: sending RELEASE SignallingTimer m_conTimer; // T313: sending CONNECT + SignallingTimer m_overlapSendTimer; // T302 for overlapped sending + SignallingTimer m_overlapRecvTimer; // T304 + SignallingTimer m_retransSetupTimer; // T302 for setup retransmission (PTMP) // Termination bool m_terminate; // Terminate flag: send RELEASE bool m_destroy; // Destroy flag: call releaseComplete() @@ -7668,36 +8059,39 @@ public: /** * Send a message * @param msg The message to be sent + * @param tei TEI value to use at Layer 2 * @param reason Optional string to write the failure reason * @return False if the message is invalid, Layer 2 is missing or refused the data */ - bool sendMessage(ISDNQ931Message* msg, String* reason = 0); + bool sendMessage(ISDNQ931Message* msg, u_int8_t tei, String* reason = 0); /** * Notification of Layer 2 up state + * @param tei TEI received by the Layer 2 * @param confirm True if this is a confirmation of a previous request. * False if it is an indication of state change on remote request * @param timeout True if the reason is a timeout. * @param layer2 Pointer to the notifier */ - virtual void multipleFrameEstablished(bool confirm, bool timeout, ISDNLayer2* layer2); + virtual void multipleFrameEstablished(u_int8_t tei, bool confirm, bool timeout, ISDNLayer2* layer2); /** * Notification of Layer 2 down state + * @param tei TEI received by the Layer 2 * @param confirm True if this is a confirmation of a previous request. * False if it is an indication of state change on remote request * @param timeout True if the reason is a timeout. * @param layer2 Pointer to the notifier */ - virtual void multipleFrameReleased(bool confirm, bool timeout, ISDNLayer2* layer2); + virtual void multipleFrameReleased(u_int8_t tei, bool confirm, bool timeout, ISDNLayer2* layer2); /** * Receive data from Layer 2 * @param data Received data - * @param ack True if data is an acknowledged frame, false if it is an unacknowledged one - * @param layer2 Pointer to the sender + * @param tei TEI received by the Layer 2 + * @param layer2 Pointer to the sender Layer 2 */ - virtual void receiveData(const DataBlock& data, bool ack, ISDNLayer2* layer2); + virtual void receiveData(const DataBlock& data, u_int8_t tei, ISDNLayer2* layer2); /** * Attach an ISDN Q.921 transport @@ -7729,11 +8123,11 @@ public: * @param diagnostic Optional value for cause diagnostic value * @return The result of the operation (true if succesfully sent) */ - inline bool sendStatus(ISDNQ931Call* call, const char* cause, + inline bool sendStatus(ISDNQ931Call* call, const char* cause, u_int8_t tei = 0, const char* display = 0, const char* diagnostic = 0) { if (!call) return false; - return sendStatus(cause,call->callRefLen(),call->callRef(), + return sendStatus(cause,call->callRefLen(),call->callRef(),tei, call->outgoing(),call->state(),display,diagnostic); } @@ -7742,16 +8136,17 @@ public: * @param call The call requesting the operation * @param release True to send RELEASE, false to send RELEASE COMPLETE * @param cause Value for Cause IE + * @param tei TEI to which the release is sent to * @param diag Optional hexified string for cause dignostic * @param display Optional value for Display IE * @param signal Optional value for Signal IE * @return The result of the operation (true if succesfully sent) */ - inline bool sendRelease(ISDNQ931Call* call, bool release, const char* cause, + inline bool sendRelease(ISDNQ931Call* call, bool release, const char* cause, u_int8_t tei = 0, const char* diag = 0, const char* display = 0, const char* signal = 0) { if (!call) return false; - return sendRelease(release,call->callRefLen(),call->callRef(), + return sendRelease(release,call->callRefLen(),call->callRef(),tei, call->outgoing(),cause,diag,display,signal); } @@ -7770,6 +8165,11 @@ public: */ void setInterval(SignallingTimer& timer, int id); + /** + * Manage timeout for the call setup message + */ + void manageTimeout(); + /** * Get a pointer to this call controller * @param name Object name. Must be ISDNQ931 @@ -7819,9 +8219,10 @@ protected: * Find a call given its call reference and direction * @param callRef The call reference to find * @param outgoing True to find an outgoing call, false to find an incoming one + * @param tei TEI of the layer associated to the call to find * @return A referenced pointer to a call or 0 */ - ISDNQ931Call* findCall(u_int32_t callRef, bool outgoing); + ISDNQ931Call* findCall(u_int32_t callRef, bool outgoing, u_int8_t tei = 0); /** * Find a call given a circuit number @@ -7864,20 +8265,23 @@ protected: /** * Process messages with global call reference or should have one * @param msg The received message + * @param tei The TEI received with the message */ - void processGlobalMsg(ISDNQ931Message* msg); + void processGlobalMsg(ISDNQ931Message* msg, u_int8_t tei = 0); /** * Process a restart request * @param msg The received message + * @param tei The TEI received with the message */ - void processMsgRestart(ISDNQ931Message* msg); + void processMsgRestart(ISDNQ931Message* msg, u_int8_t tei = 0); /** * Process messages with invalid call reference * @param msg The received message + * @param tei The TEI received with the message */ - void processInvalidMsg(ISDNQ931Message* msg); + void processInvalidMsg(ISDNQ931Message* msg, u_int8_t tei = 0); /** * Try to reserve a circuit for restarting if none. Send a restart request on it's behalf @@ -7909,7 +8313,7 @@ protected: * @return The result of the operation (true if succesfully sent) */ bool sendStatus(const char* cause, u_int8_t callRefLen, u_int32_t callRef = 0, - bool initiator = false, ISDNQ931Call::State state = ISDNQ931Call::Null, + u_int8_t tei = 0, bool initiator = false, ISDNQ931Call::State state = ISDNQ931Call::Null, const char* display = 0, const char* diagnostic = 0); /** @@ -7917,6 +8321,7 @@ protected: * @param release True to send RELEASE, false to send RELEASE COMPLETE * @param callRefLen The call reference length parameter * @param callRef The call reference + * @param tei The TEI of the Layer 2 associated with the call * @param initiator The call initiator flag * @param cause Value for Cause IE * @param diag Optional hexified string for cause dignostic @@ -7924,13 +8329,14 @@ protected: * @param signal Optional value for Signal IE * @return The result of the operation (true if succesfully sent) */ - bool sendRelease(bool release, u_int8_t callRefLen, u_int32_t callRef, - bool initiator, const char* cause, const char* diag = 0, + bool sendRelease(bool release, u_int8_t callRefLen, u_int32_t callRef, u_int8_t tei, + bool initiator, const char* cause = 0, const char* diag = 0, const char* display = 0, const char* signal = 0); + private: virtual bool control(NamedList& params) { return SignallingDumpable::control(params,this); } - Mutex m_layer; // Lock layer operation + bool q921Up() const; // Check if layer 2 may be up ISDNLayer2* m_q921; // The attached layer 2 bool m_q921Up; // Layer 2 state // Protocol data @@ -7955,6 +8361,7 @@ private: String m_numPresentation; // Number presentation String m_numScreening; // Number screening String m_format; // Data format + String m_cpeNumber; // The number of the BRI CPE // Restart data SignallingCircuit* m_restartCic; // Currently restarting circuit unsigned int m_lastRestart; // Last restarted circuit's code @@ -7995,6 +8402,7 @@ public: /** * Notification from layer 2 of data link set/release command or response + * @param tei The TEI of the notification * @param cmd True if received a command, false if received a response * @param value The value of the notification * If 'cmd' is true (command), the value is true if a request to establish data link was received @@ -8002,7 +8410,7 @@ public: * If 'cmd' is false (response), the value is the response * @param layer2 Pointer to the notifier */ - virtual void dataLinkState(bool cmd, bool value, ISDNLayer2* layer2); + virtual void dataLinkState(u_int8_t tei, bool cmd, bool value, ISDNLayer2* layer2); /** * Notification from layer 2 of data link idle timeout @@ -8013,11 +8421,10 @@ public: /** * Implements Q.921 DL-DATA and DL-UNIT DATA indication primitives * @param data Received data - * @param ack True if data is an acknowledged frame, - * false if it is an unacknowledged one + * @param tei The TEI of the Layer 2 * @param layer2 Pointer to the sender */ - virtual void receiveData(const DataBlock& data, bool ack, ISDNLayer2* layer2); + virtual void receiveData(const DataBlock& data, u_int8_t tei, ISDNLayer2* layer2); /** * Attach ISDN Q.921 pasive transport that monitors one side of the link @@ -8125,7 +8532,6 @@ private: // Drop some messages. Return true if the message should be dropped bool dropMessage(const ISDNQ931Message* msg); - Mutex m_layer; // Lock layer operation ISDNQ921Passive* m_q921Net; // Net side of the link ISDNQ921Passive* m_q921Cpe; // CPE side of the link SignallingCircuitGroup* m_cicNet; // Circuit group for the net side of the link diff --git a/modules/server/ysigchan.cpp b/modules/server/ysigchan.cpp index a2367849..740c2e56 100644 --- a/modules/server/ysigchan.cpp +++ b/modules/server/ysigchan.cpp @@ -202,18 +202,29 @@ class SigLink : public RefObject { friend class SigLinkThread; // The thread must set m_thread to 0 on terminate public: + enum Mask { + MaskSS7 = 0x01, + MaskIsdn = 0x02, + MaskMon = 0x04, + MaskNet = 0x10, + MaskCpe = 0x20, + MaskPri = 0x40, + MaskBri = 0x80, + }; enum Type { - SS7Isup, - IsdnPriNet, - IsdnPriCpe, - IsdnPriMon, - Unknown + Unknown = 0, + SS7Isup = MaskSS7, + IsdnPriNet = MaskIsdn | MaskNet | MaskPri, + IsdnBriNet = MaskIsdn | MaskNet | MaskBri, + IsdnPriCpe = MaskIsdn | MaskCpe | MaskPri, + IsdnBriCpe = MaskIsdn | MaskCpe | MaskBri, + IsdnPriMon = MaskIsdn | MaskMon, }; // Set link name and type. Append to plugin list SigLink(const char* name, Type type); // Cancel thread. Cleanup. Remove from plugin list virtual ~SigLink(); - inline int type() const + inline Type type() const { return m_type; } inline SignallingCallControl* controller() const { return m_controller; } @@ -266,7 +277,7 @@ protected: bool m_init; // True if already initialized bool m_inband; // True to send in-band tones through this link private: - int m_type; // Link type + Type m_type; // Link type String m_name; // Link name SigLinkThread* m_thread; // Event thread for call controller }; @@ -304,7 +315,7 @@ private: class SigIsdn : public SigLink { public: - SigIsdn(const char* name, bool net); + SigIsdn(const char* name, Type type); virtual ~SigIsdn(); protected: virtual bool create(NamedList& params, String& error); @@ -314,7 +325,7 @@ protected: inline ISDNQ931* q931() { return static_cast(m_controller); } private: - ISDNQ921* m_q921; + ISDNLayer2* m_q921; SignallingInterface* m_iface; SigCircuitGroup* m_group; }; @@ -1263,8 +1274,10 @@ void SigDriver::handleEvent(SignallingEvent* event) // Route the call Message* m = ch->message("call.preroute",false,true); // Parameters to be copied to call.preroute - static String params = "caller,called,callername,format,formats,callernumtype,callernumplan,callerpres,callerscreening,callednumtype,callednumplan,inn"; + static String params = "caller,called,callername,format,formats,callernumtype,callernumplan,callerpres,callerscreening,callednumtype,callednumplan,inn,overlapped"; copySigMsgParams(*m,event,¶ms); + if (m->getBoolValue("overlapped") && !m->getValue("called")) + m->setParam("called","off-hook"); // TODO: Add call control parameter ? if (!ch->startRouter(m)) { ch->hangup("temporary-failure"); @@ -1640,8 +1653,10 @@ void SigDriver::initialize() link = new SigSS7Isup(*sect); break; case SigLink::IsdnPriNet: + case SigLink::IsdnBriNet: case SigLink::IsdnPriCpe: - link = new SigIsdn(*sect,type == SigLink::IsdnPriNet); + case SigLink::IsdnBriCpe: + link = new SigIsdn(*sect,(SigLink::Type)type); break; case SigLink::IsdnPriMon: link = new SigIsdnMonitor(*sect); @@ -1675,7 +1690,9 @@ void* SigParams::getObject(const String& name) const TokenDict SigLink::s_type[] = { {"ss7-isup", SS7Isup}, {"isdn-pri-net", IsdnPriNet}, + {"isdn-bri-net", IsdnBriNet}, {"isdn-pri-cpe", IsdnPriCpe}, + {"isdn-bri-cpe", IsdnBriCpe}, {"isdn-pri-mon", IsdnPriMon}, {0,0} }; @@ -1736,7 +1753,9 @@ bool SigLink::initialize(NamedList& params) minRxUnder = 25; break; case SigLink::IsdnPriNet: + case SigLink::IsdnBriNet: case SigLink::IsdnPriCpe: + case SigLink::IsdnBriCpe: minRxUnder = 2500; break; case SigLink::IsdnPriMon: @@ -2168,8 +2187,8 @@ unsigned int SigSS7Isup::setPointCode(const NamedList& sect) /** * SigIsdn */ -SigIsdn::SigIsdn(const char* name, bool net) - : SigLink(name,net ? IsdnPriNet : IsdnPriCpe), +SigIsdn::SigIsdn(const char* name, Type type) + : SigLink(name,type), m_q921(0), m_iface(0), m_group(0) @@ -2200,12 +2219,26 @@ bool SigIsdn::create(NamedList& params, String& error) return false; // Q921 - buildName(compName,"Q921"); - params.setParam("debugname",compName); - params.setParam("network",String::boolText(IsdnPriNet == type())); + params.setParam("network",String::boolText(0 != (MaskNet & type()))); params.setParam("print-frames",params.getValue("print-layer2PDU")); - m_q921 = new ISDNQ921(params,compName); - plugin.engine()->insert(m_q921); + switch (type()) { + case IsdnBriNet: + buildName(compName,"Q921Management"); + params.setParam("debugname",compName); + m_q921 = new ISDNQ921Management(params,compName,true); + break; + case IsdnBriCpe: + buildName(compName,"Q921Management"); + params.setParam("debugname",compName); + m_q921 = new ISDNQ921Management(params,compName,false); + break; + default: + buildName(compName,"Q921"); + params.setParam("debugname",compName); + m_q921 = new ISDNQ921(params,compName); + break; + } + m_q921->engine(plugin.engine()); // Q931 buildName(compName,"Q931"); @@ -2215,12 +2248,15 @@ bool SigIsdn::create(NamedList& params, String& error) plugin.engine()->insert(q931()); // Create links between components and enable them - m_q921->SignallingReceiver::attach(m_iface); + SignallingReceiver* recv = YOBJECT(SignallingReceiver,m_q921); + if (recv) + recv->attach(m_iface); m_iface->control(SignallingInterface::Enable); controller()->attach(m_group); - m_q921->ISDNLayer2::attach(q931()); + m_q921->attach(q931()); q931()->attach(m_q921); - m_q921->multipleFrame(true,false); + if (0 == (MaskBri & type())) + m_q921->multipleFrame(0,true,false); // Start thread if (!startThread(error,plugin.engine()->tickDefault())) @@ -2234,9 +2270,11 @@ bool SigIsdn::reload(NamedList& params) if (q931()) q931()->setDebug(params.getBoolValue("print-layer3PDU",false), params.getBoolValue("extended-debug",false)); +#if 0 if (m_q921) m_q921->setDebug(params.getBoolValue("print-layer2PDU",false), params.getBoolValue("extended-debug",false)); +#endif return true; }