X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=lcr.git;a=blobdiff_plain;f=gsm.cpp;h=5d4b64d84c92b20ba1fbae85b0c05d0ed7d18c01;hp=07746f78b9af53df3ae668ccf3e39d360282a78e;hb=b461b170fa3c19c5d7c3f689884a510559cb7940;hpb=d5e639588de9c88d5398cd047c5c73f6d439a5a7 diff --git a/gsm.cpp b/gsm.cpp index 07746f7..5d4b64d 100644 --- a/gsm.cpp +++ b/gsm.cpp @@ -4,12 +4,14 @@ ** ** **---------------------------------------------------------------------------** ** Copyright: Andreas Eversberg ** +** MNCC-Interface: Harald Welte ** ** ** ** mISDN gsm ** ** ** \*****************************************************************************/ #include "main.h" +#include "mncc.h" #ifndef _GNU_SOURCE #define _GNU_SOURCE @@ -18,9 +20,11 @@ extern "C" { #include "gsm_audio.h" } -#include +#include -struct lcr_gsm *gsm = NULL; +#define SOCKET_RETRY_TIMER 5 + +//struct lcr_gsm *gsm = NULL; int new_callref = 1; @@ -29,162 +33,84 @@ static const struct _value_string { int msg_type; const char *name; } mncc_names[] = { -#if defined(MNCC_SETUP_REQ) + { 0, "New call ref" }, + { 1, "Codec negotiation" }, { MNCC_SETUP_REQ, "MNCC_SETUP_REQ" }, -#endif -#if defined(MNCC_SETUP_IND) { MNCC_SETUP_IND, "MNCC_SETUP_IND" }, -#endif -#if defined(MNCC_SETUP_RSP) { MNCC_SETUP_RSP, "MNCC_SETUP_RSP" }, -#endif -#if defined(MNCC_SETUP_CNF) { MNCC_SETUP_CNF, "MNCC_SETUP_CNF" }, -#endif -#if defined(MNCC_SETUP_COMPL_REQ) { MNCC_SETUP_COMPL_REQ, "MNCC_SETUP_COMPL_REQ" }, -#endif -#if defined(MNCC_SETUP_COMPL_IND) { MNCC_SETUP_COMPL_IND, "MNCC_SETUP_COMPL_IND" }, -#endif -#if defined(MNCC_CALL_CONF_IND) { MNCC_CALL_CONF_IND, "MNCC_CALL_CONF_IND" }, -#endif -#if defined(MNCC_CALL_PROC_REQ) { MNCC_CALL_PROC_REQ, "MNCC_CALL_PROC_REQ" }, -#endif -#if defined(MNCC_PROGRESS_REQ) { MNCC_PROGRESS_REQ, "MNCC_PROGRESS_REQ" }, -#endif -#if defined(MNCC_ALERT_REQ) { MNCC_ALERT_REQ, "MNCC_ALERT_REQ" }, -#endif -#if defined(MNCC_ALERT_IND) { MNCC_ALERT_IND, "MNCC_ALERT_IND" }, -#endif -#if defined(MNCC_NOTIFY_REQ) { MNCC_NOTIFY_REQ, "MNCC_NOTIFY_REQ" }, -#endif -#if defined(MNCC_NOTIFY_IND) { MNCC_NOTIFY_IND, "MNCC_NOTIFY_IND" }, -#endif -#if defined(MNCC_DISC_REQ) { MNCC_DISC_REQ, "MNCC_DISC_REQ" }, -#endif -#if defined(MNCC_DISC_IND) { MNCC_DISC_IND, "MNCC_DISC_IND" }, -#endif -#if defined(MNCC_REL_REQ) { MNCC_REL_REQ, "MNCC_REL_REQ" }, -#endif -#if defined(MNCC_REL_IND) { MNCC_REL_IND, "MNCC_REL_IND" }, -#endif -#if defined(MNCC_REL_CNF) { MNCC_REL_CNF, "MNCC_REL_CNF" }, -#endif -#if defined(MNCC_FACILITY_REQ) { MNCC_FACILITY_REQ, "MNCC_FACILITY_REQ" }, -#endif -#if defined(MNCC_FACILITY_IND) { MNCC_FACILITY_IND, "MNCC_FACILITY_IND" }, -#endif -#if defined(MNCC_START_DTMF_IND) { MNCC_START_DTMF_IND, "MNCC_START_DTMF_IND" }, -#endif -#if defined(MNCC_START_DTMF_RSP) { MNCC_START_DTMF_RSP, "MNCC_START_DTMF_RSP" }, -#endif -#if defined(MNCC_START_DTMF_REJ) { MNCC_START_DTMF_REJ, "MNCC_START_DTMF_REJ" }, -#endif -#if defined(MNCC_STOP_DTMF_IND) { MNCC_STOP_DTMF_IND, "MNCC_STOP_DTMF_IND" }, -#endif -#if defined(MNCC_STOP_DTMF_RSP) { MNCC_STOP_DTMF_RSP, "MNCC_STOP_DTMF_RSP" }, -#endif -#if defined(MNCC_MODIFY_REQ) { MNCC_MODIFY_REQ, "MNCC_MODIFY_REQ" }, -#endif -#if defined(MNCC_MODIFY_IND) { MNCC_MODIFY_IND, "MNCC_MODIFY_IND" }, -#endif -#if defined(MNCC_MODIFY_RSP) { MNCC_MODIFY_RSP, "MNCC_MODIFY_RSP" }, -#endif -#if defined(MNCC_MODIFY_CNF) { MNCC_MODIFY_CNF, "MNCC_MODIFY_CNF" }, -#endif -#if defined(MNCC_MODIFY_REJ) { MNCC_MODIFY_REJ, "MNCC_MODIFY_REJ" }, -#endif -#if defined(MNCC_HOLD_IND) { MNCC_HOLD_IND, "MNCC_HOLD_IND" }, -#endif -#if defined(MNCC_HOLD_CNF) { MNCC_HOLD_CNF, "MNCC_HOLD_CNF" }, -#endif -#if defined(MNCC_HOLD_REJ) { MNCC_HOLD_REJ, "MNCC_HOLD_REJ" }, -#endif -#if defined(MNCC_RETRIEVE_IND) { MNCC_RETRIEVE_IND, "MNCC_RETRIEVE_IND" }, -#endif -#if defined(MNCC_RETRIEVE_CNF) { MNCC_RETRIEVE_CNF, "MNCC_RETRIEVE_CNF" }, -#endif -#if defined(MNCC_RETRIEVE_REJ) { MNCC_RETRIEVE_REJ, "MNCC_RETRIEVE_REJ" }, -#endif -#if defined(MNCC_USERINFO_REQ) { MNCC_USERINFO_REQ, "MNCC_USERINFO_REQ" }, -#endif -#if defined(MNCC_USERINFO_IND) { MNCC_USERINFO_IND, "MNCC_USERINFO_IND" }, -#endif -#if defined(MNCC_REJ_REQ) { MNCC_REJ_REQ, "MNCC_REJ_REQ" }, -#endif -#if defined(MNCC_REJ_IND) { MNCC_REJ_IND, "MNCC_REJ_IND" }, -#endif -#if defined(MNCC_PROGRESS_IND) { MNCC_PROGRESS_IND, "MNCC_PROGRESS_IND" }, -#endif -#if defined(MNCC_CALL_PROC_IND) { MNCC_CALL_PROC_IND, "MNCC_CALL_PROC_IND" }, -#endif -#if defined(MNCC_CALL_CONF_REQ) { MNCC_CALL_CONF_REQ, "MNCC_CALL_CONF_REQ" }, -#endif -#if defined(MNCC_START_DTMF_REQ) { MNCC_START_DTMF_REQ, "MNCC_START_DTMF_REQ" }, -#endif -#if defined(MNCC_STOP_DTMF_REQ) { MNCC_STOP_DTMF_REQ, "MNCC_STOP_DTMF_REQ" }, -#endif -#if defined(MNCC_HOLD_REQ) { MNCC_HOLD_REQ, "MNCC_HOLD_REQ " }, -#endif -#if defined(MNCC_RETRIEVE_REQ) { MNCC_RETRIEVE_REQ, "MNCC_RETRIEVE_REQ" }, -#endif + + { MNCC_LCHAN_MODIFY, "MNCC_LCHAN_MODIFY" }, + + { MNCC_BRIDGE, "MNCC_BRIDGE" }, + { MNCC_FRAME_RECV, "MNCC_FRAME_RECV" }, + { MNCC_FRAME_DROP, "MNCC_FRAME_DROP" }, + { MNCC_RTP_CREATE, "MNCC_RTP_CREATE" }, + { MNCC_RTP_CONNECT, "MNCC_RTP_CONNECT" }, + { MNCC_RTP_FREE, "MNCC_RTP_FREE" }, + { 0, NULL } }; const char *mncc_name(int value) { int i = 0; + static char ukn[32]; while (mncc_names[i].name) { if (mncc_names[i].msg_type == value) return mncc_names[i].name; i++; } - return "unknown"; + SPRINT(ukn, "unknown(0x%x)", value); + return ukn; } +static int mncc_send(struct lcr_gsm *lcr_gsm, int msg_type, void *data); + /* * create and send mncc message */ @@ -197,46 +123,118 @@ struct gsm_mncc *create_mncc(int msg_type, unsigned int callref) mncc->callref = callref; return (mncc); } -int send_and_free_mncc(void *instance, 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; -#ifdef WITH_GSM_BS - ret = mncc_send((struct gsm_network *)instance, msg_type, data); -#endif -#ifdef WITH_GSM_MS - ret = mncc_send((struct osmocom_ms *)instance, msg_type, data); -#endif + if (lcr_gsm) { + ret = mncc_send(lcr_gsm, msg_type, data); + } free(data); return ret; } +void Pgsm::send_mncc_rtp_connect(void) +{ + struct gsm_mncc_rtp *nrtp; + + 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; + switch (p_g_media_type) { + case MEDIA_TYPE_GSM: + nrtp->payload_msg_type = GSM_TCHF_FRAME; + break; + case MEDIA_TYPE_GSM_EFR: + nrtp->payload_msg_type = GSM_TCHF_FRAME_EFR; + break; + case MEDIA_TYPE_AMR: + nrtp->payload_msg_type = GSM_TCH_FRAME_AMR; + break; + case MEDIA_TYPE_GSM_HR: + nrtp->payload_msg_type = GSM_TCHH_FRAME; + break; + } + nrtp->payload_type = p_g_payload_type; + PDEBUG(DEBUG_GSM, "sending MNCC RTP connect with payload_msg_type=%x, payload_type=%d\n", nrtp->payload_msg_type, nrtp->payload_type); + send_and_free_mncc(p_g_lcr_gsm, nrtp->msg_type, nrtp); +} + 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) -{ - p_callerinfo.itype = (mISDNport->ifport->interface->extension)?INFO_ITYPE_ISDN_EXTENSION:INFO_ITYPE_ISDN; - memset(&p_m_g_delete, 0, sizeof(p_m_g_delete)); - add_work(&p_m_g_delete, delete_event, this, 0); - p_m_g_instance = NULL; - 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) { - PERROR("Failed to create GSM audio codec instance\n"); - trigger_work(&p_m_g_delete); - } - p_m_g_rxpos = 0; - p_m_g_tch_connected = 0; +Pgsm::Pgsm(int type, char *portname, struct port_settings *settings, struct interface *interface) : Port(type, portname, settings, interface) +{ +#ifdef WITH_GSMHR + signed short homing[160]; + int i; +#endif + + 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; + p_g_rtp_payloads = 0; + memset(&p_g_samples, 0, sizeof(p_g_samples)); + 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_fr_decoder = NULL; + p_g_fr_encoder = NULL; + p_g_hr_decoder = NULL; + p_g_hr_encoder = NULL; + p_g_amr_decoder = NULL; + p_g_amr_encoder = NULL; + p_g_amr_cmr = 0; + p_g_amr_cmr_valid = 0; +#ifdef WITH_GSMFR + p_g_fr_decoder = gsm_fr_create(); + p_g_fr_encoder = gsm_fr_create(); + if (!p_g_fr_encoder || !p_g_fr_decoder) { + PERROR("Failed to create GSM FR codec instance\n"); + trigger_work(&p_g_delete); + } +#endif +#ifdef WITH_GSMHR + p_g_hr_decoder = gsm_hr_create(); + p_g_hr_encoder = gsm_hr_create(); + if (!p_g_hr_encoder || !p_g_hr_decoder) { + PERROR("Failed to create GSM HR codec instance\n"); + trigger_work(&p_g_delete); + } + /* Homing */ + for (i = 0; i < 160; i++) + homing[i] = 0x0008; + gsm_hr_encode(p_g_hr_encoder, homing, NULL); +#endif +#ifdef WITH_GSMAMR + p_g_amr_decoder = gsm_amr_create(); + p_g_amr_encoder = gsm_amr_create(); + if (!p_g_amr_encoder || !p_g_amr_decoder) { + PERROR("Failed to create GSM AMR codec instance\n"); + trigger_work(&p_g_delete); + } +#endif + p_g_rxpos = 0; + p_g_tch_connected = 0; + p_g_media_type = 0; PDEBUG(DEBUG_GSM, "Created new GSMPort(%s).\n", portname); } @@ -248,180 +246,285 @@ Pgsm::~Pgsm() { PDEBUG(DEBUG_GSM, "Destroyed GSM process(%s).\n", p_name); - del_work(&p_m_g_delete); + del_work(&p_g_delete); /* remove queued message */ - if (p_m_g_notify_pending) - message_free(p_m_g_notify_pending); - - /* close audio transfer socket */ - if (p_m_g_gsm_b_sock > -1) - bchannel_close(); - + 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); + +//#ifdef WITH_GSMFR /* 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_fr_encoder) + gsm_fr_destroy(p_g_fr_encoder); + if (p_g_fr_decoder) + gsm_fr_destroy(p_g_fr_decoder); +//#endif +#ifdef WITH_GSMHR + if (p_g_hr_encoder) + gsm_hr_destroy(p_g_hr_encoder); + if (p_g_hr_decoder) + gsm_hr_destroy(p_g_hr_decoder); +#endif +#ifdef WITH_GSMAMR + /* close codec */ + if (p_g_amr_encoder) + gsm_amr_destroy(p_g_amr_encoder); + if (p_g_amr_decoder) + gsm_amr_destroy(p_g_amr_decoder); +#endif } -/* 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) { - unregister_fd(&p_m_g_gsm_b_fd); - 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; -} - -static int b_handler(struct lcr_fd *fd, unsigned int what, void *instance, int index); + struct gsm_data_frame *frame = (struct gsm_data_frame *)arg; + unsigned char data[160]; + int i, cmr; -/* open external side bchannel */ -int Pgsm::bchannel_open(int index) -{ - int ret; - struct sockaddr_mISDN addr; - struct mISDNhead act; + if (!p_g_fr_decoder) + return; - if (p_m_g_gsm_b_sock > -1) { - PERROR("Socket already created for index %d\n", index); - return(-EIO); + switch (frame->msg_type) { + case GSM_TCHF_FRAME: + if (p_g_media_type != MEDIA_TYPE_GSM) { + PERROR("FR frame, but current media type mismatches.\n"); + return; + } + if (!p_g_fr_decoder) { + PERROR("FR frame, but decoder not created.\n"); + return; + } + if ((frame->data[0]>>4) != 0xd) { + PDEBUG(DEBUG_GSM, "received GSM frame with wrong magig 0x%x\n", frame->data[0]>>4); + goto bfi; + } +#ifdef WITH_GSMFR + /* decode */ + gsm_fr_decode(p_g_fr_decoder, frame->data, p_g_samples); + for (i = 0; i < 160; i++) { + data[i] = audio_s16_to_law[p_g_samples[i] & 0xffff]; + } +#endif + break; + case GSM_TCHH_FRAME: + if (p_g_media_type != MEDIA_TYPE_GSM_HR) { + PERROR("HR frame, but current media type mismatches.\n"); + return; + } + if (!p_g_hr_decoder) { + PERROR("HR frame, but decoder not created.\n"); + return; + } + if ((frame->data[0]>>4) != 0x0) + goto bfi; +#ifdef WITH_GSMHR + /* decode */ + if (gsm_hr_decode(p_g_hr_decoder, frame->data, p_g_samples)) + goto bfi; + for (i = 0; i < 160; i++) { + data[i] = audio_s16_to_law[p_g_samples[i] & 0xffff]; + } +#endif + break; + case GSM_TCHF_FRAME_EFR: + if (p_g_media_type != MEDIA_TYPE_GSM_EFR) { + PERROR("EFR frame, but current media type mismatches.\n"); + return; + } + if (!p_g_amr_decoder) { + PERROR("EFR frame, but decoder not created.\n"); + return; + } + if ((frame->data[0]>>4) != 0xc) + goto bfi; +#ifdef WITH_GSMAMR + /* decode */ + gsm_efr_decode(p_g_amr_decoder, frame->data, p_g_samples); + for (i = 0; i < 160; i++) { + data[i] = audio_s16_to_law[p_g_samples[i] & 0xffff]; + } +#endif + break; + case GSM_TCH_FRAME_AMR: + if (p_g_media_type != MEDIA_TYPE_AMR) { + PERROR("AMR frame, but current media type mismatches.\n"); + return; + } + if (!p_g_amr_decoder) { + PERROR("AMR frame, but decoder not created.\n"); + return; + } + cmr = (frame->data[1] >> 4); + if (cmr <= 7) { + p_g_amr_cmr = cmr; + p_g_amr_cmr_valid = 1; + } + if (!(frame->data[2] & 0x04)) + goto bfi; +#ifdef WITH_GSMAMR + /* decode (skip length byte in front) */ + gsm_amr_decode(p_g_amr_decoder, frame->data + 1, p_g_samples); + for (i = 0; i < 160; i++) { + data[i] = audio_s16_to_law[p_g_samples[i] & 0xffff]; + } +#endif + break; + case GSM_BAD_FRAME: + default: +bfi: + if (p_echotest) { + /* beep on bad frame */ + for (i = 0; i < 160; i++) { + if ((i & 3) > 2) + p_g_samples[i] = 15000; + else + p_g_samples[i] = -15000; + data[i] = audio_s16_to_law[p_g_samples[i] & 0xffff]; + } + } else { + /* repeat on bad frame */ + for (i = 0; i < 160; i++) { + p_g_samples[i] = (p_g_samples[i] * 14) >> 4; + data[i] = audio_s16_to_law[p_g_samples[i] & 0xffff]; + } + } } - /* 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); - } - memset(&p_m_g_gsm_b_fd, 0, sizeof(p_m_g_gsm_b_fd)); - p_m_g_gsm_b_fd.fd = p_m_g_gsm_b_sock; - register_fd(&p_m_g_gsm_b_fd, LCR_FD_READ, b_handler, this, 0); + /* record data */ + if (p_record) + record(data, 160, 0); // from down + if (p_tap) + tap(data, 160, 0); // from down + /* local echo */ + if (p_echotest) + bridge_rx(data, 160); - /* bind socket to bchannel */ - addr.family = AF_ISDN; - addr.dev = mISDNloop.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); - } + /* send to remote*/ + bridge_tx(data, 160); +} - p_m_g_gsm_b_index = index; +/* send traffic to gsm */ +int Pgsm::bridge_rx(unsigned char *data, int len) +{ + if (p_tone_name[0]) + return -EINVAL; - return(0); + return audio_send(data, len); } -/* receive from bchannel */ -void Pgsm::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len) +int Pgsm::audio_send(unsigned char *data, int len) { unsigned char frame[33]; + int ret; + + /* record data */ + if (p_record) + record(data, len, 1); // from up + if (p_tap) + tap(data, len, 1); // from up /* encoder init failed */ - if (!p_m_g_encoder) - return; + if (!p_g_fr_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) + continue; + p_g_rxpos = 0; + + switch (p_g_media_type) { + case MEDIA_TYPE_GSM: + if (!p_g_fr_encoder) { + PERROR("FR frame, but encoder not created.\n"); + break; + } +#ifdef WITH_GSMFR + /* encode data */ + gsm_fr_encode(p_g_fr_encoder, p_g_rxdata, frame); + frame_send(frame, 33, GSM_TCHF_FRAME); +#endif + break; + case MEDIA_TYPE_GSM_HR: + if (!p_g_hr_encoder) { + PERROR("HR frame, but encoder not created.\n"); + break; + } +#ifdef WITH_GSMHR + /* encode data */ + gsm_hr_encode(p_g_hr_encoder, p_g_rxdata, frame); + frame_send(frame, 15, GSM_TCHH_FRAME); +#endif + break; + case MEDIA_TYPE_GSM_EFR: + if (!p_g_amr_encoder) { + PERROR("EFR frame, but encoder not created.\n"); + break; + } +#ifdef WITH_GSMAMR /* encode data */ - gsm_audio_encode(p_m_g_encoder, p_m_g_rxdata, frame); - frame_send(frame); + gsm_efr_encode(p_g_amr_encoder, p_g_rxdata, frame); + frame_send(frame, 31, GSM_TCHF_FRAME_EFR); +#endif + break; + case MEDIA_TYPE_AMR: + if (!p_g_amr_encoder) { + PERROR("AMR frame, but encoder not created.\n"); + break; + } + if (!p_g_amr_cmr_valid) { + PDEBUG(DEBUG_GSM, "no valid CMR yet.\n"); + break; + } +#ifdef WITH_GSMAMR + /* encode data (prefix a length byte) */ + ret = gsm_amr_encode(p_g_amr_encoder, p_g_rxdata, frame + 1, p_g_amr_cmr); + frame[0] = ret; + frame_send(frame, ret + 1, GSM_TCH_FRAME_AMR); +#endif + break; } } -} -/* 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", p_m_g_gsm_b_index); + return 0; } -void Pgsm::frame_send(void *_frame) +void Pgsm::frame_send(void *_frame, int len, int msg_type) { - unsigned char buffer[sizeof(struct gsm_data_frame) + 33]; + unsigned char buffer[sizeof(struct gsm_data_frame) + len]; struct gsm_data_frame *frame = (struct gsm_data_frame *)buffer; - frame->msg_type = GSM_TCHF_FRAME; - frame->callref = p_m_g_callref; - memcpy(frame->data, _frame, 33); -#ifdef WITH_GSM_BS - mncc_send((struct gsm_network *)p_m_g_instance, frame->msg_type, frame); -#endif -#ifdef WITH_GSM_MS - mncc_send((struct osmocom_ms *)p_m_g_instance, frame->msg_type, frame); -#endif -} - - -void Pgsm::frame_receive(void *arg) -{ - struct gsm_data_frame *frame = (struct gsm_data_frame *)arg; - signed short samples[160]; - unsigned char data[160]; - int i; - - if (!p_m_g_decoder) - return; + frame->msg_type = msg_type; + frame->callref = p_g_callref; + memcpy(frame->data, _frame, len); - if ((frame->data[0]>>4) != 0xd) - PERROR("received GSM frame with wrong magig 0x%x\n", frame->data[0]>>4); - - /* decode */ - gsm_audio_decode(p_m_g_decoder, frame->data, samples); - for (i = 0; i < 160; i++) { - data[i] = audio_s16_to_law[samples[i] & 0xffff]; + if (p_g_lcr_gsm) { + mncc_send(p_g_lcr_gsm, frame->msg_type, frame); } - - /* send */ - bchannel_send(PH_DATA_REQ, 0, data, 160); } - /* * create trace */ -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; + + interface = getinterfacebyname(interface_name); + if (!interface) + return; /* select message and primitive text */ SCPY(msgtext, mncc_name(msg_type)); @@ -430,24 +533,20 @@ void gsm_trace_header(struct mISDNport *mISDNport, class PmISDN *port, unsigned if (port) { switch(port->p_type) { case PORT_TYPE_GSM_BS_OUT: - SCAT(msgtext, " LCR->BSC"); - break; case PORT_TYPE_GSM_BS_IN: - SCAT(msgtext, " LCR<-BSC"); + SCAT(msgtext, " LCR<->BSC"); break; case PORT_TYPE_GSM_MS_OUT: - SCAT(msgtext, " LCR->MS"); - break; case PORT_TYPE_GSM_MS_IN: - SCAT(msgtext, " LCR<-MS"); + 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(-1, + interface, port?numberrize_callerinfo(port->p_callerinfo.id, port->p_callerinfo.ntype, options.national, options.international):NULL, port?port->p_dialinginfo.id:NULL, direction, @@ -456,42 +555,44 @@ void gsm_trace_header(struct mISDNport *mISDNport, class PmISDN *port, unsigned msgtext); } -/* select free bchannel from loopback interface */ -int Pgsm::hunt_bchannel(void) -{ - return loop_hunt_bchannel(this, p_m_mISDNport); -} - -/* PROCEEDING INDICATION */ -void Pgsm::call_conf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) +/* modify lchan to given payload type */ +void Pgsm::modify_lchan(int media_type) { struct gsm_mncc *mode; - gsm_trace_header(p_m_mISDNport, 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); - } - end_trace(); + /* already modified to that media type */ + if (p_g_media_type == media_type) + return; - /* 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 */ + p_g_media_type = media_type; + gsm_trace_header(p_interface_name, this, MNCC_LCHAN_MODIFY, DIRECTION_OUT); + mode = create_mncc(MNCC_LCHAN_MODIFY, p_g_callref); + switch (media_type) { + case MEDIA_TYPE_GSM_EFR: + add_trace("speech", "version", "EFR given"); + mode->lchan_mode = 0x21; /* GSM V2 */ + break; + case MEDIA_TYPE_AMR: + add_trace("speech", "version", "AMR given"); + mode->lchan_mode = 0x41; /* GSM V3 */ + break; + default: + add_trace("speech", "version", "Full/Half Rate given"); + mode->lchan_mode = 0x01; /* GSM V1 */ + } + mode->lchan_type = 0x02; /* FIXME: unused */ add_trace("mode", NULL, "0x%02x", mode->lchan_mode); end_trace(); - send_and_free_mncc(p_m_g_instance, mode->msg_type, mode); - + send_and_free_mncc(p_g_lcr_gsm, mode->msg_type, mode); } -/* CALL PROCEEDING INDICATION */ +/* CALL PROCEEDING INDICATION (from network) */ void Pgsm::call_proc_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_interface_name, this, msg_type, DIRECTION_IN); end_trace(); message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_PROCEEDING); @@ -499,13 +600,16 @@ void Pgsm::call_proc_ind(unsigned int msg_type, unsigned int callref, struct gsm new_state(PORT_STATE_OUT_PROCEEDING); - if (p_m_mISDNport->earlyb && !p_m_g_tch_connected) { /* only if ... */ - gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT); + if (p_g_earlyb && !p_g_tch_connected) { /* only if ... */ + gsm_trace_header(p_interface_name, this, MNCC_FRAME_RECV, DIRECTION_OUT); end_trace(); - frame = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc(p_m_g_instance, 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; } + + /* modify to GSM FR (this is GSM user side only, so there is FR supported only) */ + modify_lchan(MEDIA_TYPE_GSM); } /* ALERTING INDICATION */ @@ -514,7 +618,7 @@ void Pgsm::alert_ind(unsigned int msg_type, unsigned int callref, struct gsm_mnc struct lcr_msg *message; struct gsm_mncc *frame; - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); + gsm_trace_header(p_interface_name, this, msg_type, DIRECTION_IN); end_trace(); message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_ALERTING); @@ -522,12 +626,17 @@ void Pgsm::alert_ind(unsigned int msg_type, unsigned int callref, struct gsm_mnc new_state(PORT_STATE_OUT_ALERTING); - if (p_m_mISDNport->earlyb && !p_m_g_tch_connected) { /* only if ... */ - gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT); + if (p_g_earlyb && !p_g_tch_connected) { /* only if ... */ + gsm_trace_header(p_interface_name, this, MNCC_FRAME_RECV, DIRECTION_OUT); end_trace(); - frame = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc(p_m_g_instance, 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; + } + + /* modify to GSM FR, if not already */ + if (!p_g_media_type) { + modify_lchan(MEDIA_TYPE_GSM); } } @@ -542,10 +651,9 @@ void Pgsm::setup_cnf(unsigned int msg_type, unsigned int callref, struct gsm_mnc 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); + SCPY(p_connectinfo.interface, p_interface_name); - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); + gsm_trace_header(p_interface_name, this, msg_type, DIRECTION_IN); if (p_connectinfo.id[0]) add_trace("connect", "number", "%s", p_connectinfo.id); else if (mncc->imsi[0]) @@ -555,24 +663,53 @@ void Pgsm::setup_cnf(unsigned int msg_type, unsigned int callref, struct gsm_mnc end_trace(); /* send resp */ - gsm_trace_header(p_m_mISDNport, this, MNCC_SETUP_COMPL_REQ, DIRECTION_OUT); - resp = create_mncc(MNCC_SETUP_COMPL_REQ, p_m_g_callref); + gsm_trace_header(p_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_m_g_instance, 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_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_interface_name, this, MNCC_FRAME_RECV, DIRECTION_OUT); end_trace(); - frame = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc(p_m_g_instance, 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)); + + /* if we have a bridge, but not yet modified, the phone accepts out requested payload. + * we force the first in list */ + if (p_g_rtp_bridge) { + if (!p_g_media_type) { + /* modify to first given type */ + modify_lchan(p_g_rtp_media_types[0]); + /* also set payload type */ + p_g_payload_type = p_g_rtp_payload_types[0]; + } + message->param.connectinfo.rtpinfo.media_types[0] = p_g_media_type; + message->param.connectinfo.rtpinfo.payload_types[0] = p_g_payload_type; + message->param.connectinfo.rtpinfo.payloads = 1; + } else { + /* modify to GSM FR, if not already + * for network side, this should have been already happened */ + if (!p_g_media_type) + modify_lchan(MEDIA_TYPE_GSM); + } + + + 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 */ @@ -580,17 +717,22 @@ 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_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_interface_name, this, MNCC_FRAME_RECV, DIRECTION_OUT); end_trace(); - frame = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc(p_m_g_instance, 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; + } + + /* modify to GSM FR, if not already */ + if (!p_g_media_type) { + modify_lchan(MEDIA_TYPE_GSM); } } @@ -601,7 +743,7 @@ 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); + gsm_trace_header(p_interface_name, this, msg_type, DIRECTION_IN); if (mncc->fields & MNCC_F_CAUSE) { location = mncc->cause.location; cause = mncc->cause.value; @@ -612,8 +754,8 @@ void Pgsm::disc_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc 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_interface_name, this, MNCC_REL_REQ, DIRECTION_OUT); #if 0 resp->fields |= MNCC_F_CAUSE; resp->cause.coding = 3; @@ -624,7 +766,7 @@ void Pgsm::disc_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc add_trace("cause", "value", "%d", resp->cause.value); #endif end_trace(); - send_and_free_mncc(p_m_g_instance, resp->msg_type, resp); + send_and_free_mncc(p_g_lcr_gsm, resp->msg_type, resp); /* sending release to endpoint */ while(p_epointlist) { @@ -636,7 +778,7 @@ void Pgsm::disc_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc free_epointlist(p_epointlist); } new_state(PORT_STATE_RELEASE); - trigger_work(&p_m_g_delete); + trigger_work(&p_g_delete); } /* CC_RELEASE INDICATION */ @@ -645,7 +787,7 @@ 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); + gsm_trace_header(p_interface_name, this, msg_type, DIRECTION_IN); if (mncc->fields & MNCC_F_CAUSE) { location = mncc->cause.location; cause = mncc->cause.value; @@ -665,7 +807,7 @@ void Pgsm::rel_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc free_epointlist(p_epointlist); } new_state(PORT_STATE_RELEASE); - trigger_work(&p_m_g_delete); + trigger_work(&p_g_delete); } /* NOTIFY INDICATION */ @@ -673,7 +815,7 @@ void Pgsm::notify_ind(unsigned int msg_type, unsigned int callref, struct gsm_mn { struct lcr_msg *message; - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); + gsm_trace_header(p_interface_name, this, msg_type, DIRECTION_IN); add_trace("notify", NULL, "%d", mncc->notify); end_trace(); @@ -693,31 +835,92 @@ void Pgsm::message_notify(unsigned int epoint_id, int message_id, union paramete 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)); + 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_m_mISDNport, this, MNCC_NOTIFY_REQ, DIRECTION_OUT); + gsm_trace_header(p_interface_name, 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 = create_mncc(MNCC_NOTIFY_REQ, p_g_callref); mncc->notify = notify; - send_and_free_mncc(p_m_g_instance, mncc->msg_type, mncc); + send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc); } } } +/* RTP create indication */ +void Pgsm::rtp_create_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) +{ + struct gsm_mncc_rtp *rtp = (struct gsm_mncc_rtp *) mncc; + + /* send queued setup, as we received remote RTP info */ + if (p_g_setup_pending) { + struct lcr_msg *message; + + 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) { + PDEBUG(DEBUG_GSM, "Got RTP peer info (%08x,%d) connecting RTP... \n", rtp->ip, rtp->port); + send_mncc_rtp_connect(); + } +} + +/* RTP connect indication */ +void Pgsm::rtp_connect_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) +{ + struct lcr_msg *message; + struct gsm_mncc_rtp *rtp = (struct gsm_mncc_rtp *) mncc; + + 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); + p_g_connect_pending = NULL; + } +} + +/* MESSAGE_PROGRESS */ +void Pgsm::message_progress(unsigned int epoint_id, int message_id, union parameter *param) +{ + if (param->progressinfo.progress == 8) { + PDEBUG(DEBUG_GSM, "Remote provides tones for us\n"); + p_g_tones = 1; + } + + if (param->progressinfo.rtpinfo.port) { + PDEBUG(DEBUG_GSM, "PROGRESS with RTP peer info, sent to BSC (%08x,%d) with media %d, pt %d\n", param->progressinfo.rtpinfo.ip, param->progressinfo.rtpinfo.port, param->progressinfo.rtpinfo.media_types[0], param->progressinfo.rtpinfo.payload_types[0]); + + /* modify channel to givne type, also sets media type */ + modify_lchan(param->progressinfo.rtpinfo.media_types[0]); + + /* connect RTP */ + p_g_rtp_ip_remote = param->progressinfo.rtpinfo.ip; + p_g_rtp_port_remote = param->progressinfo.rtpinfo.port; + /* p_g_media_type is already set by modify_lchan() */ + p_g_payload_type = param->progressinfo.rtpinfo.payload_types[0]; + send_mncc_rtp_connect(); + } +} + /* MESSAGE_ALERTING */ void Pgsm::message_alerting(unsigned int epoint_id, int message_id, union parameter *param) { 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) { + gsm_trace_header(p_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; @@ -727,16 +930,21 @@ void Pgsm::message_alerting(unsigned int epoint_id, int message_id, union parame add_trace("progress", "descr", "%d", mncc->progress.descr); } end_trace(); - send_and_free_mncc(p_m_g_instance, 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_interface_name, this, MNCC_FRAME_RECV, DIRECTION_OUT); end_trace(); - mncc = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc(p_m_g_instance, 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; + } + + /* modify to GSM FR, if not already */ + if (!p_g_media_type) { + modify_lchan(MEDIA_TYPE_GSM); } } @@ -748,11 +956,11 @@ 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_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_interface_name, this, MNCC_SETUP_RSP, DIRECTION_OUT); /* caller information */ mncc->fields |= MNCC_F_CONNECTED; mncc->connected.plan = 1; @@ -801,9 +1009,23 @@ void Pgsm::message_connect(unsigned int epoint_id, int message_id, union paramet add_trace("connected", "number", "%s", mncc->connected.number); } end_trace(); - send_and_free_mncc(p_m_g_instance, 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) { + PDEBUG(DEBUG_GSM, "CONNECT with RTP peer info, sent to BSC (%08x,%d)\n", param->connectinfo.rtpinfo.ip, param->connectinfo.rtpinfo.port); + + /* modify channel to givne type, also sets media type */ + modify_lchan(param->connectinfo.rtpinfo.media_types[0]); + + /* connect RTP */ + p_g_rtp_ip_remote = param->connectinfo.rtpinfo.ip; + p_g_rtp_port_remote = param->connectinfo.rtpinfo.port; + /* p_g_media_type is already set by modify_lchan() */ + p_g_payload_type = param->connectinfo.rtpinfo.payload_types[0]; + send_mncc_rtp_connect(); + } } /* MESSAGE_DISCONNECT */ @@ -812,9 +1034,9 @@ 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 = create_mncc(MNCC_DISC_REQ, p_g_callref); + gsm_trace_header(p_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; @@ -831,16 +1053,16 @@ void Pgsm::message_disconnect(unsigned int epoint_id, int message_id, union para add_trace("cause", "location", "%d", mncc->cause.location); add_trace("cause", "value", "%d", mncc->cause.value); end_trace(); - send_and_free_mncc(p_m_g_instance, 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_interface_name, this, MNCC_FRAME_RECV, DIRECTION_OUT); end_trace(); - mncc = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc(p_m_g_instance, 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; } } @@ -851,8 +1073,8 @@ 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 = create_mncc(MNCC_REL_REQ, p_g_callref); + gsm_trace_header(p_interface_name, this, MNCC_REL_REQ, DIRECTION_OUT); mncc->fields |= MNCC_F_CAUSE; mncc->cause.coding = 3; mncc->cause.location = param->disconnectinfo.location; @@ -861,10 +1083,10 @@ void Pgsm::message_release(unsigned int epoint_id, int message_id, union paramet add_trace("cause", "location", "%d", mncc->cause.location); add_trace("cause", "value", "%d", mncc->cause.value); end_trace(); - send_and_free_mncc(p_m_g_instance, mncc->msg_type, mncc); + send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc); new_state(PORT_STATE_RELEASE); - trigger_work(&p_m_g_delete); + trigger_work(&p_g_delete); return; } @@ -873,11 +1095,14 @@ void Pgsm::message_release(unsigned int epoint_id, int message_id, union paramet */ 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; + + 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; @@ -886,34 +1111,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 @@ -927,6 +1161,7 @@ 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); @@ -934,7 +1169,7 @@ int Pgsm::message_epoint(unsigned int epoint_id, int message_id, union parameter } - return(0); + return ret; } /* deletes only if l3id is release, otherwhise it will be triggered then */ @@ -947,68 +1182,264 @@ static int delete_event(struct lcr_work *work, void *instance, int index) return 0; } +int gsm_exit(int rc) +{ + return(rc); +} + +int gsm_init(void) +{ + /* seed the PRNG */ + srand(time(NULL)); + + return 0; +} + /* - * handler of bchannel events + * MNCC interface */ -static int b_handler(struct lcr_fd *fd, unsigned int what, void *instance, int index) + +static int mncc_q_enqueue(struct lcr_gsm *lcr_gsm, struct gsm_mncc *mncc, unsigned int len) { - class Pgsm *gsmport = (class Pgsm *)instance; - int ret; - unsigned char buffer[2048+MISDN_HEADER_LEN]; - struct mISDNhead *hh = (struct mISDNhead *)buffer; - - /* handle message from bchannel */ - if (gsmport->p_m_g_gsm_b_sock > -1) { - ret = recv(gsmport->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: - gsmport->bchannel_receive(hh, buffer+MISDN_HEADER_LEN, ret-MISDN_HEADER_LEN); - break; - case PH_ACTIVATE_IND: - gsmport->p_m_g_gsm_b_active = 1; - break; - case PH_DEACTIVATE_IND: - gsmport->p_m_g_gsm_b_active = 0; - break; + 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; + + return 0; +} + +static struct mncc_q_entry *mncc_q_dequeue(struct lcr_gsm *lcr_gsm) +{ + 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; +} + +/* 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 len = 0; + + /* 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; + } + + 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); } - } else { - if (ret < 0 && errno != EWOULDBLOCK) - PERROR("Read from GSM port, index %d failed with return code %d\n", ret); } + 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) { - free(gsm); - gsm = NULL; + 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; + } + 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 0; +} - return(rc); +/* read from OpenBSC via MNCC socket */ +static int mncc_fd_read(struct lcr_fd *lfd, void *inst, int idx) +{ + 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; + struct gsm_mncc_hello *hello = (struct gsm_mncc_hello *) 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; + + /* TODO: size check? */ + switch (mncc_prim->msg_type) { + case MNCC_SOCKET_HELLO: + if (hello->version != MNCC_SOCK_VERSION) { + PERROR("MNCC version different. BSC version is %u\n", hello->version); + mncc_fd_close(lcr_gsm, lfd); + return 0; + } + if (hello->mncc_size != sizeof(struct gsm_mncc)) { + PERROR("MNCC gsm_mncc size differs: %u %u\n", + hello->mncc_size, sizeof(struct gsm_mncc)); + mncc_fd_close(lcr_gsm, lfd); + return 0; + } + if (hello->data_frame_size != sizeof(struct gsm_data_frame)) { + PERROR("MNCC gsm_mncc size differs: %u %u\n", + hello->data_frame_size, sizeof(struct gsm_data_frame)); + mncc_fd_close(lcr_gsm, lfd); + return 0; + } + +#define CHECK_OFFSET(hello, field, lcr_gsm, lfd) \ + if (hello->field ##_offset != __builtin_offsetof(struct gsm_mncc, field)) { \ + PERROR("MNCC gsm_mncc offset of %s is %u %u\n", \ + #field, hello->field ##_offset, \ + __builtin_offsetof(struct gsm_mncc, field)); \ + mncc_fd_close(lcr_gsm, lfd); \ + return 0; \ + } + + CHECK_OFFSET(hello, called, lcr_gsm, lfd); + CHECK_OFFSET(hello, signal, lcr_gsm, lfd); + CHECK_OFFSET(hello, emergency, lcr_gsm, lfd); + CHECK_OFFSET(hello, lchan_type, lcr_gsm, lfd); +#undef CHECK_OFFSET + + break; + } + + /* 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; + } } -int gsm_init(void) +/* 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) { - /* seed the PRNG */ - srand(time(NULL)); + int rc = 0; - /* create gsm instance */ - gsm = (struct lcr_gsm *)MALLOC(sizeof(struct lcr_gsm)); + if (what & LCR_FD_READ) + rc = mncc_fd_read(lfd, inst, idx); + if (rc < 0) + return rc; - return 0; + if (what & LCR_FD_WRITE) + rc = mncc_fd_write(lfd, inst, idx); + + return rc; } -int handle_gsm(void) +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; + + 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; + } + + 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; }