X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=lcr.git;a=blobdiff_plain;f=gsm_bs.cpp;h=4855a5c5edcbe577f37eb971c93529e9d47ddbbf;hp=bf8bf6466bd58a5a7370f128f5af9b75be4b71de;hb=7f0d14c706328e1ff74fe8b8c16ae54407cc8055;hpb=5f685ed847af5a80f647383d0c450b67acce17a4 diff --git a/gsm_bs.cpp b/gsm_bs.cpp index bf8bf64..4855a5c 100644 --- a/gsm_bs.cpp +++ b/gsm_bs.cpp @@ -10,87 +10,49 @@ \*****************************************************************************/ #include "main.h" -#include "config.h" +#include "mncc.h" -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -extern "C" { -#include - -#include -#include -#include -#include -#include -#include -#include -#include -//#include -struct gsm_network *bsc_gsmnet = 0; -extern int ipacc_rtp_direct; -extern int bsc_bootstrap_network(int (*mmc_rev)(struct gsm_network *, int, void *), - const char *cfg_file); -extern int bsc_shutdown_net(struct gsm_network *net); -void talloc_ctx_init(void); -void on_dso_load_token(void); -void on_dso_load_rrlp(void); -void on_dso_load_ho_dec(void); -int bts_model_unknown_init(void); -int bts_model_bs11_init(void); -int bts_model_nanobts_init(void); -static struct log_target *stderr_target; -extern const char *openbsc_copyright; - -/* timer to store statistics */ -#define DB_SYNC_INTERVAL 60, 0 -static struct timer_list db_sync_timer; - -/* FIXME: copied from the include file, because it will con compile with C++ */ -struct vty_app_info { - const char *name; - const char *version; - const char *copyright; - void *tall_ctx; - int (*go_parent_cb)(struct vty *vty); - int (*is_config_node)(struct vty *vty, int node); -}; - -extern int bsc_vty_go_parent(struct vty *vty); -extern int bsc_vty_is_config_node(struct vty *vty, int node); -static struct vty_app_info vty_info = { - "OpenBSC", - PACKAGE_VERSION, - NULL, - NULL, - bsc_vty_go_parent, - bsc_vty_is_config_node, -}; - -void vty_init(struct vty_app_info *app_info); -int bsc_vty_init(void); +struct lcr_gsm *gsm_bs = NULL; -} +#define PAYLOAD_TYPE_GSM 3 -/* timer handling */ -static int _db_store_counter(struct counter *counter, void *data) -{ - return db_store_counter(counter); -} +/* + * DTMF stuff + */ +unsigned char dtmf_samples[16][8000]; +static int dtmf_x[4] = { 1209, 1336, 1477, 1633 }; +static int dtmf_y[4] = { 697, 770, 852, 941 }; -static void db_sync_timer_cb(void *data) +void generate_dtmf(void) { - /* store counters to database and re-schedule */ - counters_for_each(_db_store_counter, NULL); - bsc_schedule_timer(&db_sync_timer, DB_SYNC_INTERVAL); + double fx, fy, sample; + int i, x, y; + unsigned char *law; + + for (y = 0; y < 4; y++) { + fy = 2 * 3.1415927 * ((double)dtmf_y[y]) / 8000.0; + for (x = 0; x < 4; x++) { + fx = 2 * 3.1415927 * ((double)dtmf_x[x]) / 8000.0; + law = dtmf_samples[y << 2 | x]; + for (i = 0; i < 8000; i++) { + sample = sin(fy * ((double)i)) * 0.251 * 32767.0; /* -6 dB */ + sample += sin(fx * ((double)i)) * 0.158 * 32767.0; /* -8 dB */ + *law++ = audio_s16_to_law[(int)sample & 0xffff]; + } + } + } } + /* * constructor */ -Pgsm_bs::Pgsm_bs(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, int channel, int exclusive, int mode) : Pgsm(type, mISDNport, portname, settings, channel, exclusive, mode) +Pgsm_bs::Pgsm_bs(int type, char *portname, struct port_settings *settings, struct interface *interface) : Pgsm(type, portname, settings, interface) { - p_m_g_instance = gsm->network; + p_g_lcr_gsm = gsm_bs; + p_g_dtmf = NULL; + p_g_dtmf_index = 0; + PDEBUG(DEBUG_GSM, "Created new GSMBSPort(%s).\n", portname); } @@ -102,46 +64,185 @@ Pgsm_bs::~Pgsm_bs() PDEBUG(DEBUG_GSM, "Destroyed GSM BS process(%s).\n", p_name); } +static const char *media_type2name(unsigned char media_type) { + switch (media_type) { + case MEDIA_TYPE_ULAW: + return "PCMU"; + case MEDIA_TYPE_ALAW: + return "PCMA"; + case MEDIA_TYPE_GSM: + return "GSM"; + case MEDIA_TYPE_GSM_HR: + return "GSM-HR"; + case MEDIA_TYPE_GSM_EFR: + return "GSM-EFR"; + case MEDIA_TYPE_AMR: + return "AMR"; + } + + return "UKN"; +} + +/* PROCEEDING INDICATION (from MS) */ +void Pgsm_bs::call_conf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) +{ + int media_types[8]; + unsigned char payload_types[8]; + int payloads = 0; + + gsm_trace_header(p_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); + } + end_trace(); + + new_state(PORT_STATE_OUT_PROCEEDING); + + /* get list of offered payload types + * if list ist empty, the FR V1 is selected */ + select_payload_type(mncc, payload_types, media_types, &payloads, sizeof(payload_types)); + /* if no given payload type is supported, we assume */ + if (!payloads) { + media_types[0] = MEDIA_TYPE_GSM; + payload_types[0] = PAYLOAD_TYPE_GSM; + payloads = 1; + } + + /* select first payload type that matches the rtp list */ + if (p_g_rtp_bridge) { + int i, j; + + for (i = 0; i < p_g_rtp_payloads; i++) { + for (j = 0; j < payloads; j++) { + if (p_g_rtp_media_types[i] == media_types[j]) + break; + } + if (j < payloads) + break; + } + if (i == p_g_rtp_payloads) { + struct lcr_msg *message; + + /* payload offered by remote RTP is not supported */ + message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE); + message->param.disconnectinfo.cause = 65; + message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; + message_put(message); + /* send release */ + 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 = LOCATION_PRIVATE_LOCAL; + mncc->cause.value = 65; + 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, "None of the payload types are supported by MS"); + end_trace(); + send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc); + new_state(PORT_STATE_RELEASE); + trigger_work(&p_g_delete); + + return; + } + modify_lchan(p_g_rtp_media_types[i]); + /* use the payload type from received rtp list, not from locally generated payload types */ + p_g_payload_type = p_g_rtp_payload_types[i]; + } else { + /* modify to first given payload */ + modify_lchan(media_types[0]); + p_g_payload_type = payload_types[0]; + } +} + /* DTMF INDICATION */ void Pgsm_bs::start_dtmf_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); + gsm_trace_header(p_interface_name, 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; /* send resp */ - gsm_trace_header(p_m_mISDNport, this, MNCC_START_DTMF_RSP, DIRECTION_OUT); + gsm_trace_header(p_interface_name, this, MNCC_START_DTMF_RSP, DIRECTION_OUT); add_trace("keypad", NULL, "%c", mncc->keypad); end_trace(); - resp = create_mncc(MNCC_START_DTMF_RSP, p_m_g_callref); + resp = create_mncc(MNCC_START_DTMF_RSP, p_g_callref); + resp->fields |= MNCC_F_KEYPAD; resp->keypad = mncc->keypad; - send_and_free_mncc(p_m_g_instance, 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_put(message); + send_and_free_mncc(p_g_lcr_gsm, resp->msg_type, resp); + + if (p_g_rtp_bridge) { + /* if two members are bridged */ + if (p_bridge && p_bridge->first && p_bridge->first->next && !p_bridge->first->next->next) { + class Port *remote = NULL; + + /* select other member */ + if (p_bridge->first->port == this) + remote = p_bridge->first->next->port; + if (p_bridge->first->next->port == this) + remote = p_bridge->first->port; + + if (remote) { + struct lcr_msg *message; + + /* send dtmf information, because we bridge RTP directly */ + message = message_create(0, remote->p_serial, EPOINT_TO_PORT, MESSAGE_DTMF); + message->param.dtmf = mncc->keypad; + message_put(message); + } + } + } else { + /* generate DTMF tones, since we do audio forwarding inside LCR */ + switch (mncc->keypad) { + case '1': p_g_dtmf = dtmf_samples[0]; break; + case '2': p_g_dtmf = dtmf_samples[1]; break; + case '3': p_g_dtmf = dtmf_samples[2]; break; + case 'a': + case 'A': p_g_dtmf = dtmf_samples[3]; break; + case '4': p_g_dtmf = dtmf_samples[4]; break; + case '5': p_g_dtmf = dtmf_samples[5]; break; + case '6': p_g_dtmf = dtmf_samples[6]; break; + case 'b': + case 'B': p_g_dtmf = dtmf_samples[7]; break; + case '7': p_g_dtmf = dtmf_samples[8]; break; + case '8': p_g_dtmf = dtmf_samples[9]; break; + case '9': p_g_dtmf = dtmf_samples[10]; break; + case 'c': + case 'C': p_g_dtmf = dtmf_samples[11]; break; + case '*': p_g_dtmf = dtmf_samples[12]; break; + case '0': p_g_dtmf = dtmf_samples[13]; break; + case '#': p_g_dtmf = dtmf_samples[14]; break; + case 'd': + case 'D': p_g_dtmf = dtmf_samples[15]; break; + } + p_g_dtmf_index = 0; + } } void Pgsm_bs::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); + gsm_trace_header(p_interface_name, 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); + gsm_trace_header(p_interface_name, 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 = create_mncc(MNCC_STOP_DTMF_RSP, p_g_callref); resp->keypad = mncc->keypad; - send_and_free_mncc(p_m_g_instance, resp->msg_type, resp); + send_and_free_mncc(p_g_lcr_gsm, resp->msg_type, resp); + + /* stop DTMF */ + p_g_dtmf = NULL; } /* HOLD INDICATION */ @@ -150,7 +251,7 @@ void Pgsm_bs::hold_ind(unsigned int msg_type, unsigned int callref, struct gsm_m struct lcr_msg *message; struct gsm_mncc *resp, *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(); /* notify the hold of call */ @@ -160,18 +261,18 @@ void Pgsm_bs::hold_ind(unsigned int msg_type, unsigned int callref, struct gsm_m message_put(message); /* acknowledge hold */ - gsm_trace_header(p_m_mISDNport, this, MNCC_HOLD_CNF, DIRECTION_OUT); + gsm_trace_header(p_interface_name, this, MNCC_HOLD_CNF, DIRECTION_OUT); end_trace(); - resp = create_mncc(MNCC_HOLD_CNF, p_m_g_callref); - send_and_free_mncc(p_m_g_instance, resp->msg_type, resp); + resp = create_mncc(MNCC_HOLD_CNF, p_g_callref); + send_and_free_mncc(p_g_lcr_gsm, 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); + if (p_g_tch_connected) { /* it should be true */ + gsm_trace_header(p_interface_name, this, MNCC_FRAME_DROP, DIRECTION_OUT); end_trace(); - frame = create_mncc(MNCC_FRAME_DROP, p_m_g_callref); - send_and_free_mncc(p_m_g_instance, frame->msg_type, frame); - p_m_g_tch_connected = 0; + frame = create_mncc(MNCC_FRAME_DROP, p_g_callref); + send_and_free_mncc(p_g_lcr_gsm, frame->msg_type, frame); + p_g_tch_connected = 0; } } @@ -182,7 +283,7 @@ void Pgsm_bs::retr_ind(unsigned int msg_type, unsigned int callref, struct gsm_m struct lcr_msg *message; struct gsm_mncc *resp, *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(); /* notify the retrieve of call */ @@ -192,42 +293,131 @@ void Pgsm_bs::retr_ind(unsigned int msg_type, unsigned int callref, struct gsm_m message_put(message); /* acknowledge retr */ - gsm_trace_header(p_m_mISDNport, this, MNCC_RETRIEVE_CNF, DIRECTION_OUT); + gsm_trace_header(p_interface_name, this, MNCC_RETRIEVE_CNF, DIRECTION_OUT); end_trace(); - resp = create_mncc(MNCC_RETRIEVE_CNF, p_m_g_callref); - send_and_free_mncc(p_m_g_instance, resp->msg_type, resp); + resp = create_mncc(MNCC_RETRIEVE_CNF, p_g_callref); + send_and_free_mncc(p_g_lcr_gsm, resp->msg_type, resp); /* enable audio */ - if (!p_m_g_tch_connected) { /* it should be true */ - gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT); + if (!p_g_tch_connected) { /* it should be true */ + 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; } } /* + * select payload type by given list or GSM V1 FR + * return the payload type or 0 if not given + */ + +void Pgsm_bs::select_payload_type(struct gsm_mncc *mncc, unsigned char *payload_types, int *media_types, int *payloads, int max_payloads) +{ + int media_type; + unsigned char payload_type; + + *payloads = 0; + + gsm_trace_header(p_interface_name, this, 1 /* codec negotioation */, DIRECTION_NONE); + if ((mncc->fields & MNCC_F_BEARER_CAP)) { + /* select preferred payload type from list */ + int i; + unsigned char dynamic_type = 96; + + add_trace("bearer", "capa", "given by MS"); + for (i = 0; mncc->bearer_cap.speech_ver[i] >= 0; i++) { + /* select payload type we support */ + switch (mncc->bearer_cap.speech_ver[i]) { + case 0: + add_trace("speech", "version", "Full Rate given"); + media_type = MEDIA_TYPE_GSM; + payload_type = PAYLOAD_TYPE_GSM; + break; + case 2: + add_trace("speech", "version", "EFR given"); + media_type = MEDIA_TYPE_GSM_EFR; + payload_type = dynamic_type++; + break; + case 4: + add_trace("speech", "version", "AMR given"); + media_type = MEDIA_TYPE_AMR; + payload_type = dynamic_type++; + break; + case 1: + add_trace("speech", "version", "Half Rate given"); + media_type = MEDIA_TYPE_GSM_HR; + payload_type = dynamic_type++; + break; + case 5: + add_trace("speech", "version", "AMR Half Rate given"); + media_type = MEDIA_TYPE_AMR; + payload_type = dynamic_type++; + break; + default: + add_trace("speech", "version", "%d given", mncc->bearer_cap.speech_ver[i]); + media_type = 0; + payload_type = 0; + } + /* wen don't support it, so we check the next */ + if (!media_type) { + add_trace("speech", "ignored", "Not supported by LCR"); + continue; + } + if (!p_g_rtp_bridge) { + if (media_type != MEDIA_TYPE_GSM) { + add_trace("speech", "ignored", "Not suitable for LCR"); + continue; + } + } + if (*payloads <= max_payloads) { + media_types[*payloads] = media_type; + payload_types[*payloads] = payload_type; + (*payloads)++; + } + } + } else { + add_trace("bearer", "capa", "not given by MS"); + add_trace("speech", "version", "Full Rate given"); + media_types[0] = MEDIA_TYPE_GSM; + payload_types[0] = PAYLOAD_TYPE_GSM; + *payloads = 1; + } + if (!(*payloads)) + add_trace("error", "", "All given payload types unsupported"); + end_trace(); +} + +/* * handles all indications */ /* SETUP INDICATION */ void Pgsm_bs::setup_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; + struct gsm_mncc *proceeding, *frame; + struct interface *interface; + int media_types[8]; + unsigned char payload_types[8]; + int payloads = 0; + + interface = getinterfacebyname(p_interface_name); + if (!interface) { + PERROR("Cannot find interface %s.\n", p_interface_name); + return; + } /* process given callref */ - l1l2l3_trace_header(p_m_mISDNport, this, L3_NEW_L3ID_IND, DIRECTION_IN); + gsm_trace_header(p_interface_name, this, 0, DIRECTION_IN); add_trace("callref", "new", "0x%x", callref); - if (p_m_g_callref) { + if (p_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); + gsm_trace_header(p_interface_name, this, MNCC_REJ_REQ, DIRECTION_OUT); mncc->fields |= MNCC_F_CAUSE; mncc->cause.coding = 3; mncc->cause.location = 1; @@ -237,33 +427,14 @@ void Pgsm_bs::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_ add_trace("cause", "value", "%d", mncc->cause.value); add_trace("reason", NULL, "callref already in use"); 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; } - p_m_g_callref = callref; + p_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->fields |= MNCC_F_CAUSE; - 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(p_m_g_instance, mncc->msg_type, mncc); - new_state(PORT_STATE_RELEASE); - trigger_work(&p_m_g_delete); - return; - } - /* caller info */ if (mncc->clir.inv) p_callerinfo.present = INFO_PRESENT_RESTRICTED; @@ -276,8 +447,7 @@ void Pgsm_bs::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_ 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); + SCPY(p_callerinfo.interface, p_interface_name); /* dialing information */ SCAT(p_dialinginfo.id, mncc->called.number); @@ -301,46 +471,46 @@ void Pgsm_bs::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_ 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); + p_g_mode = p_capainfo.source_mode; + + /* get list of offered payload types + * if list ist empty, the FR V1 is selected */ + select_payload_type(mncc, payload_types, media_types, &payloads, sizeof(payload_types)); + /* if no given payload type is supported, we assume */ + if (!payloads) { + media_types[0] = MEDIA_TYPE_GSM; + payload_types[0] = PAYLOAD_TYPE_GSM; + payloads = 1; + } +#if 0 + /* if no given payload type is supported, we reject the call */ + if (!payloads) { + mncc = create_mncc(MNCC_REJ_REQ, callref); + gsm_trace_header(p_interface_name, this, MNCC_REJ_REQ, DIRECTION_OUT); mncc->fields |= MNCC_F_CAUSE; mncc->cause.coding = 3; mncc->cause.location = 1; - mncc->cause.value = 34; + mncc->cause.value = 65; 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"); + add_trace("reason", NULL, "Given speech codec(s) not supported"); 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; } - bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE); - if (bchannel_open(p_m_b_index)) - goto no_channel; +#endif + + /* useruser */ /* what infos did we got ... */ - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); + gsm_trace_header(p_interface_name, this, msg_type, DIRECTION_IN); if (p_callerinfo.id[0]) add_trace("calling", "number", "%s", p_callerinfo.id); else @@ -354,22 +524,17 @@ void Pgsm_bs::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_ 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"); + epoint->ep_app = new_endpointapp(epoint, 0, interface->app); //incoming 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); - mode->lchan_mode = 0x01; /* GSM V1 */ - add_trace("mode", NULL, "0x%02x", mode->lchan_mode); - end_trace(); - send_and_free_mncc(p_m_g_instance, mode->msg_type, mode); + /* modify lchan in case of no rtp bridge */ + if (!p_g_rtp_bridge) + modify_lchan(media_types[0]); /* 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) { + gsm_trace_header(p_interface_name, this, MNCC_CALL_PROC_REQ, DIRECTION_OUT); + proceeding = create_mncc(MNCC_CALL_PROC_REQ, p_g_callref); + if (p_g_tones) { proceeding->fields |= MNCC_F_PROGRESS; proceeding->progress.coding = 3; /* GSM */ proceeding->progress.location = 1; @@ -379,21 +544,20 @@ void Pgsm_bs::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_ add_trace("progress", "descr", "%d", proceeding->progress.descr); } end_trace(); - send_and_free_mncc(p_m_g_instance, proceeding->msg_type, proceeding); + send_and_free_mncc(p_g_lcr_gsm, proceeding->msg_type, proceeding); 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); + if (p_g_tones && !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; } /* 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)); @@ -402,20 +566,37 @@ void Pgsm_bs::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_ 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); + if (p_g_rtp_bridge) { + struct gsm_mncc_rtp *rtp; + int i; + + PDEBUG(DEBUG_GSM, "Request RTP peer info, before forwarding setup\n"); + p_g_setup_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); + + for (i = 0; i < (int)sizeof(message->param.setup.rtpinfo.payload_types) && i < payloads; i++) { + message->param.setup.rtpinfo.media_types[i] = media_types[i]; + message->param.setup.rtpinfo.payload_types[i] = payload_types[i]; + message->param.setup.rtpinfo.payloads++; + } + + } else + message_put(message); + } /* * BSC sends message to port */ -static int message_bsc(struct gsm_network *net, int msg_type, void *arg) +int message_bsc(struct lcr_gsm *lcr_gsm, int msg_type, void *arg) { struct gsm_mncc *mncc = (struct gsm_mncc *)arg; unsigned int callref = mncc->callref; class Port *port; class Pgsm_bs *pgsm_bs = NULL; char name[64]; - struct mISDNport *mISDNport; +// struct mISDNport *mISDNport; /* Special messages */ switch(msg_type) { @@ -427,30 +608,51 @@ static int message_bsc(struct gsm_network *net, int msg_type, void *arg) while(port) { if ((port->p_type & PORT_CLASS_GSM_MASK) == PORT_CLASS_GSM_BS) { pgsm_bs = (class Pgsm_bs *)port; - if (pgsm_bs->p_m_g_callref == callref) { + if (pgsm_bs->p_g_callref == callref) { break; } } port = port->next; } - if (msg_type == GSM_TCHF_FRAME) { - if (port) - pgsm_bs->frame_receive(arg); + if (msg_type == GSM_TCHF_FRAME + || msg_type == GSM_BAD_FRAME) { + if (port) { + /* inject DTMF, if enabled */ + if (pgsm_bs->p_g_dtmf) { + unsigned char data[160]; + int i; + + for (i = 0; i < 160; i++) { + data[i] = pgsm_bs->p_g_dtmf[pgsm_bs->p_g_dtmf_index++]; + if (pgsm_bs->p_g_dtmf_index == 8000) + pgsm_bs->p_g_dtmf_index = 0; + } + /* send */ + pgsm_bs->bridge_tx(data, 160); + } else + pgsm_bs->frame_receive(arg); + /* if we do not bridge we need to inject audio, if available */ + if (!pgsm_bs->p_bridge || pgsm_bs->p_tone_name[0]) { + unsigned char data[160]; + int i; + + i = pgsm_bs->read_audio(data, 160); + if (i) + pgsm_bs->audio_send(data, i); + } + } return 0; } if (!port) { + struct interface *interface; + if (msg_type != MNCC_SETUP_IND) return(0); - /* find gsm port */ - mISDNport = mISDNport_first; - while(mISDNport) { - if (mISDNport->gsm_bs) - break; - mISDNport = mISDNport->next; - } - if (!mISDNport) { + + interface = getinterfacebyname(lcr_gsm->interface_name); + if (!interface) { struct gsm_mncc *rej; rej = create_mncc(MNCC_REJ_REQ, callref); @@ -462,14 +664,14 @@ static int message_bsc(struct gsm_network *net, int msg_type, void *arg) add_trace("cause", "coding", "%d", rej->cause.coding); add_trace("cause", "location", "%d", rej->cause.location); add_trace("cause", "value", "%d", rej->cause.value); + add_trace("reason", NULL, "interface %s not found", lcr_gsm->interface_name); end_trace(); - send_and_free_mncc(gsm->network, rej->msg_type, rej); + send_and_free_mncc(lcr_gsm, 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_bs = new Pgsm_bs(PORT_TYPE_GSM_BS_IN, mISDNport, name, NULL, 0, 0, B_MODE_TRANSPARENT))) - + SPRINT(name, "%s-%d-in", interface->name, 0); + if (!(pgsm_bs = new Pgsm_bs(PORT_TYPE_GSM_BS_IN, name, NULL, interface))) FATAL("Cannot create Port instance.\n"); } @@ -478,6 +680,14 @@ static int message_bsc(struct gsm_network *net, int msg_type, void *arg) pgsm_bs->setup_ind(msg_type, callref, mncc); break; + case MNCC_RTP_CREATE: + pgsm_bs->rtp_create_ind(msg_type, callref, mncc); + break; + + case MNCC_RTP_CONNECT: + pgsm_bs->rtp_connect_ind(msg_type, callref, mncc); + break; + case MNCC_START_DTMF_IND: pgsm_bs->start_dtmf_ind(msg_type, callref, mncc); break; @@ -534,10 +744,8 @@ static int message_bsc(struct gsm_network *net, int msg_type, void *arg) void Pgsm_bs::message_setup(unsigned int epoint_id, int message_id, union parameter *param) { 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)); @@ -545,60 +753,75 @@ void Pgsm_bs::message_setup(unsigned int epoint_id, int message_id, union parame 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."); + /* no GSM MNCC connection */ + if (p_g_lcr_gsm->mncc_lfd.fd < 0) { + gsm_trace_header(p_interface_name, this, MNCC_SETUP_REQ, DIRECTION_OUT); + add_trace("failure", NULL, "No MNCC connection."); end_trace(); message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE); - message->param.disconnectinfo.cause = 28; + message->param.disconnectinfo.cause = 41; // temp. failure. message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; message_put(message); new_state(PORT_STATE_RELEASE); - trigger_work(&p_m_g_delete); + trigger_work(&p_g_delete); 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."); + + /* no number */ + if (!p_dialinginfo.id[0]) { + gsm_trace_header(p_interface_name, 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 = 27; // temp. unavail. + message->param.disconnectinfo.cause = 28; message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; message_put(message); new_state(PORT_STATE_RELEASE); - trigger_work(&p_m_g_delete); + trigger_work(&p_g_delete); return; } - /* 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."); + /* unsupported codec for RTP bridge */ + if (param->setup.rtpinfo.port) { + int i; + + p_g_rtp_payloads = 0; + gsm_trace_header(p_interface_name, this, 1 /* codec negotioation */, DIRECTION_NONE); + for (i = 0; i < param->setup.rtpinfo.payloads; i++) { + switch (param->setup.rtpinfo.media_types[i]) { + case MEDIA_TYPE_GSM: + case MEDIA_TYPE_GSM_EFR: + case MEDIA_TYPE_AMR: + case MEDIA_TYPE_GSM_HR: + add_trace("rtp", "payload", "%s:%d supported", media_type2name(param->setup.rtpinfo.media_types[i]), param->setup.rtpinfo.payload_types[i]); + if (p_g_rtp_payloads < (int)sizeof(p_g_rtp_payload_types)) { + p_g_rtp_media_types[p_g_rtp_payloads] = param->setup.rtpinfo.media_types[i]; + p_g_rtp_payload_types[p_g_rtp_payloads] = param->setup.rtpinfo.payload_types[i]; + p_g_rtp_payloads++; + } + break; + default: + add_trace("rtp", "payload", "%s:%d unsupported", media_type2name(param->setup.rtpinfo.media_types[i]), param->setup.rtpinfo.payload_types[i]); + } + } 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; - message_put(message); - new_state(PORT_STATE_RELEASE); - trigger_work(&p_m_g_delete); - return; + if (!p_g_rtp_payloads) { + gsm_trace_header(p_interface_name, this, MNCC_SETUP_REQ, DIRECTION_OUT); + add_trace("failure", NULL, "No payload given that is supported by GSM"); + end_trace(); + message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE); + message->param.disconnectinfo.cause = 65; + message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; + message_put(message); + new_state(PORT_STATE_RELEASE); + trigger_work(&p_g_delete); + 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); + do_screen(1, p_callerinfo.id, sizeof(p_callerinfo.id), &p_callerinfo.ntype, &p_callerinfo.present, p_interface_name); /* attach only if not already */ epointlist = p_epointlist; @@ -611,13 +834,13 @@ void Pgsm_bs::message_setup(unsigned int epoint_id, int message_id, union parame 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); + gsm_trace_header(p_interface_name, this, 0, DIRECTION_OUT); + p_g_callref = new_callref++; + add_trace("callref", "new", "0x%x", p_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); + gsm_trace_header(p_interface_name, this, MNCC_SETUP_REQ, DIRECTION_OUT); + mncc = create_mncc(MNCC_SETUP_REQ, p_g_callref); /* caller information */ mncc->fields |= MNCC_F_CALLING; mncc->calling.plan = 1; @@ -725,18 +948,22 @@ void Pgsm_bs::message_setup(unsigned int epoint_id, int message_id, union parame 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(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_SETUP); message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_PROCEEDING); message_put(message); - new_state(PORT_STATE_OUT_PROCEEDING); + /* RTP bridge */ + if (param->setup.rtpinfo.port) { + p_g_rtp_bridge = 1; + p_g_rtp_ip_remote = param->setup.rtpinfo.ip; + p_g_rtp_port_remote = param->setup.rtpinfo.port; + } else + p_g_rtp_bridge = 0; } /* @@ -764,117 +991,38 @@ int Pgsm_bs::message_epoint(unsigned int epoint_id, int message_id, union parame int gsm_bs_exit(int rc) { /* free gsm instance */ - if (gsm) { - /* shutdown network */ - if (gsm->network) - bsc_shutdown_net((struct gsm_network *)gsm->network); - /* free network */ -// if (gsm->network) { -// free((struct gsm_network *)gsm->network); /* TBD */ -// } + if (gsm_bs) { + if (gsm_bs->mncc_lfd.fd > -1) { + close(gsm_bs->mncc_lfd.fd); + unregister_fd(&gsm_bs->mncc_lfd); + } + + del_timer(&gsm_bs->socket_retry); + free(gsm_bs); + gsm_bs = NULL; } + return(rc); } -int gsm_bs_init(void) +int gsm_bs_init(struct interface *interface) { - char hlr[128], cfg[128], filename[128]; - mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - int pcapfd, rc; - - vty_info.copyright = openbsc_copyright; - - log_init(&log_info); - tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc"); - talloc_ctx_init(); - on_dso_load_token(); - on_dso_load_rrlp(); - on_dso_load_ho_dec(); - stderr_target = log_target_create_stderr(); - log_add_target(stderr_target); - - bts_model_unknown_init(); - bts_model_bs11_init(); - bts_model_nanobts_init(); - - /* enable filters */ - log_set_all_filter(stderr_target, 1); - - /* Init VTY (need to preceed options) */ - vty_init(&vty_info); - bsc_vty_init(); - - /* set debug */ - if (gsm->conf.debug[0]) - log_parse_category_mask(stderr_target, gsm->conf.debug); - - /* 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); - } + /* create gsm instance */ + gsm_bs = (struct lcr_gsm *)MALLOC(sizeof(struct lcr_gsm)); - /* use RTP proxy for audio streaming */ - ipacc_rtp_direct = 0; + SCPY(gsm_bs->interface_name, interface->name); + gsm_bs->type = LCR_GSM_TYPE_NETWORK; + gsm_bs->sun.sun_family = AF_UNIX; + SCPY(gsm_bs->sun.sun_path, "/tmp/bsc_mncc"); - /* bootstrap network */ - if (gsm->conf.openbsc_cfg[0] == '/') - SCPY(cfg, gsm->conf.openbsc_cfg); - else - SPRINT(cfg, "%s/%s", CONFIG_DATA, gsm->conf.openbsc_cfg); - rc = bsc_bootstrap_network(&message_bsc, cfg); - if (rc < 0) { - PERROR("Failed to bootstrap GSM network.\n"); - return gsm_exit(-1); - } - bsc_api_init(bsc_gsmnet, msc_bsc_api()); - gsm->network = bsc_gsmnet; - - /* init database */ - if (gsm->conf.hlr[0] == '/') - SCPY(hlr, gsm->conf.hlr); - else - SPRINT(hlr, "%s/%s", CONFIG_DATA, gsm->conf.hlr); - if (db_init(hlr)) { - PERROR("GSM DB: Failed to init database '%s'. Please check the option settings.\n", hlr); - return gsm_exit(-1); - } - printf("DB: Database initialized.\n"); - if (db_prepare()) { - PERROR("GSM DB: Failed to prepare database.\n"); - return gsm_exit(-1); - } - printf("DB: Database prepared.\n"); - - /* setup the timer */ - db_sync_timer.cb = db_sync_timer_cb; - db_sync_timer.data = NULL; - bsc_schedule_timer(&db_sync_timer, DB_SYNC_INTERVAL); + memset(&gsm_bs->socket_retry, 0, sizeof(gsm_bs->socket_retry)); + add_timer(&gsm_bs->socket_retry, mncc_socket_retry_cb, gsm_bs, 0); - return 0; -} + /* do the initial connect */ + mncc_socket_retry_cb(&gsm_bs->socket_retry, gsm_bs, 0); -/* - * handles bsc select function within LCR's main loop - */ -int handle_gsm_bs(void) -{ - int ret1, ret2; + generate_dtmf(); - ret1 = bsc_upqueue((struct gsm_network *)gsm->network); - log_reset_context(); - ret2 = bsc_select_main(1); /* polling */ - if (ret1 || ret2) - return 1; return 0; } -