X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=lcr.git;a=blobdiff_plain;f=gsm_bs.cpp;h=81a0672990c3f97adcce339de1c0a205768d9a93;hp=d062e3d5b2f6639acb413f953df529f1f11f5feb;hb=5566f74eb29be75da44e29ba72ee6f015249ce61;hpb=1e778230b935453d1183d8a457438221030dd5ee diff --git a/gsm_bs.cpp b/gsm_bs.cpp index d062e3d..81a0672 100644 --- a/gsm_bs.cpp +++ b/gsm_bs.cpp @@ -12,7 +12,9 @@ #include "main.h" #include "mncc.h" -struct lcr_gsm *gsm_bs = NULL; +struct lcr_gsm *gsm_bs_first = NULL; + +#define PAYLOAD_TYPE_GSM 3 /* * DTMF stuff @@ -47,11 +49,23 @@ void generate_dtmf(void) */ Pgsm_bs::Pgsm_bs(int type, char *portname, struct port_settings *settings, struct interface *interface) : Pgsm(type, portname, settings, interface) { - p_g_lcr_gsm = gsm_bs; + struct lcr_gsm *gsm_bs = gsm_bs_first; + + p_g_lcr_gsm = NULL; + SCPY(p_g_bs_name, interface->gsm_bs_name); + + while (gsm_bs) { + if (gsm_bs->type == LCR_GSM_TYPE_NETWORK && !strcmp(gsm_bs->name, p_g_bs_name)) { + p_g_lcr_gsm = gsm_bs; + break; + } + gsm_bs = gsm_bs->gsm_next; + } + p_g_dtmf = NULL; p_g_dtmf_index = 0; - PDEBUG(DEBUG_GSM, "Created new GSMBSPort(%s).\n", portname); + PDEBUG(DEBUG_GSM, "Created new GSMBSPort(%s %s).\n", portname, p_g_bs_name); } /* @@ -62,19 +76,147 @@ 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 select from channel type */ + if (!payloads) { + switch (mncc->lchan_type) { + case GSM_LCHAN_TCH_F: + media_types[0] = MEDIA_TYPE_GSM; + payload_types[0] = PAYLOAD_TYPE_GSM; + payloads = 1; + break; + case GSM_LCHAN_TCH_H: + media_types[0] = MEDIA_TYPE_GSM_HR; + payload_types[0] = 96; /* dynamic */ + payloads = 1; + break; + default: + mncc = create_mncc(MNCC_REL_REQ, 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 = 1; + 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, "Given lchan not supported"); + 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; + } + } + + /* 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]; + } + + if (p_g_earlyb && !p_g_tch_connected) { /* only if ... */ + struct gsm_mncc *frame; + gsm_trace_header(p_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; + } +} + /* DTMF INDICATION */ void Pgsm_bs::start_dtmf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) { struct gsm_mncc *resp; - gsm_trace_header(p_g_interface_name, 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_g_interface_name, 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_g_callref); @@ -83,15 +225,24 @@ void Pgsm_bs::start_dtmf_ind(unsigned int msg_type, unsigned int callref, struct send_and_free_mncc(p_g_lcr_gsm, resp->msg_type, resp); if (p_g_rtp_bridge) { - class Port *remote = bridge_remote(); - - 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); + /* 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 */ @@ -124,12 +275,12 @@ void Pgsm_bs::stop_dtmf_ind(unsigned int msg_type, unsigned int callref, struct { struct gsm_mncc *resp; - gsm_trace_header(p_g_interface_name, 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_g_interface_name, 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_g_callref); @@ -146,7 +297,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_g_interface_name, this, msg_type, DIRECTION_IN); + gsm_trace_header(p_interface_name, this, msg_type, DIRECTION_IN); end_trace(); /* notify the hold of call */ @@ -156,14 +307,14 @@ void Pgsm_bs::hold_ind(unsigned int msg_type, unsigned int callref, struct gsm_m message_put(message); /* acknowledge hold */ - gsm_trace_header(p_g_interface_name, 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_g_callref); send_and_free_mncc(p_g_lcr_gsm, resp->msg_type, resp); /* disable audio */ if (p_g_tch_connected) { /* it should be true */ - gsm_trace_header(p_g_interface_name, this, MNCC_FRAME_DROP, DIRECTION_OUT); + gsm_trace_header(p_interface_name, this, MNCC_FRAME_DROP, DIRECTION_OUT); end_trace(); frame = create_mncc(MNCC_FRAME_DROP, p_g_callref); send_and_free_mncc(p_g_lcr_gsm, frame->msg_type, frame); @@ -178,7 +329,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_g_interface_name, this, msg_type, DIRECTION_IN); + gsm_trace_header(p_interface_name, this, msg_type, DIRECTION_IN); end_trace(); /* notify the retrieve of call */ @@ -188,14 +339,14 @@ void Pgsm_bs::retr_ind(unsigned int msg_type, unsigned int callref, struct gsm_m message_put(message); /* acknowledge retr */ - gsm_trace_header(p_g_interface_name, 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_g_callref); send_and_free_mncc(p_g_lcr_gsm, resp->msg_type, resp); /* enable audio */ if (!p_g_tch_connected) { /* it should be true */ - gsm_trace_header(p_g_interface_name, this, MNCC_FRAME_RECV, DIRECTION_OUT); + gsm_trace_header(p_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); @@ -204,6 +355,117 @@ void Pgsm_bs::retr_ind(unsigned int msg_type, unsigned int callref, struct gsm_m } /* + * 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; + int half; + void *encoder, *decoder; + + *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++) { + half = 0; + /* 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; + encoder = p_g_fr_encoder; + decoder = p_g_fr_decoder; + break; + case 2: + add_trace("speech", "version", "EFR given"); + media_type = MEDIA_TYPE_GSM_EFR; + payload_type = dynamic_type++; + encoder = p_g_amr_encoder; + decoder = p_g_amr_decoder; + break; + case 4: + add_trace("speech", "version", "AMR given"); + media_type = MEDIA_TYPE_AMR; + payload_type = dynamic_type++; + encoder = p_g_amr_encoder; + decoder = p_g_amr_decoder; + break; + case 1: + add_trace("speech", "version", "Half Rate given"); + media_type = MEDIA_TYPE_GSM_HR; + payload_type = dynamic_type++; + encoder = p_g_hr_encoder; + decoder = p_g_hr_decoder; + half = 1; + break; + case 5: + add_trace("speech", "version", "AMR Half Rate given"); + media_type = MEDIA_TYPE_AMR; + payload_type = dynamic_type++; + encoder = p_g_amr_encoder; + decoder = p_g_amr_decoder; + half = 1; + break; + case 0x80: + add_trace("speech", "version", "Analog 8000Hz given"); + media_type = MEDIA_TYPE_ANALOG; + payload_type = dynamic_type++; + encoder = (void *)1; + decoder = (void *)1; + 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 (!half && mncc->lchan_type != GSM_LCHAN_TCH_F) { + add_trace("speech", "ignored", "Not TCH/F"); + continue; + } + if (half && mncc->lchan_type != GSM_LCHAN_TCH_H) { + add_trace("speech", "ignored", "Not TCH/H"); + continue; + } + if (!p_g_rtp_bridge) { + if (!encoder || !decoder) { + add_trace("speech", "ignored", "Codec not supported"); + 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 */ @@ -211,24 +473,28 @@ void Pgsm_bs::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_ { class Endpoint *epoint; struct lcr_msg *message; - 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_g_interface_name); + interface = getinterfacebyname(p_interface_name); if (!interface) { - PERROR("Cannot find interface %s.\n", p_g_interface_name); + PERROR("Cannot find interface %s.\n", p_interface_name); return; } /* process given callref */ - gsm_trace_header(p_g_interface_name, this, 0, DIRECTION_IN); + gsm_trace_header(p_interface_name, this, 0, DIRECTION_IN); add_trace("callref", "new", "0x%x", callref); if (p_g_callref) { /* release in case the ID is already in use */ add_trace("error", NULL, "callref already in use"); +reject: end_trace(); mncc = create_mncc(MNCC_REJ_REQ, callref); - gsm_trace_header(p_g_interface_name, 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; @@ -243,6 +509,11 @@ void Pgsm_bs::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_ trigger_work(&p_g_delete); return; } + if (callref < 0x40000000) { + /* release in case the ID is invalid */ + add_trace("error", NULL, "callref invalid, not of BSC type"); + goto reject; + } p_g_callref = callref; end_trace(); @@ -257,8 +528,21 @@ void Pgsm_bs::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_ p_callerinfo.present = INFO_PRESENT_NOTAVAIL; SCPY(p_callerinfo.imsi, mncc->imsi); p_callerinfo.screen = INFO_SCREEN_NETWORK; - p_callerinfo.ntype = INFO_NTYPE_UNKNOWN; - SCPY(p_callerinfo.interface, p_g_interface_name); + switch (mncc->calling.type) { + case 0x1: + p_callerinfo.ntype = INFO_NTYPE_INTERNATIONAL; + break; + case 0x2: + p_callerinfo.ntype = INFO_NTYPE_NATIONAL; + break; + case 0x4: + p_callerinfo.ntype = INFO_NTYPE_SUBSCRIBER; + break; + default: + p_callerinfo.ntype = INFO_NTYPE_UNKNOWN; + break; + } + SCPY(p_callerinfo.interface, p_interface_name); /* dialing information */ SCAT(p_dialinginfo.id, mncc->called.number); @@ -282,20 +566,59 @@ 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_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 select from channel type */ + if (!payloads) { + switch (mncc->lchan_type) { + case GSM_LCHAN_TCH_F: + media_types[0] = MEDIA_TYPE_GSM; + payload_types[0] = PAYLOAD_TYPE_GSM; + payloads = 1; + break; + case GSM_LCHAN_TCH_H: + media_types[0] = MEDIA_TYPE_GSM_HR; + payload_types[0] = 96; /* dynamic */ + payloads = 1; + break; + default: + 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 = 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, "Given lchan not supported"); + 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; + } + } +#if 0 + /* if no given payload type is supported, we reject the call */ + if (!payloads) { + } +#endif + /* useruser */ /* what infos did we got ... */ - gsm_trace_header(p_g_interface_name, 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 + else if (p_callerinfo.imsi[0]) 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); @@ -309,17 +632,12 @@ void Pgsm_bs::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_ 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_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(p_g_lcr_gsm, 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_g_interface_name, this, MNCC_CALL_PROC_REQ, DIRECTION_OUT); + 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; @@ -336,7 +654,7 @@ void Pgsm_bs::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_ new_state(PORT_STATE_IN_PROCEEDING); if (p_g_tones && !p_g_tch_connected) { /* only if ... */ - gsm_trace_header(p_g_interface_name, this, MNCC_FRAME_RECV, DIRECTION_OUT); + gsm_trace_header(p_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); @@ -353,15 +671,21 @@ 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->param.setup.rtpinfo.payload_type = 3; /* FIXME: receive payload type from peer */ - 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); @@ -370,35 +694,23 @@ void Pgsm_bs::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_ /* * BSC sends message to port */ -int message_bsc(struct lcr_gsm *lcr_gsm, int msg_type, void *arg) +int message_bsc(class Pgsm_bs *pgsm_bs, 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; /* Special messages */ switch(msg_type) { } - /* find callref */ - callref = mncc->callref; - port = port_first; - while(port) { - if ((port->p_type & PORT_CLASS_GSM_MASK) == PORT_CLASS_GSM_BS) { - pgsm_bs = (class Pgsm_bs *)port; - if (pgsm_bs->p_g_callref == callref) { - break; - } - } - port = port->next; - } - if (msg_type == GSM_TCHF_FRAME - || msg_type == GSM_TCHF_BAD_FRAME) { - if (port) { + || msg_type == GSM_TCHF_FRAME_EFR + || msg_type == GSM_TCHH_FRAME + || msg_type == GSM_TCH_FRAME_AMR + || msg_type == ANALOG_8000HZ + || msg_type == GSM_BAD_FRAME) { + if (pgsm_bs) { /* inject DTMF, if enabled */ if (pgsm_bs->p_g_dtmf) { unsigned char data[160]; @@ -414,19 +726,19 @@ int message_bsc(struct lcr_gsm *lcr_gsm, int msg_type, void *arg) } else pgsm_bs->frame_receive(arg); /* if we do not bridge we need to inject audio, if available */ - if (!pgsm_bs->p_bridge) { + 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->bridge_rx(data, i); + pgsm_bs->audio_send(data, i); } } return 0; } - if (!port) { + if (!pgsm_bs) { struct interface *interface; if (msg_type != MNCC_SETUP_IND) @@ -527,6 +839,13 @@ void Pgsm_bs::message_setup(unsigned int epoint_id, int message_id, union parame struct lcr_msg *message; struct epoint_list *epointlist; struct gsm_mncc *mncc; + struct interface *interface; + + interface = getinterfacebyname(p_interface_name); + if (!interface) { + PERROR("Cannot find interface %s.\n", p_interface_name); + return; + } /* copy setup infos to port */ memcpy(&p_callerinfo, ¶m->setup.callerinfo, sizeof(p_callerinfo)); @@ -536,7 +855,7 @@ void Pgsm_bs::message_setup(unsigned int epoint_id, int message_id, union parame /* no GSM MNCC connection */ if (p_g_lcr_gsm->mncc_lfd.fd < 0) { - gsm_trace_header(p_g_interface_name, this, MNCC_SETUP_REQ, DIRECTION_OUT); + 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); @@ -550,7 +869,7 @@ void Pgsm_bs::message_setup(unsigned int epoint_id, int message_id, union parame /* no number */ if (!p_dialinginfo.id[0]) { - gsm_trace_header(p_g_interface_name, this, MNCC_SETUP_REQ, DIRECTION_OUT); + 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); @@ -562,9 +881,47 @@ void Pgsm_bs::message_setup(unsigned int epoint_id, int message_id, union parame return; } + /* 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(); + 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; + } + } + // 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_g_interface_name); + 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; @@ -577,12 +934,12 @@ void Pgsm_bs::message_setup(unsigned int epoint_id, int message_id, union parame epointlist_new(epoint_id); /* creating l3id */ - gsm_trace_header(p_g_interface_name, this, 0, DIRECTION_OUT); + 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_g_interface_name, this, MNCC_SETUP_REQ, DIRECTION_OUT); + 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; @@ -691,8 +1048,14 @@ 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 + + if (interface->gsm_bs_hr) { + add_trace("lchan", "type", "TCH/H or TCH/F"); + mncc->lchan_type = GSM_LCHAN_TCH_H; + } else { + add_trace("lchan", "type", "TCH/F"); + mncc->lchan_type = GSM_LCHAN_TCH_F; + } end_trace(); send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc); @@ -702,8 +1065,6 @@ void Pgsm_bs::message_setup(unsigned int epoint_id, int message_id, union parame 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; @@ -737,31 +1098,41 @@ 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_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; - } + /* destroy all instances */ + while (gsm_bs_first) + gsm_bs_delete(gsm_bs_first->name); + return rc; +} - return(rc); +int gsm_bs_init(void) +{ + return 0; } -int gsm_bs_init(struct interface *interface) +/* add a new GSM base station instance */ +int gsm_bs_new(struct interface *interface) { + struct lcr_gsm *gsm_bs = gsm_bs_first, **gsm_bs_p = &gsm_bs_first; + + while (gsm_bs) { + gsm_bs_p = &gsm_bs->gsm_next; + gsm_bs = gsm_bs->gsm_next; + } + + PDEBUG(DEBUG_GSM, "GSM: interface for BS '%s' is created\n", interface->gsm_bs_name); + /* create gsm instance */ gsm_bs = (struct lcr_gsm *)MALLOC(sizeof(struct lcr_gsm)); SCPY(gsm_bs->interface_name, interface->name); gsm_bs->type = LCR_GSM_TYPE_NETWORK; + SCPY(gsm_bs->name, interface->gsm_bs_name); gsm_bs->sun.sun_family = AF_UNIX; - SCPY(gsm_bs->sun.sun_path, "/tmp/bsc_mncc"); + if (gsm_bs->name[0]) + SPRINT(gsm_bs->sun.sun_path, "/tmp/bsc_mncc_%s", gsm_bs->name); + else + SCPY(gsm_bs->sun.sun_path, "/tmp/bsc_mncc"); memset(&gsm_bs->socket_retry, 0, sizeof(gsm_bs->socket_retry)); add_timer(&gsm_bs->socket_retry, mncc_socket_retry_cb, gsm_bs, 0); @@ -771,5 +1142,37 @@ int gsm_bs_init(struct interface *interface) generate_dtmf(); + *gsm_bs_p = gsm_bs; + return 0; } + +int gsm_bs_delete(const char *name) +{ + struct lcr_gsm *gsm_bs = gsm_bs_first, **gsm_bs_p = &gsm_bs_first; + + PDEBUG(DEBUG_GSM, "GSM: interface for BS '%s' is deleted\n", name); + + while (gsm_bs) { + if (gsm_bs->type == LCR_GSM_TYPE_NETWORK && !strcmp(gsm_bs->name, name)) + break; + gsm_bs_p = &gsm_bs->gsm_next; + gsm_bs = gsm_bs->gsm_next; + } + + if (!gsm_bs) + return 0; + + 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); + + /* remove instance from list */ + *gsm_bs_p = gsm_bs->gsm_next; + FREE(gsm_bs, sizeof(struct lcr_gsm)); + + return 0; +} +