SS7 work in proggress.
git-svn-id: http://voip.null.ro/svn/yate@831 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
f73c76515f
commit
9f0daebea3
|
@ -18,6 +18,9 @@ OBJS = engine.o address.o sigcall.o sigtran.o \
|
|||
interface.o layer2.o layer3.o \
|
||||
router.o sccp.o tcap.o \
|
||||
q921.o q931.o
|
||||
LIBD_VER:= libyatess7.so.@PACKAGE_VERSION@
|
||||
LIBD_DEV:= libyatess7.so
|
||||
LIBD:= ../../$(LIBD_VER) ../../$(LIBD_DEV)
|
||||
DOCS = classes.png
|
||||
|
||||
LOCALFLAGS =
|
||||
|
@ -32,7 +35,7 @@ exec_prefix = @exec_prefix@
|
|||
-include YateLocal.mak
|
||||
|
||||
.PHONY: all debug ddebug xdebug docs
|
||||
all: $(LIBS) $(PROGS)
|
||||
all: $(LIBS) $(LIBD) $(PROGS)
|
||||
|
||||
docs: $(DOCS)
|
||||
|
||||
|
@ -51,7 +54,7 @@ strip: all
|
|||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@-$(RM) $(PROGS) $(LIBS) $(OBJS) core 2>/dev/null
|
||||
@-$(RM) $(PROGS) $(LIBS) $(LIBD) $(OBJS) core 2>/dev/null
|
||||
|
||||
%.o: @srcdir@/%.cpp $(INCFILES)
|
||||
$(COMPILE) -c $<
|
||||
|
@ -59,10 +62,16 @@ clean:
|
|||
Makefile: @srcdir@/Makefile.in ../../config.status
|
||||
cd ../.. && ./config.status
|
||||
|
||||
yate-%: @srcdir@/main-%.cpp $(MKDEPS) libyatess7.a ../../libyate.so $(INCFILES)
|
||||
../../$(LIBD_VER): $(OBJS)
|
||||
$(LINK) -shared -o $@ -Wl,--soname=$(LIBD_VER) $^
|
||||
|
||||
../../$(LIBD_DEV): ../../$(LIBD_VER)
|
||||
cd ../.. && ln -sf $(LIBD_VER) $(LIBD_DEV)
|
||||
|
||||
yate-%: @srcdir@/main-%.cpp $(MKDEPS) $(LIBS) ../../libyate.so $(INCFILES)
|
||||
$(COMPILE) -o $@ $(LOCALFLAGS) $< $(LIBTHR) $(LDFLAGS) $(LOCALLIBS)
|
||||
|
||||
libyatess7.a: $(OBJS)
|
||||
$(LIBS): $(OBJS)
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
yate-ss7test: LOCALLIBS += -L. -lyatess7
|
||||
|
|
|
@ -35,7 +35,12 @@ void SignallingInterface::attach(SignallingReceiver* receiver)
|
|||
{
|
||||
if (m_receiver == receiver)
|
||||
return;
|
||||
Debug(engine(),DebugStub,"Please implement SignallingReceiver::attach()");
|
||||
Debug(engine(),DebugStub,"Please implement SignallingInterface::attach()");
|
||||
m_receiver = receiver;
|
||||
if (!receiver)
|
||||
return;
|
||||
insert(receiver);
|
||||
receiver->attach(this);
|
||||
}
|
||||
|
||||
bool SignallingInterface::control(Operation oper, NamedList* params)
|
||||
|
|
|
@ -49,7 +49,7 @@ bool SS7Layer2::control(Operation oper, NamedList* params)
|
|||
|
||||
SS7MTP2::SS7MTP2(unsigned int status)
|
||||
: Mutex(false), m_status(status),
|
||||
m_bsn(0), m_fsn(0), m_bib(false), m_fib(false)
|
||||
m_bsn(127), m_fsn(127), m_bib(true), m_fib(true)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ bool SS7MTP2::transmitMSU(const SS7MSU& msu)
|
|||
buf[1] = m_fib ? m_fsn | 0x80 : m_fsn;
|
||||
m_fsn = (m_fsn + 1) & 0x7f;
|
||||
m_queue.append(packet);
|
||||
return transmitPacket(*packet,false);
|
||||
return transmitPacket(*packet,false,SignallingInterface::SS7Msu);
|
||||
}
|
||||
|
||||
// Decode a received packet into signalling units
|
||||
|
@ -187,7 +187,7 @@ bool SS7MTP2::transmitLSSU(unsigned int status)
|
|||
buf[0] = m_bib ? m_bsn | 0x80 : m_bsn;
|
||||
buf[1] = m_fib ? m_fsn | 0x80 : m_fsn;
|
||||
DataBlock packet(buf,buf[2]+3,false);
|
||||
bool ok = transmitPacket(packet,true);
|
||||
bool ok = transmitPacket(packet,true,SignallingInterface::SS7Lssu);
|
||||
unlock();
|
||||
packet.clear(false);
|
||||
return ok;
|
||||
|
@ -203,7 +203,7 @@ bool SS7MTP2::transmitFISU()
|
|||
buf[0] = m_bib ? m_bsn | 0x80 : m_bsn;
|
||||
buf[1] = m_fib ? m_fsn | 0x80 : m_fsn;
|
||||
DataBlock packet(buf,3,false);
|
||||
bool ok = transmitPacket(packet,true);
|
||||
bool ok = transmitPacket(packet,true,SignallingInterface::SS7Fisu);
|
||||
unlock();
|
||||
packet.clear(false);
|
||||
return ok;
|
||||
|
|
|
@ -26,9 +26,13 @@ int main()
|
|||
ifdefs.addParam("card","wanpipe1");
|
||||
ifdefs.addParam("device","w1g1");
|
||||
SignallingInterface* iface = static_cast<SignallingInterface*>(SignallingFactory::build(ifdefs,&ifdefs));
|
||||
link->SignallingReceiver::attach(iface);
|
||||
if (iface) {
|
||||
link->SignallingReceiver::attach(iface);
|
||||
iface->control(SignallingInterface::Enable);
|
||||
}
|
||||
else
|
||||
Debug(DebugWarn,"Failed to create '%s'",ifdefs.c_str());
|
||||
engine->start("SS7test",Thread::Normal,20000);
|
||||
iface->control(SignallingInterface::Enable);
|
||||
Thread::msleep(100);
|
||||
delete engine;
|
||||
Output("SS7 library test stopped");
|
||||
|
|
|
@ -57,15 +57,17 @@ class SS7Router;
|
|||
class SS7TCAP;
|
||||
class ISDNLayer3;
|
||||
|
||||
// Macro to create a factory that builds a component by class name
|
||||
#define YSIGFACTORY(clas,iface) \
|
||||
class clas ## Factory : public SignallingFactory \
|
||||
{ \
|
||||
protected: \
|
||||
virtual void* create(const String& type, const NamedList& name) \
|
||||
{ return (name == #clas) ? static_cast<iface*>(new clas) : 0; } \
|
||||
{ return (type == #clas) ? static_cast<iface*>(new clas) : 0; } \
|
||||
}; \
|
||||
static clas ## Factory s_ ## clas ## Factory
|
||||
|
||||
// Macro to create a factory that calls a component's static create method
|
||||
#define YSIGFACTORY2(clas,iface) \
|
||||
class clas ## Factory : public SignallingFactory \
|
||||
{ \
|
||||
|
@ -368,6 +370,17 @@ public:
|
|||
TxUnderrun,
|
||||
};
|
||||
|
||||
/**
|
||||
* Packet types
|
||||
*/
|
||||
enum PacketType {
|
||||
Unknown = 0,
|
||||
SS7Fisu,
|
||||
SS7Lssu,
|
||||
SS7Msu,
|
||||
Q921
|
||||
};
|
||||
|
||||
/**
|
||||
* Destructor, stops and detaches the interface
|
||||
*/
|
||||
|
@ -403,9 +416,10 @@ protected:
|
|||
* @param packet Packet data to send
|
||||
* @param repeat Continuously send a copy of the packet while no other
|
||||
* data is available for transmission
|
||||
* @param type Type of the packet to send
|
||||
* @return True if the interface accepted the packet
|
||||
*/
|
||||
virtual bool transmitPacket(const DataBlock& packet, bool repeat) = 0;
|
||||
virtual bool transmitPacket(const DataBlock& packet, bool repeat, PacketType type) = 0;
|
||||
|
||||
/**
|
||||
* Push a valid received Signalling Packet up the protocol stack.
|
||||
|
@ -468,10 +482,11 @@ protected:
|
|||
* @param packet Packet data to send
|
||||
* @param repeat Continuously send a copy of the packet while no other
|
||||
* data is available for transmission
|
||||
* @param type Type of the packet to send
|
||||
* @return True if the interface accepted the packet
|
||||
*/
|
||||
inline bool transmitPacket(const DataBlock& packet, bool repeat)
|
||||
{ return m_interface && m_interface->transmitPacket(packet,repeat); }
|
||||
inline bool transmitPacket(const DataBlock& packet, bool repeat, SignallingInterface::PacketType type = SignallingInterface::Unknown)
|
||||
{ return m_interface && m_interface->transmitPacket(packet,repeat,type); }
|
||||
|
||||
/**
|
||||
* Process a Signalling Packet received by the interface
|
||||
|
|
|
@ -168,6 +168,10 @@ zapchan.yate: LOCALFLAGS = libypri.o -lpri
|
|||
wpchan.yate: libypri.o
|
||||
wpchan.yate: LOCALFLAGS = libypri.o -lpri
|
||||
|
||||
ysigchan.yate wpcard.yate: ../libyatess7.so
|
||||
ysigchan.yate wpcard.yate: LOCALFLAGS = -I../contrib/yss7
|
||||
ysigchan.yate wpcard.yate: LOCALLIBS = -lyatess7
|
||||
|
||||
h323chan.yate: LOCALFLAGS = -DPHAS_TEMPLATES -D_REENTRANT -DP_HAS_SEMAPHORES @H323_INC@
|
||||
h323chan.yate: LOCALLIBS = @H323_LIB@
|
||||
|
||||
|
@ -217,6 +221,11 @@ gtk2/gtk2mozilla.yate: MODFLAGS = $(MODRELAX)
|
|||
rmanager.yate: LOCALFLAGS = $(COREDUMP_INC)
|
||||
rmanager.yate: LOCALLIBS = $(COREDUMP_LIB)
|
||||
|
||||
../libyatess7.so:
|
||||
$(MAKE) -C ../contrib/yss7
|
||||
../contrib/yss7/libyatess7.a:
|
||||
$(MAKE) -C ../contrib/yss7
|
||||
|
||||
../contrib/iax/libiax.a:
|
||||
$(MAKE) -C ../contrib/iax
|
||||
|
||||
|
|
|
@ -0,0 +1,729 @@
|
|||
/**
|
||||
* wpcard.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Wanpipe PRI cards signalling and data driver
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2006 Null Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <yatess7.h>
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#error This module is not for Windows
|
||||
#else
|
||||
|
||||
extern "C" {
|
||||
|
||||
#define INVALID_HANDLE_VALUE (-1)
|
||||
#define __LINUX__
|
||||
#include <linux/if_wanpipe.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/wanpipe.h>
|
||||
#include <linux/wanpipe_cfg.h>
|
||||
#include <linux/sdla_bitstrm.h>
|
||||
|
||||
};
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define WP_HEADER 16
|
||||
|
||||
#define WP_RD_ERROR 0
|
||||
#define WP_RD_STAMP_LO 1
|
||||
#define WP_RD_STAMP_HI 2
|
||||
|
||||
#define WP_WR_TYPE 0
|
||||
#define WP_WR_FORCE 1
|
||||
|
||||
#define WP_ERR_FIFO 0x01
|
||||
#define WP_ERR_CRC 0x02
|
||||
#define WP_ERR_ABORT 0x04
|
||||
|
||||
#define MAX_PACKET 1200
|
||||
|
||||
using namespace TelEngine;
|
||||
namespace { // anonymous
|
||||
|
||||
class WpSigThread;
|
||||
|
||||
class WpInterface : public SignallingInterface
|
||||
{
|
||||
friend class WpSigThread;
|
||||
public:
|
||||
static void* create(const String& type, const NamedList& name);
|
||||
WpInterface(const char* card, const char* device);
|
||||
virtual ~WpInterface();
|
||||
virtual bool transmitPacket(const DataBlock& packet, bool repeat, PacketType type);
|
||||
virtual bool control(Operation oper, NamedList* params);
|
||||
protected:
|
||||
virtual void timerTick(const Time& when);
|
||||
void receiveAttempt();
|
||||
private:
|
||||
bool openSocket();
|
||||
Socket m_socket;
|
||||
String m_card;
|
||||
String m_device;
|
||||
WpSigThread* m_thread;
|
||||
bool m_received;
|
||||
};
|
||||
|
||||
class WpSigThread : public Thread
|
||||
{
|
||||
public:
|
||||
inline WpSigThread(WpInterface* iface)
|
||||
: m_interface(iface)
|
||||
{ }
|
||||
virtual ~WpSigThread();
|
||||
virtual void run();
|
||||
private:
|
||||
WpInterface* m_interface;
|
||||
};
|
||||
|
||||
YSIGFACTORY2(WpInterface,SignallingInterface);
|
||||
|
||||
//class WpSigFactory : public SignallingFactory
|
||||
|
||||
void* WpInterface::create(const String& type, const NamedList& name)
|
||||
{
|
||||
if (type == "WpInterface")
|
||||
return static_cast<WpInterface*>(new WpInterface(name.getValue("card"),name.getValue("device")));
|
||||
return 0;
|
||||
}
|
||||
|
||||
WpInterface::WpInterface(const char* card, const char* device)
|
||||
: m_card(card), m_device(device), m_thread(0), m_received(false)
|
||||
{
|
||||
Debug(DebugAll,"WpInterface::WpInterface('%s','%s') [%p]",
|
||||
card,device,this);
|
||||
}
|
||||
|
||||
WpInterface::~WpInterface()
|
||||
{
|
||||
Debug(DebugAll,"WpInterface::~WpInterface() [%p]",this);
|
||||
if (m_thread) {
|
||||
m_thread->cancel();
|
||||
while (m_thread)
|
||||
Thread::yield();
|
||||
// m_thread = 0;
|
||||
}
|
||||
m_socket.terminate();
|
||||
}
|
||||
|
||||
bool WpInterface::transmitPacket(const DataBlock& packet, bool repeat, PacketType type)
|
||||
{
|
||||
if (!m_socket.valid())
|
||||
return false;
|
||||
|
||||
int sz = WP_HEADER + packet.length();
|
||||
DataBlock data(0,WP_HEADER);
|
||||
data += packet;
|
||||
unsigned char* d = static_cast<unsigned char*>(data.data());
|
||||
if (repeat)
|
||||
d[WP_WR_FORCE] = 1;
|
||||
switch (type) {
|
||||
case SS7Fisu:
|
||||
d[WP_WR_TYPE] = WANOPT_SS7_FISU;
|
||||
break;
|
||||
case SS7Lssu:
|
||||
d[WP_WR_TYPE] = WANOPT_SS7_LSSU;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
int w = m_socket.send(data.data(),data.length());
|
||||
if (Socket::socketError() == w) {
|
||||
DDebug(toString(),DebugWarn,"Error on sending packet of %u bytes: %d: %s [%p]",
|
||||
packet.length(),m_socket.error(),::strerror(m_socket.error()),this);
|
||||
return false;
|
||||
}
|
||||
if (w != sz) {
|
||||
DDebug(toString(),DebugWarn,"Sent %d instead of %d bytes [%p]",w,sz,this);
|
||||
return false;
|
||||
}
|
||||
XDebug(toString(),DebugAll,"Successfully sent %d bytes packet [%p]",w,this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WpInterface::receiveAttempt()
|
||||
{
|
||||
if (!m_socket.valid())
|
||||
return;
|
||||
unsigned char buf[WP_HEADER + MAX_PACKET];
|
||||
int r = m_socket.recv(buf,sizeof(buf));
|
||||
if (Socket::socketError() == r) {
|
||||
if (m_socket.canRetry())
|
||||
return;
|
||||
DDebug(toString(),DebugWarn,"Error on reading packet: %d: %s [%p]",
|
||||
m_socket.error(),::strerror(m_socket.error()),this);
|
||||
return;
|
||||
}
|
||||
if (r > WP_HEADER) {
|
||||
r -= WP_HEADER;
|
||||
XDebug(toString(),DebugAll,"Received %d bytes packet [%p]",r,this);
|
||||
if (buf[WP_RD_ERROR]) {
|
||||
DDebug(toString(),DebugWarn,"Packet got error: %u [%p]",
|
||||
buf[WP_RD_ERROR],this);
|
||||
if (buf[WP_RD_ERROR] & WP_ERR_FIFO)
|
||||
notify(RxOverflow);
|
||||
if (buf[WP_RD_ERROR] & WP_ERR_CRC)
|
||||
notify(CksumError);
|
||||
if (buf[WP_RD_ERROR] & WP_ERR_ABORT)
|
||||
notify(AlignError);
|
||||
return;
|
||||
}
|
||||
m_received = true;
|
||||
DataBlock data(buf+WP_HEADER,r);
|
||||
receivedPacket(data);
|
||||
data.clear(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool WpInterface::openSocket()
|
||||
{
|
||||
Debug(DebugAll,"WpInterface::openSocket() [%p]",this);
|
||||
if (!m_socket.create(AF_WANPIPE,SOCK_RAW)) {
|
||||
Debug(DebugGoOn,"Wanpipe failed to create socket, error %d: %s",
|
||||
m_socket.error(),::strerror(m_socket.error()));
|
||||
return false;
|
||||
}
|
||||
// Bind to the card/interface
|
||||
struct wan_sockaddr_ll sa;
|
||||
memset(&sa,0,sizeof(struct wan_sockaddr_ll));
|
||||
::strncpy((char*)sa.sll_device,m_device.safe(),sizeof(sa.sll_device));
|
||||
::strncpy((char*)sa.sll_card,m_card.safe(),sizeof(sa.sll_card));
|
||||
sa.sll_protocol = htons(PVC_PROT);
|
||||
sa.sll_family=AF_WANPIPE;
|
||||
if (!m_socket.bind((struct sockaddr *)&sa, sizeof(sa))) {
|
||||
Debug(DebugGoOn,"Wanpipe failed to bind socket, error %d: %s",
|
||||
m_socket.error(),::strerror(m_socket.error()));
|
||||
m_socket.terminate();
|
||||
return false;
|
||||
}
|
||||
if (!m_socket.setBlocking(false)) {
|
||||
Debug(DebugGoOn,"Wanpipe failed to set socket non-blocking, error %d: %s",
|
||||
m_socket.error(),::strerror(m_socket.error()));
|
||||
m_socket.terminate();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WpInterface::control(Operation oper, NamedList* params)
|
||||
{
|
||||
switch (oper) {
|
||||
case Enable:
|
||||
if (!(m_socket.valid() || openSocket()))
|
||||
return false;
|
||||
if (!m_thread)
|
||||
m_thread = new WpSigThread(this);
|
||||
return m_thread->startup();
|
||||
case Query:
|
||||
return m_socket.valid() && m_thread && m_thread->running();
|
||||
default:
|
||||
return SignallingInterface::control(oper,params);
|
||||
}
|
||||
}
|
||||
|
||||
void WpInterface::timerTick(const Time& when)
|
||||
{
|
||||
if (m_received)
|
||||
m_received = false;
|
||||
else {
|
||||
XDebug(toString(),DebugAll,"Not received any packets in the last tick [%p]",this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
WpSigThread::~WpSigThread()
|
||||
{
|
||||
Debug(DebugAll,"WpSigThread::~WpSigThread() [%p]",this);
|
||||
if (m_interface)
|
||||
m_interface->m_thread = 0;
|
||||
}
|
||||
|
||||
void WpSigThread::run()
|
||||
{
|
||||
Debug(DebugAll,"WpSigThread::run() [%p]",this);
|
||||
for (;;) {
|
||||
Thread::yield(true);
|
||||
if (m_interface)
|
||||
m_interface->receiveAttempt();
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
class WpChan;
|
||||
|
||||
class WpSpan : public PriSpan, public Thread
|
||||
{
|
||||
friend class WpData;
|
||||
friend class WpDriver;
|
||||
public:
|
||||
virtual ~WpSpan();
|
||||
virtual void run();
|
||||
inline int overRead() const
|
||||
{ return m_overRead; }
|
||||
|
||||
private:
|
||||
WpSpan(struct pri *_pri, PriDriver* driver, int span, int first, int chans, int dchan, Configuration& cfg, const String& sect, HANDLE fd);
|
||||
HANDLE m_fd;
|
||||
WpData *m_data;
|
||||
int m_overRead;
|
||||
};
|
||||
|
||||
class WpChan : public PriChan
|
||||
{
|
||||
friend class WpSource;
|
||||
friend class WpConsumer;
|
||||
friend class WpData;
|
||||
public:
|
||||
WpChan(const PriSpan *parent, int chan, unsigned int bufsize);
|
||||
virtual ~WpChan();
|
||||
virtual bool openData(const char* format, int echoTaps);
|
||||
|
||||
private:
|
||||
WpSource* m_wp_s;
|
||||
WpConsumer* m_wp_c;
|
||||
};
|
||||
|
||||
class WpData : public Thread
|
||||
{
|
||||
public:
|
||||
WpData(WpSpan* span, const char* card, const char* device, Configuration& cfg, const String& sect);
|
||||
~WpData();
|
||||
virtual void run();
|
||||
private:
|
||||
WpSpan* m_span;
|
||||
HANDLE m_fd;
|
||||
unsigned char* m_buffer;
|
||||
WpChan **m_chans;
|
||||
int m_samples;
|
||||
bool m_swap;
|
||||
unsigned char m_rdError;
|
||||
unsigned char m_wrError;
|
||||
};
|
||||
|
||||
#define MAX_DATA_ERRORS 250
|
||||
|
||||
static int wp_recv(HANDLE fd, void *buf, int buflen, int flags = 0)
|
||||
{
|
||||
int r = ::recv(fd,buf,buflen,flags);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int wp_send(HANDLE fd, void *buf, int buflen, int flags = 0)
|
||||
{
|
||||
int w = ::send(fd,buf,buflen,flags);
|
||||
return w;
|
||||
}
|
||||
|
||||
static int wp_read(struct pri *pri, void *buf, int buflen)
|
||||
{
|
||||
buflen -= 2;
|
||||
int sz = buflen+WP_HEADER;
|
||||
char *tmp = (char*)::calloc(sz,1);
|
||||
XDebug("wp_read",DebugAll,"pre buf=%p len=%d tmp=%p sz=%d",buf,buflen,tmp,sz);
|
||||
int r = wp_recv((HANDLE)::pri_fd(pri),tmp,sz,MSG_NOSIGNAL);
|
||||
XDebug("wp_read",DebugAll,"post r=%d",r);
|
||||
if (r > 0) {
|
||||
r -= WP_HEADER;
|
||||
if ((r > 0) && (r <= buflen)) {
|
||||
WpSpan* span = (WpSpan*)::pri_get_userdata(pri);
|
||||
if (span)
|
||||
r -= span->overRead();
|
||||
DDebug("wp_read",DebugAll,"Transferring %d for %p",r,pri);
|
||||
::memcpy(buf,tmp+WP_HEADER,r);
|
||||
r += 2;
|
||||
}
|
||||
}
|
||||
::free(tmp);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int wp_write(struct pri *pri, void *buf, int buflen)
|
||||
{
|
||||
buflen -= 2;
|
||||
int sz = buflen+WP_HEADER;
|
||||
char *tmp = (char*)::calloc(sz,1);
|
||||
::memcpy(tmp+WP_HEADER,buf,buflen);
|
||||
XDebug("wp_write",DebugAll,"pre buf=%p len=%d tmp=%p sz=%d",buf,buflen,tmp,sz);
|
||||
int w = wp_send((HANDLE)::pri_fd(pri),tmp,sz,0);
|
||||
XDebug("wp_write",DebugAll,"post w=%d",w);
|
||||
if (w > 0) {
|
||||
w -= WP_HEADER;
|
||||
DDebug("wp_write",DebugAll,"Transferred %d for %p",w,pri);
|
||||
w += 2;
|
||||
}
|
||||
::free(tmp);
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool wp_select(HANDLE fd,int samp,bool* errp = 0)
|
||||
{
|
||||
fd_set rdfds;
|
||||
fd_set errfds;
|
||||
FD_ZERO(&rdfds);
|
||||
FD_SET(fd,&rdfds);
|
||||
FD_ZERO(&errfds);
|
||||
FD_SET(fd,&errfds);
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = samp*125;
|
||||
int sel = ::select(fd+1, &rdfds, NULL, errp ? &errfds : NULL, &tv);
|
||||
if (sel < 0)
|
||||
Debug(DebugWarn,"Wanpipe select failed on %d: error %d: %s",
|
||||
fd,errno,::strerror(errno));
|
||||
if (errp)
|
||||
*errp = FD_ISSET(fd,&errfds);
|
||||
return FD_ISSET(fd,&rdfds);
|
||||
}
|
||||
|
||||
void wp_close(HANDLE fd)
|
||||
{
|
||||
if (fd == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
::close(fd);
|
||||
}
|
||||
|
||||
static HANDLE wp_open(const char* card, const char* device)
|
||||
{
|
||||
DDebug(DebugAll,"wp_open('%s','%s')",card,device);
|
||||
if (null(card) || null(device))
|
||||
return INVALID_HANDLE_VALUE;
|
||||
HANDLE fd = ::socket(AF_WANPIPE, SOCK_RAW, 0);
|
||||
if (fd == INVALID_HANDLE_VALUE) {
|
||||
Debug(DebugGoOn,"Wanpipe failed to create socket: error %d: %s",
|
||||
errno,::strerror(errno));
|
||||
return fd;
|
||||
}
|
||||
// Bind to the card/interface
|
||||
struct wan_sockaddr_ll sa;
|
||||
memset(&sa,0,sizeof(struct wan_sockaddr_ll));
|
||||
::strncpy((char*)sa.sll_device,device,sizeof(sa.sll_device));
|
||||
::strncpy((char*)sa.sll_card,card,sizeof(sa.sll_card));
|
||||
sa.sll_protocol = htons(PVC_PROT);
|
||||
sa.sll_family=AF_WANPIPE;
|
||||
if (::bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
||||
Debug(DebugGoOn,"Wanpipe failed to bind %d: error %d: %s",
|
||||
fd,errno,::strerror(errno));
|
||||
wp_close(fd);
|
||||
fd = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static struct pri* wp_create(const char* card, const char* device, int nettype, int swtype)
|
||||
{
|
||||
DDebug(DebugAll,"wp_create('%s','%s',%d,%d)",card,device,nettype,swtype);
|
||||
HANDLE fd = wp_open(card,device);
|
||||
if (fd == INVALID_HANDLE_VALUE)
|
||||
return 0;
|
||||
struct pri* p = ::pri_new_cb((int)fd, nettype, swtype, wp_read, wp_write, 0);
|
||||
if (!p)
|
||||
wp_close(fd);
|
||||
return p;
|
||||
}
|
||||
|
||||
WpSpan::WpSpan(struct pri *_pri, PriDriver* driver, int span, int first, int chans, int dchan, Configuration& cfg, const String& sect, HANDLE fd)
|
||||
: PriSpan(_pri,driver,span,first,chans,dchan,cfg,sect), Thread("WpSpan"),
|
||||
m_fd(fd), m_data(0), m_overRead(0)
|
||||
{
|
||||
Debug(&__plugin,DebugAll,"WpSpan::WpSpan() [%p]",this);
|
||||
m_overRead = cfg.getIntValue(sect,"overread",cfg.getIntValue("general","overread",0));
|
||||
}
|
||||
|
||||
WpSpan::~WpSpan()
|
||||
{
|
||||
Debug(&__plugin,DebugAll,"WpSpan::~WpSpan() [%p]",this);
|
||||
m_ok = false;
|
||||
delete m_data;
|
||||
wp_close(m_fd);
|
||||
m_fd = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
void WpSpan::run()
|
||||
{
|
||||
Debug(&__plugin,DebugAll,"WpSpan::run() [%p]",this);
|
||||
for (;;) {
|
||||
bool rd = wp_select(m_fd,5); // 5 bytes per smallest q921 frame
|
||||
Thread::check();
|
||||
runEvent(!rd);
|
||||
}
|
||||
}
|
||||
|
||||
WpSource::WpSource(WpChan *owner, const char* format, unsigned int bufsize)
|
||||
: PriSource(owner,format,bufsize),
|
||||
m_bufpos(0)
|
||||
{
|
||||
Debug(m_owner,DebugAll,"WpSource::WpSource(%p) [%p]",owner,this);
|
||||
static_cast<WpChan*>(m_owner)->m_wp_s = this;
|
||||
}
|
||||
|
||||
WpSource::~WpSource()
|
||||
{
|
||||
Debug(m_owner,DebugAll,"WpSource::~WpSource() [%p]",this);
|
||||
static_cast<WpChan*>(m_owner)->m_wp_s = 0;
|
||||
}
|
||||
|
||||
void WpSource::put(unsigned char val)
|
||||
{
|
||||
((char*)m_buffer.data())[m_bufpos] = val;
|
||||
if (++m_bufpos >= m_buffer.length()) {
|
||||
m_bufpos = 0;
|
||||
Forward(m_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
WpConsumer::WpConsumer(WpChan *owner, const char* format, unsigned int bufsize)
|
||||
: PriConsumer(owner,format,bufsize), Fifo(2*bufsize)
|
||||
{
|
||||
Debug(m_owner,DebugAll,"WpConsumer::WpConsumer(%p) [%p]",owner,this);
|
||||
static_cast<WpChan*>(m_owner)->m_wp_c = this;
|
||||
}
|
||||
|
||||
WpConsumer::~WpConsumer()
|
||||
{
|
||||
Debug(m_owner,DebugAll,"WpConsumer::~WpConsumer() [%p]",this);
|
||||
static_cast<WpChan*>(m_owner)->m_wp_c = 0;
|
||||
if (m_overruns.events())
|
||||
Debug(m_owner,DebugMild,"Consumer had %u overruns (%lu bytes)",
|
||||
m_overruns.events(),m_overruns.bytes());
|
||||
}
|
||||
|
||||
void WpConsumer::Consume(const DataBlock &data, unsigned long tStamp)
|
||||
{
|
||||
unsigned int err = put((const unsigned char*)data.data(),data.length());
|
||||
if (err)
|
||||
m_overruns.update(err);
|
||||
}
|
||||
|
||||
static Thread::Priority cfgPriority(Configuration& cfg, const String& sect)
|
||||
{
|
||||
String tmp(cfg.getValue(sect,"thread"));
|
||||
if (tmp.null())
|
||||
tmp = cfg.getValue("general","thread");
|
||||
return Thread::priority(tmp);
|
||||
}
|
||||
|
||||
WpData::WpData(WpSpan* span, const char* card, const char* device, Configuration& cfg, const String& sect)
|
||||
: Thread("WpData",cfgPriority(cfg,sect)), m_span(span), m_fd(INVALID_HANDLE_VALUE),
|
||||
m_buffer(0), m_chans(0), m_samples(50), m_swap(true), m_rdError(0), m_wrError(0)
|
||||
{
|
||||
Debug(&__plugin,DebugAll,"WpData::WpData(%p,'%s','%s') [%p]",
|
||||
span,card,device,this);
|
||||
HANDLE fd = wp_open(card,device);
|
||||
if (fd != INVALID_HANDLE_VALUE) {
|
||||
m_fd = fd;
|
||||
m_span->m_data = this;
|
||||
}
|
||||
|
||||
if (m_span->chans() == 24)
|
||||
// for T1 we typically have 23 B channels so we adjust the number
|
||||
// of samples to get multiple of 32 bit and also reduce overhead
|
||||
m_samples = 64;
|
||||
|
||||
m_samples = cfg.getIntValue("general","samples",m_samples);
|
||||
m_samples = cfg.getIntValue(sect,"samples",m_samples);
|
||||
m_swap = cfg.getBoolValue("general","bitswap",m_swap);
|
||||
m_swap = cfg.getBoolValue(sect,"bitswap",m_swap);
|
||||
}
|
||||
|
||||
WpData::~WpData()
|
||||
{
|
||||
Debug(&__plugin,DebugAll,"WpData::~WpData() [%p]",this);
|
||||
m_span->m_data = 0;
|
||||
wp_close(m_fd);
|
||||
m_fd = INVALID_HANDLE_VALUE;
|
||||
if (m_buffer)
|
||||
::free(m_buffer);
|
||||
if (m_chans)
|
||||
delete[] m_chans;
|
||||
}
|
||||
|
||||
void WpData::run()
|
||||
{
|
||||
Debug(&__plugin,DebugAll,"WpData::run() [%p]",this);
|
||||
int bchans = m_span->bchans();
|
||||
int buflen = m_samples*bchans;
|
||||
int sz = buflen+WP_HEADER;
|
||||
m_buffer = (unsigned char*)::malloc(sz);
|
||||
// Build a compacted list of allocated B channels
|
||||
m_chans = new WpChan* [bchans];
|
||||
int b = 0;
|
||||
for (int n = 0; n < bchans; n++) {
|
||||
while (!m_span->m_chans[b])
|
||||
b++;
|
||||
m_chans[n] = static_cast<WpChan*>(m_span->m_chans[b++]);
|
||||
DDebug(&__plugin,DebugInfo,"wpdata ch[%d]=%d (%p)",n,m_chans[n]->chan(),m_chans[n]);
|
||||
}
|
||||
while (m_span && (m_fd >= 0)) {
|
||||
Thread::check();
|
||||
bool oob = false;
|
||||
bool rd = wp_select(m_fd,m_samples,&oob);
|
||||
if (oob) {
|
||||
XDebug("wpdata_recv_oob",DebugAll,"pre buf=%p len=%d sz=%d",m_buffer,buflen,sz);
|
||||
int r = wp_recv(m_fd,m_buffer,sz,MSG_OOB);
|
||||
XDebug("wpdata_recv_oob",DebugAll,"post r=%d",r);
|
||||
if (r > 0)
|
||||
Debug(&__plugin,DebugInfo,"Read %d bytes of OOB data on span %d [%p]",
|
||||
r,m_span->span(),this);
|
||||
}
|
||||
|
||||
if (rd) {
|
||||
m_buffer[0] = 0;
|
||||
XDebug("wpdata_recv",DebugAll,"pre buf=%p len=%d sz=%d",m_buffer,buflen,sz);
|
||||
int r = wp_recv(m_fd,m_buffer,sz,0/*MSG_NOSIGNAL*/);
|
||||
XDebug("wpdata_recv",DebugAll,"post r=%d",r);
|
||||
r -= WP_HEADER;
|
||||
if (m_buffer[0]) {
|
||||
if (!m_rdError)
|
||||
Debug(&__plugin,DebugWarn,"Read data error 0x%02X on span %d [%p]",
|
||||
m_buffer[0],m_span->span(),this);
|
||||
if (m_rdError < MAX_DATA_ERRORS)
|
||||
m_rdError++;
|
||||
}
|
||||
else
|
||||
m_rdError = 0;
|
||||
// We should have read N bytes for each B channel
|
||||
if ((r > 0) && ((r % bchans) == 0)) {
|
||||
r /= bchans;
|
||||
const unsigned char* dat = m_buffer + WP_HEADER;
|
||||
m_span->lock();
|
||||
for (int n = r; n > 0; n--)
|
||||
for (b = 0; b < bchans; b++) {
|
||||
WpSource *s = m_chans[b]->m_wp_s;
|
||||
if (s)
|
||||
s->put(m_swap ? PriDriver::bitswap(*dat) : *dat);
|
||||
dat++;
|
||||
}
|
||||
m_span->unlock();
|
||||
}
|
||||
int wr = m_samples;
|
||||
::memset(m_buffer,0,WP_HEADER);
|
||||
unsigned char* dat = m_buffer + WP_HEADER;
|
||||
m_span->lock();
|
||||
for (int n = wr; n > 0; n--) {
|
||||
for (b = 0; b < bchans; b++) {
|
||||
WpConsumer *c = m_chans[b]->m_wp_c;
|
||||
unsigned char d = c ? c->get() : 0xff;
|
||||
*dat++ = m_swap ? PriDriver::bitswap(d) : d;
|
||||
}
|
||||
}
|
||||
m_span->unlock();
|
||||
wr = (wr * bchans) + WP_HEADER;
|
||||
XDebug("wpdata_send",DebugAll,"pre buf=%p len=%d sz=%d",m_buffer,wr,sz);
|
||||
int w = wp_send(m_fd,m_buffer,wr,MSG_DONTWAIT);
|
||||
XDebug("wpdata_send",DebugAll,"post w=%d",w);
|
||||
if (w != wr) {
|
||||
if (!m_wrError)
|
||||
Debug(&__plugin,DebugWarn,"Wrote %d data bytes instead of %d on span %d [%p]",
|
||||
w,wr,m_span->span(),this);
|
||||
if (m_wrError < MAX_DATA_ERRORS)
|
||||
m_wrError++;
|
||||
}
|
||||
else
|
||||
m_wrError = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WpChan::WpChan(const PriSpan *parent, int chan, unsigned int bufsize)
|
||||
: PriChan(parent,chan,bufsize), m_wp_s(0), m_wp_c(0)
|
||||
{
|
||||
}
|
||||
|
||||
WpChan::~WpChan()
|
||||
{
|
||||
closeData();
|
||||
}
|
||||
|
||||
bool WpChan::openData(const char* format, int echoTaps)
|
||||
{
|
||||
if (echoTaps)
|
||||
Debug(DebugWarn,"Echo cancellation requested but not available in wanpipe");
|
||||
m_span->lock();
|
||||
setSource(new WpSource(this,format,m_bufsize));
|
||||
getSource()->deref();
|
||||
setConsumer(new WpConsumer(this,format,m_bufsize));
|
||||
getConsumer()->deref();
|
||||
m_span->unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
PriSpan* WpDriver::createSpan(PriDriver* driver, int span, int first, int chans, Configuration& cfg, const String& sect)
|
||||
{
|
||||
Debug(this,DebugAll,"WpDriver::createSpan(%p,%d,%d,%d) [%p]",driver,span,first,chans,this);
|
||||
int netType = -1;
|
||||
int swType = -1;
|
||||
int dchan = -1;
|
||||
netParams(cfg,sect,chans,&netType,&swType,&dchan);
|
||||
String card;
|
||||
card << "wanpipe" << span;
|
||||
card = cfg.getValue(sect,"card",card);
|
||||
String dev;
|
||||
dev << "w" << span << "g1";
|
||||
pri* p = wp_create(card,cfg.getValue(sect,"dgroup",dev),netType,swType);
|
||||
if (!p)
|
||||
return 0;
|
||||
WpSpan *ps = new WpSpan(p,driver,span,first,chans,dchan,cfg,sect,(HANDLE)::pri_fd(p));
|
||||
ps->startup();
|
||||
dev.clear();
|
||||
dev << "w" << span << "g2";
|
||||
WpData* dat = new WpData(ps,card,cfg.getValue(sect,"bgroup",dev),cfg,sect);
|
||||
dat->startup();
|
||||
return ps;
|
||||
}
|
||||
|
||||
PriChan* WpDriver::createChan(const PriSpan* span, int chan, unsigned int bufsize)
|
||||
{
|
||||
Debug(this,DebugAll,"WpDriver::createChan(%p,%d,%u) [%p]",span,chan,bufsize,this);
|
||||
return new WpChan(span,chan,bufsize);
|
||||
}
|
||||
|
||||
WpDriver::WpDriver()
|
||||
: PriDriver("wp")
|
||||
{
|
||||
Output("Loaded module Wanpipe");
|
||||
}
|
||||
|
||||
WpDriver::~WpDriver()
|
||||
{
|
||||
Output("Unloading module Wanpipe");
|
||||
}
|
||||
|
||||
void WpDriver::initialize()
|
||||
{
|
||||
Output("Initializing module Wanpipe");
|
||||
init("wpchan");
|
||||
}
|
||||
#endif // 0
|
||||
|
||||
}; // anonymous namespace
|
||||
|
||||
#endif /* _WINDOWS */
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* ysigchan.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Signalling Channel
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2006 Null Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <yatephone.h>
|
||||
#include <yatess7.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
namespace { // anonymous
|
||||
|
||||
class SigDriver : public Driver
|
||||
{
|
||||
public:
|
||||
SigDriver();
|
||||
~SigDriver();
|
||||
virtual void initialize();
|
||||
virtual bool msgExecute(Message& msg, String& dest);
|
||||
inline SignallingEngine* engine() const
|
||||
{ return m_engine; }
|
||||
private:
|
||||
void buildStack();
|
||||
SignallingEngine* m_engine;
|
||||
};
|
||||
|
||||
static SigDriver plugin;
|
||||
|
||||
bool SigDriver::msgExecute(Message& msg, String& dest)
|
||||
{
|
||||
Debug(this,DebugStub,"Signalling call!");
|
||||
return false;
|
||||
}
|
||||
|
||||
SigDriver::SigDriver()
|
||||
: Driver("ss7","fixchans"), m_engine(0)
|
||||
{
|
||||
Output("Loaded module Signalling Channel");
|
||||
}
|
||||
|
||||
SigDriver::~SigDriver()
|
||||
{
|
||||
Output("Unloading module Signalling Channel");
|
||||
}
|
||||
|
||||
void SigDriver::buildStack()
|
||||
{
|
||||
m_engine = new SignallingEngine;
|
||||
NamedList ifdefs("WpInterface");
|
||||
ifdefs.addParam("card","wanpipe9");
|
||||
ifdefs.addParam("device","w9g1");
|
||||
SignallingInterface* iface = static_cast<SignallingInterface*>(SignallingFactory::build(ifdefs,&ifdefs));
|
||||
if (!iface) {
|
||||
Debug(this,DebugGoOn,"Failed to create interface '%s'",ifdefs.c_str());
|
||||
return;
|
||||
}
|
||||
SS7MTP2* link = new SS7MTP2;
|
||||
m_engine->insert(link);
|
||||
link->SignallingReceiver::attach(iface);
|
||||
m_engine->start("SS7test",Thread::Normal,20000);
|
||||
iface->control(SignallingInterface::Enable);
|
||||
}
|
||||
|
||||
void SigDriver::initialize()
|
||||
{
|
||||
Output("Initializing module Signalling Channel");
|
||||
setup();
|
||||
if (!m_engine)
|
||||
buildStack();
|
||||
}
|
||||
|
||||
}; // anonymous namespace
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
Loading…
Reference in New Issue