diff --git a/apps/gateway/GWSession.cpp b/apps/gateway/GWSession.cpp new file mode 100644 index 00000000..20212bdc --- /dev/null +++ b/apps/gateway/GWSession.cpp @@ -0,0 +1,147 @@ +#include +#include +#include +#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; +} diff --git a/apps/gateway/GWSession.h b/apps/gateway/GWSession.h new file mode 100644 index 00000000..c0eade6f --- /dev/null +++ b/apps/gateway/GWSession.h @@ -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 diff --git a/apps/gateway/GatewayFactory.cpp b/apps/gateway/GatewayFactory.cpp new file mode 100644 index 00000000..8cc7f267 --- /dev/null +++ b/apps/gateway/GatewayFactory.cpp @@ -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()); +} + + diff --git a/apps/gateway/GatewayFactory.h b/apps/gateway/GatewayFactory.h new file mode 100644 index 00000000..0d765e5e --- /dev/null +++ b/apps/gateway/GatewayFactory.h @@ -0,0 +1,31 @@ +#ifndef _ISDNGATEWAYFACTORY_H_ +#define _ISDNGATEWAYFACTORY_H_ + +#include "GWSession.h" +#include "AmConfigReader.h" +#include +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 + diff --git a/apps/gateway/Makefile b/apps/gateway/Makefile new file mode 100644 index 00000000..6a8547dc --- /dev/null +++ b/apps/gateway/Makefile @@ -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 diff --git a/apps/gateway/gateway.conf b/apps/gateway/gateway.conf new file mode 100644 index 00000000..4aa03d86 --- /dev/null +++ b/apps/gateway/gateway.conf @@ -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=*** \ No newline at end of file diff --git a/apps/gateway/globals.h b/apps/gateway/globals.h new file mode 100644 index 00000000..498873d6 --- /dev/null +++ b/apps/gateway/globals.h @@ -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 diff --git a/apps/gateway/mISDNChannel.cpp b/apps/gateway/mISDNChannel.cpp new file mode 100644 index 00000000..f4511b7f --- /dev/null +++ b/apps/gateway/mISDNChannel.cpp @@ -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 + +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::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::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; +} + diff --git a/apps/gateway/mISDNChannel.h b/apps/gateway/mISDNChannel.h new file mode 100644 index 00000000..53e2fbf0 --- /dev/null +++ b/apps/gateway/mISDNChannel.h @@ -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*/ diff --git a/apps/gateway/mISDNNames.cpp b/apps/gateway/mISDNNames.cpp new file mode 100644 index 00000000..d7704163 --- /dev/null +++ b/apps/gateway/mISDNNames.cpp @@ -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 +#include +#include +} +//} + +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"; +} diff --git a/apps/gateway/mISDNNames.h b/apps/gateway/mISDNNames.h new file mode 100644 index 00000000..5fbca477 --- /dev/null +++ b/apps/gateway/mISDNNames.h @@ -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 + + diff --git a/apps/gateway/mISDNStack.cpp b/apps/gateway/mISDNStack.cpp new file mode 100644 index 00000000..30e47a4c --- /dev/null +++ b/apps/gateway/mISDNStack.cpp @@ -0,0 +1,548 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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::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::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::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;j32)?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; +} diff --git a/apps/gateway/mISDNStack.h b/apps/gateway/mISDNStack.h new file mode 100644 index 00000000..6d9e006b --- /dev/null +++ b/apps/gateway/mISDNStack.h @@ -0,0 +1,89 @@ +#ifndef _MISDNSTACK_H_ +#define _MISDNSTACK_H_ + +#include +#include "globals.h" +namespace mISDN { //this is hack to cover definition of dprint which is defined in mISDN and sems core +extern "C" { +#include +#include +#include +} +} +#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 CR_map; + std::map 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 + +