X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=lcr.git;a=blobdiff_plain;f=gsm.cpp;h=3f778b4d2a3540a45bd54f49d8ea12b7f5e5e7a1;hp=86e7f4498de9cc5f02079a33c7c52c2b0b06de73;hb=5463e1b62a39ce417b610584e3d34a8bc30ac15e;hpb=6bf7c7f9db360d6031417b012dcb0e90e2251cd6 diff --git a/gsm.cpp b/gsm.cpp index 86e7f44..3f778b4 100644 --- a/gsm.cpp +++ b/gsm.cpp @@ -4,37 +4,105 @@ ** ** **---------------------------------------------------------------------------** ** Copyright: Andreas Eversberg ** +** MNCC-Interface: Harald Welte ** ** ** ** mISDN gsm ** ** ** \*****************************************************************************/ #include "main.h" +#include "mncc.h" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif extern "C" { -#include "openbsc/gsm_data.h" -#include "openbsc/mncc.h" -#include "openbsc/trau_frame.h" -#include "openbsc/select.h" -#include "openbsc/debug.h" -#include "openbsc/e1_input.h" -#include "bootstrap.h" #include "gsm_audio.h" - -#undef AF_ISDN -#undef PF_ISDN -extern int AF_ISDN; -#define PF_ISDN AF_ISDN } -struct lcr_gsm *gsm = NULL; +#include +#include + +#define SOCKET_RETRY_TIMER 5 + +//struct lcr_gsm *gsm = NULL; + +int new_callref = 1; + +/* names of MNCC-SAP */ +static const struct _value_string { + int msg_type; + const char *name; +} mncc_names[] = { + { 0, "New call ref" }, + { MNCC_SETUP_REQ, "MNCC_SETUP_REQ" }, + { MNCC_SETUP_IND, "MNCC_SETUP_IND" }, + { MNCC_SETUP_RSP, "MNCC_SETUP_RSP" }, + { MNCC_SETUP_CNF, "MNCC_SETUP_CNF" }, + { MNCC_SETUP_COMPL_REQ, "MNCC_SETUP_COMPL_REQ" }, + { MNCC_SETUP_COMPL_IND, "MNCC_SETUP_COMPL_IND" }, + { MNCC_CALL_CONF_IND, "MNCC_CALL_CONF_IND" }, + { MNCC_CALL_PROC_REQ, "MNCC_CALL_PROC_REQ" }, + { MNCC_PROGRESS_REQ, "MNCC_PROGRESS_REQ" }, + { MNCC_ALERT_REQ, "MNCC_ALERT_REQ" }, + { MNCC_ALERT_IND, "MNCC_ALERT_IND" }, + { MNCC_NOTIFY_REQ, "MNCC_NOTIFY_REQ" }, + { MNCC_NOTIFY_IND, "MNCC_NOTIFY_IND" }, + { MNCC_DISC_REQ, "MNCC_DISC_REQ" }, + { MNCC_DISC_IND, "MNCC_DISC_IND" }, + { MNCC_REL_REQ, "MNCC_REL_REQ" }, + { MNCC_REL_IND, "MNCC_REL_IND" }, + { MNCC_REL_CNF, "MNCC_REL_CNF" }, + { MNCC_FACILITY_REQ, "MNCC_FACILITY_REQ" }, + { MNCC_FACILITY_IND, "MNCC_FACILITY_IND" }, + { MNCC_START_DTMF_IND, "MNCC_START_DTMF_IND" }, + { MNCC_START_DTMF_RSP, "MNCC_START_DTMF_RSP" }, + { MNCC_START_DTMF_REJ, "MNCC_START_DTMF_REJ" }, + { MNCC_STOP_DTMF_IND, "MNCC_STOP_DTMF_IND" }, + { MNCC_STOP_DTMF_RSP, "MNCC_STOP_DTMF_RSP" }, + { MNCC_MODIFY_REQ, "MNCC_MODIFY_REQ" }, + { MNCC_MODIFY_IND, "MNCC_MODIFY_IND" }, + { MNCC_MODIFY_RSP, "MNCC_MODIFY_RSP" }, + { MNCC_MODIFY_CNF, "MNCC_MODIFY_CNF" }, + { MNCC_MODIFY_REJ, "MNCC_MODIFY_REJ" }, + { MNCC_HOLD_IND, "MNCC_HOLD_IND" }, + { MNCC_HOLD_CNF, "MNCC_HOLD_CNF" }, + { MNCC_HOLD_REJ, "MNCC_HOLD_REJ" }, + { MNCC_RETRIEVE_IND, "MNCC_RETRIEVE_IND" }, + { MNCC_RETRIEVE_CNF, "MNCC_RETRIEVE_CNF" }, + { MNCC_RETRIEVE_REJ, "MNCC_RETRIEVE_REJ" }, + { MNCC_USERINFO_REQ, "MNCC_USERINFO_REQ" }, + { MNCC_USERINFO_IND, "MNCC_USERINFO_IND" }, + { MNCC_REJ_REQ, "MNCC_REJ_REQ" }, + { MNCC_REJ_IND, "MNCC_REJ_IND" }, + { MNCC_PROGRESS_IND, "MNCC_PROGRESS_IND" }, + { MNCC_CALL_PROC_IND, "MNCC_CALL_PROC_IND" }, + { MNCC_CALL_CONF_REQ, "MNCC_CALL_CONF_REQ" }, + { MNCC_START_DTMF_REQ, "MNCC_START_DTMF_REQ" }, + { MNCC_STOP_DTMF_REQ, "MNCC_STOP_DTMF_REQ" }, + { MNCC_HOLD_REQ, "MNCC_HOLD_REQ " }, + { MNCC_RETRIEVE_REQ, "MNCC_RETRIEVE_REQ" }, + { 0, NULL } +}; + +const char *mncc_name(int value) +{ + int i = 0; -static unsigned int new_callref = 1; + while (mncc_names[i].name) { + if (mncc_names[i].msg_type == value) + return mncc_names[i].name; + i++; + } + return "unknown"; +} +static int mncc_send(struct lcr_gsm *lcr_gsm, int msg_type, void *data); /* * create and send mncc message */ -static struct gsm_mncc *create_mncc(int msg_type, unsigned int callref) +struct gsm_mncc *create_mncc(int msg_type, unsigned int callref) { struct gsm_mncc *mncc; @@ -43,39 +111,57 @@ static struct gsm_mncc *create_mncc(int msg_type, unsigned int callref) mncc->callref = callref; return (mncc); } -static int send_and_free_mncc(struct gsm_network *net, unsigned int msg_type, void *data) +int send_and_free_mncc(struct lcr_gsm *lcr_gsm, unsigned int msg_type, void *data) { - int ret; + int ret = 0; - ret = mncc_send(net, msg_type, data); + if (lcr_gsm) { + ret = mncc_send(lcr_gsm, msg_type, data); + } free(data); return ret; } +static int delete_event(struct lcr_work *work, void *instance, int index); /* * constructor */ -Pgsm::Pgsm(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, int channel, int exclusive, int mode) : PmISDN(type, mISDNport, portname, settings, channel, exclusive, mode) +Pgsm::Pgsm(int type, char *portname, struct port_settings *settings, struct interface *interface) : Port(type, portname, settings) { - p_callerinfo.itype = (mISDNport->ifport->interface->extension)?INFO_ITYPE_ISDN_EXTENSION:INFO_ITYPE_ISDN; - p_m_g_callref = 0; - p_m_g_mode = 0; - p_m_g_gsm_b_sock = -1; - p_m_g_gsm_b_index = -1; - p_m_g_gsm_b_active = 0; - p_m_g_notify_pending = NULL; - p_m_g_decoder = gsm_audio_create(); - p_m_g_encoder = gsm_audio_create(); - if (!p_m_g_encoder || !p_m_g_decoder) { + p_g_tones = 0; + if (interface->is_tones == IS_YES) + p_g_tones = 1; + p_g_earlyb = 0; + if (interface->is_earlyb == IS_YES) + p_g_earlyb = 1; + p_g_rtp_bridge = 0; + if (interface->rtp_bridge) + p_g_rtp_bridge = 1; + SCPY(p_g_interface_name, interface->name); + p_callerinfo.itype = (interface->extension)?INFO_ITYPE_ISDN_EXTENSION:INFO_ITYPE_ISDN; + memset(&p_g_delete, 0, sizeof(p_g_delete)); + add_work(&p_g_delete, delete_event, this, 0); + p_g_lcr_gsm = NULL; + p_g_callref = 0; + p_g_mode = 0; + p_g_gsm_b_sock = -1; + p_g_gsm_b_index = -1; + p_g_gsm_b_active = 0; + p_g_notify_pending = NULL; + p_g_setup_pending = NULL; + p_g_connect_pending = NULL; + p_g_decoder = gsm_audio_create(); + p_g_encoder = gsm_audio_create(); + if (!p_g_encoder || !p_g_decoder) { PERROR("Failed to create GSM audio codec instance\n"); - p_m_delete = 1; + trigger_work(&p_g_delete); } - p_m_g_rxpos = 0; - p_m_g_tch_connected = 0; + p_g_rxpos = 0; + p_g_tch_connected = 0; - PDEBUG(DEBUG_GSM, "Created new mISDNPort(%s).\n", portname); + PDEBUG(DEBUG_GSM, "Created new GSMPort(%s).\n", portname); } /* @@ -85,237 +171,127 @@ Pgsm::~Pgsm() { PDEBUG(DEBUG_GSM, "Destroyed GSM process(%s).\n", p_name); - /* remove queued message */ - if (p_m_g_notify_pending) - message_free(p_m_g_notify_pending); + del_work(&p_g_delete); - /* close audio transfer socket */ - if (p_m_g_gsm_b_sock > -1) - bchannel_close(); + /* remove queued message */ + if (p_g_notify_pending) + message_free(p_g_notify_pending); + if (p_g_setup_pending) + message_free(p_g_setup_pending); + if (p_g_connect_pending) + message_free(p_g_connect_pending); /* close codec */ - if (p_m_g_encoder) - gsm_audio_destroy(p_m_g_encoder); - if (p_m_g_decoder) - gsm_audio_destroy(p_m_g_decoder); + if (p_g_encoder) + gsm_audio_destroy(p_g_encoder); + if (p_g_decoder) + gsm_audio_destroy(p_g_decoder); } -/* close bsc side bchannel */ -void Pgsm::bchannel_close(void) +/* receive encoded frame from gsm */ +void Pgsm::frame_receive(void *arg) { - if (p_m_g_gsm_b_sock > -1) - close(p_m_g_gsm_b_sock); - p_m_g_gsm_b_sock = -1; - p_m_g_gsm_b_index = -1; - p_m_g_gsm_b_active = 0; -} + struct gsm_data_frame *frame = (struct gsm_data_frame *)arg; + signed short samples[160]; + unsigned char data[160]; + int i; -/* open bsc side bchannel */ -int Pgsm::bchannel_open(int index) -{ - int ret; - unsigned int on = 1; - struct sockaddr_mISDN addr; - struct mISDNhead act; - - if (p_m_g_gsm_b_sock > -1) { - PERROR("Socket already created for index %d\n", index); - return(-EIO); - } + if (!p_g_decoder) + return; - /* open socket */ - ret = p_m_g_gsm_b_sock = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW); - if (ret < 0) { - PERROR("Failed to open bchannel-socket for index %d\n", index); - bchannel_close(); - return(ret); - } + if ((frame->data[0]>>4) != 0xd) + PERROR("received GSM frame with wrong magig 0x%x\n", frame->data[0]>>4); - /* set nonblocking io */ - ret = ioctl(p_m_g_gsm_b_sock, FIONBIO, &on); - if (ret < 0) { - PERROR("Failed to set bchannel-socket index %d into nonblocking IO\n", index); - bchannel_close(); - return(ret); - } - - /* bind socket to bchannel */ - addr.family = AF_ISDN; - addr.dev = gsm->gsm_port; - addr.channel = index+1+(index>15); - ret = bind(p_m_g_gsm_b_sock, (struct sockaddr *)&addr, sizeof(addr)); - if (ret < 0) { - PERROR("Failed to bind bchannel-socket for index %d\n", index); - bchannel_close(); - return(ret); - } - /* activate bchannel */ - PDEBUG(DEBUG_GSM, "Activating GSM side channel index %i.\n", index); - act.prim = PH_ACTIVATE_REQ; - act.id = 0; - ret = sendto(p_m_g_gsm_b_sock, &act, MISDN_HEADER_LEN, 0, NULL, 0); - if (ret < 0) { - PERROR("Failed to activate index %d\n", index); - bchannel_close(); - return(ret); + /* decode */ + gsm_audio_decode(p_g_decoder, frame->data, samples); + for (i = 0; i < 160; i++) { + data[i] = audio_s16_to_law[samples[i] & 0xffff]; } - p_m_g_gsm_b_index = index; - - return(0); + /* send to remote*/ + bridge_tx(data, 160); } -/* receive from bchannel */ -void Pgsm::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len) +/* send traffic to gsm */ +int Pgsm::bridge_rx(unsigned char *data, int len) { - int i, j, k; unsigned char frame[33]; - struct decoded_trau_frame tf; /* encoder init failed */ - if (!p_m_g_encoder) - return; + if (!p_g_encoder) + return -EINVAL; /* (currently) not connected, so don't flood tch! */ - if (!p_m_g_tch_connected) - return; + if (!p_g_tch_connected) + return -EINVAL; /* write to rx buffer */ while(len--) { - p_m_g_rxdata[p_m_g_rxpos++] = audio_law_to_s32[*data++]; - if (p_m_g_rxpos == 160) { - p_m_g_rxpos = 0; + p_g_rxdata[p_g_rxpos++] = audio_law_to_s32[*data++]; + if (p_g_rxpos == 160) { + p_g_rxpos = 0; /* encode data */ - gsm_audio_encode(p_m_g_encoder, p_m_g_rxdata, frame); - - /* set c-bits and t-bits */ - tf.c_bits[0] = 1; - tf.c_bits[1] = 1; - tf.c_bits[2] = 1; - tf.c_bits[3] = 0; - tf.c_bits[4] = 0; - memset(&tf.c_bits[5], 0, 6); - memset(&tf.c_bits[11], 1, 10); - memset(&tf.t_bits[0], 1, 4); - - /* reassemble d-bits */ - i = 0; - j = 0; - k = 0; - while(i < 260) { - tf.d_bits[i] = (frame[j] >> k) & 1; - if (++k == 8) { - k = 0; - j++; - } - i++; - } - - trau_send(&tf); + gsm_audio_encode(p_g_encoder, p_g_rxdata, frame); + frame_send(frame); } } -} - -/* transmit to bchannel */ -void Pgsm::bchannel_send(unsigned int prim, unsigned int id, unsigned char *data, int len) -{ - unsigned char buf[MISDN_HEADER_LEN+len]; - struct mISDNhead *hh = (struct mISDNhead *)buf; - int ret; - - if (!p_m_g_gsm_b_active) - return; - /* make and send frame */ - hh->prim = PH_DATA_REQ; - hh->id = 0; - memcpy(buf+MISDN_HEADER_LEN, data, len); - ret = sendto(p_m_g_gsm_b_sock, buf, MISDN_HEADER_LEN+len, 0, NULL, 0); - if (ret <= 0) - PERROR("Failed to send to socket index %d\n", index); + return 0; } -void Pgsm::trau_send(void *_tf) +void Pgsm::frame_send(void *_frame) { - struct decoded_trau_frame *tf = (struct decoded_trau_frame *)_tf; - unsigned char data[sizeof(struct gsm_trau_frame) + sizeof(struct decoded_trau_frame)]; - struct gsm_trau_frame *frame = (struct gsm_trau_frame *)data; + unsigned char buffer[sizeof(struct gsm_data_frame) + 33]; + struct gsm_data_frame *frame = (struct gsm_data_frame *)buffer; - frame->msg_type = GSM_TRAU_FRAME; - frame->callref = p_m_g_callref; - memcpy(frame->data, tf, sizeof(struct decoded_trau_frame)); - mncc_send((struct gsm_network *)gsm->network, frame->msg_type, frame); -} - - -void Pgsm::trau_receive(void *_frame) -{ - struct gsm_trau_frame *frm = (struct gsm_trau_frame *)_frame; - struct decoded_trau_frame *tf = (struct decoded_trau_frame *)frm->data; -//struct decoded_trau_frame *tf = (struct decoded_trau_frame *)_frame; - unsigned char frame[33]; - signed short samples[160]; - unsigned char data[160]; - int i, j, k; - - if (!p_m_g_decoder) - return; + frame->msg_type = GSM_TCHF_FRAME; + frame->callref = p_g_callref; + memcpy(frame->data, _frame, 33); -// printf("got trau %d %d %d %d %d\n", tf->c_bits[0], tf->c_bits[1], tf->c_bits[2], tf->c_bits[3], tf->c_bits[4]); - if (tf->c_bits[0]!=0 || tf->c_bits[1]!=0 || tf->c_bits[2]!=0 || tf->c_bits[3]!=1 || tf->c_bits[4]!=0) - PERROR("illegal trau (C1-C5) %d %d %d %d %d\n", tf->c_bits[0], tf->c_bits[1], tf->c_bits[2], tf->c_bits[3], tf->c_bits[4]); - - /* set GSM_MAGIC */ - memset(&frame, 0, sizeof(frame)); -// frame[0] = 0xd << 4; - - /* reassemble bits */ - i = 0; - j = 0; - k = 0; - while(i < 260) { - if (tf->d_bits[i] > 1) - PERROR("fix!\n"); - frame[j] |= (tf->d_bits[i] << k); - if (++k == 8) { - k = 0; - j++; - } - i++; + if (p_g_lcr_gsm) { + mncc_send(p_g_lcr_gsm, frame->msg_type, frame); } - - /* decode */ - gsm_audio_decode(p_m_g_decoder, frame, samples); - for (i = 0; i < 160; i++) { - data[i] = audio_s16_to_law[samples[i] & 0xffff]; - } - - /* send */ - bchannel_send(PH_DATA_REQ, 0, data, 160); } - /* * create trace - **/ -static void gsm_trace_header(struct mISDNport *mISDNport, class PmISDN *port, unsigned int msg_type, int direction) + */ +void gsm_trace_header(const char *interface_name, class Pgsm *port, unsigned int msg_type, int direction) { char msgtext[64]; + struct interface *interface = interface_first; + + while (interface) { + if (!strcmp(interface->name, interface_name)) + break; + interface = interface->next; + } + if (!interface) + return; /* select message and primitive text */ - SCPY(msgtext, get_mncc_name(msg_type)); + SCPY(msgtext, mncc_name(msg_type)); /* add direction */ - if (direction == DIRECTION_OUT) - SCAT(msgtext, " MSC->BSC"); - else - SCAT(msgtext, " MSC<-BSC"); + if (port) { + switch(port->p_type) { + case PORT_TYPE_GSM_BS_OUT: + case PORT_TYPE_GSM_BS_IN: + SCAT(msgtext, " LCR<->BSC"); + break; + case PORT_TYPE_GSM_MS_OUT: + case PORT_TYPE_GSM_MS_IN: + SCAT(msgtext, " LCR<->MS"); + break; + } + } else + SCAT(msgtext, " ----"); /* init trace with given values */ - start_trace(mISDNport?mISDNport->portnum:-1, - mISDNport?(mISDNport->ifport?mISDNport->ifport->interface:NULL):NULL, + start_trace(0, + interface, port?numberrize_callerinfo(port->p_callerinfo.id, port->p_callerinfo.ntype, options.national, options.international):NULL, port?port->p_dialinginfo.id:NULL, direction, @@ -324,326 +300,60 @@ static void gsm_trace_header(struct mISDNport *mISDNport, class PmISDN *port, un msgtext); } - - -/* select bchannel */ -int Pgsm::hunt_bchannel(void) -{ - int channel; - int i; - - chan_trace_header(p_m_mISDNport, this, "CHANNEL SELECTION (setup)", DIRECTION_NONE); - add_trace("channel", "reserved", "%d", p_m_mISDNport->b_reserved); - if (p_m_mISDNport->b_reserved >= p_m_mISDNport->b_num) { // of out chan.. - add_trace("conclusion", NULL, "all channels are reserved"); - end_trace(); - return(-34); // no channel - } - /* find channel */ - i = 0; - channel = 0; - while(i < p_m_mISDNport->b_num) { - if (p_m_mISDNport->b_port[i] == NULL) { - channel = i+1+(i>=15); - break; - } - i++; - } - if (!channel) { - add_trace("conclusion", NULL, "no channel available"); - end_trace(); - return(-6); // channel unacceptable - } - add_trace("conclusion", NULL, "channel available"); - add_trace("connect", "channel", "%d", channel); - end_trace(); - return(channel); -} - - -/* - * handles all indications - */ -/* SETUP INDICATION */ -void Pgsm::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) +/* PROCEEDING INDICATION */ +void Pgsm::call_conf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) { - int ret; - class Endpoint *epoint; - struct lcr_msg *message; - int channel; - struct gsm_mncc *mode, *proceeding, *frame; - - /* emergency shutdown */ - printf("%d %d\n", mncc->emergency, !gsm->conf.noemergshut); - if (mncc->emergency && !gsm->conf.noemergshut) { - start_trace(p_m_mISDNport->portnum, - p_m_mISDNport->ifport->interface, - NULL, - NULL, - DIRECTION_NONE, - CATEGORY_CH, - 0, - "EMERGENCY SHUTDOWN (due to received emergency call)"); - end_trace(); - quit = 1; - } - /* process given callref */ - l1l2l3_trace_header(p_m_mISDNport, this, L3_NEW_L3ID_IND, DIRECTION_IN); - add_trace("callref", "new", "0x%x", callref); - if (p_m_g_callref) { - /* release in case the ID is already in use */ - add_trace("error", NULL, "callref already in use"); - end_trace(); - mncc = create_mncc(MNCC_REJ_REQ, callref); - gsm_trace_header(p_m_mISDNport, this, MNCC_REJ_REQ, DIRECTION_OUT); - mncc->cause = 1; - mncc->cause_coding = 3; - mncc->cause_location = 1; - mncc->cause_value = 47; - add_trace("cause", "coding", "%d", mncc->cause_coding); - add_trace("cause", "location", "%d", mncc->cause_location); - add_trace("cause", "value", "%d", mncc->cause_value); - add_trace("reason", NULL, "callref already in use"); - end_trace(); - send_and_free_mncc((struct gsm_network *)gsm->network, mncc->msg_type, mncc); - new_state(PORT_STATE_RELEASE); - p_m_delete = 1; - return; - } - p_m_g_callref = callref; - end_trace(); - - /* if blocked, release call with MT_RELEASE_COMPLETE */ - if (p_m_mISDNport->ifport->block) { - mncc = create_mncc(MNCC_REJ_REQ, p_m_g_callref); - gsm_trace_header(p_m_mISDNport, this, MNCC_REJ_REQ, DIRECTION_OUT); - mncc->cause = 1; - mncc->cause_coding = 3; - mncc->cause_location = 1; - mncc->cause_value = 27; - add_trace("cause", "coding", "%d", mncc->cause_coding); - add_trace("cause", "location", "%d", mncc->cause_location); - add_trace("cause", "value", "%d", mncc->cause_value); - add_trace("reason", NULL, "port is blocked"); - end_trace(); - send_and_free_mncc((struct gsm_network *)gsm->network, mncc->msg_type, mncc); - new_state(PORT_STATE_RELEASE); - p_m_delete = 1; - return; - } + struct gsm_mncc *mode; - /* caller info */ - if (mncc->clir_inv) - p_callerinfo.present = INFO_PRESENT_RESTRICTED; - else - p_callerinfo.present = INFO_PRESENT_ALLOWED; - if (mncc->calling_number[0]) - SCPY(p_callerinfo.id, mncc->calling_number); - else - p_callerinfo.present = INFO_PRESENT_NOTAVAIL; - SCPY(p_callerinfo.imsi, mncc->imsi); - p_callerinfo.screen = INFO_SCREEN_NETWORK; - p_callerinfo.ntype = INFO_NTYPE_UNKNOWN; - p_callerinfo.isdn_port = p_m_portnum; - SCPY(p_callerinfo.interface, p_m_mISDNport->ifport->interface->name); - - /* dialing information */ - SCAT(p_dialinginfo.id, mncc->called_number); - switch (mncc->called_type) { - case 0x1: - p_dialinginfo.ntype = INFO_NTYPE_INTERNATIONAL; - break; - case 0x2: - p_dialinginfo.ntype = INFO_NTYPE_NATIONAL; - break; - case 0x4: - p_dialinginfo.ntype = INFO_NTYPE_SUBSCRIBER; - break; - default: - p_dialinginfo.ntype = INFO_NTYPE_UNKNOWN; - break; - } - if (mncc->emergency) { - SCPY(p_dialinginfo.id, "emergency"); + gsm_trace_header(p_g_interface_name, this, msg_type, DIRECTION_IN); + if (mncc->fields & MNCC_F_CAUSE) { + add_trace("cause", "coding", "%d", mncc->cause.coding); + add_trace("cause", "location", "%", mncc->cause.location); + add_trace("cause", "value", "%", mncc->cause.value); } - p_dialinginfo.sending_complete = 1; - - /* bearer capability */ - // todo - p_capainfo.bearer_capa = INFO_BC_SPEECH; - p_capainfo.bearer_info1 = (options.law=='a')?3:2; - p_capainfo.bearer_mode = INFO_BMODE_CIRCUIT; - p_capainfo.source_mode = B_MODE_TRANSPARENT; - p_m_g_mode = p_capainfo.source_mode; - - /* useruser */ - - /* hunt channel */ - ret = channel = hunt_bchannel(); - if (ret < 0) - goto no_channel; - - /* open channel */ - ret = seize_bchannel(channel, 1); - if (ret < 0) { - no_channel: - mncc = create_mncc(MNCC_REJ_REQ, p_m_g_callref); - gsm_trace_header(p_m_mISDNport, this, MNCC_REJ_REQ, DIRECTION_OUT); - mncc->cause = 1; - mncc->cause_coding = 3; - mncc->cause_location = 1; - mncc->cause_value = 34; - add_trace("cause", "coding", "%d", mncc->cause_coding); - add_trace("cause", "location", "%d", mncc->cause_location); - add_trace("cause", "value", "%d", mncc->cause_value); - add_trace("reason", NULL, "no channel"); - end_trace(); - send_and_free_mncc((struct gsm_network *)gsm->network, mncc->msg_type, mncc); - new_state(PORT_STATE_RELEASE); - p_m_delete = 1; - return; - } - bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE); - if (bchannel_open(p_m_b_index)) - goto no_channel; - - /* what infos did we got ... */ - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); - if (p_callerinfo.id[0]) - add_trace("calling", "number", "%s", p_callerinfo.id); - else - SPRINT(p_callerinfo.id, "imsi-%s", p_callerinfo.imsi); - add_trace("calling", "imsi", "%s", p_callerinfo.imsi); - add_trace("dialing", "number", "%s", p_dialinginfo.id); end_trace(); - /* create endpoint */ - if (p_epointlist) - FATAL("Incoming call but already got an endpoint.\n"); - if (!(epoint = new Endpoint(p_serial, 0))) - FATAL("No memory for Endpoint instance\n"); - if (!(epoint->ep_app = new DEFAULT_ENDPOINT_APP(epoint, 0))) //incoming - FATAL("No memory for Endpoint Application instance\n"); - epointlist_new(epoint->ep_serial); - /* modify lchan to GSM codec V1 */ - gsm_trace_header(p_m_mISDNport, this, MNCC_LCHAN_MODIFY, DIRECTION_OUT); - mode = create_mncc(MNCC_LCHAN_MODIFY, p_m_g_callref); + gsm_trace_header(p_g_interface_name, this, MNCC_LCHAN_MODIFY, DIRECTION_OUT); + mode = create_mncc(MNCC_LCHAN_MODIFY, p_g_callref); mode->lchan_mode = 0x01; /* GSM V1 */ + mode->lchan_type = 0x02; add_trace("mode", NULL, "0x%02x", mode->lchan_mode); end_trace(); - send_and_free_mncc((struct gsm_network *)gsm->network, mode->msg_type, mode); - - /* send call proceeding */ - gsm_trace_header(p_m_mISDNport, this, MNCC_CALL_PROC_REQ, DIRECTION_OUT); - proceeding = create_mncc(MNCC_CALL_PROC_REQ, p_m_g_callref); - if (p_m_mISDNport->tones) { - proceeding->progress = 1; - proceeding->progress_coding = 3; /* GSM */ - proceeding->progress_location = 1; - proceeding->progress_descr = 8; - add_trace("progress", "coding", "%d", proceeding->progress_coding); - add_trace("progress", "location", "%d", proceeding->progress_location); - add_trace("progress", "descr", "%d", proceeding->progress_descr); - } - end_trace(); - send_and_free_mncc((struct gsm_network *)gsm->network, proceeding->msg_type, proceeding); + send_and_free_mncc(p_g_lcr_gsm, mode->msg_type, mode); - new_state(PORT_STATE_IN_PROCEEDING); - - if (p_m_mISDNport->tones && !p_m_g_tch_connected) { /* only if ... */ - gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT); - end_trace(); - frame = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc((struct gsm_network *)gsm->network, frame->msg_type, frame); - p_m_g_tch_connected = 1; - } - - /* send setup message to endpoit */ - message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_SETUP); - message->param.setup.isdn_port = p_m_portnum; - message->param.setup.port_type = p_type; -// message->param.setup.dtmf = 0; - memcpy(&message->param.setup.dialinginfo, &p_dialinginfo, sizeof(struct dialing_info)); - memcpy(&message->param.setup.callerinfo, &p_callerinfo, sizeof(struct caller_info)); - memcpy(&message->param.setup.capainfo, &p_capainfo, sizeof(struct capa_info)); - SCPY((char *)message->param.setup.useruser.data, (char *)mncc->useruser_info); - message->param.setup.useruser.len = strlen(mncc->useruser_info); - message->param.setup.useruser.protocol = mncc->useruser_proto; - message_put(message); } -/* DTMF INDICATION */ -void Pgsm::start_dtmf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) +/* CALL PROCEEDING INDICATION */ +void Pgsm::call_proc_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) { struct lcr_msg *message; - struct gsm_mncc *resp; - - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); - add_trace("keypad", NULL, "%c", mncc->keypad); - end_trace(); - SPRINT(p_dialinginfo.id, "%c", mncc->keypad); - p_dialinginfo.ntype = INFO_NTYPE_UNKNOWN; + struct gsm_mncc *frame; - /* send resp */ - gsm_trace_header(p_m_mISDNport, this, MNCC_START_DTMF_RSP, DIRECTION_OUT); - add_trace("keypad", NULL, "%c", mncc->keypad); + gsm_trace_header(p_g_interface_name, this, msg_type, DIRECTION_IN); end_trace(); - resp = create_mncc(MNCC_START_DTMF_RSP, p_m_g_callref); - resp->keypad = mncc->keypad; - send_and_free_mncc((struct gsm_network *)gsm->network, resp->msg_type, resp); - /* send dialing information */ - message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_INFORMATION); - memcpy(&message->param.information, &p_dialinginfo, sizeof(struct dialing_info)); + message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_PROCEEDING); message_put(message); -} -void Pgsm::stop_dtmf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) -{ - struct gsm_mncc *resp; - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); - add_trace("keypad", NULL, "%c", mncc->keypad); - end_trace(); - - /* send resp */ - gsm_trace_header(p_m_mISDNport, this, MNCC_STOP_DTMF_RSP, DIRECTION_OUT); - add_trace("keypad", NULL, "%c", mncc->keypad); - end_trace(); - resp = create_mncc(MNCC_STOP_DTMF_RSP, p_m_g_callref); - resp->keypad = mncc->keypad; - send_and_free_mncc((struct gsm_network *)gsm->network, resp->msg_type, resp); -} - -/* PROCEEDING INDICATION */ -void Pgsm::call_conf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) -{ - struct gsm_mncc *mode; + new_state(PORT_STATE_OUT_PROCEEDING); - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); - if (mncc->cause) { - add_trace("cause", "coding", "%d", mncc->cause_coding); - add_trace("cause", "location", "%", mncc->cause_location); - add_trace("cause", "value", "%", mncc->cause_value); + if (p_g_earlyb && !p_g_tch_connected) { /* only if ... */ + gsm_trace_header(p_g_interface_name, this, MNCC_FRAME_RECV, DIRECTION_OUT); + end_trace(); + frame = create_mncc(MNCC_FRAME_RECV, p_g_callref); + send_and_free_mncc(p_g_lcr_gsm, frame->msg_type, frame); + p_g_tch_connected = 1; } - end_trace(); - - /* modify lchan to GSM codec V1 */ - gsm_trace_header(p_m_mISDNport, this, MNCC_LCHAN_MODIFY, DIRECTION_OUT); - mode = create_mncc(MNCC_LCHAN_MODIFY, p_m_g_callref); - mode->lchan_mode = 0x01; /* GSM V1 */ - add_trace("mode", NULL, "0x%02x", mode->lchan_mode); - end_trace(); - send_and_free_mncc((struct gsm_network *)gsm->network, mode->msg_type, mode); - } /* ALERTING INDICATION */ void Pgsm::alert_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) { struct lcr_msg *message; + struct gsm_mncc *frame; - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); + gsm_trace_header(p_g_interface_name, this, msg_type, DIRECTION_IN); end_trace(); message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_ALERTING); @@ -651,6 +361,13 @@ void Pgsm::alert_ind(unsigned int msg_type, unsigned int callref, struct gsm_mnc new_state(PORT_STATE_OUT_ALERTING); + if (p_g_earlyb && !p_g_tch_connected) { /* only if ... */ + gsm_trace_header(p_g_interface_name, this, MNCC_FRAME_RECV, DIRECTION_OUT); + end_trace(); + frame = create_mncc(MNCC_FRAME_RECV, p_g_callref); + send_and_free_mncc(p_g_lcr_gsm, frame->msg_type, frame); + p_g_tch_connected = 1; + } } /* CONNECT INDICATION */ @@ -659,40 +376,51 @@ void Pgsm::setup_cnf(unsigned int msg_type, unsigned int callref, struct gsm_mnc struct gsm_mncc *resp, *frame; struct lcr_msg *message; - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); - end_trace(); - - SCPY(p_connectinfo.id, mncc->calling_number); + SCPY(p_connectinfo.id, mncc->connected.number); SCPY(p_connectinfo.imsi, mncc->imsi); p_connectinfo.present = INFO_PRESENT_ALLOWED; p_connectinfo.screen = INFO_SCREEN_NETWORK; p_connectinfo.ntype = INFO_NTYPE_UNKNOWN; - p_connectinfo.isdn_port = p_m_portnum; - SCPY(p_connectinfo.interface, p_m_mISDNport->ifport->interface->name); - /* send resp */ - gsm_trace_header(p_m_mISDNport, this, MNCC_SETUP_COMPL_REQ, DIRECTION_OUT); + SCPY(p_connectinfo.interface, p_g_interface_name); + + gsm_trace_header(p_g_interface_name, this, msg_type, DIRECTION_IN); if (p_connectinfo.id[0]) add_trace("connect", "number", "%s", p_connectinfo.id); - else + else if (mncc->imsi[0]) SPRINT(p_connectinfo.id, "imsi-%s", p_connectinfo.imsi); - add_trace("connect", "imsi", "%s", p_connectinfo.imsi); - resp = create_mncc(MNCC_SETUP_COMPL_REQ, p_m_g_callref); + if (mncc->imsi[0]) + add_trace("connect", "imsi", "%s", p_connectinfo.imsi); end_trace(); - send_and_free_mncc((struct gsm_network *)gsm->network, resp->msg_type, resp); - message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT); - memcpy(&message->param.connectinfo, &p_connectinfo, sizeof(struct connect_info)); - message_put(message); + /* send resp */ + gsm_trace_header(p_g_interface_name, this, MNCC_SETUP_COMPL_REQ, DIRECTION_OUT); + resp = create_mncc(MNCC_SETUP_COMPL_REQ, p_g_callref); + end_trace(); + send_and_free_mncc(p_g_lcr_gsm, resp->msg_type, resp); new_state(PORT_STATE_CONNECT); - if (!p_m_g_tch_connected) { /* only if ... */ - gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT); + if (!p_g_tch_connected) { /* only if ... */ + gsm_trace_header(p_g_interface_name, this, MNCC_FRAME_RECV, DIRECTION_OUT); end_trace(); - frame = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc((struct gsm_network *)gsm->network, frame->msg_type, frame); - p_m_g_tch_connected = 1; + frame = create_mncc(MNCC_FRAME_RECV, p_g_callref); + send_and_free_mncc(p_g_lcr_gsm, frame->msg_type, frame); + p_g_tch_connected = 1; } + + message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT); + memcpy(&message->param.connectinfo, &p_connectinfo, sizeof(struct connect_info)); + message->param.connectinfo.rtpinfo.payload_type = 3; /* FIXME: receive payload type from peer */ + + if (p_g_rtp_bridge) { + struct gsm_mncc_rtp *rtp; + + PDEBUG(DEBUG_GSM, "Request RTP peer info, before forwarding connect msg\n"); + p_g_connect_pending = message; + rtp = (struct gsm_mncc_rtp *) create_mncc(MNCC_RTP_CREATE, p_g_callref); + send_and_free_mncc(p_g_lcr_gsm, rtp->msg_type, rtp); + } else + message_put(message); } /* CONNECT ACK INDICATION */ @@ -700,17 +428,17 @@ void Pgsm::setup_compl_ind(unsigned int msg_type, unsigned int callref, struct g { struct gsm_mncc *frame; - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); + gsm_trace_header(p_g_interface_name, this, msg_type, DIRECTION_IN); end_trace(); new_state(PORT_STATE_CONNECT); - if (!p_m_g_tch_connected) { /* only if ... */ - gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT); + if (!p_g_tch_connected) { /* only if ... */ + gsm_trace_header(p_g_interface_name, this, MNCC_FRAME_RECV, DIRECTION_OUT); end_trace(); - frame = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc((struct gsm_network *)gsm->network, frame->msg_type, frame); - p_m_g_tch_connected = 1; + frame = create_mncc(MNCC_FRAME_RECV, p_g_callref); + send_and_free_mncc(p_g_lcr_gsm, frame->msg_type, frame); + p_g_tch_connected = 1; } } @@ -721,30 +449,30 @@ void Pgsm::disc_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc int cause = 16, location = 0; struct gsm_mncc *resp; - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); - if (mncc->cause) { - location = mncc->cause_location; - cause = mncc->cause_value; - add_trace("cause", "coding", "%d", mncc->cause_coding); + gsm_trace_header(p_g_interface_name, this, msg_type, DIRECTION_IN); + if (mncc->fields & MNCC_F_CAUSE) { + location = mncc->cause.location; + cause = mncc->cause.value; + add_trace("cause", "coding", "%d", mncc->cause.coding); add_trace("cause", "location", "%d", location); add_trace("cause", "value", "%d", cause); } end_trace(); /* send release */ - resp = create_mncc(MNCC_REL_REQ, p_m_g_callref); - gsm_trace_header(p_m_mISDNport, this, MNCC_REL_REQ, DIRECTION_OUT); + resp = create_mncc(MNCC_REL_REQ, p_g_callref); + gsm_trace_header(p_g_interface_name, this, MNCC_REL_REQ, DIRECTION_OUT); #if 0 - resp->cause = 1; - resp->cause_coding = 3; - resp->cause_location = 1; - resp->cause_value = cause; - add_trace("cause", "coding", "%d", resp->cause_coding); - add_trace("cause", "location", "%d", resp->cause_location); - add_trace("cause", "value", "%d", resp->cause_value); + resp->fields |= MNCC_F_CAUSE; + resp->cause.coding = 3; + resp->cause.location = 1; + resp->cause.value = cause; + add_trace("cause", "coding", "%d", resp->cause.coding); + add_trace("cause", "location", "%d", resp->cause.location); + add_trace("cause", "value", "%d", resp->cause.value); #endif end_trace(); - send_and_free_mncc((struct gsm_network *)gsm->network, resp->msg_type, resp); + send_and_free_mncc(p_g_lcr_gsm, resp->msg_type, resp); /* sending release to endpoint */ while(p_epointlist) { @@ -756,7 +484,7 @@ void Pgsm::disc_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc free_epointlist(p_epointlist); } new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_g_delete); } /* CC_RELEASE INDICATION */ @@ -765,13 +493,13 @@ void Pgsm::rel_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc int location = 0, cause = 16; struct lcr_msg *message; - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); - if (mncc->cause) { - location = mncc->cause_location; - cause = mncc->cause_value; - add_trace("cause", "coding", "%d", mncc->cause_coding); - add_trace("cause", "location", "%d", mncc->cause_location); - add_trace("cause", "value", "%d", mncc->cause_value); + gsm_trace_header(p_g_interface_name, this, msg_type, DIRECTION_IN); + if (mncc->fields & MNCC_F_CAUSE) { + location = mncc->cause.location; + cause = mncc->cause.value; + add_trace("cause", "coding", "%d", mncc->cause.coding); + add_trace("cause", "location", "%d", mncc->cause.location); + add_trace("cause", "value", "%d", mncc->cause.value); } end_trace(); @@ -785,438 +513,109 @@ void Pgsm::rel_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc free_epointlist(p_epointlist); } new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_g_delete); } /* NOTIFY INDICATION */ void Pgsm::notify_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) { - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); - end_trace(); -} - - -/* HOLD INDICATION */ -void Pgsm::hold_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) -{ struct lcr_msg *message; - struct gsm_mncc *resp, *frame; - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); + gsm_trace_header(p_g_interface_name, this, msg_type, DIRECTION_IN); + add_trace("notify", NULL, "%d", mncc->notify); end_trace(); - /* notify the hold of call */ message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_NOTIFY); - message->param.notifyinfo.notify = INFO_NOTIFY_REMOTE_HOLD; - message->param.notifyinfo.local = 1; /* call is held by supplementary service */ + message->param.notifyinfo.notify = mncc->notify; message_put(message); - - /* acknowledge hold */ - gsm_trace_header(p_m_mISDNport, this, MNCC_HOLD_CNF, DIRECTION_OUT); - end_trace(); - resp = create_mncc(MNCC_HOLD_CNF, p_m_g_callref); - send_and_free_mncc((struct gsm_network *)gsm->network, resp->msg_type, resp); - - /* disable audio */ - if (p_m_g_tch_connected) { /* it should be true */ - gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_DROP, DIRECTION_OUT); - end_trace(); - frame = create_mncc(MNCC_FRAME_DROP, p_m_g_callref); - send_and_free_mncc((struct gsm_network *)gsm->network, frame->msg_type, frame); - p_m_g_tch_connected = 0; - } } - -/* RETRIEVE INDICATION */ -void Pgsm::retr_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) +/* MESSAGE_NOTIFY */ +void Pgsm::message_notify(unsigned int epoint_id, int message_id, union parameter *param) { - struct lcr_msg *message; - struct gsm_mncc *resp, *frame; - - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); - end_trace(); - - /* notify the retrieve of call */ - message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_NOTIFY); - message->param.notifyinfo.notify = INFO_NOTIFY_REMOTE_RETRIEVAL; - message->param.notifyinfo.local = 1; /* call is retrieved by supplementary service */ - message_put(message); - - /* acknowledge retr */ - gsm_trace_header(p_m_mISDNport, this, MNCC_RETRIEVE_CNF, DIRECTION_OUT); - end_trace(); - resp = create_mncc(MNCC_RETRIEVE_CNF, p_m_g_callref); - send_and_free_mncc((struct gsm_network *)gsm->network, resp->msg_type, resp); + struct gsm_mncc *mncc; + int notify; - /* enable audio */ - if (!p_m_g_tch_connected) { /* it should be true */ - gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT); - end_trace(); - frame = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc((struct gsm_network *)gsm->network, frame->msg_type, frame); - p_m_g_tch_connected = 1; +// printf("if = %d\n", param->notifyinfo.notify); + if (param->notifyinfo.notify>INFO_NOTIFY_NONE) { + notify = param->notifyinfo.notify & 0x7f; + if (p_state!=PORT_STATE_CONNECT /*&& p_state!=PORT_STATE_IN_PROCEEDING*/ && p_state!=PORT_STATE_IN_ALERTING) { + /* queue notification */ + if (p_g_notify_pending) + message_free(p_g_notify_pending); + p_g_notify_pending = message_create(ACTIVE_EPOINT(p_epointlist), p_serial, EPOINT_TO_PORT, message_id); + memcpy(&p_g_notify_pending->param, param, sizeof(union parameter)); + } else { + /* sending notification */ + gsm_trace_header(p_g_interface_name, this, MNCC_NOTIFY_REQ, DIRECTION_OUT); + add_trace("notify", NULL, "%d", notify); + end_trace(); + mncc = create_mncc(MNCC_NOTIFY_REQ, p_g_callref); + mncc->notify = notify; + send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc); + } } } -/* - * BSC sends message to port - */ -static int message_bcs(struct gsm_network *net, int msg_type, void *arg) +/* RTP create indication */ +void Pgsm::rtp_create_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) { - struct gsm_mncc *mncc = (struct gsm_mncc *)arg; - unsigned int callref = mncc->callref; - class Port *port; - class Pgsm *pgsm = NULL; - char name[64]; - struct mISDNport *mISDNport; - - /* Special messages */ - switch(msg_type) { - } + struct gsm_mncc_rtp *rtp = (struct gsm_mncc_rtp *) mncc; - /* find callref */ - callref = mncc->callref; - port = port_first; - while(port) { - if ((port->p_type & PORT_CLASS_mISDN_MASK) == PORT_CLASS_mISDN_GSM) { - pgsm = (class Pgsm *)port; - if (pgsm->p_m_g_callref == callref) { - break; - } - } - port = port->next; - } - - if (msg_type == GSM_TRAU_FRAME) { - if (port) - pgsm->trau_receive((struct gsm_trau_frame *)arg); - return 0; - } - - if (!port) { - if (msg_type != MNCC_SETUP_IND) - return(0); - /* find gsm port */ - mISDNport = mISDNport_first; - while(mISDNport) { - if (mISDNport->gsm) - break; - mISDNport = mISDNport->next; - } - if (!mISDNport) { - struct gsm_mncc *rej; - - rej = create_mncc(MNCC_REJ_REQ, callref); - rej->cause = 1; - rej->cause_coding = 3; - rej->cause_location = 1; - rej->cause_value = 27; - gsm_trace_header(NULL, NULL, MNCC_REJ_REQ, DIRECTION_OUT); - add_trace("cause", "coding", "%d", rej->cause_coding); - add_trace("cause", "location", "%d", rej->cause_location); - add_trace("cause", "value", "%d", rej->cause_value); - end_trace(); - send_and_free_mncc((struct gsm_network *)gsm->network, rej->msg_type, rej); - return 0; - } - /* creating port object, transparent until setup with hdlc */ - SPRINT(name, "%s-%d-in", mISDNport->ifport->interface->name, mISDNport->portnum); - if (!(pgsm = new Pgsm(PORT_TYPE_GSM_IN, mISDNport, name, NULL, 0, 0, B_MODE_TRANSPARENT))) + /* send queued setup, as we received remote RTP info */ + if (p_g_setup_pending) { + struct lcr_msg *message; - FATAL("Cannot create Port instance.\n"); + message = p_g_setup_pending; + PDEBUG(DEBUG_GSM, "Got RTP peer info (%08x,%d) forwarding setup\n", rtp->ip, rtp->port); + message->param.setup.rtpinfo.ip = rtp->ip; + message->param.setup.rtpinfo.port = rtp->port; + message_put(message); + p_g_setup_pending = NULL; } + if (p_g_connect_pending) { + struct gsm_mncc_rtp *nrtp; - switch(msg_type) { - case MNCC_SETUP_IND: - pgsm->setup_ind(msg_type, callref, mncc); - break; - - case MNCC_START_DTMF_IND: - pgsm->start_dtmf_ind(msg_type, callref, mncc); - break; - - case MNCC_STOP_DTMF_IND: - pgsm->stop_dtmf_ind(msg_type, callref, mncc); - break; - - case MNCC_CALL_CONF_IND: - pgsm->call_conf_ind(msg_type, callref, mncc); - break; - - case MNCC_ALERT_IND: - pgsm->alert_ind(msg_type, callref, mncc); - break; - - case MNCC_SETUP_CNF: - pgsm->setup_cnf(msg_type, callref, mncc); - break; - - case MNCC_SETUP_COMPL_IND: - pgsm->setup_compl_ind(msg_type, callref, mncc); - break; - - case MNCC_DISC_IND: - pgsm->disc_ind(msg_type, callref, mncc); - break; - - case MNCC_REL_IND: - case MNCC_REL_CNF: - case MNCC_REJ_IND: - pgsm->rel_ind(msg_type, callref, mncc); - break; - - case MNCC_NOTIFY_IND: - pgsm->notify_ind(msg_type, callref, mncc); - break; - - case MNCC_HOLD_IND: - pgsm->hold_ind(msg_type, callref, mncc); - break; - - case MNCC_RETRIEVE_IND: - pgsm->retr_ind(msg_type, callref, mncc); - break; - - default: - PDEBUG(DEBUG_GSM, "Pgsm(%s) gsm port with (caller id %s) received unhandled nessage: 0x%x\n", pgsm->p_name, pgsm->p_callerinfo.id, msg_type); + PDEBUG(DEBUG_GSM, "Got RTP peer info (%08x,%d) connecting RTP... \n", rtp->ip, rtp->port); + nrtp = (struct gsm_mncc_rtp *) create_mncc(MNCC_RTP_CONNECT, p_g_callref); + nrtp->ip = p_g_rtp_ip_remote; + nrtp->port = p_g_rtp_port_remote; + send_and_free_mncc(p_g_lcr_gsm, nrtp->msg_type, nrtp); } - return(0); } -/* MESSAGE_SETUP */ -void Pgsm::message_setup(unsigned int epoint_id, int message_id, union parameter *param) +/* RTP connect indication */ +void Pgsm::rtp_connect_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) { struct lcr_msg *message; - int ret; - struct epoint_list *epointlist; - struct gsm_mncc *mncc; - int channel; - - /* copy setup infos to port */ - memcpy(&p_callerinfo, ¶m->setup.callerinfo, sizeof(p_callerinfo)); - memcpy(&p_dialinginfo, ¶m->setup.dialinginfo, sizeof(p_dialinginfo)); - memcpy(&p_capainfo, ¶m->setup.capainfo, sizeof(p_capainfo)); - memcpy(&p_redirinfo, ¶m->setup.redirinfo, sizeof(p_redirinfo)); - - /* no number */ - if (!p_dialinginfo.id[0]) { - gsm_trace_header(p_m_mISDNport, this, MNCC_SETUP_REQ, DIRECTION_OUT); - add_trace("failure", NULL, "No dialed subscriber given."); - end_trace(); - message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE); - message->param.disconnectinfo.cause = 28; - message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; - message_put(message); - new_state(PORT_STATE_RELEASE); - p_m_delete = 1; - return; - } - - /* release if port is blocked */ - if (p_m_mISDNport->ifport->block) { - gsm_trace_header(p_m_mISDNport, this, MNCC_SETUP_REQ, DIRECTION_OUT); - add_trace("failure", NULL, "Port blocked."); - end_trace(); - message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE); - message->param.disconnectinfo.cause = 27; // temp. unavail. - message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; - message_put(message); - new_state(PORT_STATE_RELEASE); - p_m_delete = 1; - return; - } + struct gsm_mncc_rtp *rtp = (struct gsm_mncc_rtp *) mncc; - /* hunt channel */ - ret = channel = hunt_bchannel(); - if (ret < 0) - goto no_channel; - /* open channel */ - ret = seize_bchannel(channel, 1); - if (ret < 0) { - no_channel: - gsm_trace_header(p_m_mISDNport, this, MNCC_SETUP_REQ, DIRECTION_OUT); - add_trace("failure", NULL, "No internal audio channel available."); - end_trace(); - message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE); - message->param.disconnectinfo.cause = 34; - message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; + if (p_g_connect_pending) { + message = p_g_connect_pending; + PDEBUG(DEBUG_GSM, "Got RTP peer info (%08x,%d) forwarding connect\n", rtp->ip, rtp->port); + message->param.connectinfo.rtpinfo.ip = rtp->ip; + message->param.connectinfo.rtpinfo.port = rtp->port; message_put(message); - new_state(PORT_STATE_RELEASE); - p_m_delete = 1; - return; - } - bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE); - if (bchannel_open(p_m_b_index)) - goto no_channel; - -// SCPY(&p_m_tones_dir, param->setup.ext.tones_dir); - /* screen outgoing caller id */ - do_screen(1, p_callerinfo.id, sizeof(p_callerinfo.id), &p_callerinfo.ntype, &p_callerinfo.present, p_m_mISDNport->ifport->interface); - - /* attach only if not already */ - epointlist = p_epointlist; - while(epointlist) { - if (epointlist->epoint_id == epoint_id) - break; - epointlist = epointlist->next; - } - if (!epointlist) - epointlist_new(epoint_id); - - /* creating l3id */ - l1l2l3_trace_header(p_m_mISDNport, this, L3_NEW_L3ID_REQ, DIRECTION_OUT); - p_m_g_callref = new_callref++; - add_trace("callref", "new", "0x%x", p_m_g_callref); - end_trace(); - - gsm_trace_header(p_m_mISDNport, this, MNCC_SETUP_REQ, DIRECTION_OUT); - mncc = create_mncc(MNCC_SETUP_REQ, p_m_g_callref); - /* caller information */ - mncc->calling = 1; - mncc->calling_plan = 1; - switch (p_callerinfo.ntype) { - case INFO_NTYPE_UNKNOWN: - mncc->calling_type = 0x0; - break; - case INFO_NTYPE_INTERNATIONAL: - mncc->calling_type = 0x1; - break; - case INFO_NTYPE_NATIONAL: - mncc->calling_type = 0x2; - break; - case INFO_NTYPE_SUBSCRIBER: - mncc->calling_type = 0x4; - break; - default: /* INFO_NTYPE_NOTPRESENT */ - mncc->calling = 0; - break; - } - switch (p_callerinfo.screen) { - case INFO_SCREEN_USER: - mncc->calling_screen = 0; - break; - default: /* INFO_SCREEN_NETWORK */ - mncc->calling_screen = 3; - break; + p_g_connect_pending = NULL; } - switch (p_callerinfo.present) { - case INFO_PRESENT_ALLOWED: - mncc->calling_present = 0; - break; - case INFO_PRESENT_RESTRICTED: - mncc->calling_present = 1; - break; - default: /* INFO_PRESENT_NOTAVAIL */ - mncc->calling_present = 2; - break; - } - if (mncc->calling) { - SCPY(mncc->calling_number, p_callerinfo.id); - add_trace("calling", "type", "%d", mncc->calling_type); - add_trace("calling", "plan", "%d", mncc->calling_plan); - add_trace("calling", "present", "%d", mncc->calling_present); - add_trace("calling", "screen", "%d", mncc->calling_screen); - add_trace("calling", "number", "%s", mncc->calling_number); - } - /* dialing information */ - mncc->called = 1; - if (!strncmp(p_dialinginfo.id, "imsi-", 5)) { - SCPY(mncc->imsi, p_dialinginfo.id+5); - add_trace("dialing", "imsi", "%s", mncc->imsi); - } else { - SCPY(mncc->called_number, p_dialinginfo.id); - add_trace("dialing", "number", "%s", mncc->called_number); - } - - /* sending user-user */ - - /* redirecting number */ - mncc->redirecting = 1; - mncc->redirecting_plan = 1; - switch (p_redirinfo.ntype) { - case INFO_NTYPE_UNKNOWN: - mncc->redirecting_type = 0x0; - break; - case INFO_NTYPE_INTERNATIONAL: - mncc->redirecting_type = 0x1; - break; - case INFO_NTYPE_NATIONAL: - mncc->redirecting_type = 0x2; - break; - case INFO_NTYPE_SUBSCRIBER: - mncc->redirecting_type = 0x4; - break; - default: /* INFO_NTYPE_NOTPRESENT */ - mncc->redirecting = 0; - break; - } - switch (p_redirinfo.screen) { - case INFO_SCREEN_USER: - mncc->redirecting_screen = 0; - break; - default: /* INFO_SCREE_NETWORK */ - mncc->redirecting_screen = 3; - break; - } - switch (p_redirinfo.present) { - case INFO_PRESENT_ALLOWED: - mncc->redirecting_present = 0; - break; - case INFO_PRESENT_RESTRICTED: - mncc->redirecting_present = 1; - break; - default: /* INFO_PRESENT_NOTAVAIL */ - mncc->redirecting_present = 2; - break; - } - /* sending redirecting number only in ntmode */ - if (mncc->redirecting) { - SCPY(mncc->redirecting_number, p_redirinfo.id); - add_trace("redir", "type", "%d", mncc->redirecting_type); - add_trace("redir", "plan", "%d", mncc->redirecting_plan); - add_trace("redir", "present", "%d", mncc->redirecting_present); - add_trace("redir", "screen", "%d", mncc->redirecting_screen); - add_trace("redir", "number", "%s", mncc->redirecting_number); - } - /* bearer capability */ - //todo - - end_trace(); - send_and_free_mncc((struct gsm_network *)gsm->network, mncc->msg_type, mncc); - - new_state(PORT_STATE_OUT_SETUP); - - message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_PROCEEDING); - message_put(message); - - new_state(PORT_STATE_OUT_PROCEEDING); } -/* MESSAGE_NOTIFY */ -void Pgsm::message_notify(unsigned int epoint_id, int message_id, union parameter *param) +/* MESSAGE_PROGRESS */ +void Pgsm::message_progress(unsigned int epoint_id, int message_id, union parameter *param) { - struct gsm_mncc *mncc; - int notify; + if (param->progressinfo.progress == 8) { + PDEBUG(DEBUG_GSM, "Remote provides tones for us\n"); + p_g_tones = 1; + } - printf("if = %d\n", param->notifyinfo.notify); - if (param->notifyinfo.notify>INFO_NOTIFY_NONE) { - notify = param->notifyinfo.notify & 0x7f; - if (p_state!=PORT_STATE_CONNECT /*&& p_state!=PORT_STATE_IN_PROCEEDING*/ && p_state!=PORT_STATE_IN_ALERTING) { - /* queue notification */ - if (p_m_g_notify_pending) - message_free(p_m_g_notify_pending); - p_m_g_notify_pending = message_create(ACTIVE_EPOINT(p_epointlist), p_serial, EPOINT_TO_PORT, message_id); - memcpy(&p_m_g_notify_pending->param, param, sizeof(union parameter)); - } else { - /* sending notification */ - gsm_trace_header(p_m_mISDNport, this, MNCC_NOTIFY_REQ, DIRECTION_OUT); - add_trace("notify", NULL, "%d", notify); - end_trace(); - mncc = create_mncc(MNCC_NOTIFY_REQ, p_m_g_callref); - mncc->notify = notify; - send_and_free_mncc((struct gsm_network *)gsm->network, mncc->msg_type, mncc); - } + if (param->progressinfo.rtpinfo.port) { + struct gsm_mncc_rtp *rtp; + + PDEBUG(DEBUG_GSM, "CONNECT with RTP peer info, sent to BSC (%08x,%d)\n", param->progressinfo.rtpinfo.ip, param->progressinfo.rtpinfo.port); + rtp = (struct gsm_mncc_rtp *) create_mncc(MNCC_RTP_CONNECT, p_g_callref); + rtp->ip = param->progressinfo.rtpinfo.ip; + rtp->port = param->progressinfo.rtpinfo.port; + send_and_free_mncc(p_g_lcr_gsm, rtp->msg_type, rtp); } } @@ -1226,28 +625,28 @@ void Pgsm::message_alerting(unsigned int epoint_id, int message_id, union parame struct gsm_mncc *mncc; /* send alert */ - gsm_trace_header(p_m_mISDNport, this, MNCC_ALERT_REQ, DIRECTION_OUT); - mncc = create_mncc(MNCC_ALERT_REQ, p_m_g_callref); - if (p_m_mISDNport->tones) { - mncc->progress = 1; - mncc->progress_coding = 3; /* GSM */ - mncc->progress_location = 1; - mncc->progress_descr = 8; - add_trace("progress", "coding", "%d", mncc->progress_coding); - add_trace("progress", "location", "%d", mncc->progress_location); - add_trace("progress", "descr", "%d", mncc->progress_descr); + gsm_trace_header(p_g_interface_name, this, MNCC_ALERT_REQ, DIRECTION_OUT); + mncc = create_mncc(MNCC_ALERT_REQ, p_g_callref); + if (p_g_tones) { + mncc->fields |= MNCC_F_PROGRESS; + mncc->progress.coding = 3; /* GSM */ + mncc->progress.location = 1; + mncc->progress.descr = 8; + add_trace("progress", "coding", "%d", mncc->progress.coding); + add_trace("progress", "location", "%d", mncc->progress.location); + add_trace("progress", "descr", "%d", mncc->progress.descr); } end_trace(); - send_and_free_mncc((struct gsm_network *)gsm->network, mncc->msg_type, mncc); + send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc); new_state(PORT_STATE_IN_ALERTING); - if (p_m_mISDNport->tones && !p_m_g_tch_connected) { /* only if ... */ - gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT); + if (p_g_tones && !p_g_tch_connected) { /* only if ... */ + gsm_trace_header(p_g_interface_name, this, MNCC_FRAME_RECV, DIRECTION_OUT); end_trace(); - mncc = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc((struct gsm_network *)gsm->network, mncc->msg_type, mncc); - p_m_g_tch_connected = 1; + mncc = create_mncc(MNCC_FRAME_RECV, p_g_callref); + send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc); + p_g_tch_connected = 1; } } @@ -1259,62 +658,73 @@ void Pgsm::message_connect(unsigned int epoint_id, int message_id, union paramet /* copy connected information */ memcpy(&p_connectinfo, ¶m->connectinfo, sizeof(p_connectinfo)); /* screen outgoing caller id */ - do_screen(1, p_connectinfo.id, sizeof(p_connectinfo.id), &p_connectinfo.ntype, &p_connectinfo.present, p_m_mISDNport->ifport->interface); + do_screen(1, p_connectinfo.id, sizeof(p_connectinfo.id), &p_connectinfo.ntype, &p_connectinfo.present, p_g_interface_name); /* send connect */ - mncc = create_mncc(MNCC_SETUP_RSP, p_m_g_callref); - gsm_trace_header(p_m_mISDNport, this, MNCC_SETUP_RSP, DIRECTION_OUT); + mncc = create_mncc(MNCC_SETUP_RSP, p_g_callref); + gsm_trace_header(p_g_interface_name, this, MNCC_SETUP_RSP, DIRECTION_OUT); /* caller information */ - mncc->connected = 1; - mncc->connected_plan = 1; + mncc->fields |= MNCC_F_CONNECTED; + mncc->connected.plan = 1; switch (p_callerinfo.ntype) { case INFO_NTYPE_UNKNOWN: - mncc->connected_type = 0x0; + mncc->connected.type = 0x0; break; case INFO_NTYPE_INTERNATIONAL: - mncc->connected_type = 0x1; + mncc->connected.type = 0x1; break; case INFO_NTYPE_NATIONAL: - mncc->connected_type = 0x2; + mncc->connected.type = 0x2; break; case INFO_NTYPE_SUBSCRIBER: - mncc->connected_type = 0x4; + mncc->connected.type = 0x4; break; default: /* INFO_NTYPE_NOTPRESENT */ - mncc->connected = 0; + mncc->fields &= ~MNCC_F_CONNECTED; break; } switch (p_callerinfo.screen) { case INFO_SCREEN_USER: - mncc->connected_screen = 0; + mncc->connected.screen = 0; break; default: /* INFO_SCREEN_NETWORK */ - mncc->connected_screen = 3; + mncc->connected.screen = 3; break; } switch (p_callerinfo.present) { case INFO_PRESENT_ALLOWED: - mncc->connected_present = 0; + mncc->connected.present = 0; break; case INFO_PRESENT_RESTRICTED: - mncc->connected_present = 1; + mncc->connected.present = 1; break; default: /* INFO_PRESENT_NOTAVAIL */ - mncc->connected_present = 2; + mncc->connected.present = 2; break; } - if (mncc->connected) { - SCPY(mncc->connected_number, p_connectinfo.id); - add_trace("connected", "type", "%d", mncc->connected_type); - add_trace("connected", "plan", "%d", mncc->connected_plan); - add_trace("connected", "present", "%d", mncc->connected_present); - add_trace("connected", "screen", "%d", mncc->connected_screen); - add_trace("connected", "number", "%s", mncc->connected_number); + if (mncc->fields & MNCC_F_CONNECTED) { + SCPY(mncc->connected.number, p_connectinfo.id); + add_trace("connected", "type", "%d", mncc->connected.type); + add_trace("connected", "plan", "%d", mncc->connected.plan); + add_trace("connected", "present", "%d", mncc->connected.present); + add_trace("connected", "screen", "%d", mncc->connected.screen); + add_trace("connected", "number", "%s", mncc->connected.number); } end_trace(); - send_and_free_mncc((struct gsm_network *)gsm->network, mncc->msg_type, mncc); + send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc); new_state(PORT_STATE_CONNECT_WAITING); + + if (param->connectinfo.rtpinfo.port) { + struct gsm_mncc_rtp *rtp; + + PDEBUG(DEBUG_GSM, "CONNECT with RTP peer info, sent to BSC (%08x,%d)\n", param->connectinfo.rtpinfo.ip, param->connectinfo.rtpinfo.port); + rtp = (struct gsm_mncc_rtp *) create_mncc(MNCC_RTP_CONNECT, p_g_callref); + rtp->ip = param->connectinfo.rtpinfo.ip; + rtp->port = param->connectinfo.rtpinfo.port; + send_and_free_mncc(p_g_lcr_gsm, rtp->msg_type, rtp); + } + } /* MESSAGE_DISCONNECT */ @@ -1323,35 +733,35 @@ void Pgsm::message_disconnect(unsigned int epoint_id, int message_id, union para struct gsm_mncc *mncc; /* send disconnect */ - mncc = create_mncc(MNCC_DISC_REQ, p_m_g_callref); - gsm_trace_header(p_m_mISDNport, this, MNCC_DISC_REQ, DIRECTION_OUT); - if (p_m_mISDNport->tones) { - mncc->progress = 1; - mncc->progress_coding = 3; /* GSM */ - mncc->progress_location = 1; - mncc->progress_descr = 8; - add_trace("progress", "coding", "%d", mncc->progress_coding); - add_trace("progress", "location", "%d", mncc->progress_location); - add_trace("progress", "descr", "%d", mncc->progress_descr); - } - mncc->cause = 1; - mncc->cause_coding = 3; - mncc->cause_location = param->disconnectinfo.location; - mncc->cause_value = param->disconnectinfo.cause; - add_trace("cause", "coding", "%d", mncc->cause_coding); - add_trace("cause", "location", "%d", mncc->cause_location); - add_trace("cause", "value", "%d", mncc->cause_value); + mncc = create_mncc(MNCC_DISC_REQ, p_g_callref); + gsm_trace_header(p_g_interface_name, this, MNCC_DISC_REQ, DIRECTION_OUT); + if (p_g_tones) { + mncc->fields |= MNCC_F_PROGRESS; + mncc->progress.coding = 3; /* GSM */ + mncc->progress.location = 1; + mncc->progress.descr = 8; + add_trace("progress", "coding", "%d", mncc->progress.coding); + add_trace("progress", "location", "%d", mncc->progress.location); + add_trace("progress", "descr", "%d", mncc->progress.descr); + } + mncc->fields |= MNCC_F_CAUSE; + mncc->cause.coding = 3; + mncc->cause.location = param->disconnectinfo.location; + mncc->cause.value = param->disconnectinfo.cause; + add_trace("cause", "coding", "%d", mncc->cause.coding); + add_trace("cause", "location", "%d", mncc->cause.location); + add_trace("cause", "value", "%d", mncc->cause.value); end_trace(); - send_and_free_mncc((struct gsm_network *)gsm->network, mncc->msg_type, mncc); + send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc); new_state(PORT_STATE_OUT_DISCONNECT); - if (p_m_mISDNport->tones && !p_m_g_tch_connected) { /* only if ... */ - gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT); + if (p_g_tones && !p_g_tch_connected) { /* only if ... */ + gsm_trace_header(p_g_interface_name, this, MNCC_FRAME_RECV, DIRECTION_OUT); end_trace(); - mncc = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc((struct gsm_network *)gsm->network, mncc->msg_type, mncc); - p_m_g_tch_connected = 1; + mncc = create_mncc(MNCC_FRAME_RECV, p_g_callref); + send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc); + p_g_tch_connected = 1; } } @@ -1362,40 +772,36 @@ void Pgsm::message_release(unsigned int epoint_id, int message_id, union paramet struct gsm_mncc *mncc; /* send release */ - mncc = create_mncc(MNCC_REL_REQ, p_m_g_callref); - gsm_trace_header(p_m_mISDNport, this, MNCC_REL_REQ, DIRECTION_OUT); - mncc->cause = 1; - mncc->cause_coding = 3; - mncc->cause_location = param->disconnectinfo.location; - mncc->cause_value = param->disconnectinfo.cause; - add_trace("cause", "coding", "%d", mncc->cause_coding); - add_trace("cause", "location", "%d", mncc->cause_location); - add_trace("cause", "value", "%d", mncc->cause_value); + mncc = create_mncc(MNCC_REL_REQ, p_g_callref); + gsm_trace_header(p_g_interface_name, this, MNCC_REL_REQ, DIRECTION_OUT); + mncc->fields |= MNCC_F_CAUSE; + mncc->cause.coding = 3; + mncc->cause.location = param->disconnectinfo.location; + mncc->cause.value = param->disconnectinfo.cause; + add_trace("cause", "coding", "%d", mncc->cause.coding); + add_trace("cause", "location", "%d", mncc->cause.location); + add_trace("cause", "value", "%d", mncc->cause.value); end_trace(); - send_and_free_mncc((struct gsm_network *)gsm->network, mncc->msg_type, mncc); + send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_g_delete); return; } - /* * endpoint sends messages to the port */ int Pgsm::message_epoint(unsigned int epoint_id, int message_id, union parameter *param) { - if (PmISDN::message_epoint(epoint_id, message_id, param)) - return(1); + int ret = 0; - switch(message_id) { - case MESSAGE_SETUP: /* dial-out command received from epoint */ - if (p_state!=PORT_STATE_IDLE) - break; - message_setup(epoint_id, message_id, param); - break; + if (Port::message_epoint(epoint_id, message_id, param)) + return 1; + switch(message_id) { case MESSAGE_NOTIFY: /* display and notifications */ + ret = 1; message_notify(epoint_id, message_id, param); break; @@ -1404,34 +810,43 @@ int Pgsm::message_epoint(unsigned int epoint_id, int message_id, union parameter // break; case MESSAGE_PROCEEDING: /* message not handles */ + ret = 1; + break; + + case MESSAGE_PROGRESS: + ret = 1; + message_progress(epoint_id, message_id, param); break; case MESSAGE_ALERTING: /* call of endpoint is ringing */ + ret = 1; if (p_state!=PORT_STATE_IN_PROCEEDING) break; message_alerting(epoint_id, message_id, param); - if (p_m_g_notify_pending) { + if (p_g_notify_pending) { /* send pending notify message during connect */ - message_notify(ACTIVE_EPOINT(p_epointlist), p_m_g_notify_pending->type, &p_m_g_notify_pending->param); - message_free(p_m_g_notify_pending); - p_m_g_notify_pending = NULL; + message_notify(ACTIVE_EPOINT(p_epointlist), p_g_notify_pending->type, &p_g_notify_pending->param); + message_free(p_g_notify_pending); + p_g_notify_pending = NULL; } break; case MESSAGE_CONNECT: /* call of endpoint is connected */ + ret = 1; if (p_state!=PORT_STATE_IN_PROCEEDING && p_state!=PORT_STATE_IN_ALERTING) break; message_connect(epoint_id, message_id, param); - if (p_m_g_notify_pending) { + if (p_g_notify_pending) { /* send pending notify message during connect */ - message_notify(ACTIVE_EPOINT(p_epointlist), p_m_g_notify_pending->type, &p_m_g_notify_pending->param); - message_free(p_m_g_notify_pending); - p_m_g_notify_pending = NULL; + message_notify(ACTIVE_EPOINT(p_epointlist), p_g_notify_pending->type, &p_g_notify_pending->param); + message_free(p_g_notify_pending); + p_g_notify_pending = NULL; } break; case MESSAGE_DISCONNECT: /* call has been disconnected */ + ret = 1; if (p_state!=PORT_STATE_IN_PROCEEDING && p_state!=PORT_STATE_IN_ALERTING && p_state!=PORT_STATE_OUT_SETUP @@ -1445,233 +860,243 @@ int Pgsm::message_epoint(unsigned int epoint_id, int message_id, union parameter break; case MESSAGE_RELEASE: /* release isdn port */ + ret = 1; if (p_state==PORT_STATE_RELEASE) break; message_release(epoint_id, message_id, param); break; - default: - PDEBUG(DEBUG_GSM, "Pgsm(%s) gsm port with (caller id %s) received unhandled nessage: %d\n", p_name, p_callerinfo.id, message_id); } - return(1); + return ret; } - -/* - * handler - */ -int Pgsm::handler(void) +/* deletes only if l3id is release, otherwhise it will be triggered then */ +static int delete_event(struct lcr_work *work, void *instance, int index) { - int ret; - int work = 0; - unsigned char buffer[2048+MISDN_HEADER_LEN]; - struct mISDNhead *hh = (struct mISDNhead *)buffer; - - if ((ret = PmISDN::handler())) - return(ret); - - /* handle destruction */ - if (p_m_delete) { - delete this; - return(-1); - } + class Pgsm *gsmport = (class Pgsm *)instance; - /* handle message from bchannel */ - if (p_m_g_gsm_b_sock > -1) { - ret = recv(p_m_g_gsm_b_sock, buffer, sizeof(buffer), 0); - if (ret >= (int)MISDN_HEADER_LEN) { - switch(hh->prim) { - /* we don't care about confirms, we use rx data to sync tx */ - case PH_DATA_CNF: - break; - /* we receive audio data, we respond to it AND we send tones */ - case PH_DATA_IND: - bchannel_receive(hh, buffer+MISDN_HEADER_LEN, ret-MISDN_HEADER_LEN); - break; - case PH_ACTIVATE_IND: - p_m_g_gsm_b_active = 1; - break; - case PH_DEACTIVATE_IND: - p_m_g_gsm_b_active = 0; - break; - } - work = 1; - } else { - if (ret < 0 && errno != EWOULDBLOCK) - PERROR("Read from GSM port, index %d failed with return code %d\n", ret); - } - } + delete gsmport; - return(work); + return 0; } +int gsm_exit(int rc) +{ + return(rc); +} + +int gsm_init(void) +{ + /* seed the PRNG */ + srand(time(NULL)); + + return 0; +} /* - * handles bsc select function within LCR's main loop + * MNCC interface */ -int handle_gsm(void) + +static int mncc_q_enqueue(struct lcr_gsm *lcr_gsm, struct gsm_mncc *mncc, unsigned int len) { - int ret1, ret2; + struct mncc_q_entry *qe; + + qe = (struct mncc_q_entry *) MALLOC(sizeof(*qe)+sizeof(*mncc)+len); + if (!qe) + return -ENOMEM; + + qe->next = NULL; + qe->len = len; + memcpy(qe->data, mncc, len); + + /* in case of empty list ... */ + if (!lcr_gsm->mncc_q_hd && !lcr_gsm->mncc_q_tail) { + /* the list head and tail both point to the new qe */ + lcr_gsm->mncc_q_hd = lcr_gsm->mncc_q_tail = qe; + } else { + /* append to tail of list */ + lcr_gsm->mncc_q_tail->next = qe; + lcr_gsm->mncc_q_tail = qe; + } + + lcr_gsm->mncc_lfd.when |= LCR_FD_WRITE; - ret1 = bsc_upqueue((struct gsm_network *)gsm->network); - ret2 = bsc_select_main(1); /* polling */ - if (ret1 || ret2) - return 1; return 0; } -static void gsm_sock_close(void) +static struct mncc_q_entry *mncc_q_dequeue(struct lcr_gsm *lcr_gsm) { - if (gsm->gsm_sock > -1) - close(gsm->gsm_sock); - gsm->gsm_sock = -1; + struct mncc_q_entry *qe = lcr_gsm->mncc_q_hd; + if (!qe) + return NULL; + + /* dequeue the successfully sent message */ + lcr_gsm->mncc_q_hd = qe->next; + if (!qe) + return NULL; + if (qe == lcr_gsm->mncc_q_tail) + lcr_gsm->mncc_q_tail = NULL; + + return qe; } -static int gsm_sock_open(char *portname) +/* routine called by LCR code if it wants to send a message to OpenBSC */ +static int mncc_send(struct lcr_gsm *lcr_gsm, int msg_type, void *data) { - int ret; - int cnt; - unsigned long on = 1; - struct sockaddr_mISDN addr; - struct mISDN_devinfo devinfo; - int pri, bri; - - /* check port counts */ - ret = ioctl(mISDNsocket, IMGETCOUNT, &cnt); - if (ret < 0) { - fprintf(stderr, "Cannot get number of mISDN devices. (ioctl IMGETCOUNT failed ret=%d)\n", ret); - return(ret); - } + int len = 0; - if (cnt <= 0) { - PERROR_RUNTIME("Found no card. Please be sure to load card drivers.\n"); - return -EIO; - } - gsm->gsm_port = mISDN_getportbyname(mISDNsocket, cnt, portname); - if (gsm->gsm_port < 0) { - PERROR_RUNTIME("Port name '%s' not found, did you load loopback interface for GSM?.\n", portname); - return gsm->gsm_port; - } - /* get protocol */ - bri = pri = 0; - devinfo.id = gsm->gsm_port; - ret = ioctl(mISDNsocket, IMGETDEVINFO, &devinfo); - if (ret < 0) { - PERROR_RUNTIME("Cannot get device information for port %d. (ioctl IMGETDEVINFO failed ret=%d)\n", gsm->gsm_port, ret); - return ret; - } - if (devinfo.Dprotocols & (1 << ISDN_P_TE_S0)) { - bri = 1; - } - if (devinfo.Dprotocols & (1 << ISDN_P_TE_E1)) { - pri = 1; - } - if (!pri && !pri) { - PERROR_RUNTIME("GSM port %d does not support TE PRI or TE BRI.\n", gsm->gsm_port); - } - /* open socket */ - if ((gsm->gsm_sock = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_TE_S0)) < 0) { - PERROR_RUNTIME("GSM port %d failed to open socket.\n", gsm->gsm_port); - gsm_sock_close(); - return gsm->gsm_sock; - } - /* set nonblocking io */ - if ((ret = ioctl(gsm->gsm_sock, FIONBIO, &on)) < 0) { - PERROR_RUNTIME("GSM port %d failed to set socket into nonblocking io.\n", gsm->gsm_port); - gsm_sock_close(); - return ret; + /* FIXME: the caller should provide this */ + switch (msg_type) { + case GSM_TCHF_FRAME: + len = sizeof(struct gsm_data_frame) + 33; + break; + default: + len = sizeof(struct gsm_mncc); + break; } - /* bind socket to dchannel */ - memset(&addr, 0, sizeof(addr)); - addr.family = AF_ISDN; - addr.dev = gsm->gsm_port; - addr.channel = 0; - if ((ret = bind(gsm->gsm_sock, (struct sockaddr *)&addr, sizeof(addr))) < 0) { - PERROR_RUNTIME("GSM port %d failed to bind socket. (name = %s errno=%d)\n", gsm->gsm_port, portname, errno); - gsm_sock_close(); - return (ret); + + return mncc_q_enqueue(lcr_gsm, (struct gsm_mncc *)data, len); +} + +/* close MNCC socket */ +static int mncc_fd_close(struct lcr_gsm *lcr_gsm, struct lcr_fd *lfd) +{ + class Port *port; + class Pgsm *pgsm = NULL; + struct lcr_msg *message; + + PERROR("Lost MNCC socket, retrying in %u seconds\n", SOCKET_RETRY_TIMER); + close(lfd->fd); + unregister_fd(lfd); + lfd->fd = -1; + + /* free all the calls that were running through the MNCC interface */ + port = port_first; + while(port) { + if ((port->p_type & PORT_CLASS_mISDN_MASK) == PORT_CLASS_GSM) { + pgsm = (class Pgsm *)port; + if (pgsm->p_g_lcr_gsm == lcr_gsm) { + message = message_create(pgsm->p_serial, ACTIVE_EPOINT(pgsm->p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE); + message->param.disconnectinfo.cause = 27; // temp. unavail. + message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; + message_put(message); + pgsm->new_state(PORT_STATE_RELEASE); + trigger_work(&pgsm->p_g_delete); + } + } + port = port->next; } + /* flush the queue */ + while (mncc_q_dequeue(lcr_gsm)) + ; + + /* start the re-connect timer */ + schedule_timer(&lcr_gsm->socket_retry, SOCKET_RETRY_TIMER, 0); + return 0; } - -int gsm_exit(int rc) +/* write to OpenBSC via MNCC socket */ +static int mncc_fd_write(struct lcr_fd *lfd, void *inst, int idx) { - /* free gsm instance */ - if (gsm) { - if (gsm->gsm_sock > -1) - gsm_sock_close(); - /* shutdown network */ - if (gsm->network) - shutdown_net((struct gsm_network *)gsm->network); - /* free network */ - if (gsm->network) { - free((struct gsm_network *)gsm->network); /* TBD */ + struct lcr_gsm *lcr_gsm = (struct lcr_gsm *) inst; + struct mncc_q_entry *qe, *qe2; + int rc; + + while (1) { + qe = lcr_gsm->mncc_q_hd; + if (!qe) { + lfd->when &= ~LCR_FD_WRITE; + break; } - free(gsm); - gsm = NULL; + rc = write(lfd->fd, qe->data, qe->len); + if (rc == 0) + return mncc_fd_close(lcr_gsm, lfd); + if (rc < 0) + return rc; + if (rc < (int)qe->len) + return -1; + /* dequeue the successfully sent message */ + qe2 = mncc_q_dequeue(lcr_gsm); + assert(qe == qe2); + free(qe); } - - return(rc); + return 0; } -int gsm_init(void) +/* read from OpenBSC via MNCC socket */ +static int mncc_fd_read(struct lcr_fd *lfd, void *inst, int idx) { - char hlr[128], filename[128]; - mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - int pcapfd; - - /* create gsm instance */ - gsm = (struct lcr_gsm *)MALLOC(sizeof(struct lcr_gsm)); - gsm->gsm_sock = -1; - - /* parse options */ - if (!gsm_conf(&gsm->conf)) { - PERROR("%s", gsm_conf_error); - return gsm_exit(-EINVAL); + struct lcr_gsm *lcr_gsm = (struct lcr_gsm *) inst; + int rc; + static char buf[sizeof(struct gsm_mncc)+1024]; + struct gsm_mncc *mncc_prim = (struct gsm_mncc *) buf; + + memset(buf, 0, sizeof(buf)); + rc = recv(lfd->fd, buf, sizeof(buf), 0); + if (rc == 0) + return mncc_fd_close(lcr_gsm, lfd); + if (rc < 0) + return rc; + + /* Hand the MNCC message into LCR */ + switch (lcr_gsm->type) { +#ifdef WITH_GSM_BS + case LCR_GSM_TYPE_NETWORK: + return message_bsc(lcr_gsm, mncc_prim->msg_type, mncc_prim); +#endif +#ifdef WITH_GSM_MS + case LCR_GSM_TYPE_MS: + return message_ms(lcr_gsm, mncc_prim->msg_type, mncc_prim); +#endif + default: + return 0; } +} - /* set debug */ - debug_parse_category_mask(gsm->conf.debug); +/* file descriptor callback if we can read or write form MNCC socket */ +static int mncc_fd_cb(struct lcr_fd *lfd, unsigned int what, void *inst, int idx) +{ + int rc = 0; - /* init database */ - if (gsm->conf.hlr[0] == '/') - SCPY(hlr, gsm->conf.hlr); - else - SPRINT(hlr, "%s/%s", CONFIG_DATA, gsm->conf.hlr); + if (what & LCR_FD_READ) + rc = mncc_fd_read(lfd, inst, idx); + if (rc < 0) + return rc; - // TODO multiple base stations - if (gsm->conf.numbts < 1 || gsm->conf.numbts > 1) { - PERROR("Expecting exactly one BTS. You defined %d.\n", gsm->conf.numbts); - return gsm_exit(-1); - } + if (what & LCR_FD_WRITE) + rc = mncc_fd_write(lfd, inst, idx); - /* bootstrap network */ - gsm->network = bootstrap_network(&message_bcs, gsm->conf.bts[0].type, gsm->conf.mcc, gsm->conf.mnc, gsm->conf.lac, gsm->conf.bts[0].frequency[0], gsm->conf.bts[0].card, !gsm->conf.keep_l2, gsm->conf.short_name, gsm->conf.long_name, hlr, gsm->conf.allow_all); - if (!gsm->network) { - PERROR("Failed to bootstrap GSM network.\n"); - return gsm_exit(-1); - } + return rc; +} + +int mncc_socket_retry_cb(struct lcr_timer *timer, void *inst, int index) +{ + struct lcr_gsm *lcr_gsm = (struct lcr_gsm *) inst; + int fd, rc; - /* open gsm loop interface */ - if (gsm_sock_open(gsm->conf.interface_bsc)) { - return gsm_exit(-1); + lcr_gsm->mncc_lfd.fd = -1; + + fd = socket(PF_UNIX, SOCK_SEQPACKET, 0); + if (fd < 0) { + PERROR("Cannot create SEQPACKET socket, giving up!\n"); + return fd; } - /* open pcap file */ - if (gsm->conf.pcapfile[0]) { - if (gsm->conf.pcapfile[0] == '/') - SCPY(filename, gsm->conf.pcapfile); - else - SPRINT(filename, "%s/%s", CONFIG_DATA, gsm->conf.pcapfile); - pcapfd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, mode); - if (pcapfd < 0) { - PERROR("Failed to open file for pcap\n"); - return gsm_exit(-1); - } - e1_set_pcap_fd(pcapfd); + rc = connect(fd, (struct sockaddr *) &lcr_gsm->sun, + sizeof(lcr_gsm->sun)); + if (rc < 0) { + PERROR("Could not connect to MNCC socket %s, " + "retrying in %u seconds\n", lcr_gsm->sun.sun_path, + SOCKET_RETRY_TIMER); + close(fd); + schedule_timer(&lcr_gsm->socket_retry, SOCKET_RETRY_TIMER, 0); + } else { + PDEBUG(DEBUG_GSM, "Connected to MNCC socket %s!\n", lcr_gsm->sun.sun_path); + lcr_gsm->mncc_lfd.fd = fd; + register_fd(&lcr_gsm->mncc_lfd, LCR_FD_READ, &mncc_fd_cb, lcr_gsm, 0); } return 0;