mirror of https://github.com/sipwise/sems.git
initial version. git-svn-id: http://svn.berlios.de/svnroot/repos/sems/trunk@1137 8eb893ce-cfd4-0310-b710-fb5ebe64c474sayer/1.4-spce2.6
parent
841b2f663f
commit
c97003fc76
@ -0,0 +1,147 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "globals.h"
|
||||
#include "GWSession.h"
|
||||
#include "AmMediaProcessor.h"
|
||||
#include "AmApi.h"
|
||||
#include "AmUAC.h"
|
||||
#include "AmSession.h"
|
||||
#include "AmSessionContainer.h"
|
||||
#include "ampi/UACAuthAPI.h"
|
||||
#include "GatewayFactory.h"
|
||||
#include "log.h"
|
||||
#include"mISDNChannel.h"
|
||||
|
||||
GWSession::GWSession(const string& auth_realm, const string& auth_user, const string& auth_pwd) :
|
||||
credentials(auth_realm, auth_user, auth_pwd) {
|
||||
DBG("new GWSession@%p\n", this);
|
||||
}
|
||||
UACAuthCred* GWSession::getCredentials() {
|
||||
return &credentials;
|
||||
}
|
||||
|
||||
void GWSession::setOtherLeg(AmAudio *otherleg) {
|
||||
m_OtherLeg=otherleg;
|
||||
}
|
||||
void GWSession::onInvite(const AmSipRequest& req) {
|
||||
DBG("GWSession::onInvite\n");
|
||||
// rtp_str.setMonitorRTPTimeout(false);
|
||||
invite_req=req;
|
||||
return;
|
||||
}
|
||||
|
||||
void GWSession::onSessionStart(const AmSipRequest& req) {
|
||||
DBG("GWSession::onSessionStart\n");
|
||||
try {
|
||||
string sdp_reply;
|
||||
acceptAudio(req.body,req.hdrs,&sdp_reply);
|
||||
if(dlg.reply(req,200,"OK Isdn side state is: CONNECTED", "application/sdp",sdp_reply) != 0)
|
||||
throw AmSession::Exception(500,"could not send response");
|
||||
}catch(const AmSession::Exception& e){
|
||||
ERROR("%i %s\n",e.code,e.reason.c_str());
|
||||
setStopped();
|
||||
AmSipDialog::reply_error(req,e.code,e.reason);
|
||||
return;
|
||||
}
|
||||
DBG("GWSession::onSessionStart Setting Audio\n");
|
||||
setInOut((AmAudio *)(m_OtherLeg),(AmAudio *)(m_OtherLeg));
|
||||
AmSession::onSessionStart(req);
|
||||
AmMediaProcessor::instance()->addSession(this, callgroup);
|
||||
}
|
||||
void GWSession::onSessionStart(const AmSipReply& reply) {
|
||||
DBG("GWSession::onSessionStart(reply)\n");
|
||||
DBG("calling ((mISDNChannel*)m_otherleg)->accept();\n");
|
||||
int ret=((mISDNChannel*)m_OtherLeg)->accept();
|
||||
DBG("GWSession::onSessionStart Setting Audio\n");
|
||||
setInOut((AmAudio *)(m_OtherLeg),(AmAudio *)(m_OtherLeg));
|
||||
AmSession::onSessionStart(reply);
|
||||
|
||||
}
|
||||
|
||||
void GWSession::onBye(const AmSipRequest& req) {
|
||||
DBG("GWSession::onBye\n");
|
||||
int ret=((mISDNChannel*)m_OtherLeg)->hangup();
|
||||
AmSession::onBye(req);
|
||||
|
||||
}
|
||||
void GWSession::onCancel() {
|
||||
DBG("GWSession::onCancel\n");
|
||||
int ret=((mISDNChannel*)m_OtherLeg)->hangup();
|
||||
AmSession::onCancel();
|
||||
|
||||
}
|
||||
|
||||
GWSession::~GWSession()
|
||||
{
|
||||
INFO("destroying GWSession!\n");
|
||||
}
|
||||
|
||||
// we just need a hack this function for INVITE as orginal executes onSessionStart imediately after OnInvite
|
||||
void GWSession::onSipRequest(const AmSipRequest& req)
|
||||
{
|
||||
DBG("GWSession::onSipRequest check 1\n");
|
||||
if(req.method == "INVITE"){
|
||||
dlg.updateStatus(req);
|
||||
onInvite(req);
|
||||
} else {
|
||||
DBG("GWSession::onSipRequest calling parent\n");
|
||||
AmSession::onSipRequest(req);
|
||||
}
|
||||
}
|
||||
|
||||
void GWSession::onSipReply(const AmSipReply& reply) {
|
||||
int status = dlg.getStatus();
|
||||
DBG("GWSession::onSipReply: code = %i, reason = %s\n, status = %i\n", reply.code,reply.reason.c_str(),dlg.getStatus());
|
||||
if((dlg.getStatus()==AmSipDialog::Pending)&&(reply.code==183)) { onProgress(reply); }
|
||||
if((dlg.getStatus()==AmSipDialog::Pending)&&(reply.code>=300)) {
|
||||
int ret=((mISDNChannel*)m_OtherLeg)->hangup();
|
||||
}
|
||||
DBG("GWSession::onSipReply calling parent\n");
|
||||
AmSession::onSipReply(reply);
|
||||
}
|
||||
|
||||
void GWSession::on_stop() {
|
||||
DBG("GWSession::on_stop\n");
|
||||
if (!getDetached())
|
||||
AmMediaProcessor::instance()->clearSession(this);
|
||||
else
|
||||
clearAudio();
|
||||
}
|
||||
void GWSession::onRinging(const AmSipReply& reply) {
|
||||
DBG("GWSession::onRinging\n");
|
||||
//TODO
|
||||
}
|
||||
void GWSession::onProgress(const AmSipReply& reply){
|
||||
DBG("GWSession::onProgress\n");
|
||||
//TODO
|
||||
}
|
||||
|
||||
GWSession* GWSession::CallFromOutside(std::string &fromnumber, std::string &tonumber, int backend, AmAudio* chan) {
|
||||
AmArg* c_args = new AmArg();
|
||||
std::string user=gwconf.getParameter("auth_user","");
|
||||
std::string r_uri="sip:@";
|
||||
r_uri.insert(4,tonumber);r_uri.append(gwconf.getParameter("calleddomain",""));
|
||||
std::string from="sip:@";
|
||||
from.insert(4,fromnumber);from.append(gwconf.getParameter("callerdomain",""));
|
||||
std::string from_uri="sip:@";
|
||||
from_uri.insert(4,fromnumber);from_uri.append(gwconf.getParameter("callerdomain",""));
|
||||
std::string to="sip:@";
|
||||
to.insert(4,tonumber);to.append(gwconf.getParameter("calleddomain",""));
|
||||
DBG ("GWSession::CallFromOutside user=%s r_uri=%s from=%s to=%s\n",user.c_str(),r_uri.c_str(),from.c_str(),to.c_str());
|
||||
AmSession* s = AmUAC::dialout(user, //user
|
||||
"gateway", //app_name
|
||||
r_uri, //r_uri
|
||||
from, //from
|
||||
from_uri , //from_uri
|
||||
to, //to
|
||||
string(""), // local_tag (callid)
|
||||
string(""), // headers
|
||||
c_args);
|
||||
DBG("GWCall::CallFromOutside session=%p\n",s);
|
||||
//this is static function so we dont that 'this' pointer yet as GWcall object is created in factory::onInvite. so we use pointer returned by dialout
|
||||
((GWSession*)s)->setOtherLeg(chan);
|
||||
//for early media?
|
||||
// ((GWSession*)s)->setInOut(chan,chan);
|
||||
return (GWSession *)s;
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
#ifndef _ISDNGATEWAYDIALOG_H_
|
||||
#define _ISDNGATEWAYDIALOG_H_
|
||||
|
||||
#include "AmApi.h"
|
||||
#include "AmSession.h"
|
||||
#include "ampi/UACAuthAPI.h"
|
||||
|
||||
class GWSession : public AmSession, public CredentialHolder
|
||||
{
|
||||
public:
|
||||
GWSession(const string& auth_realm, const string& auth_user, const string& auth_pwd);
|
||||
~GWSession();
|
||||
|
||||
inline UACAuthCred* getCredentials(); //auth interface
|
||||
AmSipRequest invite_req;
|
||||
|
||||
static GWSession* CallFromOutside(std::string &fromnumber, std::string &tonumber, int backend, AmAudio* device);
|
||||
void setOtherLeg(AmAudio *otherleg);
|
||||
void onProgress(const AmSipReply& reply);
|
||||
|
||||
//Parent methods
|
||||
//virtual void process(AmEvent* ev);
|
||||
//virtual AmPayloadProviderInterface* getPayloadProvider();
|
||||
//virtual void negotiate(const string& sdp_body,
|
||||
//virtual void onDtmf(int event, int duration);
|
||||
//virtual void onStart(){}
|
||||
void onInvite(const AmSipRequest& req);
|
||||
void onCancel();
|
||||
void onSessionStart(const AmSipRequest& req);
|
||||
void onSessionStart(const AmSipReply& reply);
|
||||
//virtual void onEarlySessionStart(const AmSipReply& reply){}
|
||||
void onRinging(const AmSipReply& reply);
|
||||
void onBye(const AmSipRequest& req);
|
||||
//virtual void onSipEvent(AmSipEvent* sip_ev);
|
||||
void onSipRequest(const AmSipRequest& req);
|
||||
void onSipReply(const AmSipReply& reply);
|
||||
//virtual void onRtpTimeout();
|
||||
//virtual void onSendRequest(const string& method, const string& content_type, const string& body, string& hdrs, int flags, unsigned int cseq);
|
||||
//virtual void onSendReply(const AmSipRequest& req, unsigned int code,const string& reason,const string& content_type, const string& body,string& hdrs,int flags)
|
||||
void on_stop();
|
||||
|
||||
private:
|
||||
UACAuthCred credentials;
|
||||
AmAudio *m_OtherLeg;
|
||||
|
||||
};
|
||||
#endif
|
||||
@ -0,0 +1,93 @@
|
||||
|
||||
#include "GatewayFactory.h"
|
||||
#include "GWSession.h"
|
||||
#include "mISDNStack.h"
|
||||
#include "AmUtils.h"
|
||||
#include "log.h"
|
||||
|
||||
EXPORT_SESSION_FACTORY(GatewayFactory,MODULE_NAME);
|
||||
|
||||
AmConfigReader gwconf;
|
||||
|
||||
GatewayFactory::GatewayFactory(const string& _app_name)
|
||||
: AmSessionFactory(_app_name)
|
||||
{
|
||||
INFO("GatewayFactory constructor\n");
|
||||
|
||||
if(mISDNStack::GetPortInfo()!=OK) {
|
||||
ERROR("mISDNStack::GetPortInfo failed");
|
||||
return;
|
||||
}
|
||||
if(!mISDNStack::instance()){
|
||||
ERROR("mISDN stack not initialized.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int GatewayFactory::onLoad()
|
||||
{
|
||||
INFO("gateway version %s loading (mISDN) ...\n", GW_VERSION);
|
||||
if (gwconf.loadFile(AmConfig::ModConfigPath + string(MODULE_NAME)+ ".conf")) {
|
||||
// if (gwconf.loadFile(AmConfig::ModConfigPath +"gateway.conf")) {
|
||||
DBG("cant load conf file %s/%s.conf\n",AmConfig::ModConfigPath.c_str(),MODULE_NAME);
|
||||
exit(-1);
|
||||
}
|
||||
configureModule(gwconf);
|
||||
auth_enable = (gwconf.getParameter("auth_enable", "no")=="yes");
|
||||
auth_realm = gwconf.getParameter("auth_realm", "");
|
||||
auth_user = gwconf.getParameter("auth_user", "");
|
||||
auth_pwd = gwconf.getParameter("auth_pwd", "");
|
||||
if(auth_enable) {
|
||||
uac_auth_f = AmPlugIn::instance()->getFactory4Seh("uac_auth");
|
||||
DBG("uac_auth_f == 0x%.16lX\n",(unsigned long)uac_auth_f);
|
||||
} else uac_auth_f=NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
AmSession* GatewayFactory::onInvite(const AmSipRequest& req)
|
||||
{
|
||||
INFO("IsdnGatewayFactory::onInvite()\n");
|
||||
if (req.user.empty())
|
||||
throw AmSession::Exception(500,"gateway: internal error, user is empty\n");
|
||||
DBG("received onInvite for outgoing call!\n");
|
||||
GWSession* session=new GWSession(auth_realm,auth_user,auth_pwd);
|
||||
// if (uac_auth_f != NULL) {
|
||||
// DBG("UAC Auth enabled for session.\n");
|
||||
// AmSessionEventHandler* h = uac_auth_f->getHandler(session);
|
||||
// if (h != NULL ) session->addHandler(h);
|
||||
// }
|
||||
DBG("calling (mISDNStack::instance())->placeCall(req, session, tonumber, fromnumber);\n");
|
||||
int ret=(mISDNStack::instance())->placeCall(req, session, req.user, req.from);
|
||||
if(ret==FAIL) {
|
||||
ERROR("mISDNStack::placeCall failed\n");
|
||||
return NULL;
|
||||
}
|
||||
DBG("now returning GatewayDialog\n");
|
||||
return session;
|
||||
}
|
||||
// this is session creator for incoming calls
|
||||
//call actually starts in isdn module as CC_SETUP, and there GWSession::CallFromOutSide is executed
|
||||
// which calls AmUac::dialout which in turn calls this function
|
||||
//session pointer is returned back so we can finish filling object with data there
|
||||
AmSession* GatewayFactory::onInvite(const AmSipRequest& req, AmArg& session_params)
|
||||
{
|
||||
INFO("GatewayFactory::onInvite(with args)\n");
|
||||
// GWCall* call = new GWCall();
|
||||
GWSession* session=new GWSession(auth_realm,auth_user,auth_pwd);
|
||||
DBG("GatewayFactory::onInvite(with args) session=%p\n",session);
|
||||
if (uac_auth_f != NULL) {
|
||||
DBG("UAC Auth enabled for session.\n");
|
||||
AmSessionEventHandler* h = uac_auth_f->getHandler(session);
|
||||
if (h != NULL ) session->addHandler(h);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
GatewayFactory::~GatewayFactory()
|
||||
{
|
||||
DBG("gateway: destructor of GatewayFactory: cleaning up.\n");
|
||||
// mISDNStack::instance()->shutdown();
|
||||
delete(mISDNStack::instance());
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
#ifndef _ISDNGATEWAYFACTORY_H_
|
||||
#define _ISDNGATEWAYFACTORY_H_
|
||||
|
||||
#include "GWSession.h"
|
||||
#include "AmConfigReader.h"
|
||||
#include <string>
|
||||
using std::string;
|
||||
|
||||
class GatewayFactory: public AmSessionFactory
|
||||
{
|
||||
public:
|
||||
GatewayFactory(const string& _app_name);
|
||||
~GatewayFactory();
|
||||
//Auth api
|
||||
AmSessionEventHandlerFactory* uac_auth_f;
|
||||
|
||||
int onLoad();
|
||||
AmSession* onInvite(const AmSipRequest& req);
|
||||
AmSession* onInvite(const AmSipRequest& req, AmArg& session_params);
|
||||
private:
|
||||
static GatewayFactory* _instance;
|
||||
bool auth_enable;
|
||||
std::string auth_realm;
|
||||
std::string auth_user;
|
||||
std::string auth_pwd;
|
||||
|
||||
};
|
||||
|
||||
extern AmConfigReader gwconf;
|
||||
#endif
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
plug_in_name = gateway
|
||||
|
||||
module_ldflags =-lmISDN -lisdnnet
|
||||
module_cflags =
|
||||
|
||||
COREPATH ?=../../core
|
||||
module_cflags += -DGW_VERSION="\"0.1\""
|
||||
# -I/usr/local/src/lcr/mISDN-2008_01_12/include -I/usr/local/src/lcr/mISDNuser-2008_01_12/include
|
||||
include $(COREPATH)/plug-in/Makefile.app_module
|
||||
@ -0,0 +1,9 @@
|
||||
callerdomain=192.168.0.15
|
||||
calleddomain=netitel.pl
|
||||
msn=6240495,6240496,6240497,6233270
|
||||
outprefixes=0*,1*,2*,3*,4*,5*,6*,7*,8*,9*,*
|
||||
defaultmsn=6240495
|
||||
auth_enable=yes
|
||||
auth_realm=netitel.pl
|
||||
auth_user=semsgw
|
||||
auth_pwd=***
|
||||
@ -0,0 +1,9 @@
|
||||
#ifndef _GLOBALS_H_
|
||||
#define _GLOBALS_H_
|
||||
|
||||
#define MODULE_NAME "gateway"
|
||||
#define FAIL 0
|
||||
#define OK 1
|
||||
#define MAX_NUM_LEN 20
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,656 @@
|
||||
#include "AmApi.h"
|
||||
//#include "GWCall.h"
|
||||
#include "globals.h"
|
||||
#include "GatewayFactory.h"
|
||||
#include "mISDNChannel.h"
|
||||
#include "mISDNNames.h"
|
||||
#include "amci/codecs.h"
|
||||
#include <math.h>
|
||||
|
||||
using namespace mISDN;
|
||||
|
||||
//we need to swap bits in bytes for isdn audio. this 2 functions are borowed from asterisk
|
||||
char flip_table[256];
|
||||
void init_flip_bits(void) {
|
||||
int i,k;
|
||||
for (i = 0 ; i < 256 ; i++) {
|
||||
unsigned char sample = 0 ;
|
||||
for (k = 0; k<8; k++) {
|
||||
if ( i & 1 << k ) sample |= 0x80 >> k;
|
||||
}
|
||||
flip_table[i] = sample;
|
||||
}
|
||||
}
|
||||
static char * flip_buf_bits ( char * buf , int len) {
|
||||
int i;
|
||||
char * start = buf;
|
||||
for (i = 0 ; i < len; i++) {
|
||||
buf[i] = flip_table[(unsigned char)buf[i]];
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
/* First AmAudio interface This is short. */
|
||||
int mISDNChannel::read(unsigned int user_ts, unsigned int size) {
|
||||
// DBG("mISDNChannel::read user_ts=%d size=%d buffersize=%d\n",user_ts,size,fromISDN_buffer.size());
|
||||
//i know this is not fastest implementation but its short and works
|
||||
fromISDN_buffer.copy((char *)((unsigned char*)samples),size);
|
||||
fromISDN_buffer.erase(0,size);
|
||||
return size;
|
||||
}
|
||||
int mISDNChannel::write(unsigned int user_ts, unsigned int size) {
|
||||
char buf[4096+mISDN_HEADER_LEN];
|
||||
mISDN::iframe_t *frame = (mISDN::iframe_t *)buf;
|
||||
int ret;
|
||||
// DBG("mISDNChannel::write user_ts=%d size=%d\n",user_ts,size);
|
||||
if(m_BC==0) {
|
||||
// DBG("bchannel is already detached or not yet initialised\n"); //we silently discard this to avoid log flooding
|
||||
return 0;
|
||||
}
|
||||
if(size>=4096) {
|
||||
DBG("truncating output audio (%d)\n",size);
|
||||
size=4096;
|
||||
}
|
||||
memcpy(buf + (mISDN_HEADER_LEN), (unsigned char*)samples, size);
|
||||
flip_buf_bits( buf + mISDN_HEADER_LEN, size);
|
||||
frame->addr = m_BC | FLG_MSG_DOWN;
|
||||
frame->prim = DL_DATA | REQUEST;
|
||||
frame->dinfo = 0;
|
||||
frame->len = size;
|
||||
ret = mISDN::mISDN_write(mISDNStack::instance()->m_mISDNdevice, buf, frame->len+mISDN_HEADER_LEN, 8000);
|
||||
// DBG("mISDNChannel::write: sending packet directly to isdn 0x%x 0x%x %d ret=%d\n",frame->addr, frame->prim,frame->len,ret);
|
||||
return ret;
|
||||
}
|
||||
void mISDNChannel::bchan_receive(char *msg_buf,int msg_buf_s) {
|
||||
int len=msg_buf_s-mISDN_HEADER_LEN;
|
||||
std::string tmp;
|
||||
// DBG("mISDNChannel::bchannel_receive size=%d buffersize=%d\n",msg_buf_s - mISDN_HEADER_LEN,fromISDN_buffer.size());
|
||||
flip_buf_bits( msg_buf + mISDN_HEADER_LEN, len);
|
||||
tmp.assign(msg_buf + mISDN_HEADER_LEN,len);
|
||||
fromISDN_buffer.append(tmp);
|
||||
return;
|
||||
}
|
||||
//void mISDNChannel::bchannel_send() {
|
||||
// char buf[PLAY_SIZE+mISDN_HEADER_LEN];
|
||||
// mISDN::iframe_t *frame = (mISDN::iframe_t *)buf;
|
||||
// int l,ret;
|
||||
// int i,t;
|
||||
// char *s;
|
||||
//
|
||||
// flip_buf_bits( buf + mISDN_HEADER_LEN, PLAY_SIZE);
|
||||
// frame->addr = m_BC | FLG_MSG_DOWN;
|
||||
// frame->prim = DL_DATA | REQUEST;
|
||||
// frame->dinfo = 0;
|
||||
// frame->len = PLAY_SIZE;
|
||||
// ret = mISDN::mISDN_write(mISDNStack::instance()->m_mISDNdevice, buf, PLAY_SIZE+mISDN_HEADER_LEN, 8000);
|
||||
// DBG("mISDNChannel::bchannel_send write=%d\n",ret);
|
||||
// return;
|
||||
//}
|
||||
|
||||
|
||||
//some code borrowed from Linux Call Router
|
||||
static signed char _mISDN_l3_ie2pos[128] = {
|
||||
-1,-1,-1,-1, 0,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,
|
||||
2,-1,-1,-1, 3,-1,-1,-1, 4,-1,-1,-1, 5,-1, 6,-1,
|
||||
7,-1,-1,-1,-1,-1,-1, 8, 9,10,-1,-1,11,-1,-1,-1,
|
||||
-1,-1,-1,-1,12,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
13,-1,14,15,16,17,18,19,-1,-1,-1,-1,20,21,-1,-1,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,22,23,-1,-1,
|
||||
24,25,-1,-1,26,-1,-1,-1,27,28,-1,-1,29,30,31,-1
|
||||
};
|
||||
|
||||
static unsigned char _mISDN_l3_pos2ie[32] = {
|
||||
0x04, 0x08, 0x10, 0x14, 0x18, 0x1c, 0x1e, 0x20,
|
||||
0x27, 0x28, 0x29, 0x2c, 0x34, 0x40, 0x42, 0x43,
|
||||
0x44, 0x45, 0x46, 0x47, 0x4c, 0x4d, 0x6c, 0x6d,
|
||||
0x70, 0x71, 0x74, 0x78, 0x79, 0x7c, 0x7d, 0x7e
|
||||
};
|
||||
|
||||
int mISDN_get_free_ext_ie(mISDN::Q931_info_t *qi) {
|
||||
int i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (qi->ext[i].ie.off == 0)
|
||||
return(i);
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int mISDN_AddIE(mISDN::Q931_info_t *qi, u_char *p, u_char ie, u_char *iep) {
|
||||
u_char *ps;
|
||||
mISDN::ie_info_t *ies;
|
||||
int l;
|
||||
|
||||
if (ie & 0x80) { /* one octett IE */
|
||||
if (ie == IE_MORE_DATA)
|
||||
ies = &qi->more_data;
|
||||
else if (ie == IE_COMPLETE)
|
||||
ies = &qi->sending_complete;
|
||||
else if ((ie & 0xf0) == IE_CONGESTION)
|
||||
ies = &qi->congestion_level;
|
||||
else {
|
||||
return(-1);
|
||||
}
|
||||
l = 0;
|
||||
} else {
|
||||
if (!iep || !iep[0])
|
||||
return(-3);
|
||||
ies = &qi->bearer_capability;
|
||||
if (_mISDN_l3_ie2pos[ie]<0) {
|
||||
return(-2);
|
||||
}
|
||||
ies += _mISDN_l3_ie2pos[ie];
|
||||
if (ies->off) {
|
||||
while (ies->repeated)
|
||||
ies = &qi->ext[ies->ridx].ie;;
|
||||
l = mISDN_get_free_ext_ie(qi);
|
||||
if (l < 0) { // overflow
|
||||
return(-3);
|
||||
}
|
||||
ies->ridx = l;
|
||||
ies->repeated = 1;
|
||||
ies = &qi->ext[l].ie;
|
||||
ies->cs_flg = 0;
|
||||
qi->ext[l].v.codeset = 0;
|
||||
qi->ext[l].v.val = ie;
|
||||
}
|
||||
l = iep[0] + 1;
|
||||
}
|
||||
ps = (u_char *) qi;
|
||||
ps += L3_EXTRA_SIZE;
|
||||
ies->off = (u16)(p - ps);
|
||||
*p++ = ie;
|
||||
if (l)
|
||||
memcpy(p, iep, l);
|
||||
return(l+1);
|
||||
}
|
||||
|
||||
|
||||
mISDNChannel::mISDNChannel() : AmAudio(new AmAudioSimpleFormat(CODEC_ALAW)){
|
||||
DBG("this is temporary constructor\n");
|
||||
init();
|
||||
m_port=mISDNStack::instance()->mISDNport_first;
|
||||
}
|
||||
|
||||
mISDNChannel::mISDNChannel(mISDNport *port) : AmAudio(new AmAudioSimpleFormat(CODEC_ALAW)){
|
||||
init();
|
||||
m_port=port;
|
||||
}
|
||||
|
||||
mISDNChannel::~mISDNChannel() {
|
||||
unregister_CR();
|
||||
unregister_BC();
|
||||
DBG("mISDNChannel destructor ends\n");
|
||||
}
|
||||
void mISDNChannel::init() {
|
||||
|
||||
m_frame=(mISDN::iframe_t*)m_last_msg;
|
||||
m_qi = (mISDN::Q931_info_t*)(m_last_msg + mISDN_HEADER_LEN);
|
||||
m_ie_data =(char *)m_qi + L3_EXTRA_SIZE;
|
||||
/* init the audio buffers for in&out */
|
||||
// pthread_mutex_init(&fromISDN_lock, NULL);
|
||||
// pthread_mutex_init(&toISDN_lock, NULL);
|
||||
fromISDN_buffer.assign("");
|
||||
}
|
||||
|
||||
void mISDNChannel::setSession(GWSession* session) {
|
||||
m_session = session;
|
||||
}
|
||||
GWSession* mISDNChannel::getSession() {
|
||||
return m_session;
|
||||
}
|
||||
|
||||
void mISDNChannel::unregister_CR() {
|
||||
mISDNStack* stack=mISDNStack::instance();
|
||||
std::map<int,mISDNChannel*>::iterator CR_iter=stack->CR_map.find(m_CR);;
|
||||
if(CR_iter==stack->CR_map.end()) {
|
||||
DBG("mISDNChannel::unregister_CR Cant find myself in CR_map this=%p (0x%08x)\n",this,m_CR);
|
||||
} else {
|
||||
DBG("mISDNChannel::unregister_CR removing channel from CR_map this=%p (0x%08x)\n",this,m_CR);
|
||||
stack->CR_map.erase(CR_iter);
|
||||
}
|
||||
m_CR=0;
|
||||
}
|
||||
void mISDNChannel::unregister_BC() {
|
||||
mISDNStack* stack=mISDNStack::instance();
|
||||
if(m_BC!=0) {
|
||||
std::map<int,mISDNChannel*>::iterator BC_iter=stack->BC_map.find(m_BC&STACK_ID_MASK);;
|
||||
if(BC_iter==stack->BC_map.end()) {
|
||||
DBG("mISDNChannel::unregister_BC Cant find myself in BC_map %p (0x%08x)\n",this,m_BC);
|
||||
} else {
|
||||
DBG("mISDNChannel::unregister_BC is removing channel from BC_map this=%p (0x%08x)\n",this,m_BC);
|
||||
stack->BC_map.erase(BC_iter);
|
||||
}
|
||||
m_BC=0;
|
||||
} else DBG("mISDNChannel::unregister_BC BC already removed or not initialized, this=%p (0x%08x)\n",this,m_BC);
|
||||
}
|
||||
|
||||
int mISDNChannel::placeCall(const AmSipRequest &req, std::string tonumber, std::string fromnumber) {
|
||||
int ret;
|
||||
m_called=tonumber;
|
||||
m_TON_d=0; //Unknown
|
||||
m_NPI_d=1; // ISDN E.164
|
||||
if(fromnumber.empty()) {
|
||||
m_caller=gwconf.getParameter("out_msn","");
|
||||
} else
|
||||
m_caller=fromnumber;
|
||||
m_TON_r=0; //Unknown
|
||||
m_NPI_r=1; // ISDN E.164
|
||||
m_Screening_r=0; // user provided
|
||||
m_Presentation_r=0; // allowed
|
||||
return call();
|
||||
|
||||
}
|
||||
int mISDNChannel::accept() {
|
||||
mISDNStack* stack=mISDNStack::instance();
|
||||
char buf[MAX_MSG_SIZE];
|
||||
mISDN::Q931_info_t *qi;
|
||||
mISDN::iframe_t *frame = (mISDN::iframe_t *)buf;
|
||||
DBG("mISDNChannel::accept\n");
|
||||
frame->prim = CC_CONNECT | REQUEST;
|
||||
frame->addr = m_port->upper_id | FLG_MSG_DOWN;
|
||||
frame->dinfo= m_CR;
|
||||
frame->len= 0;
|
||||
DBG("Sending CC_CONNECT | REQUEST for CR=0x%04x \n",m_CR);
|
||||
mISDN::mISDN_write(stack->m_mISDNdevice, buf, mISDN_HEADER_LEN+frame->len, TIMEOUT_1SEC);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int mISDNChannel::hangup() {
|
||||
mISDNStack* stack=mISDNStack::instance();
|
||||
char buf[MAX_MSG_SIZE];
|
||||
mISDN::Q931_info_t *qi;
|
||||
mISDN::iframe_t *frame = (mISDN::iframe_t *)buf;
|
||||
|
||||
DBG("mISDNChannel::hangup\n");
|
||||
frame->prim = CC_DISCONNECT | REQUEST;
|
||||
frame->addr = m_port->upper_id | FLG_MSG_DOWN;
|
||||
frame->dinfo= m_CR;
|
||||
frame->len= 0;
|
||||
DBG("Sending CC_DISCONNECT | REQUEST for CR=0x%04x \n",m_CR);
|
||||
mISDN::mISDN_write(stack->m_mISDNdevice, buf, mISDN_HEADER_LEN+frame->len, TIMEOUT_1SEC);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int mISDNChannel::call() {
|
||||
mISDNStack* stack=mISDNStack::instance();
|
||||
unsigned char buf[MAX_MSG_SIZE], *np, *p, *msg, ie[64];
|
||||
mISDN::Q931_info_t *qi;
|
||||
mISDN::iframe_t *frame = (mISDN::iframe_t *)buf;
|
||||
int ret,len,i;
|
||||
|
||||
INFO("mISDN is making outbound call from %s to %s\n", m_caller.c_str(), m_called.c_str());
|
||||
//making new isdn call ref
|
||||
m_CR=stack->GenerateCR();
|
||||
frame->prim = CC_NEW_CR | REQUEST;
|
||||
frame->addr = m_port->upper_id | FLG_MSG_DOWN;
|
||||
frame->dinfo= m_CR;
|
||||
frame->len=0;
|
||||
DBG("sending CC_NEW_CR | REQUEST to device=%d addr=0x%08x dinfo=0x%08x\n",mISDNStack::instance()->m_mISDNdevice,frame->addr,frame->dinfo);
|
||||
ret=mISDN::mISDN_write(mISDNStack::instance()->m_mISDNdevice, buf, mISDN_HEADER_LEN+frame->len, TIMEOUT_1SEC);
|
||||
if ( ret<0) {
|
||||
ERROR("mISDNChannel::call error on NEW_CR | REQUEST %d\n", ret);
|
||||
return FAIL;
|
||||
}
|
||||
stack->CR_map[m_CR]=this;
|
||||
DBG("Adding self (%p) to channel_map my CR=0x%08x \n",this,m_CR);
|
||||
p = msg = buf + mISDN_HEADER_LEN;
|
||||
qi = (mISDN::Q931_info_t *)p;
|
||||
memset(qi, 0, sizeof(mISDN::Q931_info_t));
|
||||
qi->type = MT_SETUP;
|
||||
p += L3_EXTRA_SIZE;
|
||||
p++; /* needed to avoid offset 0 in IE array */
|
||||
ret = mISDN_AddIE(qi, p, IE_COMPLETE, NULL);
|
||||
if ( ret<0) {
|
||||
ERROR("mISDNChannel::call Add IE_COMPLETE error %d\n", ret);
|
||||
return FAIL;
|
||||
}
|
||||
p += ret;
|
||||
ret = mISDN_AddIE(qi, p, IE_BEARER, (unsigned char*)"\x3\x90\x90\xa3"); /* Audio */
|
||||
// ret = mISDN_AddIE(qi, p, IE_BEARER, (unsigned char*)"\x2\x88\x90"); /* default Datatransmission 64k */
|
||||
if (ret<0) { ERROR("mISDNChannel::call Add IE_BEARER error %d\n", ret); return FAIL;}
|
||||
p += ret;
|
||||
ie[0] = m_caller.size() + 1;
|
||||
ie[1] = 0x00 + (m_TON_r << 4) + m_NPI_r; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
|
||||
if (m_Presentation_r >= 0) {
|
||||
ie[1] = 0x00 + (m_TON_r<<4) + m_NPI_r;
|
||||
ie[2] = 0x80 + (m_Presentation_r<<5) + m_Screening_r;
|
||||
for (i=0; i<=m_caller.size(); i++)
|
||||
ie[3+i] = m_caller[i] & 0x7f;
|
||||
} else {
|
||||
ie[1] = 0x80 + (m_TON_r<<4) + m_NPI_r;
|
||||
for (i=0; i<=m_caller.size(); i++)
|
||||
ie[2+i] = m_caller[i] & 0x7f;
|
||||
}
|
||||
ret = mISDN_AddIE(qi, p, IE_CALLING_PN, ie);
|
||||
if (ret<0) { ERROR("mISDNChannel::call Add IE_CALLING_PN error %d\n", ret);return FAIL; }
|
||||
p += ret;
|
||||
ie[0] =m_called.size() + 1;
|
||||
ie[1] = 0x80 + (m_TON_d << 4) + m_NPI_d; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
|
||||
for (i=0; i<=m_called.size(); i++)
|
||||
ie[2+i] = m_called[i] & 0x7f;
|
||||
ret = mISDN_AddIE(qi, p, IE_CALLED_PN, ie);
|
||||
if (ret<0) { ERROR("mISDNChannel::call Add IE_CALLED_PN error %d\n", ret);return FAIL; }
|
||||
p += ret;
|
||||
frame->prim = CC_SETUP | REQUEST;
|
||||
frame->addr = m_port->upper_id | FLG_MSG_DOWN;
|
||||
frame->dinfo= m_CR;
|
||||
frame->len= p - msg;
|
||||
ret=mISDN::mISDN_write(mISDNStack::instance()->m_mISDNdevice, buf, mISDN_HEADER_LEN+frame->len, TIMEOUT_1SEC);
|
||||
if ( ret<0) {
|
||||
ERROR("mISDNChannel::call error dending CC_SETUP | REQUEST %d\n", ret);
|
||||
return FAIL;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
int mISDNChannel::processMsg(char *msg_buf,int msg_buf_s) {
|
||||
mISDNStack* stack=mISDNStack::instance();
|
||||
char buf[MAX_MSG_SIZE];
|
||||
int buf_s;
|
||||
mISDN::Q931_info_t *qi;
|
||||
mISDN::iframe_t *frame = (mISDN::iframe_t *)buf;
|
||||
char *p;
|
||||
|
||||
p=(char *)&m_last_msg;
|
||||
memcpy(p, msg_buf, msg_buf_s);
|
||||
m_last_msg_s=msg_buf_s;
|
||||
memset(p+msg_buf_s,0,MAX_MSG_SIZE-msg_buf_s);
|
||||
switch(m_frame->prim) {
|
||||
case CC_SETUP |INDICATION:
|
||||
DBG("CC_SETUP | INDICATION for CR=0x%04x \n",m_CR);
|
||||
GetIEchannel_id();
|
||||
DBG("Creating Bchannel for CR=0x%04x \n",m_CR);
|
||||
bchan_create();
|
||||
GetCallerNum();
|
||||
GetCalledNum();
|
||||
m_session=GWSession::CallFromOutside(m_caller, m_called, 0, this);
|
||||
if(m_session!=NULL) {
|
||||
unsigned char buf[MAX_MSG_SIZE];
|
||||
// mISDN::Q931_info_t *qi;
|
||||
mISDN::iframe_t *frame = (mISDN::iframe_t *)buf;
|
||||
int ret,len;
|
||||
frame->prim = CC_PROCEEDING | REQUEST;
|
||||
frame->addr = m_port->upper_id | FLG_MSG_DOWN;
|
||||
frame->dinfo= m_CR;
|
||||
frame->len= 0;
|
||||
ret=mISDN::mISDN_write(mISDNStack::instance()->m_mISDNdevice, buf, mISDN_HEADER_LEN+frame->len, TIMEOUT_1SEC);
|
||||
DBG("m_session=%p Not null sending CC_PROCEEDING |REQUEST\n",m_session);
|
||||
}
|
||||
break;
|
||||
case CC_PROCEEDING |INDICATION:
|
||||
DBG("CC_PROCEEDING | INDICATION for CR=0x%04x \n",m_CR);
|
||||
// m_session->acceptAudio(m_session->invite_req.body,m_req.hdrs,&sdp_reply);
|
||||
// m_session->dlg.reply(m_session->invite_req,180, "Isdn side state is: PROCEEDING", "application/sdp",sdp_reply);
|
||||
m_session->dlg.reply(m_session->invite_req,180, "Isdn side state is: PROCEEDING");
|
||||
DBG("check 2 CC_PROCEEDING | INDICATION for CR=0x%04x \n",m_CR);
|
||||
GetIEchannel_id();
|
||||
DBG("Creating Bchannel for CR=0x%04x \n",m_CR);
|
||||
bchan_create();
|
||||
break;
|
||||
case CC_ALERTING | INDICATION:
|
||||
DBG("CC_ALERTING | INDICATION for CR=0x%04x \n",m_CR);
|
||||
// m_session->acceptAudio(m_session->invite_req.body,m_req.hdrs,&sdp_reply);
|
||||
// m_session->dlg.reply(m_session->invite_req,183, "Isdn side state is: ALERTING", "application/sdp",sdp_reply);
|
||||
m_session->dlg.reply(m_session->invite_req,183, "Isdn side state is: ALERTING");
|
||||
break;
|
||||
case CC_CONNECT | INDICATION:
|
||||
case CC_CONNECT_ACKNOWLEDGE | INDICATION:
|
||||
DBG("CC_CONNECT(_ACKNOWLEDGE) | INDICATION for CR=0x%04x \n",m_CR);
|
||||
DBG("Activating Bchannel for CR=0x%04x \n",m_CR);
|
||||
bchan_activate();
|
||||
m_session->onSessionStart(m_session->invite_req);
|
||||
break;
|
||||
case CC_DISCONNECT | INDICATION:
|
||||
case CC_DISCONNECT | CONFIRM:
|
||||
DBG("CC_DISCONNECT | INDICATION or CONFIRMfor CR=0x%04x \n",m_CR);
|
||||
m_session->setInOut(NULL,NULL);
|
||||
m_session->setStopped();
|
||||
if(m_session->dlg.getStatus()==AmSipDialog::Pending) {
|
||||
DBG("Sip side not yet connected sending reply\n");
|
||||
m_session->dlg.reply(m_session->invite_req,487, "Isdn side state is: DISCONNECTING");
|
||||
//maybe this would be better
|
||||
// throw AmSession::Exception(487, "call terminated");
|
||||
} else {
|
||||
DBG("Sip side already connected sending bye\n");
|
||||
m_session->dlg.bye();
|
||||
}
|
||||
// mISDN::mISDN_write_frame(stack->m_mISDNdevice, buf, m_port->upper_id | FLG_MSG_DOWN, CC_RELEASE | REQUEST, m_CR, 0, NULL, TIMEOUT_1SEC);
|
||||
frame->prim = CC_RELEASE | REQUEST;
|
||||
frame->addr = m_port->upper_id | FLG_MSG_DOWN;
|
||||
frame->dinfo= m_CR;
|
||||
frame->len= 0;
|
||||
DBG("Sending CC_RELEASE | REQUEST for CR=0x%04x \n",m_CR);
|
||||
mISDN::mISDN_write(stack->m_mISDNdevice, buf, mISDN_HEADER_LEN+frame->len, TIMEOUT_1SEC);
|
||||
bchan_deactivate();
|
||||
break;
|
||||
case CC_RELEASE | INDICATION:
|
||||
case CC_RELEASE_COMPLETE | INDICATION:
|
||||
DBG("CC_RELEASE(_COMPLETE) | INDICATION for CR=0x%04x \n",m_CR);
|
||||
// mISDN::mISDN_write_frame(stack->m_mISDNdevice, buf, m_port->upper_id | FLG_MSG_DOWN, CC_RELEASE_COMPLETE | REQUEST, m_CR, 0, NULL, TIMEOUT_1SEC);
|
||||
frame->prim = CC_RELEASE_COMPLETE| REQUEST;
|
||||
frame->addr = m_port->upper_id | FLG_MSG_DOWN;
|
||||
frame->dinfo= m_CR;
|
||||
frame->len= 0;
|
||||
DBG("Sending CC_RELEASE_COMPLETE | REQUEST for CR=0x%04x \n",m_CR);
|
||||
mISDN::mISDN_write(stack->m_mISDNdevice, buf, mISDN_HEADER_LEN+frame->len, TIMEOUT_1SEC);
|
||||
bchan_destroy();
|
||||
break;
|
||||
default:
|
||||
ERROR("mISDNChannel::processMsg unhandled: prim(0x%x) addr(0x%x) msg->len(%d)\n", m_frame->prim, m_frame->addr, msg_buf_s);
|
||||
return FAIL;
|
||||
break;
|
||||
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
int mISDNChannel::GetIEchannel_id(){
|
||||
char *p;
|
||||
m_bchannel=-1; //no ie or error (for now)
|
||||
|
||||
if (m_qi->channel_id.off==0) { ERROR("No channel_id IE here\n"); return FAIL; }
|
||||
p = m_ie_data + m_qi->channel_id.off;
|
||||
//p[0] is 0x18 - code for this ie;
|
||||
DBG("mISDNChannel::GetIEchannel_id p= 0x%02hhx 0x%02hhx 0x%02hhx\n",p[0],p[1],p[2]);
|
||||
if(p[1]<1) { ERROR("IE Too short\n"); return FAIL; }
|
||||
if(p[2] & 0x40) { ERROR("Channels on other interfaces not supported\n"); return FAIL; } // bit 7 - other interface
|
||||
if(p[2] & 0x04) { ERROR("using d-channel not supported\n"); return FAIL; } // bit 3 - d channel;
|
||||
if(m_port->pri) {
|
||||
switch((p[2]&0x03)) {
|
||||
case 0: m_bchannel=-2; return OK; break; //no Channel
|
||||
case 1: break; //channel num in folowing bytes
|
||||
case 2: ERROR("Reserved bit set\n"); return FAIL; break; // reserver bit;
|
||||
case 3: m_bchannel=-3; return OK; break; //ANY channel
|
||||
}
|
||||
if(p[1] < 3) { ERROR("IE Too short for PRI\n"); return FAIL; } // we need extended info on channel num for pri
|
||||
if(p[3] & 0x10) { ERROR("channel map not supported\n"); return FAIL; } // bit 3 - d channel;
|
||||
m_bchannel=p[4]&0x7f;
|
||||
if(m_bchannel<1 || m_bchannel==16) {
|
||||
ERROR("PRI channel out of range (%d)\n",m_bchannel);
|
||||
m_bchannel=-1;
|
||||
return FAIL;
|
||||
}
|
||||
DBG("mISDNChannel::GetIEchannel_id will use PRI b_channel=%d\n",m_bchannel);
|
||||
return OK;
|
||||
} else { //BRI
|
||||
if ((p[2] & 0x20)) { ERROR("req for bri channel on PRI interface\n"); return FAIL; } // bit 6 - 0=BRI, 1-other(PRI);
|
||||
switch((p[2]&0x03)) {
|
||||
case 0: m_bchannel=-2; break; //no Channel
|
||||
case 1: m_bchannel=1; break; //channel 1
|
||||
case 2: m_bchannel=2; break; //channel 2
|
||||
case 3: m_bchannel=-3; break; //ANY channel
|
||||
}
|
||||
DBG("mISDNChannel::GetIEchannel_id will use BRI b_channel=%d\n",m_bchannel);
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
int mISDNChannel::GetCallerNum(){
|
||||
char *p;
|
||||
int len;
|
||||
|
||||
if (m_qi->calling_nr.off==0) { ERROR("No calling_nr IE here\n"); return FAIL; }
|
||||
p = m_ie_data + m_qi->calling_nr.off;
|
||||
//p[0] is 0x6C - code for this ie;
|
||||
DBG("mISDNChannel::GetCallerNum p= 0x%02hhx 0x%02hhx 0x%02hhx 0x%02hhx\n",p[0],p[1],p[2],p[3]);
|
||||
p++;//now we scan each byte as some are optional
|
||||
if(p[0]<1) { ERROR("IE Too short\n"); return FAIL; }
|
||||
if(p[0]>=MAX_NUM_LEN-1) { ERROR("Number too long for MAX_NUM_LEN \n"); return FAIL; }
|
||||
len=p[0];
|
||||
p++;
|
||||
m_TON_r=(p[0]&0x70)>>4;
|
||||
m_NPI_r=p[0]&0x0F;
|
||||
if(!(p[0]&80)) { //if this is not last byte
|
||||
len--;p++; //warning p is shifted here to next byte (optional Presentation/Screening)
|
||||
m_Presentation_r=(p[0]&0x60)>>5;
|
||||
m_Screening_r=p[0]&0x03;
|
||||
} else DBG("mISDNChannel::GetCallerNum no Presentation/Screening byte\n");
|
||||
len--;p++;
|
||||
DBG("mISDNChannel::GetCallerNum len=%d TON=%d NPI=%d Presentation=%d Screening=%d\n",len,m_TON_r,m_NPI_r,m_Presentation_r,m_Screening_r);
|
||||
// memcpy(&m_caller, p, len);
|
||||
// m_caller[len]='\0';
|
||||
m_caller.assign(p,len);
|
||||
DBG("mISDNChannel::GetCallerNum %s %s %s %s %s\n",m_caller.c_str(),mISDNNames::TON(m_TON_r),mISDNNames::NPI(m_NPI_r),mISDNNames::Presentation(m_Presentation_r),mISDNNames::Screening(m_Screening_r));
|
||||
return OK;
|
||||
}
|
||||
int mISDNChannel::GetCalledNum(){
|
||||
char *p;
|
||||
int len;
|
||||
|
||||
if (m_qi->called_nr.off==0) { ERROR("No called_nr IE here\n"); return FAIL; }
|
||||
p = m_ie_data + m_qi->called_nr.off;
|
||||
//p[0] is 0x70 - code for this ie;
|
||||
DBG("mISDNChannel::GetCalledNum p= 0x%02hhx 0x%02hhx 0x%02hhx\n",p[0],p[1],p[2]);
|
||||
if(p[1]<1) { ERROR("IE Too short\n"); return FAIL; }
|
||||
if(p[1]>=MAX_NUM_LEN-1) { ERROR("Number too long for MAX_NUM_LEN \n"); return FAIL; }
|
||||
len=p[1];
|
||||
m_TON_d=(p[2]&0x70)>>4;
|
||||
m_NPI_d=p[2]&0x0F;
|
||||
DBG("mISDNChannel::GetCalledNum len=%d TON=%d NPI=%d\n",len,m_TON_d,m_NPI_d);
|
||||
// memcpy(&m_called, p+3, len-1);
|
||||
// m_called[len-1]='\0';
|
||||
m_called.assign(p+3,len-1);
|
||||
DBG("mISDNChannel::GetCalledNum %s %s %s\n",m_called.c_str(),mISDNNames::TON(m_TON_d),mISDNNames::NPI(m_NPI_d));
|
||||
return OK;
|
||||
}
|
||||
int mISDNChannel::bchan_event(char *msg_buf,int msg_buf_s) {
|
||||
char *p;
|
||||
|
||||
p=(char *)&m_last_msg;
|
||||
memcpy(p, msg_buf, msg_buf_s);
|
||||
m_last_msg_s=msg_buf_s;
|
||||
memset(p+msg_buf_s,0,MAX_MSG_SIZE-msg_buf_s);
|
||||
switch(m_frame->prim) {
|
||||
case PH_DATA | CONFIRM:
|
||||
case DL_DATA | CONFIRM:
|
||||
// DBG("PH_DATA or DL_DATA confirm prim(0x%x) addr(0x%x) msg->len(%d) \n", m_frame->prim, m_frame->addr, msg_buf_s);
|
||||
// bchannel_send(); //we are now transmiting directly from Channel(AmAudio)::write
|
||||
break;
|
||||
case PH_DATA | INDICATION:
|
||||
case DL_DATA | INDICATION:
|
||||
// DBG("PH_DATA or DL_DATA IND prim(0x%x) addr(0x%x) msg->len(%d) \n", m_frame->prim, m_frame->addr, msg_buf_s);
|
||||
bchan_receive(msg_buf,msg_buf_s);
|
||||
break;
|
||||
case PH_CONTROL | INDICATION:
|
||||
case PH_SIGNAL | INDICATION:
|
||||
DBG("PH_CONTROL or PH_SIGNAL IND prim(0x%x) addr(0x%x) msg->len(%d) \n", m_frame->prim, m_frame->addr, msg_buf_s);
|
||||
break;
|
||||
case PH_ACTIVATE | INDICATION:
|
||||
case DL_ESTABLISH | INDICATION:
|
||||
case PH_ACTIVATE | CONFIRM:
|
||||
case DL_ESTABLISH | CONFIRM:
|
||||
DBG("(PH|DL)_(ESTABLISH|ACTIVATE (IND|CONFIRM): bchannel is now activated (address 0x%x).\n", m_frame->addr);
|
||||
break;
|
||||
case PH_DEACTIVATE | INDICATION:
|
||||
case DL_RELEASE | INDICATION:
|
||||
case PH_DEACTIVATE | CONFIRM:
|
||||
case DL_RELEASE | CONFIRM:
|
||||
DBG("(PH|DL)_(RELEASE|DEACTIVATE (IND|CONFIRM): bchannel is now de-activated (address 0x%x).\n", m_frame->addr);
|
||||
bchan_destroy();
|
||||
unregister_BC();
|
||||
break;
|
||||
default:
|
||||
ERROR("child message not handled: prim(0x%x) addr(0x%x) msg->len(%d)\n", m_frame->prim, m_frame->addr, msg_buf_s);
|
||||
return FAIL;
|
||||
break;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
int mISDNChannel::bchan_create() {
|
||||
layer_info_t li;
|
||||
mISDN_pid_t pid;
|
||||
int ret;
|
||||
mISDNStack* stack=mISDNStack::instance();
|
||||
|
||||
if(m_bchannel<=0) { ERROR("b-channel num not known or invalid (%d)\n",m_bchannel); return FAIL; }
|
||||
if(m_port->b_stid[m_bchannel-1]==0) { ERROR("No stack for b-channel (%d)\n",m_bchannel); return FAIL; }
|
||||
if(m_port->b_addr[m_bchannel-1]!=0) { ERROR("Stack already created for b-channel (%d)\n",m_bchannel); return FAIL; }
|
||||
memset(&li, 0, sizeof(li));
|
||||
memset(&pid, 0, sizeof(pid));
|
||||
li.object_id = -1;
|
||||
li.extentions = 0;
|
||||
li.st = m_port->b_stid[m_bchannel-1];
|
||||
strcpy(li.name, "B L4");
|
||||
// li.pid.layermask = ISDN_LAYER((4));
|
||||
li.pid.layermask = ISDN_LAYER((3));
|
||||
// li.pid.protocol[4] = ISDN_PID_L4_B_USER;
|
||||
li.pid.protocol[3] = ISDN_PID_L3_B_TRANS;
|
||||
ret = mISDN_new_layer(mISDNStack::instance()->m_mISDNdevice, &li);
|
||||
if(ret || !li.id) { ERROR("mISDN_new_layer() failed to add bchannel %d\n", m_bchannel); return FAIL; }
|
||||
m_BC=m_port->b_addr[m_bchannel-1]=li.id;
|
||||
pid.protocol[1] = ISDN_PID_L1_B_64TRANS;
|
||||
pid.protocol[2] = ISDN_PID_L2_B_TRANS;
|
||||
pid.protocol[3] = ISDN_PID_L3_B_TRANS;
|
||||
// pid.protocol[3] = ISDN_PID_L3_B_DSP;
|
||||
// pid.protocol[4] = ISDN_PID_L4_B_USER;
|
||||
pid.layermask = ISDN_LAYER((1)) | ISDN_LAYER((2)) | ISDN_LAYER((3)) ;
|
||||
// pid.layermask = ISDN_LAYER((1)) | ISDN_LAYER((2)) | ISDN_LAYER((3)) | ISDN_LAYER((4));
|
||||
ret = mISDN_set_stack(stack->m_mISDNdevice, m_port->b_stid[m_bchannel-1], &pid);
|
||||
if(ret) { ERROR("mISDN_set_stack failed to add bchannel %d\n", m_bchannel); return FAIL; }
|
||||
ret = mISDN_get_setstack_ind(stack->m_mISDNdevice, m_BC);
|
||||
if(ret) { ERROR("mISDN_set_stack_ind failed to add bchannel %d\n", m_bchannel); return FAIL; }
|
||||
m_BC=m_port->b_addr[m_bchannel-1]=mISDN_get_layerid(stack->m_mISDNdevice, m_port->b_stid[m_bchannel-1], 3);
|
||||
// m_BC=m_port->b_addr[m_bchannel-1]=mISDN_get_layerid(stack->m_mISDNdevice, m_port->b_stid[m_bchannel-1], 4);
|
||||
if(m_BC==0) { ERROR("mISDN_get_layerid failed to add bchannel %d\n", m_bchannel); return FAIL; }
|
||||
stack->BC_map[m_BC&STACK_ID_MASK]=this;
|
||||
m_port->b_port[m_bchannel-1]=this;
|
||||
DBG("Successfully created stack for port %d. addr=0x%08x\n",m_bchannel,m_BC);
|
||||
return OK;
|
||||
}
|
||||
int mISDNChannel::bchan_activate() {
|
||||
mISDN::iframe_t frame;
|
||||
mISDNStack* stack=mISDNStack::instance();
|
||||
int ret;
|
||||
|
||||
if(m_BC==0) { ERROR("bchannel (%d) not created\n",m_bchannel); return FAIL; }
|
||||
DBG("sending DL_ESTABLISH | REQUEST to device=%d for bchannel=%d addr=0x%08x dinfo=0x%08x\n",stack->m_mISDNdevice, m_bchannel,frame.addr, frame.dinfo);
|
||||
ret = mISDN::mISDN_write_frame(stack->m_mISDNdevice, &frame, m_BC | FLG_MSG_DOWN, DL_ESTABLISH | REQUEST, 0, 0, NULL, TIMEOUT_1SEC);
|
||||
return OK;
|
||||
}
|
||||
int mISDNChannel::bchan_deactivate() {
|
||||
mISDN::iframe_t frame;
|
||||
mISDNStack* stack=mISDNStack::instance();
|
||||
int ret;
|
||||
|
||||
DBG("sending DL_RELEASE | REQUEST to device=%d for bchannel=%d addr=0x%08x dinfo=0x%08x\n",stack->m_mISDNdevice, m_bchannel,frame.addr, frame.dinfo);
|
||||
ret = mISDN::mISDN_write_frame(stack->m_mISDNdevice, &frame, m_BC | FLG_MSG_DOWN, DL_RELEASE | REQUEST, 0, 0, NULL, TIMEOUT_1SEC);
|
||||
return OK;
|
||||
}
|
||||
|
||||
int mISDNChannel::bchan_destroy() {
|
||||
mISDN::iframe_t frame;
|
||||
mISDNStack* stack=mISDNStack::instance();
|
||||
int ret;
|
||||
|
||||
ret = mISDN_clear_stack(stack->m_mISDNdevice, m_port->b_stid[m_bchannel-1]);
|
||||
DBG("sending MGR_DELLAYER | REQUEST to device=%d for bchannel=%d addr=0x%08x dinfo=0x%08x\n",stack->m_mISDNdevice, m_bchannel,frame.addr, frame.dinfo);
|
||||
ret = mISDN::mISDN_write_frame(stack->m_mISDNdevice, &frame, m_BC | FLG_MSG_DOWN, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC);
|
||||
unregister_BC();
|
||||
m_port->b_port[m_bchannel-1]=NULL;
|
||||
m_port->b_addr[m_bchannel-1]=0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
#ifndef _mISDNCHANNEL_H_
|
||||
#define _mISDNCHANNEL_H_
|
||||
|
||||
#include "AmApi.h"
|
||||
#include "GWSession.h"
|
||||
//#include "GWCall.h"
|
||||
#include "globals.h"
|
||||
#include "mISDNStack.h"
|
||||
|
||||
extern char flip_table[256];
|
||||
void init_flip_bits(void);
|
||||
class mISDNChannel: public AmAudio {
|
||||
|
||||
public:
|
||||
/* AmAudio interface */
|
||||
int read(unsigned int user_ts, unsigned int size);
|
||||
int write(unsigned int user_ts, unsigned int size);
|
||||
/* buffers for audio data */
|
||||
//this buffer is feeded by isdn in 128byte chunks, while media processor eats 160bytes each time.
|
||||
std::string fromISDN_buffer;
|
||||
// pthread_mutex_t fromISDN_lock;
|
||||
// std::string toISDN_buffer;
|
||||
// pthread_mutex_t toISDN_lock;
|
||||
|
||||
int m_CR; //call reference (dinfo)
|
||||
int m_BC; //b-channel (addr)
|
||||
mISDNport *m_port;
|
||||
char m_bchannel;
|
||||
|
||||
char m_last_msg[MAX_MSG_SIZE]; //we copy here packet from kernel
|
||||
int m_last_msg_s;
|
||||
mISDN::iframe_t* m_frame; //there are pointers to places in m_last_msg buffer
|
||||
mISDN::Q931_info_t* m_qi; //
|
||||
char* m_ie_data; //
|
||||
|
||||
std::string m_caller; // caller number
|
||||
int m_TON_r,m_NPI_r,m_Screening_r,m_Presentation_r; //caler number attributes
|
||||
std::string m_called; //calee number
|
||||
int m_TON_d,m_NPI_d; //calee number attributes
|
||||
|
||||
mISDNChannel(); /* constructor */
|
||||
mISDNChannel(mISDNport *port);
|
||||
~mISDNChannel();
|
||||
void init();
|
||||
void setSession(GWSession*);
|
||||
GWSession* getSession();
|
||||
void unregister_CR();
|
||||
void unregister_BC();
|
||||
|
||||
|
||||
int processMsg(char *msg_buf,int msg_buf_s);
|
||||
int bchan_event(char *msg_buf,int msg_buf_s);
|
||||
int placeCall(const AmSipRequest &req, std::string tonumber, std::string fromnumber); /* place a call */
|
||||
int call();
|
||||
int accept(); /* accept a call */
|
||||
int hangup(); /* hangup a call */
|
||||
|
||||
//ie processing functions
|
||||
int GetIEchannel_id();
|
||||
int GetCallerNum();
|
||||
int GetCalledNum();
|
||||
|
||||
private:
|
||||
GWSession* m_session;
|
||||
|
||||
int bchan_create();
|
||||
int bchan_activate();
|
||||
int bchan_deactivate();
|
||||
int bchan_destroy();
|
||||
void bchan_receive(char *msg_buf,int msg_buf_s);
|
||||
// void bchan_send();
|
||||
|
||||
// int id;
|
||||
};
|
||||
#endif /*header*/
|
||||
@ -0,0 +1,99 @@
|
||||
#include "mISDNNames.h"
|
||||
//namespace mISDN { //this is hack to cover definition of dprint which is defined in mISDN and sems core
|
||||
extern "C" {
|
||||
#include <mISDNuser/mISDNlib.h>
|
||||
#include <mISDNuser/net_l2.h>
|
||||
#include <mISDNuser/l3dss1.h>
|
||||
}
|
||||
//}
|
||||
|
||||
const char* mISDNNames::isdn_prim[4] = {" REQUEST"," CONFIRM"," INDICATION"," RESPONSE" };
|
||||
const char* mISDNNames::IE_Names[37] ={ "bearer_capability", "cause", "call_id", "call_state", "channel_id", "facility",
|
||||
"progress", "net_fac", "notify", "display", "date", "keypad", "signal", "info_rate", "end2end_transit",
|
||||
"transit_delay_sel", "pktl_bin_para","pktl_window", "pkt_size", "closed_userg", "connected_nr", "connected_sub",
|
||||
"calling_nr", "calling_sub", "called_nr", "called_sub", "redirect_nr", "redirect_dn", "transit_net_sel",
|
||||
"restart_ind", "llc", "hlc", "useruser", "more_data", "sending_complete", "congestion_level", "comprehension_required"};
|
||||
|
||||
|
||||
const char* mISDNNames::Message(int i) {
|
||||
switch(i) {
|
||||
case CC_TIMEOUT: return "TIMEOUT";
|
||||
case CC_SETUP: return "SETUP";
|
||||
case CC_SETUP_ACKNOWLEDGE: return "SETUP_ACK";
|
||||
case CC_PROCEEDING: return "PROCEEDING";
|
||||
case CC_ALERTING: return "ALERTING";
|
||||
case CC_CONNECT: return "CONNECT";
|
||||
case CC_CONNECT_ACKNOWLEDGE: return "CONNECT_ACK";
|
||||
case CC_DISCONNECT: return "DISCONNECT";
|
||||
case CC_RELEASE: return "RELEASE";
|
||||
case CC_RELEASE_COMPLETE: return "RELEASE_COMP";
|
||||
case CC_INFORMATION: return "INFORMATION";
|
||||
case CC_PROGRESS: return "PROGRESS";
|
||||
case CC_NOTIFY: return "NOTIFY";
|
||||
case CC_SUSPEND: return "SUSPEND";
|
||||
case CC_SUSPEND_ACKNOWLEDGE: return "SUSPEND_ACK";
|
||||
case CC_SUSPEND_REJECT: return "SUSPEND_REJ";
|
||||
case CC_RESUME: return "RESUME";
|
||||
case CC_RESUME_ACKNOWLEDGE: return "RESUME_ACK";
|
||||
case CC_RESUME_REJECT: return "RESUME_REJ";
|
||||
case CC_HOLD: return "HOLD";
|
||||
case CC_HOLD_ACKNOWLEDGE: return "HOLD_ACK";
|
||||
case CC_HOLD_REJECT: return "HOLD_REJ";
|
||||
case CC_RETRIEVE: return "RETRIEVE";
|
||||
case CC_RETRIEVE_ACKNOWLEDGE: return "RETRIEVE_ACK";
|
||||
case CC_RETRIEVE_REJECT: return "RETRIEVE_REJ";
|
||||
case CC_FACILITY: return "FACILITY";
|
||||
case CC_STATUS: return "STATUS";
|
||||
case CC_RESTART: return "RESTART";
|
||||
case CC_RELEASE_CR: return "RELEASE_CR";
|
||||
case CC_NEW_CR: return "NEW_CR";
|
||||
case DL_ESTABLISH: return "DL_ESTABLISH";
|
||||
case DL_RELEASE: return "DL_RELEASE";
|
||||
case PH_ACTIVATE: return "PH_ACTIVATE";
|
||||
case PH_DEACTIVATE: return "PH_DEACTIVATE";
|
||||
case MGR_SHORTSTATUS: return "MGR_SHORTSTATUS";
|
||||
}
|
||||
return "ERROR";
|
||||
}
|
||||
const char* mISDNNames::NPI(int i) {
|
||||
switch(i) {
|
||||
case 0x00: return "Unknown";
|
||||
case 0x01: return "ISDN/Tel E.164";
|
||||
case 0x03: return "Data X.121 ";
|
||||
case 0x04: return "Telex F.69";
|
||||
case 0x08: return "National";
|
||||
case 0x09: return "Private";
|
||||
case 0x0F: return "Reserved";
|
||||
}
|
||||
return "ERROR";
|
||||
}
|
||||
const char* mISDNNames::TON(int i) {
|
||||
switch(i) {
|
||||
case 0x00: return "Unknown";
|
||||
case 0x01: return "International";
|
||||
case 0x02: return "National";
|
||||
case 0x03: return "NetworkSpec";
|
||||
case 0x04: return "Subscriber";
|
||||
case 0x06: return "abbreviated";
|
||||
case 0x07: return "Reserved";
|
||||
}
|
||||
return "ERROR";
|
||||
}
|
||||
const char* mISDNNames::Presentation(int i) {
|
||||
switch(i) {
|
||||
case 0x00: return "Presentation Allowed";
|
||||
case 0x01: return "Presentation Restricted";
|
||||
case 0x02: return "Number not available";
|
||||
case 0x03: return "Reserved";
|
||||
}
|
||||
return "ERROR";
|
||||
}
|
||||
const char* mISDNNames::Screening(int i) {
|
||||
switch(i) {
|
||||
case 0x00: return "User-privided not screened";
|
||||
case 0x01: return "User-privided verified and passed";
|
||||
case 0x02: return "User-privided verified and failed";
|
||||
case 0x03: return "Network provided";
|
||||
}
|
||||
return "ERROR";
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
#ifndef _MISDNNAMES_H_
|
||||
#define _MISDNNAMES_H_
|
||||
class mISDNNames {
|
||||
public:
|
||||
static const char* Message(int i);
|
||||
static const char* NPI(int i);
|
||||
static const char* TON(int i);
|
||||
static const char* Presentation(int i);
|
||||
static const char* Screening(int i);
|
||||
static const char* isdn_prim[4];
|
||||
static const char* IE_Names[37];
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@ -0,0 +1,548 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "AmSession.h"
|
||||
|
||||
#include "globals.h"
|
||||
#include "mISDNStack.h"
|
||||
#include "mISDNChannel.h"
|
||||
#include "mISDNNames.h"
|
||||
|
||||
using namespace mISDN;
|
||||
|
||||
mISDNStack* mISDNStack::_instance=NULL;
|
||||
|
||||
mISDNStack* mISDNStack::instance()
|
||||
{
|
||||
if(!_instance) {
|
||||
DBG("mISDNStack::instance spawning new\n");
|
||||
_instance = new mISDNStack();
|
||||
if(_instance->init() != OK){
|
||||
delete _instance;
|
||||
_instance = 0;
|
||||
} else {
|
||||
DBG("mISDNStack::instance start\n");
|
||||
_instance->start();
|
||||
init_flip_bits();
|
||||
}
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
mISDNChannel* mISDNStack::NewCR(mISDNport *port,mISDN::iframe_t *frame) {
|
||||
std::map<int,mISDNChannel*>::iterator iter=CR_map.find(frame->dinfo);;
|
||||
if(iter==CR_map.end()) {
|
||||
mISDNChannel* chan=NULL;
|
||||
DBG("This is new CR, spawning new object\n");
|
||||
chan = new mISDNChannel(port);
|
||||
CR_map[frame->dinfo]=chan;
|
||||
chan->m_CR=frame->dinfo;
|
||||
DBG("pointer to chan is %p\n",chan);
|
||||
return chan;
|
||||
} else {
|
||||
DBG("got previous CR porinter is %p\n",iter->second);
|
||||
return iter->second;
|
||||
}
|
||||
}
|
||||
|
||||
mISDNChannel* mISDNStack::FindCR(mISDN::iframe_t *frame) {
|
||||
std::map<int,mISDNChannel*>::iterator iter=CR_map.find(frame->dinfo);;
|
||||
if(iter!=CR_map.end()) {
|
||||
// DBG("got previous CR porinter is %p\n",iter->second);
|
||||
return iter->second;
|
||||
} else {
|
||||
ERROR("CR 0x%08x not found in CR_map\n",frame->dinfo);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
mISDNChannel* mISDNStack::FindBC(mISDN::iframe_t *frame) {
|
||||
std::map<int,mISDNChannel*>::iterator iter=BC_map.find(frame->addr&STACK_ID_MASK);;
|
||||
if(iter!=BC_map.end()) {
|
||||
// DBG("got previous BC porinter is %p\n",iter->second);
|
||||
return iter->second;
|
||||
} else {
|
||||
ERROR("BC address 0x%08x not found in BC_map\n",frame->addr);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int mISDNStack::placeCall(const AmSipRequest &req, GWSession *session, const std::string &tonumber, const std::string &fromnumber) {
|
||||
//we will have code to choose right port here.
|
||||
mISDNChannel *chan = new mISDNChannel(); //(device);
|
||||
if(chan==NULL) {
|
||||
ERROR("Cant allocate new mISDNChannel\n");
|
||||
return FAIL;
|
||||
}
|
||||
session->setOtherLeg(chan);
|
||||
chan->setSession(session);
|
||||
DBG("calling ((mISDNChannel*)m_pstndevice)->placeCall(m_req, tonumber, fromnumber);\n");
|
||||
return chan->placeCall(req, tonumber, fromnumber);
|
||||
}
|
||||
|
||||
|
||||
int mISDNStack::GetPortInfo() {
|
||||
int err;
|
||||
int i, num_cards, p;
|
||||
int useable, nt, pri;
|
||||
unsigned char buff[1025];
|
||||
iframe_t *frm = (iframe_t *)buff;
|
||||
stack_info_t *stinf;
|
||||
int device;
|
||||
|
||||
if ((device = mISDN_open()) < 0) {
|
||||
ERROR("mISDNStack::mISDNStack: mISDN_open() failed: ret=%d errno=%d (%s) Check for mISDN modules and device.\n", device, errno, strerror(errno));
|
||||
return FAIL;
|
||||
}
|
||||
DBG("mISDNStack::mISDNStack: mISDN_open %d\n",device);
|
||||
/* get number of stacks */
|
||||
i = 1;
|
||||
num_cards = mISDN_get_stack_count(device);
|
||||
if (num_cards <= 0) {
|
||||
ERROR("Found no card. Please be sure to load card drivers.\n");
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
/* loop the number of cards and get their info */
|
||||
while(i <= num_cards) {
|
||||
err = mISDN_get_stack_info(device, i, buff, sizeof(buff));
|
||||
if (err <= 0) { ERROR("mISDN_get_stack_info() failed: port=%d err=%d\n", i, err); break; }
|
||||
stinf = (stack_info_t *)&frm->data.p;
|
||||
nt = pri = 0;
|
||||
useable = 1;
|
||||
switch(stinf->pid.protocol[0] & ~ISDN_PID_FEATURE_MASK) {
|
||||
case ISDN_PID_L0_TE_S0: INFO("Port %2d: TE-mode BRI S/T interface line (for phone lines)\n",i); break;
|
||||
case ISDN_PID_L0_NT_S0: nt = 1; INFO("Port %2d: NT-mode BRI S/T interface port (for phones)\n",i); break;
|
||||
case ISDN_PID_L0_TE_E1: pri = 1; INFO("Port %2d: TE-mode PRI E1 interface line (for phone lines)\n",i); break;
|
||||
case ISDN_PID_L0_NT_E1: nt = 1;pri = 1; INFO("Port %2d: NT-mode PRI E1 interface port (for phones)\n",i); break;
|
||||
default: useable = 0; ERROR("unknown type 0x%08x\n",stinf->pid.protocol[0]);
|
||||
}
|
||||
if (nt) {
|
||||
if (stinf->pid.protocol[1] == 0) { useable = 0; INFO(" -> Missing layer 1 NT-mode protocol.\n"); }
|
||||
p = 2; while(p <= MAX_LAYER_NR) { if (stinf->pid.protocol[p]) {useable = 0; INFO(" -> Layer %d protocol 0x%08x is detected, but not allowed for NT.\n", p, stinf->pid.protocol[p]); } p++; }
|
||||
if (useable) {
|
||||
if (pri) INFO(" -> Interface is Point-To-Point (PRI).\n");
|
||||
else INFO(" -> Interface can be Poin-To-Point/Multipoint.\n");
|
||||
}
|
||||
} else {
|
||||
if (stinf->pid.protocol[1] == 0) { useable = 0; INFO(" -> Missing layer 1 protocol.\n"); }
|
||||
if (stinf->pid.protocol[2] == 0) { useable = 0; INFO(" -> Missing layer 2 protocol.\n"); }
|
||||
if (stinf->pid.protocol[2] & ISDN_PID_L2_DF_PTP) { INFO(" -> Interface is Poin-To-Point.\n"); }
|
||||
if (stinf->pid.protocol[3] == 0) { useable = 0; INFO(" -> Missing layer 3 protocol.\n"); }
|
||||
else { switch(stinf->pid.protocol[3] & ~ISDN_PID_FEATURE_MASK) {
|
||||
case ISDN_PID_L3_DSS1USER: INFO(" -> Protocol: DSS1 (Euro ISDN)\n"); break;
|
||||
default: useable = 0; INFO(" -> Protocol: unknown protocol 0x%08x\n",stinf->pid.protocol[3]);
|
||||
}
|
||||
}
|
||||
p = 4; while(p <= MAX_LAYER_NR) { if (stinf->pid.protocol[p]) { useable = 0; INFO(" -> Layer %d protocol 0x%08x is detected, but not allowed for TE.\n", p, stinf->pid.protocol[p]); } p++; }
|
||||
INFO(" -> childcnt: %d\n",stinf->childcnt);
|
||||
}
|
||||
if (!useable) ERROR(" * Port %2d NOT useable. (maybe somethind is using it already?)\n",i);
|
||||
i++;
|
||||
}
|
||||
/* close mISDN */
|
||||
if ((err = mISDN_close(device))) {
|
||||
ERROR("mISDN_close() failed: err=%d '%s'\n", err, strerror(err));
|
||||
return FAIL;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
mISDNStack::mISDNStack() : m_mISDNdevice(0),m_entity(0) {
|
||||
mISDNport_first=NULL;
|
||||
}
|
||||
|
||||
mISDNStack::~mISDNStack() {
|
||||
}
|
||||
|
||||
int mISDNStack::init() {
|
||||
unsigned char buff[1025];
|
||||
iframe_t *frm = (iframe_t *)buff;
|
||||
int ret;
|
||||
struct mISDNport *mISDNport, **mISDNportp;
|
||||
int i, cnt,port;
|
||||
int pri, ports;
|
||||
int nt, ptp, ptmp;
|
||||
net_stack_t *nst;
|
||||
manager_t *mgr;
|
||||
layer_info_t li;
|
||||
stack_info_t *stinf;
|
||||
|
||||
|
||||
if ((m_mISDNdevice = mISDN_open()) < 0) { ERROR("mISDNStack::init: mISDN_open() failed: ret=%d errno=%d (%s) Check for mISDN modules and device.\n", m_mISDNdevice, errno, strerror(errno)); return FAIL; }
|
||||
DBG("mISDNStack::init: mISDN_opened %d\n",m_mISDNdevice);
|
||||
/* create entity for layer 3 TE-mode */
|
||||
mISDN_write_frame(m_mISDNdevice, buff, 0, MGR_NEWENTITY | REQUEST, 0, 0, NULL, TIMEOUT_1SEC);
|
||||
ret = mISDN_read_frame(m_mISDNdevice, frm, sizeof(iframe_t), 0, MGR_NEWENTITY | CONFIRM, TIMEOUT_1SEC);
|
||||
if (ret < (int)mISDN_HEADER_LEN) { ERROR("Cannot request MGR_NEWENTITY from mISDN. header too small.\n");return FAIL; }
|
||||
m_entity = frm->dinfo & 0xffff;
|
||||
if (!m_entity) { ERROR("Cannot request MGR_NEWENTITY from mISDN. Exitting due to software bug.\n"); return FAIL; }
|
||||
DBG("our entity for l3-processes is 0x%08x.\n", m_entity);
|
||||
m_crcount=1; //entity and crcount is used to generate uniqe addr for outgoing calls
|
||||
|
||||
cnt = mISDN_get_stack_count(m_mISDNdevice);
|
||||
if(cnt<=0) { ERROR("no devices\n"); return FAIL;}
|
||||
for(port=1;port<=cnt;port++) {
|
||||
pri = ports = nt = 0;
|
||||
ret = mISDN_get_stack_info(m_mISDNdevice, port, buff, sizeof(buff));
|
||||
if (ret < 0) { ERROR("Cannot get stack info for port %d (ret=%d)\n", port, ret); }
|
||||
stinf = (stack_info_t *)&frm->data.p;
|
||||
switch(stinf->pid.protocol[0] & ~ISDN_PID_FEATURE_MASK) {
|
||||
case ISDN_PID_L0_TE_S0: DBG("TE-mode BRI S/T interface line\n"); pri = 0; nt = 0; break;
|
||||
case ISDN_PID_L0_NT_S0: DBG("NT-mode BRI S/T interface port\n"); pri = 0; nt = 1; break;
|
||||
case ISDN_PID_L0_TE_E1: DBG("TE-mode PRI E1 interface line\n"); pri = 1; nt = 0; break;
|
||||
case ISDN_PID_L0_NT_E1: DBG("LT-mode PRI E1 interface port\n"); pri = 1; nt = 1; break;
|
||||
default:
|
||||
ERROR("unknown port(%d) type 0x%08x\n", port, stinf->pid.protocol[0]);
|
||||
}
|
||||
if (nt) {
|
||||
DBG("Port %d (nt) proto1=0x%08x proto2=0x%08x\n",port, stinf->pid.protocol[1],stinf->pid.protocol[2]);
|
||||
if (stinf->pid.protocol[1] == 0) { ERROR("Given port %d: Missing layer 1 NT-mode protocol.\n", port); }
|
||||
if (stinf->pid.protocol[2]) { ERROR("Given port %d: Layer 2 protocol 0x%08x is detected, but not allowed for NT.\n", port, stinf->pid.protocol[2]); }
|
||||
ERROR("NT mode not supported yet\n");
|
||||
return FAIL;
|
||||
} else { //(te)
|
||||
DBG("Port %d (te) proto1=0x%08x proto2=0x%08x proto3=0x%08x proto4=0x%08x (nul proto4 is good here)\n",port, stinf->pid.protocol[1],stinf->pid.protocol[2],stinf->pid.protocol[3],stinf->pid.protocol[4]);
|
||||
if (stinf->pid.protocol[1] == 0) { ERROR("Given port %d: Missing layer 1 protocol.\n", port);}
|
||||
if (stinf->pid.protocol[2] == 0) { ERROR("Given port %d: Missing layer 2 protocol.\n", port);}
|
||||
if (stinf->pid.protocol[2] & ISDN_PID_L2_DF_PTP) { ptp=1;DBG("Port %d is point-to-point.\n",port); } else { ptp=0;}
|
||||
if (stinf->pid.protocol[3] == 0) { ERROR("Given port %d: Missing layer 3 protocol.\n", port);}
|
||||
else {
|
||||
switch(stinf->pid.protocol[3] & ~ISDN_PID_FEATURE_MASK) {
|
||||
case ISDN_PID_L3_DSS1USER: break;
|
||||
default:
|
||||
ERROR("Given port %d: own protocol 0x%08x", port,stinf->pid.protocol[3]);
|
||||
}
|
||||
}
|
||||
if (stinf->pid.protocol[4]) { ERROR("Given port %d: Layer 4 protocol not allowed.\n", port); }
|
||||
}
|
||||
/* add mISDNport structure */
|
||||
mISDNportp = &mISDNport_first;
|
||||
while(*mISDNportp)
|
||||
mISDNportp = &((*mISDNportp)->next);
|
||||
mISDNport = (struct mISDNport *)malloc(sizeof(struct mISDNport));
|
||||
*mISDNportp = mISDNport;
|
||||
|
||||
/* allocate ressources of port */
|
||||
mISDNport->d_stid = stinf->id;
|
||||
DBG("d_stid = 0x%x.\n", mISDNport->d_stid);
|
||||
/* create layer intance */
|
||||
memset(&li, 0, sizeof(li));
|
||||
strcpy(&li.name[0], "te l4");
|
||||
li.object_id = -1;
|
||||
li.extentions = 0;
|
||||
// li.pid.protocol[4] = ISDN_PID_L4_CAPI20;
|
||||
li.pid.protocol[4] = ISDN_PID_L4_B_USER;
|
||||
li.pid.layermask = ISDN_LAYER(4);
|
||||
li.st = mISDNport->d_stid;
|
||||
DBG("setting mISDN_new_layer on port %d, li.st=0x%08x \n",port,li.st);
|
||||
ret = mISDN_new_layer(m_mISDNdevice, &li);
|
||||
if (ret) { ERROR("Cannot add layer4 of port %d (ret %d)\n", port, ret);
|
||||
// mISDNport_close(mISDNport);
|
||||
return FAIL;
|
||||
}
|
||||
mISDNport->upper_id = li.id;
|
||||
ret = mISDN_register_layer(m_mISDNdevice, mISDNport->d_stid, mISDNport->upper_id);
|
||||
if (ret) { ERROR("Cannot register layer4 of port %d\n", port);
|
||||
// mISDNport_close(mISDNport);
|
||||
return FAIL;
|
||||
}
|
||||
mISDNport->lower_id = mISDN_get_layerid(m_mISDNdevice, mISDNport->d_stid, 3);
|
||||
if (mISDNport->lower_id < 0) { ERROR("Cannot get layer(%d) id of port %d\n", nt?1:3, port);
|
||||
// mISDNport_close(mISDNport);
|
||||
return FAIL;
|
||||
}
|
||||
mISDNport->upper_id = mISDN_get_layerid(m_mISDNdevice, mISDNport->d_stid, 4);
|
||||
if (mISDNport->upper_id < 0) { ERROR("Cannot get layer4 id of port %d\n", port);
|
||||
// mISDNport_close(mISDNport);
|
||||
return FAIL;
|
||||
}
|
||||
DBG("Layer 4 of port %d added (0x%08x) lower_id=0x%08x.\n", port,mISDNport->upper_id,mISDNport->lower_id);
|
||||
|
||||
mISDNport->b_num = stinf->childcnt;
|
||||
mISDNport->portnum = port;
|
||||
mISDNport->ntmode = nt;
|
||||
mISDNport->pri = pri;
|
||||
mISDNport->ptp = ptp;
|
||||
DBG("Port has %d b-channels.\n", mISDNport->b_num);
|
||||
i = 0;
|
||||
while(i < mISDNport->b_num) {
|
||||
mISDNport->b_stid[i] = stinf->child[i];
|
||||
DBG("b_stid[%d] = 0x%x.\n", i, mISDNport->b_stid[i]);
|
||||
i++;
|
||||
}
|
||||
/* if te-mode, query state link */
|
||||
// if (!mISDNport->ntmode) {
|
||||
iframe_t act;
|
||||
/* L2 */
|
||||
act.prim = MGR_SHORTSTATUS | REQUEST;
|
||||
act.addr = mISDNport->upper_id | MSG_BROADCAST;
|
||||
act.dinfo = SSTATUS_BROADCAST_BIT | SSTATUS_ALL;
|
||||
act.len = 0;
|
||||
DBG("sending MGR_SHORTSTATUS request for port %d addr=0x%08x.\n", port,act.addr);
|
||||
mISDN_write(m_mISDNdevice, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC);
|
||||
// }
|
||||
act.prim = PH_ACTIVATE| REQUEST;
|
||||
act.addr = mISDNport->upper_id | FLG_MSG_DOWN;
|
||||
act.dinfo = 0;
|
||||
act.len = 0;
|
||||
DBG("sending PH_ACTIVATE request (l1 up) for port %d addr=0x%08x.\n", port,act.addr);
|
||||
mISDN_write(m_mISDNdevice, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC);
|
||||
/* if ptp AND te-mode, pull up the link */
|
||||
// if (mISDNport->ptp && !mISDNport->ntmode) {
|
||||
// iframe_t act;
|
||||
/* L2 */
|
||||
act.prim = DL_ESTABLISH | REQUEST;
|
||||
// act.addr = (mISDNport->upper_id & ~LAYER_ID_MASK) | 4 | FLG_MSG_DOWN;
|
||||
act.addr = mISDNport->upper_id | FLG_MSG_DOWN;
|
||||
act.dinfo = 0;
|
||||
act.len = 0;
|
||||
DBG("sending DL_ESTABLISH request (l2 up) for port %d addr=0x%08x.\n", port,act.addr);
|
||||
mISDN_write(m_mISDNdevice, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC);
|
||||
// }
|
||||
/* initially, we assume that the link is down, exept for nt-ptmp */
|
||||
mISDNport->l2link = (mISDNport->ntmode && !mISDNport->ptp)?1:0;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void mISDNStack::on_stop(void) {
|
||||
unsigned char buff[1025];
|
||||
DBG("mISDNStack::on_stop\n");
|
||||
if (m_mISDNdevice >= 0) {
|
||||
mISDN_write_frame(m_mISDNdevice, buff, 0, MGR_DELENTITY | REQUEST, m_entity, 0, NULL, TIMEOUT_1SEC);
|
||||
mISDN_close(m_mISDNdevice); m_mISDNdevice = -1;
|
||||
DBG("mISDN device closed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
int mISDNStack::GenerateCR() {
|
||||
int cr;
|
||||
//llock()
|
||||
if (m_crcount++ > 0x7fff)
|
||||
m_crcount = 0x0001;
|
||||
cr=(m_entity<<16) | m_crcount;
|
||||
//unlock();
|
||||
return cr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void mISDNStack::run()
|
||||
{
|
||||
DBG("running mISDNStack::run...\n");
|
||||
char msg_buf[MAX_MSG_SIZE];
|
||||
int msg_buf_s;
|
||||
iframe_t* frame;
|
||||
mISDNport *port;
|
||||
mISDNChannel* channel;
|
||||
|
||||
while(true){
|
||||
// DBG("tick\n");
|
||||
msg_buf_s = mISDN_read(m_mISDNdevice,&msg_buf,MAX_MSG_SIZE, TIMEOUT_10SEC);;
|
||||
if(msg_buf_s == -1){
|
||||
switch(errno){
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
continue;
|
||||
default: break;
|
||||
};
|
||||
ERROR("running mISDNStack::run Error in mISDN_read %s\n",strerror(errno));
|
||||
break;
|
||||
}
|
||||
if(msg_buf_s == 0){/*DBG("got 0, cotinuing\n");*/ continue; }
|
||||
|
||||
frame=(iframe_t*)msg_buf;
|
||||
if (frame->dinfo==(signed long)0xffffffff && frame->prim==(PH_DATA|CONFIRM)) {
|
||||
ERROR("SERIOUS BUG, dinfo == 0xffffffff, prim == PH_DATA | CONFIRM !!!!\n");
|
||||
}
|
||||
// DBG("Got something msg_buf_s=%d prim=0x%08x addr=0x%08x dinfo=0x%08x\n",msg_buf_s,frame->prim,frame->addr,frame->dinfo);
|
||||
port = mISDNport_first;
|
||||
while(port) {
|
||||
if ((frame->addr&MASTER_ID_MASK) == (unsigned int)(port->upper_id&MASTER_ID_MASK))
|
||||
break;
|
||||
port = port->next;
|
||||
}
|
||||
if (!port) {
|
||||
ERROR("message belongs to no mISDNport: prim(0x%x) addr(0x%x) msg->len(%d)\n", frame->prim, frame->addr, msg_buf_s);
|
||||
continue;
|
||||
}
|
||||
if (frame->addr&FLG_CHILD_STACK) { /* child stack */
|
||||
/* b-channel data and messages */
|
||||
// DBG("processing child stack for %s (%d) prim(0x%x) addr(0x%x) dinfo=0x%x msg->len(%d) \n",port->name, port->portnum, frame->prim, frame->addr,frame->dinfo, msg_buf_s);
|
||||
channel=FindBC(frame);
|
||||
if(channel==NULL) {
|
||||
DBG("b-channel is not associated to an ISDNPort (address 0x%x), ignoring.\n", frame->addr);
|
||||
continue;
|
||||
}
|
||||
if(channel->bchan_event(msg_buf,msg_buf_s)!=OK)
|
||||
ERROR("Error processing bchan_event in channel object\n");
|
||||
continue;
|
||||
} else { /* d-message */
|
||||
//next two are debug packet dumps uncomment if needed
|
||||
l1l2l3_trace_header(port, frame->addr, frame->prim, 1);
|
||||
if(msg_buf_s>16) DBG("IE: %s",mISDNStack::dumpIE(msg_buf,msg_buf_s).c_str());
|
||||
/* general messages not(yet) related to CR */
|
||||
switch(frame->prim) {
|
||||
case CC_NEW_CR | INDICATION:
|
||||
DBG("CC_NEW_CR | INDICATION for %s (%d) \n",port->name, port->portnum);
|
||||
channel=NewCR(port,frame);
|
||||
continue; break;
|
||||
case CC_NEW_CR | CONFIRM:
|
||||
DBG("CC_NEW_CR | CONFIRM for %s (%d) Is this possible?\n",port->name, port->portnum);
|
||||
continue; break;
|
||||
case CC_RELEASE_CR | INDICATION:
|
||||
DBG("CC_RELEASE_CR | INDICATION for %s (%d) \n",port->name, port->portnum);
|
||||
channel=FindCR(frame);
|
||||
if(channel!=NULL) {
|
||||
DBG("should delete channel=%p but we will leave it to have media procesor happy just unregister CR from map\n",channel);
|
||||
channel->unregister_CR();
|
||||
// delete(channel);
|
||||
} else ERROR("Channel not found for CC_RELEASE_CR | INDICATION %s (%d) prim(0x%x) addr(0x%x) msg->len(%d) \n",port->name, port->portnum, frame->prim, frame->addr, msg_buf_s);
|
||||
continue; break;
|
||||
case CC_RELEASE_CR | CONFIRM:
|
||||
DBG("CC_RELEASE_CR | CONFIRM for %s (%d) Is this possible?\n",port->name, port->portnum);
|
||||
continue; break;
|
||||
case MGR_SHORTSTATUS | INDICATION:
|
||||
case MGR_SHORTSTATUS | CONFIRM:
|
||||
DBG("MGR_SHORTSTATUS ind or confirm for %s (%d) prim(0x%x) addr(0x%x) msg->len(%d) \n",port->name, port->portnum, frame->prim, frame->addr, msg_buf_s);
|
||||
switch(frame->dinfo) {
|
||||
case SSTATUS_L1_ACTIVATED: port->l1link = 1;DBG("MGR_SHORTSTATUS->SSTATUS_L1_ACTIVATED for %s (%d) prim(0x%x) addr(0x%x) msg->len(%d) \n",port->name, port->portnum, frame->prim, frame->addr, msg_buf_s);break;
|
||||
case SSTATUS_L1_DEACTIVATED: port->l1link = 0;DBG("MGR_SHORTSTATUS->SSTATUS_L1_DEACTIVATED for %s (%d) prim(0x%x) addr(0x%x) msg->len(%d) \n",port->name, port->portnum, frame->prim, frame->addr, msg_buf_s);break;
|
||||
case SSTATUS_L2_ESTABLISHED: port->l2link = 1;DBG("MGR_SHORTSTATUS->SSTATUS_L2_ESTABLISHED for %s (%d) prim(0x%x) addr(0x%x) msg->len(%d) \n",port->name, port->portnum, frame->prim, frame->addr, msg_buf_s);break;
|
||||
case SSTATUS_L2_RELEASED: port->l2link = 0;DBG("MGR_SHORTSTATUS->SSTATUS_L2_RELEASED for %s (%d) prim(0x%x) addr(0x%x) msg->len(%d) \n",port->name, port->portnum, frame->prim, frame->addr, msg_buf_s);break;
|
||||
}
|
||||
continue; break;
|
||||
case PH_ACTIVATE | CONFIRM:
|
||||
case PH_ACTIVATE | INDICATION:
|
||||
DBG("PH_ACTIVATE ind or confirm for %s (%d) prim(0x%x) addr(0x%x) msg->len(%d) \n",port->name, port->portnum, frame->prim, frame->addr, msg_buf_s);
|
||||
port->l1link = 1;
|
||||
continue; break;
|
||||
case PH_DEACTIVATE | CONFIRM:
|
||||
case PH_DEACTIVATE | INDICATION:
|
||||
DBG("PH_DEACTIVATE ind or confirm for %s (%d) prim(0x%x) addr(0x%x) msg->len(%d) \n",port->name, port->portnum, frame->prim, frame->addr, msg_buf_s);
|
||||
port->l1link = 0;
|
||||
continue; break;
|
||||
case PH_CONTROL | CONFIRM:
|
||||
case PH_CONTROL | INDICATION:
|
||||
DBG("Received PH_CONTROL for port %d (%s).\n", port->portnum, port->name);
|
||||
continue; break;
|
||||
case DL_ESTABLISH | INDICATION:
|
||||
case DL_ESTABLISH | CONFIRM:
|
||||
DBG("DL_ESTABLISH ind or confirm for %s (%d) prim(0x%x) addr(0x%x) msg->len(%d) \n",port->name, port->portnum, frame->prim, frame->addr, msg_buf_s);
|
||||
port->l2link = 1;
|
||||
continue; break;
|
||||
case DL_RELEASE | INDICATION:
|
||||
case DL_RELEASE | CONFIRM:
|
||||
DBG("DL_RELEASE ind or confirm for %s (%d) prim(0x%x) addr(0x%x) msg->len(%d) \n",port->name, port->portnum, frame->prim, frame->addr, msg_buf_s);
|
||||
port->l2link = 0;
|
||||
continue; break;
|
||||
default:
|
||||
DBG("GOT d-msg from %s port %d prim 0x%x dinfo 0x%x addr 0x%x\n", (port->ntmode)?"NT":"TE", port->portnum, frame->prim, frame->dinfo, frame->addr);
|
||||
}
|
||||
/* d-message */
|
||||
if (port->ntmode) {
|
||||
ERROR("NT mode not supported yet\n");
|
||||
continue;
|
||||
} else {
|
||||
/* l3-data is sent to channel object for processing */
|
||||
channel=FindCR(frame);
|
||||
if(channel==NULL) {
|
||||
ERROR("Cant find channel for message\n");
|
||||
continue;
|
||||
}
|
||||
if(channel->processMsg(msg_buf,msg_buf_s)!=OK)
|
||||
ERROR("Error processing msg in channel object\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////////////////////////////
|
||||
void mISDNStack::l1l2l3_trace_header(struct mISDNport *mISDNport, int port, unsigned long prim, int direction)
|
||||
{
|
||||
|
||||
string msgtext;
|
||||
msgtext.assign(mISDNNames::Message(prim&0xffffff00));
|
||||
msgtext.append(mISDNNames::isdn_prim[prim&0x00000003]);
|
||||
|
||||
/* add direction */
|
||||
if (direction && (prim&0xffffff00)!=CC_NEW_CR && (prim&0xffffff00)!=CC_RELEASE_CR) {
|
||||
if (mISDNport) {
|
||||
if (mISDNport->ntmode) { if (direction == DIRECTION_OUT) msgtext.append(" N->U"); else msgtext.append(" N<-U"); }
|
||||
else { if (direction == DIRECTION_OUT) msgtext.append(" U->N"); else msgtext.append(" U<-N"); }
|
||||
}
|
||||
}
|
||||
DBG("prim=0x%08lx port=0x%08x %s\n",prim,port, msgtext.c_str());
|
||||
}
|
||||
|
||||
#define QI_DUMP(_x_,_y_) sprintf(x," %25s off=0x%04x ridx=0x%04x res1=0x%04x cs_flg=0x%04x",_y_,qi->_x_.off,qi->_x_.ridx,qi->_x_.res1,qi->_x_.cs_flg);ret.append(x);\
|
||||
if(qi->_x_.off>0) {sprintf(x," 0x%02hhx 0x%02hhx 0x%02hhx 0x%02hhx\n",\
|
||||
y[qi->_x_.off],\
|
||||
y[qi->_x_.off+1],\
|
||||
y[qi->_x_.off+2],\
|
||||
y[qi->_x_.off+3]\
|
||||
);ret.append(x);} else ret.append(" \n");
|
||||
std::string mISDNStack::dumpIE(char *buf,int len) {
|
||||
Q931_info_t* qi;
|
||||
ie_info_t *ie;
|
||||
char buf2[MAX_MSG_SIZE];
|
||||
u16 *arr;
|
||||
char *p,*x,*y;
|
||||
std::string ret,tmp_a,tmp_b;
|
||||
int i,j;
|
||||
|
||||
qi = (Q931_info_t*)(buf + mISDN_HEADER_LEN);
|
||||
x =(char*)&buf2;
|
||||
arr=(u16 *)qi;
|
||||
ie=&(qi->bearer_capability);
|
||||
p=(char*)qi;
|
||||
y=p;
|
||||
y+=L3_EXTRA_SIZE;
|
||||
sprintf(x,"type=0x%02hhx crlen=0x%02hhx cr=0x%04x\n",qi->type,qi->crlen,qi->cr);ret.append(x);
|
||||
for(i=0;i<37;i++) {
|
||||
if(ie[i].off>0) {
|
||||
sprintf(x," %25s off=0x%04x ridx=0x%04x res1=0x%04x cs_flg=0x%04x",mISDNNames::IE_Names[i],ie[i].off,ie[i].ridx,ie[i].res1,ie[i].cs_flg);ret.append(x);
|
||||
tmp_a.assign("");tmp_b.assign(" ");
|
||||
for(j=0;j<y[ie[i].off+1];j++) {
|
||||
sprintf(x," 0x%02hhx",y[ie[i].off+2+j]);tmp_a.append(x);
|
||||
sprintf(x,"%c",(y[ie[i].off+2+j]>32)?y[ie[i].off+2+j]:'.');tmp_b.append(x);
|
||||
}
|
||||
ret.append(tmp_a);ret.append(tmp_b);ret.append("\n");
|
||||
if((ie[i].repeated>0) || (ie[i].ridx>0)) {
|
||||
QI_DUMP(ext[i].ie,"extinfo ")
|
||||
sprintf(x," extinfo[%d]: cs.codeset=0x%04x cs.locked=0x%04x cs.res1=0x%04x cs.len=0x%04x | v.codeset=0x%04x v.res1=0x%04x v.val=0x%04x\n",
|
||||
i,qi->ext[i].cs.codeset,qi->ext[i].cs.locked,qi->ext[i].cs.res1,qi->ext[i].cs.len,
|
||||
qi->ext[i].v.codeset,qi->ext[i].v.res1,qi->ext[i].v.val);
|
||||
ret.append(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
ret.append("=========================\n");
|
||||
for(i=0;i<=(len-mISDN_HEADER_LEN)/2;i++) {
|
||||
sprintf(x," 0x%04x (%c %c),",arr[i],
|
||||
(p[2*i]>=32)?p[2*i]:'.',
|
||||
((p[2*i+1])>=32)?p[2*i+1]:'.');
|
||||
ret.append(x);
|
||||
}
|
||||
ret.append("\n");
|
||||
if(len>(mISDN_HEADER_LEN+L3_EXTRA_SIZE)) {
|
||||
ret.append("tail:");
|
||||
for(i=0;i<=(len-(mISDN_HEADER_LEN+L3_EXTRA_SIZE));i++) {
|
||||
sprintf(x," 0x%02hhx (%c),",y[i],(y[i]>32)?y[i]:'.');ret.append(x);
|
||||
}
|
||||
ret.append("\n");
|
||||
} else ret.append("no tail\n");
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
#ifndef _MISDNSTACK_H_
|
||||
#define _MISDNSTACK_H_
|
||||
|
||||
#include <pthread.h>
|
||||
#include "globals.h"
|
||||
namespace mISDN { //this is hack to cover definition of dprint which is defined in mISDN and sems core
|
||||
extern "C" {
|
||||
#include <mISDNuser/mISDNlib.h>
|
||||
#include <mISDNuser/net_l2.h>
|
||||
#include <mISDNuser/l3dss1.h>
|
||||
}
|
||||
}
|
||||
#include "GWSession.h"
|
||||
|
||||
class mISDNChannel; //forward
|
||||
|
||||
struct mISDNport {
|
||||
mISDN::net_stack_t nst; /* MUST be the first entry, so &nst equals &mISDNlist */
|
||||
mISDN::manager_t mgr;
|
||||
int upper_id; /* id to transfer data down */
|
||||
int lower_id; /* id to transfer data up */
|
||||
int d_stid;
|
||||
// mISDN::msg_queue_t downqueue; /* l4->l3 */
|
||||
struct mISDNport *next;
|
||||
char name[64]; /* name of port, if available */
|
||||
int portnum; /* port number 1..n */
|
||||
int ptp; /* if ptp is set, we keep track of l2link */
|
||||
int l1link; /* if l1 is available */
|
||||
int l2link; /* if l2 is available */
|
||||
int use; /* counts the number of port that uses this port */
|
||||
int ntmode; /* is TRUE if port is nt mode */
|
||||
int pri; /* is TRUE if port is a primary rate interface */
|
||||
int b_num; /* number of bchannels */
|
||||
class mISDNChannel *b_port[128]; /* bchannel assigned to port object */
|
||||
int b_stid[128]; /* stack ids */
|
||||
unsigned long b_addr[128]; /* addresses on b-channels */
|
||||
int b_state[128]; /* statemachine, 0 = IDLE */
|
||||
};
|
||||
|
||||
//forward declaration;
|
||||
class mISDNChannel;
|
||||
|
||||
class mISDNStack: public AmThread
|
||||
{
|
||||
static mISDNStack* _instance;
|
||||
int init();
|
||||
void run();
|
||||
void on_stop();
|
||||
|
||||
public:
|
||||
mISDNStack();
|
||||
~mISDNStack();
|
||||
|
||||
int placeCall(const AmSipRequest &req, GWSession *session, const std::string &tonumber, const std::string &fromnumber);
|
||||
int m_mISDNdevice;
|
||||
int m_entity;
|
||||
int m_crcount;
|
||||
mISDNport *mISDNport_first;
|
||||
/* here are some maps and functions to lookup CallReference and B-channel number and decide to which channel object pass message */
|
||||
std::map<int,mISDNChannel*> CR_map;
|
||||
std::map<int,mISDNChannel*> BC_map;
|
||||
mISDNChannel* NewCR(mISDNport *port,mISDN::iframe_t *frame);
|
||||
mISDNChannel* FindCR(mISDN::iframe_t *frame);
|
||||
mISDNChannel* FindBC(mISDN::iframe_t *frame);
|
||||
|
||||
static mISDNStack* instance();
|
||||
static int GetPortInfo();
|
||||
int GenerateCR();
|
||||
void l1l2l3_trace_header(struct mISDNport *mISDNport, int port, unsigned long prim, int direction);
|
||||
std::string dumpIE(char *buf,int len);
|
||||
|
||||
};
|
||||
#define DIRECTION_NONE 0
|
||||
#define DIRECTION_OUT 1
|
||||
#define DIRECTION_IN 2
|
||||
enum {
|
||||
B_STATE_IDLE, /* not open */
|
||||
B_STATE_ACTIVATING, /* DL_ESTABLISH sent */
|
||||
B_STATE_ACTIVE, /* channel active */
|
||||
B_STATE_DEACTIVATING, /* DL_RELEASE sent */
|
||||
B_STATE_EXPORTING, /* BCHANNEL_ASSIGN sent */
|
||||
B_STATE_REMOTE, /* bchannel assigned to remote application */
|
||||
B_STATE_IMPORTING, /* BCHANNEL_REMOVE sent */
|
||||
};
|
||||
#define ISDN_PID_L3_B_USER 0x430000ff
|
||||
#define ISDN_PID_L4_B_USER 0x440000ff
|
||||
#endif
|
||||
|
||||
|
||||
Loading…
Reference in new issue