diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/common/bts.c | 18 | ||||
-rw-r--r-- | src/common/rsl.c | 206 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/Makefile.am | 2 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/l1_if.c | 90 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/l1_if.h | 2 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/main.c | 1 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/oml.c | 189 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/tch.c | 335 |
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: |