Add new ORTP based libosmo-trau based voice support

Using osmo-bts-sysmo and this code, it is now possible to do FR and AMR
based voice calls on TCH/F.

A lot of CPU is wasted in the conversion between the RTP formats and the
L1 specific formats for the codec frames.  All data needs to be shifted
by four bits, and the order of bits needs to be reversed in every byte.
This commit is contained in:
Harald Welte 2011-09-03 15:41:12 +02:00
parent b7bec6d488
commit 526b0c62cf
9 changed files with 788 additions and 58 deletions

View File

@ -33,4 +33,7 @@ int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan);
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx);
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, uint8_t *rtp_pl,
unsigned int rtp_pl_len);
#endif

View File

@ -34,6 +34,7 @@
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/protocol/gsm_12_21.h>
#include <osmocom/gsm/lapdm.h>
#include <osmocom/trau/osmo_ortp.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/abis.h>
@ -48,6 +49,7 @@ void *tall_bts_ctx;
int bts_init(struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb;
struct gsm_bts_trx *trx;
bts->role = btsb = talloc_zero(bts, struct gsm_bts_role_bts);
@ -59,6 +61,22 @@ int bts_init(struct gsm_bts *bts)
/* set BTS to dependency */
oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_DEPENDENCY);
/* initialize bts data structure */
llist_for_each_entry(trx, &bts->trx_list, list) {
int i;
for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
struct gsm_bts_trx_ts *ts = &trx->ts[i];
int k;
for (k = 0; k < ARRAY_SIZE(ts->lchan); k++) {
struct gsm_lchan *lchan = &ts->lchan[k];
INIT_LLIST_HEAD(&lchan->dl_tch_queue);
}
}
}
osmo_rtp_init(tall_bts_ctx);
return bts_model_init(bts);
}

View File

@ -30,7 +30,7 @@
#include <osmocom/gsm/rsl.h>
#include <osmocom/gsm/lapdm.h>
#include <osmocom/gsm/protocol/gsm_12_21.h>
#include <osmocom/trau/rtp.h>
#include <osmocom/trau/osmo_ortp.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
@ -68,6 +68,19 @@ int osmo_in_array(unsigned int search, const unsigned int *arr, unsigned int siz
}
#define OSMO_IN_ARRAY(search, arr) osmo_in_array(search, arr, ARRAY_SIZE(arr))
int msgb_queue_flush(struct llist_head *list)
{
struct msgb *msg, *msg2;
int count = 0;
llist_for_each_entry_safe(msg, msg2, list, list) {
msgb_free(msg);
count++;
}
return count;
}
/* FIXME: move this to libosmocore */
void gsm48_gen_starting_time(uint8_t *out, struct gsm_time *gtime)
{
@ -76,6 +89,32 @@ void gsm48_gen_starting_time(uint8_t *out, struct gsm_time *gtime)
out[1] = (gtime->t3 << 5) | gtime->t2;
}
/* compute lchan->rsl_cmode and lchan->tch_mode from RSL CHAN MODE IE */
static void lchan_tchmode_from_cmode(struct gsm_lchan *lchan,
struct rsl_ie_chan_mode *cm)
{
lchan->rsl_cmode = cm->spd_ind;
switch (cm->chan_rate) {
case RSL_CMOD_SP_GSM1:
lchan->tch_mode = GSM48_CMODE_SPEECH_V1;
break;
case RSL_CMOD_SP_GSM2:
lchan->tch_mode = GSM48_CMODE_SPEECH_EFR;
break;
case RSL_CMOD_SP_GSM3:
lchan->tch_mode = GSM48_CMODE_SPEECH_AMR;
break;
case RSL_CMOD_SP_NT_14k5:
lchan->tch_mode = GSM48_CMODE_DATA_14k5;
break;
case RSL_CMOD_SP_NT_12k0:
lchan->tch_mode = GSM48_CMODE_DATA_12k0;
break;
case RSL_CMOD_SP_NT_6k0:
lchan->tch_mode = GSM48_CMODE_DATA_6k0;
break;
}
}
/*
@ -547,8 +586,9 @@ static int rsl_rx_chan_activ(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
struct gsm_lchan *lchan = msg->lchan;
struct rsl_ie_chan_mode *cm;
struct tlv_parsed tp;
uint8_t type, mode;
uint8_t type;
rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
@ -566,7 +606,8 @@ static int rsl_rx_chan_activ(struct msgb *msg)
msgb_free(msg);
return rsl_tx_chan_nack(msg->trx, msg, RSL_ERR_MAND_IE_ERROR);
}
mode = *TLVP_VAL(&tp, RSL_IE_CHAN_MODE);
cm = (struct rsl_ie_chan_mode *) TLVP_VAL(&tp, RSL_IE_CHAN_MODE);
lchan_tchmode_from_cmode(lchan, cm);
/* 9.3.7 Encryption Information */
if (TLVP_PRESENT(&tp, RSL_IE_ENCR_INFO)) {
@ -635,10 +676,19 @@ static int rsl_rx_chan_activ(struct msgb *msg)
copy_sacch_si_to_lchan(lchan);
}
/* 9.3.52 MultiRate Configuration */
if (TLVP_PRESENT(&tp, RSL_IE_MR_CONFIG)) {
if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_conf)) {
LOGP(DRSL, LOGL_ERROR, "Error parsing MultiRate conf IE\n");
return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT);
}
memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
}
/* 9.3.53 MultiRate Control */
/* 9.3.54 Supported Codec Types */
LOGP(DRSL, LOGL_INFO, " chan_nr=0x%02x type=0x%02x mode=0x%02x\n", dch->chan_nr, type, mode);
LOGP(DRSL, LOGL_INFO, " chan_nr=0x%02x type=0x%02x mode=0x%02x\n",
dch->chan_nr, type, lchan->tch_mode);
/* actually activate the channel in the BTS */
return bts_model_rsl_chan_act(msg->lchan, &tp);
@ -651,8 +701,9 @@ static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan)
if (lchan->abis_ip.rtp_socket) {
rsl_tx_ipac_dlcx_ind(lchan, RSL_ERR_NORMAL_UNSPEC);
rtp_socket_free(lchan->abis_ip.rtp_socket);
osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
lchan->abis_ip.rtp_socket = NULL;
msgb_queue_flush(&lchan->dl_tch_queue);
}
rc = bts_model_rsl_chan_rel(lchan);
@ -795,6 +846,90 @@ static int rsl_rx_encr_cmd(struct msgb *msg)
}
}
/* 8.4.11 MODE MODIFY NEGATIVE ACKNOWLEDGE */
static int rsl_tx_mode_modif_nack(struct gsm_lchan *lchan, uint8_t cause)
{
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
LOGP(DRSL, LOGL_NOTICE, "%s Tx MODE MODIFY NACK (cause = 0x%02x)\n",
gsm_lchan_name(lchan), cause);
msg->len = 0;
msg->data = msg->tail = msg->l3h;
/* 9.3.26 Cause */
msgb_tlv_put(msg, RSL_IE_CAUSE, 1, &cause);
rsl_dch_push_hdr(msg, RSL_MT_MODE_MODIFY_NACK, chan_nr);
msg->lchan = lchan;
return abis_rsl_sendmsg(msg);
}
/* 8.4.10 MODE MODIFY ACK */
static int rsl_tx_mode_modif_ack(struct gsm_lchan *lchan)
{
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
LOGP(DRSL, LOGL_INFO, "%s Tx MODE MODIF ACK\n", gsm_lchan_name(lchan));
rsl_dch_push_hdr(msg, RSL_MT_MODE_MODIFY_ACK, chan_nr);
msg->trx = lchan->ts->trx;
return abis_rsl_sendmsg(msg);
}
/* 8.4.9 MODE MODIFY */
static int rsl_rx_mode_modif(struct msgb *msg)
{
struct gsm_lchan *lchan = msg->lchan;
struct rsl_ie_chan_mode *cm;
struct tlv_parsed tp;
int rc;
rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
/* 9.3.6 Channel Mode */
if (!TLVP_PRESENT(&tp, RSL_IE_CHAN_MODE)) {
LOGP(DRSL, LOGL_NOTICE, "missing Channel Mode\n");
msgb_free(msg);
return rsl_tx_mode_modif_nack(lchan, RSL_ERR_MAND_IE_ERROR);
}
cm = (struct rsl_ie_chan_mode *) TLVP_VAL(&tp, RSL_IE_CHAN_MODE);
lchan_tchmode_from_cmode(lchan, cm);
/* 9.3.7 Encryption Information */
if (TLVP_PRESENT(&tp, RSL_IE_ENCR_INFO)) {
uint8_t len = TLVP_LEN(&tp, RSL_IE_ENCR_INFO);
const uint8_t *val = TLVP_VAL(&tp, RSL_IE_ENCR_INFO);
if (encr_info2lchan(lchan, val, len) < 0)
return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT);
}
/* 9.3.45 Main channel reference */
/* 9.3.52 MultiRate Configuration */
if (TLVP_PRESENT(&tp, RSL_IE_MR_CONFIG)) {
if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_conf)) {
LOGP(DRSL, LOGL_ERROR, "Error parsing MultiRate conf IE\n");
return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT);
}
memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
}
/* 9.3.53 MultiRate Control */
/* 9.3.54 Supported Codec Types */
rc = bts_model_rsl_mode_modify(msg->lchan);
/* FIXME: delay this until L1 says OK? */
rsl_tx_mode_modif_ack(msg->lchan);
return rc;
}
/* 8.4.20 SACCH INFO MODify */
static int rsl_rx_sacch_inf_mod(struct msgb *msg)
{
@ -884,10 +1019,10 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
else
name = "MDCX";
ia.s_addr = lchan->abis_ip.bound_ip;
ia.s_addr = htonl(lchan->abis_ip.bound_ip);
LOGP(DRSL, LOGL_INFO, "%s RSL Tx IPAC_%s_ACK (local %s:%u)\n",
gsm_lchan_name(lchan), name, inet_ntoa(ia),
ntohs(lchan->abis_ip.bound_port));
lchan->abis_ip.bound_port);
/* Connection ID */
msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, htons(lchan->abis_ip.conn_id));
@ -899,7 +1034,7 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
/* locally bound port */
msgb_tv16_put(msg, RSL_IE_IPAC_LOCAL_PORT,
htons(lchan->abis_ip.bound_port));
lchan->abis_ip.bound_port);
if (inc_pt2) {
/* RTP Payload Type 2 */
@ -1035,7 +1170,7 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
}
/* FIXME: select default value depending on speech_mode */
//if (!payload_type)
lchan->abis_ip.rtp_socket = rtp_socket_create(lchan->ts->trx);
lchan->abis_ip.rtp_socket = osmo_rtp_socket_create(lchan->ts->trx);
if (!lchan->abis_ip.rtp_socket) {
LOGP(DRSL, LOGL_ERROR,
"%s IPAC Failed to create RTP/RTCP sockets\n",
@ -1043,49 +1178,73 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
inc_ip_port, dch->c.msg_type);
}
lchan->abis_ip.rtp_socket->priv = lchan;
lchan->abis_ip.rtp_socket->rx_cb = &bts_model_rtp_rx_cb;
rc = rtp_socket_bind(lchan->abis_ip.rtp_socket, INADDR_ANY);
#warning remove hard-coded IP address
rc = osmo_rtp_socket_bind(lchan->abis_ip.rtp_socket,
"192.168.100.239", -1);
if (rc < 0) {
LOGP(DRSL, LOGL_ERROR,
"%s IPAC Failed to bind RTP/RTCP sockets\n",
gsm_lchan_name(lchan));
rtp_socket_free(lchan->abis_ip.rtp_socket);
osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
lchan->abis_ip.rtp_socket = NULL;
msgb_queue_flush(&lchan->dl_tch_queue);
return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
inc_ip_port, dch->c.msg_type);
}
rc = osmo_rtp_get_bound_ip_port(lchan->abis_ip.rtp_socket,
&lchan->abis_ip.bound_ip,
&lchan->abis_ip.bound_port);
if (rc < 0)
LOGP(DRSL, LOGL_ERROR, "%s IPAC cannot obtain "
"locally bound IP/port: %d\n",
gsm_lchan_name(lchan), rc);
/* FIXME: multiplex connection, BSC proxy */
} else {
/* MDCX */
if (!lchan->abis_ip.rtp_socket) {
LOGP(DRSL, LOGL_ERROR, "%s Rx RSL IPAC MDCX, "
"but we have no RTP socket!\n");
"but we have no RTP socket!\n",
gsm_lchan_name(lchan));
return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
inc_ip_port, dch->c.msg_type);
}
}
if (connect_ip && connect_port) {
rc = rtp_socket_connect(lchan->abis_ip.rtp_socket,
ntohl(*connect_ip), ntohs(*connect_port));
struct in_addr ia;
ia.s_addr = *connect_ip;
rc = osmo_rtp_socket_connect(lchan->abis_ip.rtp_socket,
inet_ntoa(ia), ntohs(*connect_port));
if (rc < 0) {
LOGP(DRSL, LOGL_ERROR,
"%s Failed to connect RTP/RTCP sockets\n",
gsm_lchan_name(lchan));
rtp_socket_free(lchan->abis_ip.rtp_socket);
osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
lchan->abis_ip.rtp_socket = NULL;
msgb_queue_flush(&lchan->dl_tch_queue);
return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
inc_ip_port, dch->c.msg_type);
}
/* save IP address and port number */
lchan->abis_ip.connect_ip = *connect_ip;
lchan->abis_ip.connect_port = *connect_port;
lchan->abis_ip.connect_ip = ntohl(*connect_ip);
lchan->abis_ip.connect_port = ntohs(*connect_port);
}
/* Everything has succeeded, we can store new values in lchan */
if (payload_type)
if (payload_type) {
lchan->abis_ip.rtp_payload = *payload_type;
if (payload_type2)
if (lchan->abis_ip.rtp_socket)
osmo_rtp_socket_set_pt(lchan->abis_ip.rtp_socket,
*payload_type);
}
if (payload_type2) {
lchan->abis_ip.rtp_payload2 = *payload_type2;
if (lchan->abis_ip.rtp_socket)
osmo_rtp_socket_set_pt(lchan->abis_ip.rtp_socket,
*payload_type2);
}
if (speech_mode)
lchan->abis_ip.speech_mode = *speech_mode;
@ -1109,8 +1268,9 @@ static int rsl_rx_ipac_dlcx(struct msgb *msg)
if (TLVP_PRESENT(&tp, RSL_IE_IPAC_CONN_ID))
inc_conn_id = 1;
rtp_socket_free(lchan->abis_ip.rtp_socket);
osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
lchan->abis_ip.rtp_socket = NULL;
msgb_queue_flush(&lchan->dl_tch_queue);
return rsl_tx_ipac_dlcx_ack(lchan, inc_conn_id);
}
@ -1336,6 +1496,8 @@ static int rsl_rx_dchan(struct gsm_bts_trx *trx, struct msgb *msg)
ret = rsl_rx_encr_cmd(msg);
break;
case RSL_MT_MODE_MODIFY_REQ:
ret = rsl_rx_mode_modif(msg);
break;
case RSL_MT_PHY_CONTEXT_REQ:
case RSL_MT_PREPROC_CONFIG:
case RSL_MT_RTD_REP:
@ -1404,8 +1566,8 @@ static int rsl_rx_ipaccess(struct gsm_bts_trx *trx, struct msgb *msg)
//return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR);
}
LOGP(DRSL, LOGL_INFO, "%s Rx RSL IPA %s\n", gsm_lchan_name(msg->lchan),
rsl_msg_name(dch->c.msg_type));
LOGP(DRSL, LOGL_INFO, "%s Rx RSL %s\n", gsm_lchan_name(msg->lchan),
rsl_ipac_msg_name(dch->c.msg_type));
switch (dch->c.msg_type) {
case RSL_MT_IPAC_CRCX:

View File

@ -1,6 +1,6 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS)
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS)
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) -lortp
bin_PROGRAMS = sysmobts sysmobts-remote l1fwd-proxy

View File

@ -165,11 +165,10 @@ struct msgb *sysp_msgb_alloc(void)
return msg;
}
/* prepare a PH-DATA.req primitive in response to a PH-RTS.ind */
static struct msgb *alloc_ph_data_req(GsmL1_PhReadyToSendInd_t *rts_ind)
static GsmL1_PhDataReq_t *
data_req_from_rts_ind(GsmL1_Prim_t *l1p,
const GsmL1_PhReadyToSendInd_t *rts_ind)
{
struct msgb *msg = l1p_msgb_alloc();
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq;
l1p->id = GsmL1_PrimId_PhDataReq;
@ -182,7 +181,25 @@ static struct msgb *alloc_ph_data_req(GsmL1_PhReadyToSendInd_t *rts_ind)
data_req->subCh = rts_ind->subCh;
data_req->u8BlockNbr = rts_ind->u8BlockNbr;
return msg;
return data_req;
}
static GsmL1_PhEmptyFrameReq_t *
empty_req_from_rts_ind(GsmL1_Prim_t *l1p,
const GsmL1_PhReadyToSendInd_t *rts_ind)
{
GsmL1_PhEmptyFrameReq_t *empty_req = &l1p->u.phEmptyFrameReq;
l1p->id = GsmL1_PrimId_PhEmptyFrameReq;
empty_req->hLayer1 = rts_ind->hLayer1;
empty_req->u8Tn = rts_ind->u8Tn;
empty_req->u32Fn = rts_ind->u32Fn;
empty_req->sapi = rts_ind->sapi;
empty_req->subCh = rts_ind->subCh;
empty_req->u8BlockNbr = rts_ind->u8BlockNbr;
return empty_req;
}
/* obtain a ptr to the lapdm_channel for a given hLayer2 */
@ -208,14 +225,12 @@ static const uint8_t fill_frame[GSM_MACBLOCK_LEN] = {
static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
GsmL1_PhReadyToSendInd_t *rts_ind)
{
struct msgb *resp_msg = alloc_ph_data_req(rts_ind);
struct gsm_bts_trx *trx = fl1->priv;
struct gsm_bts *bts = trx->bts;
struct gsm_bts_role_bts *btsb = bts->role;
GsmL1_Prim_t *l1p = msgb_l1prim(resp_msg);
GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq;
GsmL1_PhEmptyFrameReq_t *empty_req = &l1p->u.phEmptyFrameReq;
GsmL1_MsgUnitParam_t *msu_param = &data_req->msgUnitParam;
struct msgb *resp_msg;
GsmL1_PhDataReq_t *data_req;
GsmL1_MsgUnitParam_t *msu_param;
struct lapdm_channel *lc;
struct lapdm_entity *le;
struct gsm_lchan *lchan;
@ -231,13 +246,39 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
g_time.t1, g_time.t2, g_time.t3,
get_value_string(femtobts_l1sapi_names, rts_ind->sapi));
/* copy over parameters from PH-RTS.ind into PH-DATA.req */
data_req->hLayer1 = rts_ind->hLayer1;
data_req->u8Tn = rts_ind->u8Tn;
data_req->u32Fn = rts_ind->u32Fn;
data_req->sapi = rts_ind->sapi;
data_req->subCh = rts_ind->subCh;
data_req->u8BlockNbr = rts_ind->u8BlockNbr;
/* In case of TCH downlink trasnmission, we already have a l1
* primitive msgb pre-allocated and pre-formatted in the
* dl_tch_queue. All we need to do is to pull it off the queue
* and transmit it */
switch (rts_ind->sapi) {
case GsmL1_Sapi_TchF:
case GsmL1_Sapi_TchH:
/* resolve the L2 entity using rts_ind->hLayer2 */
lchan = l1if_hLayer2_to_lchan(trx, rts_ind->hLayer2);
if (!lchan)
break;
/* get a msgb from the dl_tx_queue */
resp_msg = msgb_dequeue(&lchan->dl_tch_queue);
if (!resp_msg)
break;
/* fill header */
data_req_from_rts_ind(msgb_l1prim(resp_msg), rts_ind);
/* actually transmit it */
goto tx;
break;
default:
break;
}
/* in all other cases, we need to allocate a new PH-DATA.ind
* primitive msgb and start to fill it */
resp_msg = l1p_msgb_alloc();
data_req = data_req_from_rts_ind(msgb_l1prim(resp_msg), rts_ind);
msu_param = &data_req->msgUnitParam;
/* set default size */
msu_param->u8Size = GSM_MACBLOCK_LEN;
switch (rts_ind->sapi) {
@ -306,10 +347,13 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
rc = paging_gen_msg(btsb->paging_state, msu_param->u8Buffer, &g_time);
break;
case GsmL1_Sapi_TchF:
#warning Send actual speech data on the TCH
case GsmL1_Sapi_TchH:
/* only hit in case we have a RTP underflow, as real TCH
* frames are handled way above */
goto empty_frame;
break;
case GsmL1_Sapi_FacchF:
case GsmL1_Sapi_FacchH:
/* resolve the L2 entity using rts_ind->hLayer2 */
lc = get_lapdm_chan_by_hl2(trx, rts_ind->hLayer2);
le = &lc->lapdm_dcch;
@ -317,7 +361,6 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
if (rc < 0)
goto empty_frame;
else {
data_req->sapi = GsmL1_Sapi_FacchF;
memcpy(msu_param->u8Buffer, pp.oph.msg->data, GSM_MACBLOCK_LEN);
msgb_free(pp.oph.msg);
}
@ -335,14 +378,7 @@ tx:
empty_frame:
/* in case we decide to send an empty frame... */
memset(l1p, 0, sizeof(*l1p));
l1p->id = GsmL1_PrimId_PhEmptyFrameReq;
empty_req->hLayer1 = rts_ind->hLayer1;
empty_req->u8Tn = rts_ind->u8Tn;
empty_req->u32Fn = rts_ind->u32Fn;
empty_req->sapi = rts_ind->sapi;
empty_req->subCh = rts_ind->subCh;
empty_req->u8BlockNbr = rts_ind->u8BlockNbr;
empty_req_from_rts_ind(msgb_l1prim(resp_msg), rts_ind);
goto tx;
}

View File

@ -53,6 +53,8 @@ struct msgb *sysp_msgb_alloc(void);
uint32_t l1if_lchan_to_hLayer2(struct gsm_lchan *lchan);
struct gsm_lchan *l1if_hLayer2_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer2);
/* tch.c */
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
#endif /* _FEMTO_L1_H */

View File

@ -104,6 +104,7 @@ static void print_help()
" -V --version Print version information and exit\n"
" -e --log-level Set a global log-level\n"
" -B --bsc-host Specify host-name of the BSC\n"
" -p --dsp-trace Set DSP trace flags\n"
);
}

View File

@ -452,6 +452,73 @@ static void alive_timer_cb(void *data)
}
static void lchan2lch_par(GsmL1_LogChParam_t *lch_par, struct gsm_lchan *lchan)
{
int j;
LOGPC(DL1C, LOGL_INFO, "%s: %s tch_mode=0x%02x\n",
gsm_lchan_name(lchan), __FUNCTION__, lchan->tch_mode);
switch (lchan->tch_mode) {
case GSM48_CMODE_SIGN:
case GSM48_CMODE_SPEECH_V1:
case GSM48_CMODE_SPEECH_EFR:
lch_par->tch.amrCmiPhase = GsmL1_AmrCmiPhase_NA;
lch_par->tch.amrInitCodecMode = GsmL1_AmrCodecMode_Unset;
for (j = 0; j < ARRAY_SIZE(lch_par->tch.amrActiveCodecSet); j++)
lch_par->tch.amrActiveCodecSet[j] = GsmL1_AmrCodec_Unset;
break;
case GSM48_CMODE_SPEECH_AMR:
lch_par->tch.amrCmiPhase = GsmL1_AmrCmiPhase_Odd; /* FIXME? */
if (lchan->mr_conf.icmi)
lch_par->tch.amrInitCodecMode = lchan->mr_conf.smod;
/* else: FIXME (implicit rule by TS 05.09 ?!?) */
/* initialize to clean state */
for (j = 0; j < ARRAY_SIZE(lch_par->tch.amrActiveCodecSet); j++)
lch_par->tch.amrActiveCodecSet[j] = GsmL1_AmrCodec_Unset;
j = 0;
if (lchan->mr_conf.m4_75)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_4_75;
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
break;
if (lchan->mr_conf.m5_15)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_5_15;
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
break;
if (lchan->mr_conf.m5_90)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_5_9;
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
break;
if (lchan->mr_conf.m6_70)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_6_7;
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
break;
if (lchan->mr_conf.m7_40)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_7_4;
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
break;
if (lchan->mr_conf.m7_95)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_7_95;
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
break;
if (lchan->mr_conf.m10_2)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_10_2;
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
break;
if (lchan->mr_conf.m12_2)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_12_2;
break;
}
}
int lchan_activate(struct gsm_lchan *lchan)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx);
@ -462,7 +529,6 @@ int lchan_activate(struct gsm_lchan *lchan)
struct msgb *msg = l1p_msgb_alloc();
GsmL1_MphActivateReq_t *act_req;
GsmL1_LogChParam_t *lch_par;
int j;
act_req = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphActivateReq, fl1h);
lch_par = &act_req->logChPrm;
@ -493,11 +559,7 @@ int lchan_activate(struct gsm_lchan *lchan)
break;
case GsmL1_Sapi_TchH:
case GsmL1_Sapi_TchF:
#warning Set AMR parameters for TCH
lch_par->tch.amrCmiPhase = GsmL1_AmrCmiPhase_NA;
lch_par->tch.amrInitCodecMode = GsmL1_AmrCodecMode_Unset;
for (j = 0; j < ARRAY_SIZE(lch_par->tch.amrActiveCodecSet); j++)
lch_par->tch.amrActiveCodecSet[i] = GsmL1_AmrCodec_Unset;
lchan2lch_par(lch_par, lchan);
break;
default:
break;
@ -517,6 +579,121 @@ int lchan_activate(struct gsm_lchan *lchan)
return 0;
}
const struct value_string femtobts_l1cfgt_names[] = {
{ GsmL1_ConfigParamId_SetNbTsc, "Set NB TSC" },
{ GsmL1_ConfigParamId_SetTxPowerLevel, "Set Tx power level" },
{ GsmL1_ConfigParamId_SetLogChParams, "Set logical channel params" },
{ GsmL1_ConfigParamId_SetCipheringParams,"Configure ciphering params" },
{ 0, NULL }
};
static void dump_lch_par(int logl, GsmL1_LogChParam_t *lch_par, GsmL1_Sapi_t sapi)
{
int i;
switch (sapi) {
case GsmL1_Sapi_Rach:
LOGPC(DL1C, logl, "BSIC=0x%08x", lch_par->rach.u8Bsic);
break;
case GsmL1_Sapi_Agch:
LOGPC(DL1C, logl, "BS_AG_BLKS_RES=%u ",
lch_par->agch.u8NbrOfAgch);
break;
case GsmL1_Sapi_Sacch:
LOGPC(DL1C, logl, "MS Power Level 0x%02x",
lch_par->sacch.u8MsPowerLevel);
break;
case GsmL1_Sapi_TchF:
case GsmL1_Sapi_TchH:
LOGPC(DL1C, logl, "amrCmiPhase=0x%02x amrInitCodec=0x%02x (",
lch_par->tch.amrCmiPhase,
lch_par->tch.amrInitCodecMode);
for (i = 0; i < ARRAY_SIZE(lch_par->tch.amrActiveCodecSet); i++) {
LOGPC(DL1C, logl, "%x ",
lch_par->tch.amrActiveCodecSet[i]);
}
break;
/* FIXME: PRACH / PTCCH */
}
LOGPC(DL1C, logl, ")\n");
}
static int chmod_modif_compl_cb(struct msgb *l1_msg, void *data)
{
struct gsm_lchan *lchan = data;
GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg);
GsmL1_MphConfigCnf_t *cc = &l1p->u.mphConfigCnf;
LOGP(DL1C, LOGL_INFO, "%s MPH-CONFIG.conf (%s) ",
gsm_lchan_name(lchan),
get_value_string(femtobts_l1cfgt_names, cc->cfgParamId));
switch (cc->cfgParamId) {
case GsmL1_ConfigParamId_SetLogChParams:
dump_lch_par(LOGL_INFO,
&cc->cfgParams.setLogChParams.logChParams,
cc->cfgParams.setLogChParams.sapi);
break;
}
msgb_free(l1_msg);
return 0;
}
static int tx_confreq_logchpar(struct gsm_lchan *lchan, uint8_t direction)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx);
struct msgb *msg = l1p_msgb_alloc();
GsmL1_MphConfigReq_t *conf_req;
GsmL1_LogChParam_t *lch_par;
/* channel mode, encryption and/or multirate have changed */
/* update multi-rate config */
conf_req = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphConfigReq, fl1h);
conf_req->cfgParamId = GsmL1_ConfigParamId_SetLogChParams;
conf_req->cfgParams.setLogChParams.sapi = GsmL1_Sapi_TchF;
conf_req->cfgParams.setLogChParams.u8Tn = lchan->ts->nr;
conf_req->cfgParams.setLogChParams.subCh = lchan_to_GsmL1_SubCh_t(lchan);
conf_req->cfgParams.setLogChParams.dir = direction;
lch_par = &conf_req->cfgParams.setLogChParams.logChParams;
lchan2lch_par(lch_par, lchan);
/* FIXME: update encryption */
LOGP(DL1C, LOGL_INFO, "%s MPH-CONFIG.req (%s) ",
gsm_lchan_name(lchan),
get_value_string(femtobts_l1sapi_names,
conf_req->cfgParams.setLogChParams.sapi));
LOGP(DL1C, LOGL_INFO, "cfgParams Tn=%u, subCh=%u, dir=0x%x ",
conf_req->cfgParams.setLogChParams.u8Tn,
conf_req->cfgParams.setLogChParams.subCh,
conf_req->cfgParams.setLogChParams.dir);
dump_lch_par(LOGL_INFO,
&conf_req->cfgParams.setLogChParams.logChParams,
conf_req->cfgParams.setLogChParams.sapi);
return l1if_req_compl(fl1h, msg, 0, chmod_modif_compl_cb, lchan);
}
int bts_model_rsl_mode_modify(struct gsm_lchan *lchan)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx);
/* channel mode, encryption and/or multirate have changed */
/* update multi-rate config */
tx_confreq_logchpar(lchan, GsmL1_Dir_RxUplink);
tx_confreq_logchpar(lchan, GsmL1_Dir_TxDownlink);
/* FIXME: update encryption */
return 0;
}
static int lchan_deact_compl_cb(struct msgb *l1_msg, void *data)
{
struct gsm_lchan *lchan = data;

View File

@ -31,7 +31,9 @@
#include <osmocom/core/utils.h>
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/bits.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/trau/osmo_ortp.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/bts.h>
@ -46,14 +48,312 @@
#include "femtobts.h"
#include "l1_if.h"
int l1if_tch_rx(struct gsm_lchan *lchan,
struct msgb *l1p_msg)
/* input octet-aligned, output not octet-aligned */
void osmo_nibble_shift_right(uint8_t *out, const uint8_t *in,
unsigned int num_nibbles)
{
unsigned int i;
unsigned int num_whole_bytes = num_nibbles / 2;
/* first byte: upper nibble empty, lower nibble from src */
out[0] = (in[0] >> 4);
/* bytes 1.. */
for (i = 1; i < num_whole_bytes; i++)
out[i] = ((in[i-1] & 0xF) << 4) | (in[i] >> 4);
/* shift the last nibble, in case there's an odd count */
i = num_whole_bytes;
if (num_nibbles & 1)
out[i] = (in[i-1] & 0xF) << 4;
else
out[i] = ((in[i-1] & 0xF) << 4) | (in[i] >> 4);
}
/* input unaligned, output octet-aligned */
void osmo_nibble_shift_left_unal(uint8_t *out, const uint8_t *in,
unsigned int num_nibbles)
{
unsigned int i;
unsigned int num_whole_bytes = num_nibbles / 2;
for (i = 0; i < num_whole_bytes; i++)
out[i] = ((in[i] & 0xF) << 4) | (in[i+1] >> 4);
/* shift the last nibble, in case there's an odd count */
i = num_whole_bytes;
if (num_nibbles & 1)
out[i] = (in[i] & 0xF) << 4;
}
#define GSM_FR_BITS 260
#define GSM_EFR_BITS 244
#define GSM_FR_BYTES 33 /* TS 101318 Chapter 5.1: 260 bits + 4bit sig */
#define GSM_HR_BYTES 14 /* TS 101318 Chapter 5.2: 112 bits, no sig */
#define GSM_EFR_BYTES 31 /* TS 101318 Chapter 5.3: 244 bits + 4bit sig */
static struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len)
{
struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
uint8_t *cur;
/* step1: reverse the bit-order of each payload byte */
osmo_revbytebits_buf(l1_payload, payload_len);
cur = msgb_put(msg, GSM_FR_BYTES);
/* step2: we need to shift the entire L1 payload by 4 bits right */
osmo_nibble_shift_right(cur, l1_payload, GSM_FR_BITS/4);
cur[0] |= 0xD0;
return msg;
}
/*! \brief convert GSM-FR from RTP payload to L1 format
* \param[out] l1_payload payload part of L1 buffer
* \param[in] rtp_payload pointer to RTP payload data
* \param[in] payload_len length of \a rtp_payload
* \returns number of \a l1_payload bytes filled
*/
static int rtppayload_to_l1_fr(uint8_t *l1_payload, uint8_t *rtp_payload,
unsigned int payload_len)
{
/* step2: we need to shift the RTP payload left by one nibble*/
osmo_nibble_shift_left_unal(l1_payload, rtp_payload, GSM_FR_BITS/4);
/* step1: reverse the bit-order of each payload byte */
osmo_revbytebits_buf(l1_payload, payload_len);
return GSM_FR_BYTES;
}
#ifdef GsmL1_TchPlType_Efr
static struct msgb *l1_to_rtppayload_efr(uint8_t *l1_payload, uint8_t payload_len)
{
struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
uint8_t *cur;
/* step1: reverse the bit-order of each payload byte */
osmo_revbytebits_buf(l1_payload, payload_len);
cur = msgb_put(msg, GSM_EFR_BYTES);
/* step 2: we need to shift the entire L1 payload by 4 bits right */
osmo_nibble_shift_right(cur, l1_payload, GSM_EFR_BITS/4);
cur[0] |= 0xC0;
return msg;
}
#else
#warning No EFR support in L1
#endif
static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len)
{
struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
uint8_t *cur;
#warning Test GSM HR
cur = msgb_put(msg, GSM_HR_BYTES);
memcpy(cur, l1_payload, GSM_HR_BYTES);
return msg;
}
/*! \brief convert GSM-FR from RTP payload to L1 format
* \param[out] l1_payload payload part of L1 buffer
* \param[in] rtp_payload pointer to RTP payload data
* \param[in] payload_len length of \a rtp_payload
* \returns number of \a l1_payload bytes filled
*/
static int rtppayload_to_l1_hr(uint8_t *l1_payload, uint8_t *rtp_payload,
unsigned int payload_len)
{
#warning Implement GSM HR
return 0;
}
#define AMR_TOC_QBIT 0x04
static struct msgb *l1_to_rtppayload_amr(uint8_t *l1_payload, uint8_t payload_len)
{
struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
uint8_t *cur;
uint8_t ft = l1_payload[2] & 0xF;
uint8_t cmr = l1_payload[1];
uint8_t amr_if2_len = payload_len - 2;
/* RFC 3267 4.4.1 Payload Header */
msgb_put_u8(msg, (cmr << 4));
/* RFC 3267 AMR TOC */
msgb_put_u8(msg, AMR_TOC_QBIT | (ft << 3));
cur = msgb_put(msg, amr_if2_len-1);
/* step1: reverse the bit-order within every byte */
osmo_revbytebits_buf(l1_payload+2, amr_if2_len);
/* step2: shift everything left by one nibble */
osmo_nibble_shift_left_unal(cur, l1_payload+2, amr_if2_len*2 -1);
return msg;
}
enum amr_frame_type {
AMR_FT_SID_AMR = 8,
};
/*! \brief convert AMR from RTP payload to L1 format
* \param[out] l1_payload payload part of L1 buffer
* \param[in] rtp_payload pointer to RTP payload data
* \param[in] payload_len length of \a rtp_payload
* \returns number of \a l1_payload bytes filled
*/
static int rtppayload_to_l1_amr(uint8_t *l1_payload, uint8_t *rtp_payload, uint8_t payload_len)
{
uint8_t ft = (rtp_payload[1] >> 3) & 0xf;
uint8_t cmr = rtp_payload[0] >> 4;
uint8_t amr_if2_core_len = payload_len - 2;
/* CMC is in upper 4 bits of RTP payload header, and we simply
* copy the CMR into the CMC */
l1_payload[1] = cmr;
#if 0
/* check for bad quality indication */
if (rtp_payload[1] & AMR_TOC_QBIT) {
/* obtain frame type from AMR FT */
l1_payload[2] = ft;
} else {
/* bad quality, we should indicate that... */
if (ft == AMR_FT_SID_AMR) {
/* FIXME: Should we do GsmL1_TchPlType_Amr_SidBad? */
l1_payload[2] = ft;
} else {
l1_payload[2] = ft;
}
}
#endif
/* step1: shift everything right one nibble; make space for FT */
osmo_nibble_shift_right(l1_payload+2, rtp_payload+2, amr_if2_core_len*2 -1);
/* step2: reverse the bit-order within every byte of the IF2
* core frame contained in the RTP payload */
osmo_revbytebits_buf(l1_payload+2, amr_if2_core_len);
/* lower 4 bit of first FR2 byte contains FT */
l1_payload[2] |= ft;
return payload_len+1;
}
#define RTP_MSGB_ALLOC_SIZE 512
/*! \brief call-back function for incoming RTP
* \param rs RTP Socket
* \param[in] rtp_pl buffer containing RTP payload
* \param[in] rtp_pl_len length of \a rtp_pl
*
* This function prepares a msgb with a L1 PH-DATA.req primitive and
* queues it into lchan->dl_tch_queue.
*
* Note that the actual L1 primitive header is not fully initialized
* yet, as things like the frame number, etc. are unknown at the time we
* pre-fill the primtive.
*/
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, uint8_t *rtp_pl,
unsigned int rtp_pl_len)
{
struct gsm_lchan *lchan = rs->priv;
struct msgb *msg = l1p_msgb_alloc();
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq;
GsmL1_MsgUnitParam_t *msu_param = &data_req->msgUnitParam;
uint8_t *payload_type = &msu_param->u8Buffer[0];
uint8_t *l1_payload = &msu_param->u8Buffer[1];
int rc;
DEBUGP(DL1C, "%s RTP IN: %s\n", gsm_lchan_name(lchan),
osmo_hexdump(rtp_pl, rtp_pl_len));
switch (lchan->tch_mode) {
case GSM48_CMODE_SPEECH_V1:
if (lchan->type == GSM_LCHAN_TCH_F) {
*payload_type = GsmL1_TchPlType_Fr;
rc = rtppayload_to_l1_fr(l1_payload,
rtp_pl, rtp_pl_len);
} else{
*payload_type = GsmL1_TchPlType_Hr;
rc = rtppayload_to_l1_hr(l1_payload,
rtp_pl, rtp_pl_len);
}
break;
#ifdef GsmL1_TchPlType_Efr
case GSM48_CMODE_SPEECH_EFR:
*payload_type = GsmL1_TchPlType_Efr;
rc = FIXME;
break;
#endif
case GSM48_CMODE_SPEECH_AMR:
*payload_type = GsmL1_TchPlType_Amr;
rc = rtppayload_to_l1_amr(l1_payload, rtp_pl, rtp_pl_len);
break;
default:
/* we don't support CSD modes */
rc = -1;
break;
}
if (rc < 0) {
LOGP(DL1C, LOGL_ERROR, "%s unable to parse RTP payload\n",
gsm_lchan_name(lchan));
msgb_free(msg);
return;
}
msu_param->u8Size = rc + 1;
DEBUGP(DL1C, "%s RTP->L1: %s\n", gsm_lchan_name(lchan),
osmo_hexdump(msu_param->u8Buffer, msu_param->u8Size));
/* make sure the number of entries in the dl_tch_queue is never
* more than 3 */
{
struct msgb *tmp;
int count = 0;
llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
count++;
DEBUGP(DL1C, "%s DL TCH queue length = %u\n",
gsm_lchan_name(lchan), count);
while (count >= 2) {
tmp = msgb_dequeue(&lchan->dl_tch_queue);
msgb_free(tmp);
count--;
}
}
/* enqueue msgb to be transmitted to L1 */
msgb_enqueue(&lchan->dl_tch_queue, msg);
}
/*! \brief receive a traffic L1 primitive for a given lchan */
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
{
GsmL1_Prim_t *l1p = msgb_l1prim(l1p_msg);
GsmL1_PhDataInd_t *data_ind = &l1p->u.phDataInd;
uint8_t payload_type = data_ind->msgUnitParam.u8Buffer[0];
uint8_t *payload = data_ind->msgUnitParam.u8Buffer + 1;
uint8_t payload_len;
struct msgb *rmsg = NULL;
if (data_ind->msgUnitParam.u8Size < 1) {
LOGP(DL1C, LOGL_ERROR, "%s Rx Payload size 0\n",
@ -64,6 +364,9 @@ int l1if_tch_rx(struct gsm_lchan *lchan,
switch (payload_type) {
case GsmL1_TchPlType_Fr:
#ifdef GsmL1_TchPlType_Efr
case GsmL1_TchPlType_Efr:
#endif
if (lchan->type != GSM_LCHAN_TCH_F)
goto err_payload_match;
break;
@ -75,6 +378,7 @@ int l1if_tch_rx(struct gsm_lchan *lchan,
if (lchan->type != GSM_LCHAN_TCH_H &&
lchan->type != GSM_LCHAN_TCH_F)
goto err_payload_match;
break;
default:
LOGP(DL1C, LOGL_NOTICE, "%s Rx Payload Type %s is unsupported\n",
gsm_lchan_name(lchan),
@ -85,6 +389,33 @@ int l1if_tch_rx(struct gsm_lchan *lchan,
LOGP(DL1C, LOGL_DEBUG, "%s Rx codec frame (%u): %s\n", gsm_lchan_name(lchan),
payload_len, osmo_hexdump(payload, payload_len));
switch (payload_type) {
case GsmL1_TchPlType_Fr:
rmsg = l1_to_rtppayload_fr(payload, payload_len);
break;
case GsmL1_TchPlType_Hr:
rmsg = l1_to_rtppayload_hr(payload, payload_len);
break;
#ifdef GsmL1_TchPlType_Efr
case GsmL1_TchPlType_Efr
rmsg = l1_to_rtppayload_efr(payload, payload_len);
break;
#endif
case GsmL1_TchPlType_Amr:
rmsg = l1_to_rtppayload_amr(payload, payload_len);
break;
}
if (rmsg) {
LOGP(DL1C, LOGL_DEBUG, "%s Rx -> RTP: %s\n",
gsm_lchan_name(lchan), osmo_hexdump(rmsg->data, rmsg->len));
/* hand rmsg to RTP code for transmission */
if (lchan->abis_ip.rtp_socket)
osmo_rtp_send_frame(lchan->abis_ip.rtp_socket,
rmsg->data, rmsg->len, 160);
msgb_free(rmsg);
}
return 0;
err_payload_match: