aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/bts.c18
-rw-r--r--src/common/rsl.c206
-rw-r--r--src/osmo-bts-sysmo/Makefile.am2
-rw-r--r--src/osmo-bts-sysmo/l1_if.c90
-rw-r--r--src/osmo-bts-sysmo/l1_if.h2
-rw-r--r--src/osmo-bts-sysmo/main.c1
-rw-r--r--src/osmo-bts-sysmo/oml.c189
-rw-r--r--src/osmo-bts-sysmo/tch.c335
8 files changed, 785 insertions, 58 deletions
diff --git a/src/common/bts.c b/src/common/bts.c
index 35999294..6dd0362c 100644
--- a/src/common/bts.c
+++ b/src/common/bts.c
@@ -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);
}
diff --git a/src/common/rsl.c b/src/common/rsl.c
index 4b8798b9..59185124 100644
--- a/src/common/rsl.c
+++ b/src/common/rsl.c
@@ -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:
diff --git a/src/osmo-bts-sysmo/Makefile.am b/src/osmo-bts-sysmo/Makefile.am
index 6172d380..81433b77 100644
--- a/src/osmo-bts-sysmo/Makefile.am
+++ b/src/osmo-bts-sysmo/Makefile.am
@@ -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
diff --git a/src/osmo-bts-sysmo/l1_if.c b/src/osmo-bts-sysmo/l1_if.c
index c97436e6..01f146c6 100644
--- a/src/osmo-bts-sysmo/l1_if.c
+++ b/src/osmo-bts-sysmo/l1_if.c
@@ -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;
}
diff --git a/src/osmo-bts-sysmo/l1_if.h b/src/osmo-bts-sysmo/l1_if.h
index c47fd1fa..a4b573b6 100644
--- a/src/osmo-bts-sysmo/l1_if.h
+++ b/src/osmo-bts-sysmo/l1_if.h
@@ -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 */
diff --git a/src/osmo-bts-sysmo/main.c b/src/osmo-bts-sysmo/main.c
index 5a170430..e09caae4 100644
--- a/src/osmo-bts-sysmo/main.c
+++ b/src/osmo-bts-sysmo/main.c
@@ -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"
);
}
diff --git a/src/osmo-bts-sysmo/oml.c b/src/osmo-bts-sysmo/oml.c
index a7469e25..8d011b64 100644
--- a/src/osmo-bts-sysmo/oml.c
+++ b/src/osmo-bts-sysmo/oml.c
@@ -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;
diff --git a/src/osmo-bts-sysmo/tch.c b/src/osmo-bts-sysmo/tch.c
index 87de8437..6710edde 100644
--- a/src/osmo-bts-sysmo/tch.c
+++ b/src/osmo-bts-sysmo/tch.c
@@ -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: