Merge branch 'zecke/features/rtp-bridge'

* Implement the rtp-bridge mode for MNCC
* Audio doesn't flow through the NITB at all
* It only works with IPv4 BTSes right now
* We need to select an audio codec way too early
* No tandem free operation
* Early assignment always equals TCH/F
This commit is contained in:
Holger Hans Peter Freyther 2015-09-14 10:53:35 +02:00
commit e07e9f7a6f
6 changed files with 270 additions and 7 deletions

View File

@ -120,6 +120,11 @@ struct gsm_subscriber_connection {
/* Are we part of a special "silent" call */
int silent_call;
/* MNCC rtp bridge markers */
int mncc_rtp_bridge;
int mncc_rtp_create_pending;
int mncc_rtp_connect_pending;
/* bsc structures */
struct osmo_bsc_sccp_con *sccp_con;

View File

@ -92,6 +92,9 @@ struct gsm_call {
#define MNCC_FRAME_RECV 0x0201
#define MNCC_FRAME_DROP 0x0202
#define MNCC_LCHAN_MODIFY 0x0203
#define MNCC_RTP_CREATE 0x0204
#define MNCC_RTP_CONNECT 0x0205
#define MNCC_RTP_FREE 0x0206
#define GSM_TCHF_FRAME 0x0300
#define GSM_TCHF_FRAME_EFR 0x0301
@ -163,7 +166,7 @@ struct gsm_data_frame {
unsigned char data[0];
};
#define MNCC_SOCK_VERSION 4
#define MNCC_SOCK_VERSION 5
struct gsm_mncc_hello {
uint32_t msg_type;
uint32_t version;
@ -179,6 +182,15 @@ struct gsm_mncc_hello {
uint32_t lchan_type_offset;
};
struct gsm_mncc_rtp {
uint32_t msg_type;
uint32_t callref;
uint32_t ip;
uint16_t port;
uint32_t payload_type;
uint32_t payload_msg_type;
};
char *get_mncc_name(int value);
void mncc_set_cause(struct gsm_mncc *data, int loc, int val);
void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg);

View File

@ -9,4 +9,6 @@ struct mncc_int {
extern struct mncc_int mncc_int;
uint8_t mncc_codec_for_mode(int lchan_type);
#endif

View File

@ -52,6 +52,7 @@
#include <openbsc/bsc_api.h>
#include <openbsc/osmo_msc.h>
#include <openbsc/handover.h>
#include <openbsc/mncc_int.h>
#include <osmocom/abis/e1_input.h>
#include <osmocom/core/bitvec.h>
@ -67,6 +68,8 @@
void *tall_locop_ctx;
void *tall_authciphop_ctx;
static int tch_rtp_signal(struct gsm_lchan *lchan, int signal);
static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn);
static int gsm48_tx_simple(struct gsm_subscriber_connection *conn,
uint8_t pdisc, uint8_t msg_type);
@ -1458,6 +1461,15 @@ static int switch_for_handover(struct gsm_lchan *old_lchan,
{
struct rtp_socket *old_rs, *new_rs, *other_rs;
/* Ask the new socket to send to the already known port. */
if (new_lchan->conn->mncc_rtp_bridge) {
LOGP(DHO, LOGL_DEBUG, "Forwarding RTP\n");
rsl_ipacc_mdcx(new_lchan,
old_lchan->abis_ip.connect_ip,
old_lchan->abis_ip.connect_port, 0);
return 0;
}
if (ipacc_rtp_direct) {
LOGP(DHO, LOGL_ERROR, "unable to handover in direct RTP mode\n");
return 0;
@ -1502,11 +1514,19 @@ static int switch_for_handover(struct gsm_lchan *old_lchan,
return 0;
}
static void maybe_switch_for_handover(struct gsm_lchan *lchan)
{
struct gsm_lchan *old_lchan;
old_lchan = bsc_handover_pending(lchan);
if (old_lchan)
switch_for_handover(old_lchan, lchan);
}
/* some other part of the code sends us a signal */
static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
struct gsm_lchan *lchan = signal_data, *old_lchan;
struct gsm_lchan *lchan = signal_data;
int rc;
struct gsm_network *net;
struct gsm_trans *trans;
@ -1514,6 +1534,10 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
if (subsys != SS_ABISIP)
return 0;
/* RTP bridge handling */
if (lchan->conn && lchan->conn->mncc_rtp_bridge)
return tch_rtp_signal(lchan, signal);
/* in case we use direct BTS-to-BTS RTP */
if (ipacc_rtp_direct)
return 0;
@ -1555,9 +1579,7 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
* Do we have a handover pending for this new lchan? In that
* case re-route the audio from the old channel to the new one.
*/
old_lchan = bsc_handover_pending(lchan);
if (old_lchan)
switch_for_handover(old_lchan, lchan);
maybe_switch_for_handover(lchan);
break;
case S_ABISIP_DLCX_IND:
/* the BTS tells us a RTP stream has been disconnected */
@ -2900,11 +2922,218 @@ static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg)
static int _gsm48_lchan_modify(struct gsm_trans *trans, void *arg)
{
struct gsm_mncc *mode = arg;
struct gsm_lchan *lchan = trans->conn->lchan;
/*
* We were forced to make an assignment a lot earlier and
* we should avoid sending another assignment that might
* even lead to a different kind of lchan (TCH/F vs. TCH/H).
* In case of rtp-bridge it is too late to change things
* here.
*/
if (trans->conn->mncc_rtp_bridge && lchan->tch_mode != GSM48_CMODE_SIGN)
return 0;
return gsm0808_assign_req(trans->conn, mode->lchan_mode,
trans->conn->lchan->type != GSM_LCHAN_TCH_H);
}
static void mncc_recv_rtp(struct gsm_network *net, uint32_t callref,
int cmd, uint32_t addr, uint16_t port, uint32_t payload_type,
uint32_t payload_msg_type)
{
uint8_t data[sizeof(struct gsm_mncc)];
struct gsm_mncc_rtp *rtp;
memset(&data, 0, sizeof(data));
rtp = (struct gsm_mncc_rtp *) &data[0];
rtp->callref = callref;
rtp->msg_type = cmd;
rtp->ip = addr;
rtp->port = port;
rtp->payload_type = payload_type;
rtp->payload_msg_type = payload_msg_type;
mncc_recvmsg(net, NULL, cmd, (struct gsm_mncc *)data);
}
static void mncc_recv_rtp_sock(struct gsm_network *net, struct gsm_trans *trans, int cmd)
{
struct gsm_lchan *lchan;
int msg_type;
lchan = trans->conn->lchan;
switch (lchan->abis_ip.rtp_payload) {
case RTP_PT_GSM_FULL:
msg_type = GSM_TCHF_FRAME;
break;
case RTP_PT_GSM_EFR:
msg_type = GSM_TCHF_FRAME_EFR;
break;
case RTP_PT_GSM_HALF:
msg_type = GSM_TCHH_FRAME;
break;
case RTP_PT_AMR:
msg_type = GSM_TCH_FRAME_AMR;
break;
default:
LOGP(DMNCC, LOGL_ERROR, "%s unknown payload type %d\n",
gsm_lchan_name(lchan), lchan->abis_ip.rtp_payload);
msg_type = 0;
break;
}
return mncc_recv_rtp(net, trans->callref, cmd,
lchan->abis_ip.bound_ip,
lchan->abis_ip.bound_port,
lchan->abis_ip.rtp_payload,
msg_type);
}
static void mncc_recv_rtp_err(struct gsm_network *net, uint32_t callref, int cmd)
{
return mncc_recv_rtp(net, callref, cmd, 0, 0, 0, 0);
}
static int tch_rtp_create(struct gsm_network *net, uint32_t callref)
{
struct gsm_bts *bts;
struct gsm_lchan *lchan;
struct gsm_trans *trans;
/* Find callref */
trans = trans_find_by_callref(net, callref);
if (!trans) {
LOGP(DMNCC, LOGL_ERROR, "RTP create for non-existing trans\n");
mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE);
return -EIO;
}
log_set_context(BSC_CTX_SUBSCR, trans->subscr);
if (!trans->conn) {
LOGP(DMNCC, LOGL_NOTICE, "RTP create for trans without conn\n");
mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE);
return 0;
}
lchan = trans->conn->lchan;
bts = lchan->ts->trx->bts;
if (!is_ipaccess_bts(bts)) {
/*
* I want this to be straight forward and have no audio flow
* through the nitb/osmo-mss system. This currently means that
* this will not work with BS11/Nokia type BTS. We would need
* to have a trau<->rtp bridge for these but still preferable
* in another process.
*/
LOGP(DMNCC, LOGL_ERROR, "RTP create only works with IP systems\n");
mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE);
return -EINVAL;
}
trans->conn->mncc_rtp_bridge = 1;
/*
* *sigh* we need to pick a codec now. Pick the most generic one
* right now and hope we could fix that later on. This is very
* similiar to the routine above.
* Fallback to the internal MNCC mode to select a route.
*/
if (lchan->tch_mode == GSM48_CMODE_SIGN) {
trans->conn->mncc_rtp_create_pending = 1;
return gsm0808_assign_req(trans->conn,
mncc_codec_for_mode(lchan->type),
lchan->type != GSM_LCHAN_TCH_H);
}
mncc_recv_rtp_sock(trans->net, trans, MNCC_RTP_CREATE);
return 0;
}
static int tch_rtp_connect(struct gsm_network *net, void *arg)
{
struct gsm_lchan *lchan;
struct gsm_trans *trans;
struct gsm_mncc_rtp *rtp = arg;
/* Find callref */
trans = trans_find_by_callref(net, rtp->callref);
if (!trans) {
LOGP(DMNCC, LOGL_ERROR, "RTP connect for non-existing trans\n");
mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT);
return -EIO;
}
log_set_context(BSC_CTX_SUBSCR, trans->subscr);
if (!trans->conn) {
LOGP(DMNCC, LOGL_ERROR, "RTP connect for trans without conn\n");
mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT);
return 0;
}
lchan = trans->conn->lchan;
/* TODO: Check if payload_msg_type is compatible with what we have */
if (rtp->payload_type != lchan->abis_ip.rtp_payload) {
LOGP(DMNCC, LOGL_ERROR, "RTP connect with different RTP payload\n");
mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT);
}
/*
* FIXME: payload2 can't be sent with MDCX as the osmo-bts code
* complains about both rtp and rtp payload2 being present in the
* same package!
*/
trans->conn->mncc_rtp_connect_pending = 1;
return rsl_ipacc_mdcx(lchan, rtp->ip, rtp->port, 0);
}
static int tch_rtp_signal(struct gsm_lchan *lchan, int signal)
{
struct gsm_network *net;
struct gsm_trans *tmp, *trans = NULL;
net = lchan->ts->trx->bts->network;
llist_for_each_entry(tmp, &net->trans_list, entry) {
if (!tmp->conn)
continue;
if (tmp->conn->lchan != lchan && tmp->conn->ho_lchan != lchan)
continue;
trans = tmp;
break;
}
if (!trans) {
LOGP(DMNCC, LOGL_ERROR, "%s IPA abis signal but no transaction.\n",
gsm_lchan_name(lchan));
return 0;
}
switch (signal) {
case S_ABISIP_CRCX_ACK:
if (lchan->conn->mncc_rtp_create_pending) {
lchan->conn->mncc_rtp_create_pending = 0;
LOGP(DMNCC, LOGL_NOTICE, "%s sending pending RTP create ind.\n",
gsm_lchan_name(lchan));
mncc_recv_rtp_sock(net, trans, MNCC_RTP_CREATE);
}
/*
* TODO: this appears to be too early? Why not until after
* the handover detect or the handover complete?
*/
maybe_switch_for_handover(lchan);
break;
case S_ABISIP_MDCX_ACK:
if (lchan->conn->mncc_rtp_connect_pending) {
lchan->conn->mncc_rtp_connect_pending = 0;
LOGP(DMNCC, LOGL_NOTICE, "%s sending pending RTP connect ind.\n",
gsm_lchan_name(lchan));
mncc_recv_rtp_sock(net, trans, MNCC_RTP_CONNECT);
}
break;
}
return 0;
}
static struct downstate {
uint32_t states;
int type;
@ -2985,6 +3214,13 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg)
return tch_recv_mncc(net, data->callref, 0);
case MNCC_FRAME_RECV:
return tch_recv_mncc(net, data->callref, 1);
case MNCC_RTP_CREATE:
return tch_rtp_create(net, data->callref);
case MNCC_RTP_CONNECT:
return tch_rtp_connect(net, arg);
case MNCC_RTP_FREE:
/* unused right now */
return -EIO;
case GSM_TCHF_FRAME:
case GSM_TCHF_FRAME_EFR:
case GSM_TCHH_FRAME:

View File

@ -83,6 +83,9 @@ static struct mncc_names {
{"MNCC_FRAME_RECV", 0x0201},
{"MNCC_FRAME_DROP", 0x0202},
{"MNCC_LCHAN_MODIFY", 0x0203},
{"MNCC_RTP_CREATE", 0x0204},
{"MNCC_RTP_CONNECT", 0x0205},
{"MNCC_RTP_FREE", 0x0206},
{"GSM_TCHF_FRAME", 0x0300},
{"GSM_TCHF_FRAME_EFR", 0x0301},

View File

@ -65,16 +65,21 @@ static struct gsm_call *get_call_ref(uint32_t callref)
return NULL;
}
static uint8_t determine_lchan_mode(struct gsm_mncc *setup)
uint8_t mncc_codec_for_mode(int lchan_type)
{
/* FIXME: check codec capabilities of the phone */
if (setup->lchan_type != GSM_LCHAN_TCH_H)
if (lchan_type != GSM_LCHAN_TCH_H)
return mncc_int.def_codec[0];
else
return mncc_int.def_codec[1];
}
static uint8_t determine_lchan_mode(struct gsm_mncc *setup)
{
return mncc_codec_for_mode(setup->lchan_type);
}
/* on incoming call, look up database and send setup to remote subscr. */
static int mncc_setup_ind(struct gsm_call *call, int msg_type,
struct gsm_mncc *setup)