Add TCH messages to PH-/MPH-/TCH-SAP interface
This part moves TCH handling from osmo-bts-sysmo to common part. The RTP handling is done at the common part, so they can be used by other BTS models.
This commit is contained in:
parent
7cc199ea95
commit
12472df8f0
|
@ -28,9 +28,6 @@ int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
|
||||||
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx);
|
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx);
|
||||||
int bts_model_trx_close(struct gsm_bts_trx *trx);
|
int bts_model_trx_close(struct gsm_bts_trx *trx);
|
||||||
|
|
||||||
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
|
||||||
unsigned int rtp_pl_len);
|
|
||||||
|
|
||||||
int bts_model_vty_init(struct gsm_bts *bts);
|
int bts_model_vty_init(struct gsm_bts *bts);
|
||||||
|
|
||||||
void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts);
|
void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts);
|
||||||
|
|
|
@ -286,6 +286,72 @@ static int l1sap_handover_rach(struct gsm_bts_trx *trx,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TCH-RTS-IND prim recevied from bts model */
|
||||||
|
static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
|
||||||
|
struct osmo_phsap_prim *l1sap, struct ph_tch_param *rts_ind)
|
||||||
|
{
|
||||||
|
struct msgb *resp_msg;
|
||||||
|
struct osmo_phsap_prim *resp_l1sap, empty_l1sap;
|
||||||
|
struct gsm_time g_time;
|
||||||
|
struct gsm_lchan *lchan;
|
||||||
|
uint8_t chan_nr;
|
||||||
|
uint8_t tn, ss;
|
||||||
|
uint32_t fn;
|
||||||
|
|
||||||
|
chan_nr = rts_ind->chan_nr;
|
||||||
|
fn = rts_ind->fn;
|
||||||
|
tn = L1SAP_CHAN2TS(chan_nr);
|
||||||
|
|
||||||
|
gsm_fn2gsmtime(&g_time, fn);
|
||||||
|
|
||||||
|
DEBUGP(DL1P, "Rx TCH-RTS.ind %02u/%02u/%02u chan_nr=%d\n",
|
||||||
|
g_time.t1, g_time.t2, g_time.t3, chan_nr);
|
||||||
|
|
||||||
|
/* get timeslot and subslot */
|
||||||
|
tn = L1SAP_CHAN2TS(chan_nr);
|
||||||
|
if (L1SAP_IS_CHAN_TCHH(chan_nr))
|
||||||
|
ss = L1SAP_CHAN2SS_TCHH(chan_nr); /* TCH/H */
|
||||||
|
else
|
||||||
|
ss = 0; /* TCH/F */
|
||||||
|
lchan = &trx->ts[tn].lchan[ss];
|
||||||
|
|
||||||
|
if (!lchan->loopback && lchan->abis_ip.rtp_socket) {
|
||||||
|
osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket);
|
||||||
|
/* FIXME: we _assume_ that we never miss TDMA
|
||||||
|
* frames and that we always get to this point
|
||||||
|
* for every to-be-transmitted voice frame. A
|
||||||
|
* better solution would be to compute
|
||||||
|
* rx_user_ts based on how many TDMA frames have
|
||||||
|
* elapsed since the last call */
|
||||||
|
lchan->abis_ip.rtp_socket->rx_user_ts += GSM_RTP_DURATION;
|
||||||
|
}
|
||||||
|
/* get a msgb from the dl_tx_queue */
|
||||||
|
resp_msg = msgb_dequeue(&lchan->dl_tch_queue);
|
||||||
|
if (!resp_msg) {
|
||||||
|
LOGP(DL1P, LOGL_DEBUG, "%s DL TCH Tx queue underrun\n",
|
||||||
|
gsm_lchan_name(lchan));
|
||||||
|
resp_l1sap = &empty_l1sap;
|
||||||
|
} else {
|
||||||
|
resp_msg->l2h = resp_msg->data;
|
||||||
|
msgb_push(resp_msg, sizeof(*resp_l1sap));
|
||||||
|
resp_msg->l1h = resp_msg->data;
|
||||||
|
resp_l1sap = msgb_l1sap_prim(resp_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(resp_l1sap, 0, sizeof(*resp_l1sap));
|
||||||
|
osmo_prim_init(&resp_l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_REQUEST,
|
||||||
|
resp_msg);
|
||||||
|
resp_l1sap->u.tch.chan_nr = chan_nr;
|
||||||
|
resp_l1sap->u.tch.fn = fn;
|
||||||
|
|
||||||
|
DEBUGP(DL1P, "Tx TCH.req %02u/%02u/%02u chan_nr=%d\n",
|
||||||
|
g_time.t1, g_time.t2, g_time.t3, chan_nr);
|
||||||
|
|
||||||
|
l1sap_down(trx, resp_l1sap);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* DATA received from bts model */
|
/* DATA received from bts model */
|
||||||
static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
|
static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
|
||||||
struct osmo_phsap_prim *l1sap, struct ph_data_param *data_ind)
|
struct osmo_phsap_prim *l1sap, struct ph_data_param *data_ind)
|
||||||
|
@ -334,6 +400,57 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TCH received from bts model */
|
||||||
|
static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
|
||||||
|
struct ph_tch_param *tch_ind)
|
||||||
|
{
|
||||||
|
struct msgb *msg = l1sap->oph.msg;
|
||||||
|
struct gsm_time g_time;
|
||||||
|
struct gsm_lchan *lchan;
|
||||||
|
uint8_t tn, ss, chan_nr;
|
||||||
|
uint32_t fn;
|
||||||
|
|
||||||
|
chan_nr = tch_ind->chan_nr;
|
||||||
|
fn = tch_ind->fn;
|
||||||
|
tn = L1SAP_CHAN2TS(chan_nr);
|
||||||
|
if (L1SAP_IS_CHAN_TCHH(chan_nr))
|
||||||
|
ss = L1SAP_CHAN2SS_TCHH(chan_nr);
|
||||||
|
else
|
||||||
|
ss = 0;
|
||||||
|
lchan = &trx->ts[tn].lchan[ss];
|
||||||
|
|
||||||
|
gsm_fn2gsmtime(&g_time, fn);
|
||||||
|
|
||||||
|
DEBUGP(DL1P, "Rx TCH.ind %02u/%02u/%02u chan_nr=%d\n",
|
||||||
|
g_time.t1, g_time.t2, g_time.t3, chan_nr);
|
||||||
|
|
||||||
|
msgb_pull(msg, sizeof(*l1sap));
|
||||||
|
|
||||||
|
/* hand msg to RTP code for transmission */
|
||||||
|
if (lchan->abis_ip.rtp_socket)
|
||||||
|
osmo_rtp_send_frame(lchan->abis_ip.rtp_socket,
|
||||||
|
msg->data, msg->len, 160);
|
||||||
|
|
||||||
|
/* if loopback is enabled, also queue received RTP data */
|
||||||
|
if (lchan->loopback) {
|
||||||
|
struct msgb *tmp;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
/* make sure the queue doesn't get too long */
|
||||||
|
llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
|
||||||
|
count++;
|
||||||
|
while (count >= 1) {
|
||||||
|
tmp = msgb_dequeue(&lchan->dl_tch_queue);
|
||||||
|
msgb_free(tmp);
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
msgb_enqueue(&lchan->dl_tch_queue, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* RACH received from bts model */
|
/* RACH received from bts model */
|
||||||
static int l1sap_ph_rach_ind(struct gsm_bts_trx *trx,
|
static int l1sap_ph_rach_ind(struct gsm_bts_trx *trx,
|
||||||
struct osmo_phsap_prim *l1sap, struct ph_rach_ind_param *rach_ind)
|
struct osmo_phsap_prim *l1sap, struct ph_rach_ind_param *rach_ind)
|
||||||
|
@ -389,9 +506,15 @@ int l1sap_up(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
|
||||||
case OSMO_PRIM(PRIM_PH_RTS, PRIM_OP_INDICATION):
|
case OSMO_PRIM(PRIM_PH_RTS, PRIM_OP_INDICATION):
|
||||||
rc = l1sap_ph_rts_ind(trx, l1sap, &l1sap->u.data);
|
rc = l1sap_ph_rts_ind(trx, l1sap, &l1sap->u.data);
|
||||||
break;
|
break;
|
||||||
|
case OSMO_PRIM(PRIM_TCH_RTS, PRIM_OP_INDICATION):
|
||||||
|
rc = l1sap_tch_rts_ind(trx, l1sap, &l1sap->u.tch);
|
||||||
|
break;
|
||||||
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_INDICATION):
|
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_INDICATION):
|
||||||
rc = l1sap_ph_data_ind(trx, l1sap, &l1sap->u.data);
|
rc = l1sap_ph_data_ind(trx, l1sap, &l1sap->u.data);
|
||||||
break;
|
break;
|
||||||
|
case OSMO_PRIM(PRIM_TCH, PRIM_OP_INDICATION):
|
||||||
|
rc = l1sap_tch_ind(trx, l1sap, &l1sap->u.tch);
|
||||||
|
break;
|
||||||
case OSMO_PRIM(PRIM_PH_RACH, PRIM_OP_INDICATION):
|
case OSMO_PRIM(PRIM_PH_RACH, PRIM_OP_INDICATION):
|
||||||
rc = l1sap_ph_rach_ind(trx, l1sap, &l1sap->u.rach_ind);
|
rc = l1sap_ph_rach_ind(trx, l1sap, &l1sap->u.rach_ind);
|
||||||
break;
|
break;
|
||||||
|
@ -441,6 +564,34 @@ int l1sap_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
|
||||||
return l1sap_down(ts->trx, l1sap);
|
return l1sap_down(ts->trx, l1sap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! \brief call-back function for incoming RTP */
|
||||||
|
void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
||||||
|
unsigned int rtp_pl_len)
|
||||||
|
{
|
||||||
|
struct gsm_lchan *lchan = rs->priv;
|
||||||
|
struct msgb *msg, *tmp;
|
||||||
|
struct osmo_phsap_prim *l1sap;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
msg = l1sap_msgb_alloc(rtp_pl_len);
|
||||||
|
if (!msg)
|
||||||
|
return;
|
||||||
|
memcpy(msgb_put(msg, rtp_pl_len), rtp_pl, rtp_pl_len);
|
||||||
|
msgb_pull(msg, sizeof(*l1sap));
|
||||||
|
|
||||||
|
|
||||||
|
/* make sure the queue doesn't get too long */
|
||||||
|
llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
|
||||||
|
count++;
|
||||||
|
while (count >= 2) {
|
||||||
|
tmp = msgb_dequeue(&lchan->dl_tch_queue);
|
||||||
|
msgb_free(tmp);
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
msgb_enqueue(&lchan->dl_tch_queue, msg);
|
||||||
|
}
|
||||||
|
|
||||||
static int l1sap_chan_act_dact_modify(struct gsm_bts_trx *trx, uint8_t chan_nr,
|
static int l1sap_chan_act_dact_modify(struct gsm_bts_trx *trx, uint8_t chan_nr,
|
||||||
enum osmo_mph_info_type type, uint8_t sacch_only)
|
enum osmo_mph_info_type type, uint8_t sacch_only)
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,7 +42,6 @@
|
||||||
#include <osmo-bts/oml.h>
|
#include <osmo-bts/oml.h>
|
||||||
#include <osmo-bts/amr.h>
|
#include <osmo-bts/amr.h>
|
||||||
#include <osmo-bts/signal.h>
|
#include <osmo-bts/signal.h>
|
||||||
#include <osmo-bts/bts_model.h>
|
|
||||||
#include <osmo-bts/measurement.h>
|
#include <osmo-bts/measurement.h>
|
||||||
#include <osmo-bts/pcu_if.h>
|
#include <osmo-bts/pcu_if.h>
|
||||||
#include <osmo-bts/handover.h>
|
#include <osmo-bts/handover.h>
|
||||||
|
@ -1481,7 +1480,7 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
|
||||||
OSMO_RTP_P_JITBUF,
|
OSMO_RTP_P_JITBUF,
|
||||||
btsb->rtp_jitter_buf_ms);
|
btsb->rtp_jitter_buf_ms);
|
||||||
lchan->abis_ip.rtp_socket->priv = lchan;
|
lchan->abis_ip.rtp_socket->priv = lchan;
|
||||||
lchan->abis_ip.rtp_socket->rx_cb = &bts_model_rtp_rx_cb;
|
lchan->abis_ip.rtp_socket->rx_cb = &l1sap_rtp_rx_cb;
|
||||||
|
|
||||||
if (connect_ip && connect_port) {
|
if (connect_ip && connect_port) {
|
||||||
/* if CRCX specifies a remote IP, we can bind()
|
/* if CRCX specifies a remote IP, we can bind()
|
||||||
|
|
|
@ -38,8 +38,6 @@
|
||||||
#include <osmocom/gsm/gsm_utils.h>
|
#include <osmocom/gsm/gsm_utils.h>
|
||||||
#include <osmocom/gsm/lapdm.h>
|
#include <osmocom/gsm/lapdm.h>
|
||||||
|
|
||||||
#include <osmocom/trau/osmo_ortp.h>
|
|
||||||
|
|
||||||
#include <osmo-bts/logging.h>
|
#include <osmo-bts/logging.h>
|
||||||
#include <osmo-bts/bts.h>
|
#include <osmo-bts/bts.h>
|
||||||
#include <osmo-bts/oml.h>
|
#include <osmo-bts/oml.h>
|
||||||
|
@ -542,6 +540,93 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
|
||||||
|
struct osmo_phsap_prim *l1sap)
|
||||||
|
{
|
||||||
|
struct femtol1_hdl *fl1 = trx_femtol1_hdl(trx);
|
||||||
|
struct gsm_lchan *lchan;
|
||||||
|
uint32_t u32Fn;
|
||||||
|
uint8_t u8Tn, subCh, u8BlockNbr = 0, sapi, ss;
|
||||||
|
uint8_t chan_nr;
|
||||||
|
GsmL1_Prim_t *l1p;
|
||||||
|
struct msgb *nmsg;
|
||||||
|
|
||||||
|
chan_nr = l1sap->u.tch.chan_nr;
|
||||||
|
u32Fn = l1sap->u.tch.fn;
|
||||||
|
u8Tn = L1SAP_CHAN2TS(chan_nr);
|
||||||
|
u8BlockNbr = (u32Fn % 13) >> 2;
|
||||||
|
if (L1SAP_IS_CHAN_TCHH(chan_nr)) {
|
||||||
|
ss = subCh = L1SAP_CHAN2SS_TCHH(chan_nr);
|
||||||
|
sapi = GsmL1_Sapi_TchH;
|
||||||
|
} else {
|
||||||
|
subCh = 0x1f;
|
||||||
|
ss = 0;
|
||||||
|
sapi = GsmL1_Sapi_TchF;
|
||||||
|
}
|
||||||
|
|
||||||
|
lchan = &trx->ts[u8Tn].lchan[ss];
|
||||||
|
|
||||||
|
/* create new message and fill data */
|
||||||
|
if (msg) {
|
||||||
|
msgb_pull(msg, sizeof(*l1sap));
|
||||||
|
/* create new message */
|
||||||
|
nmsg = l1p_msgb_alloc();
|
||||||
|
if (!nmsg)
|
||||||
|
return -ENOMEM;
|
||||||
|
l1p = msgb_l1prim(nmsg);
|
||||||
|
l1if_tch_encode(lchan,
|
||||||
|
l1p->u.phDataReq.msgUnitParam.u8Buffer,
|
||||||
|
&l1p->u.phDataReq.msgUnitParam.u8Size,
|
||||||
|
msg->data, msg->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no message/data, we generate an empty traffic msg */
|
||||||
|
if (!nmsg)
|
||||||
|
nmsg = gen_empty_tch_msg(lchan);
|
||||||
|
|
||||||
|
/* no traffic message, we generate an empty msg */
|
||||||
|
if (!nmsg) {
|
||||||
|
nmsg = l1p_msgb_alloc();
|
||||||
|
if (!nmsg)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
l1p = msgb_l1prim(nmsg);
|
||||||
|
|
||||||
|
/* if we provide data, or if data is already in nmsg */
|
||||||
|
if (l1p->u.phDataReq.msgUnitParam.u8Size) {
|
||||||
|
/* data request */
|
||||||
|
GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq;
|
||||||
|
|
||||||
|
l1p->id = GsmL1_PrimId_PhDataReq;
|
||||||
|
|
||||||
|
data_req->hLayer1 = fl1->hLayer1;
|
||||||
|
data_req->u8Tn = u8Tn;
|
||||||
|
data_req->u32Fn = u32Fn;
|
||||||
|
data_req->sapi = sapi;
|
||||||
|
data_req->subCh = subCh;
|
||||||
|
data_req->u8BlockNbr = u8BlockNbr;
|
||||||
|
} else {
|
||||||
|
/* empty frame */
|
||||||
|
GsmL1_PhEmptyFrameReq_t *empty_req =
|
||||||
|
&l1p->u.phEmptyFrameReq;
|
||||||
|
|
||||||
|
l1p->id = GsmL1_PrimId_PhEmptyFrameReq;
|
||||||
|
|
||||||
|
empty_req->hLayer1 = fl1->hLayer1;
|
||||||
|
empty_req->u8Tn = u8Tn;
|
||||||
|
empty_req->u32Fn = u32Fn;
|
||||||
|
empty_req->sapi = sapi;
|
||||||
|
empty_req->subCh = subCh;
|
||||||
|
empty_req->u8BlockNbr = u8BlockNbr;
|
||||||
|
}
|
||||||
|
/* send message to DSP's queue */
|
||||||
|
osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], nmsg);
|
||||||
|
|
||||||
|
msgb_free(msg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int mph_info_req(struct gsm_bts_trx *trx, struct msgb *msg,
|
static int mph_info_req(struct gsm_bts_trx *trx, struct msgb *msg,
|
||||||
struct osmo_phsap_prim *l1sap)
|
struct osmo_phsap_prim *l1sap)
|
||||||
{
|
{
|
||||||
|
@ -590,6 +675,9 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
|
||||||
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST):
|
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST):
|
||||||
rc = ph_data_req(trx, msg, l1sap);
|
rc = ph_data_req(trx, msg, l1sap);
|
||||||
break;
|
break;
|
||||||
|
case OSMO_PRIM(PRIM_TCH, PRIM_OP_REQUEST):
|
||||||
|
rc = ph_tch_req(trx, msg, l1sap);
|
||||||
|
break;
|
||||||
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_REQUEST):
|
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_REQUEST):
|
||||||
rc = mph_info_req(trx, msg, l1sap);
|
rc = mph_info_req(trx, msg, l1sap);
|
||||||
break;
|
break;
|
||||||
|
@ -656,6 +744,12 @@ static uint8_t chan_nr_by_sapi(enum gsm_phys_chan_config pchan,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case GsmL1_Sapi_TchF:
|
||||||
|
cbits = 0x01;
|
||||||
|
break;
|
||||||
|
case GsmL1_Sapi_TchH:
|
||||||
|
cbits = 0x02 + subCh;
|
||||||
|
break;
|
||||||
case GsmL1_Sapi_Ptcch:
|
case GsmL1_Sapi_Ptcch:
|
||||||
if (!L1SAP_IS_PTCCH(u32Fn)) {
|
if (!L1SAP_IS_PTCCH(u32Fn)) {
|
||||||
LOGP(DL1C, LOGL_FATAL, "Not expecting PTCCH at frame "
|
LOGP(DL1C, LOGL_FATAL, "Not expecting PTCCH at frame "
|
||||||
|
@ -700,7 +794,6 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
|
||||||
uint8_t chan_nr, link_id;
|
uint8_t chan_nr, link_id;
|
||||||
uint32_t fn;
|
uint32_t fn;
|
||||||
|
|
||||||
|
|
||||||
/* check if primitive should be handled by common part */
|
/* check if primitive should be handled by common part */
|
||||||
chan_nr = chan_nr_by_sapi(trx->ts[rts_ind->u8Tn].pchan, rts_ind->sapi,
|
chan_nr = chan_nr_by_sapi(trx->ts[rts_ind->u8Tn].pchan, rts_ind->sapi,
|
||||||
rts_ind->subCh, rts_ind->u8Tn, rts_ind->u32Fn);
|
rts_ind->subCh, rts_ind->u8Tn, rts_ind->u32Fn);
|
||||||
|
@ -711,11 +804,19 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
MSGB_ABORT(l1p_msg, "No room for primitive\n");
|
MSGB_ABORT(l1p_msg, "No room for primitive\n");
|
||||||
l1sap = msgb_l1sap_prim(l1p_msg);
|
l1sap = msgb_l1sap_prim(l1p_msg);
|
||||||
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS,
|
if (rts_ind->sapi == GsmL1_Sapi_TchF
|
||||||
PRIM_OP_INDICATION, l1p_msg);
|
|| rts_ind->sapi == GsmL1_Sapi_TchH) {
|
||||||
l1sap->u.data.link_id = link_id;
|
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH_RTS,
|
||||||
l1sap->u.data.chan_nr = chan_nr;
|
PRIM_OP_INDICATION, l1p_msg);
|
||||||
l1sap->u.data.fn = fn;
|
l1sap->u.tch.chan_nr = chan_nr;
|
||||||
|
l1sap->u.tch.fn = fn;
|
||||||
|
} else {
|
||||||
|
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS,
|
||||||
|
PRIM_OP_INDICATION, l1p_msg);
|
||||||
|
l1sap->u.data.link_id = link_id;
|
||||||
|
l1sap->u.data.chan_nr = chan_nr;
|
||||||
|
l1sap->u.data.fn = fn;
|
||||||
|
}
|
||||||
|
|
||||||
return l1sap_up(trx, l1sap);
|
return l1sap_up(trx, l1sap);
|
||||||
}
|
}
|
||||||
|
@ -726,50 +827,6 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
|
||||||
g_time.t1, g_time.t2, g_time.t3,
|
g_time.t1, g_time.t2, g_time.t3,
|
||||||
get_value_string(femtobts_l1sapi_names, rts_ind->sapi));
|
get_value_string(femtobts_l1sapi_names, rts_ind->sapi));
|
||||||
|
|
||||||
/* 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_hLayer_to_lchan(trx, rts_ind->hLayer2);
|
|
||||||
if (!lchan)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (!lchan->loopback && lchan->abis_ip.rtp_socket) {
|
|
||||||
osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket);
|
|
||||||
/* FIXME: we _assume_ that we never miss TDMA
|
|
||||||
* frames and that we always get to this point
|
|
||||||
* for every to-be-transmitted voice frame. A
|
|
||||||
* better solution would be to compute
|
|
||||||
* rx_user_ts based on how many TDMA frames have
|
|
||||||
* elapsed since the last call */
|
|
||||||
lchan->abis_ip.rtp_socket->rx_user_ts += GSM_RTP_DURATION;
|
|
||||||
}
|
|
||||||
/* get a msgb from the dl_tx_queue */
|
|
||||||
resp_msg = msgb_dequeue(&lchan->dl_tch_queue);
|
|
||||||
/* if there is none, try to generate empty TCH frame
|
|
||||||
* like AMR SID_BAD */
|
|
||||||
if (!resp_msg) {
|
|
||||||
LOGP(DL1C, LOGL_DEBUG, "%s DL TCH Tx queue underrun\n",
|
|
||||||
gsm_lchan_name(lchan));
|
|
||||||
resp_msg = gen_empty_tch_msg(lchan);
|
|
||||||
/* if there really is none, break here and send empty */
|
|
||||||
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
|
/* in all other cases, we need to allocate a new PH-DATA.ind
|
||||||
* primitive msgb and start to fill it */
|
* primitive msgb and start to fill it */
|
||||||
resp_msg = l1p_msgb_alloc();
|
resp_msg = l1p_msgb_alloc();
|
||||||
|
@ -831,12 +888,6 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
|
||||||
msgb_free(pp.oph.msg);
|
msgb_free(pp.oph.msg);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GsmL1_Sapi_TchF:
|
|
||||||
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_FacchF:
|
||||||
case GsmL1_Sapi_FacchH:
|
case GsmL1_Sapi_FacchH:
|
||||||
/* resolve the L2 entity using rts_ind->hLayer2 */
|
/* resolve the L2 entity using rts_ind->hLayer2 */
|
||||||
|
@ -1070,7 +1121,7 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
|
||||||
case GsmL1_Sapi_TchF:
|
case GsmL1_Sapi_TchF:
|
||||||
case GsmL1_Sapi_TchH:
|
case GsmL1_Sapi_TchH:
|
||||||
/* TCH speech frame handling */
|
/* TCH speech frame handling */
|
||||||
rc = l1if_tch_rx(lchan, l1p_msg);
|
rc = l1if_tch_rx(trx, chan_nr, l1p_msg);
|
||||||
break;
|
break;
|
||||||
case GsmL1_Sapi_Pdtch:
|
case GsmL1_Sapi_Pdtch:
|
||||||
case GsmL1_Sapi_Pacch:
|
case GsmL1_Sapi_Pacch:
|
||||||
|
|
|
@ -110,7 +110,9 @@ uint32_t l1if_lchan_to_hLayer(struct gsm_lchan *lchan);
|
||||||
struct gsm_lchan *l1if_hLayer_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer);
|
struct gsm_lchan *l1if_hLayer_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer);
|
||||||
|
|
||||||
/* tch.c */
|
/* tch.c */
|
||||||
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
|
void l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
|
||||||
|
const uint8_t *rtp_pl, unsigned int rtp_pl_len);
|
||||||
|
int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg);
|
||||||
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
|
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
|
||||||
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan);
|
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan);
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include <osmo-bts/gsm_data.h>
|
#include <osmo-bts/gsm_data.h>
|
||||||
#include <osmo-bts/measurement.h>
|
#include <osmo-bts/measurement.h>
|
||||||
#include <osmo-bts/amr.h>
|
#include <osmo-bts/amr.h>
|
||||||
|
#include <osmo-bts/l1sap.h>
|
||||||
|
|
||||||
#include <sysmocom/femtobts/superfemto.h>
|
#include <sysmocom/femtobts/superfemto.h>
|
||||||
#include <sysmocom/femtobts/gsml1prim.h>
|
#include <sysmocom/femtobts/gsml1prim.h>
|
||||||
|
@ -425,7 +426,7 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
|
||||||
|
|
||||||
#define RTP_MSGB_ALLOC_SIZE 512
|
#define RTP_MSGB_ALLOC_SIZE 512
|
||||||
|
|
||||||
/*! \brief call-back function for incoming RTP
|
/*! \brief function for incoming RTP via TCH.req
|
||||||
* \param rs RTP Socket
|
* \param rs RTP Socket
|
||||||
* \param[in] rtp_pl buffer containing RTP payload
|
* \param[in] rtp_pl buffer containing RTP payload
|
||||||
* \param[in] rtp_pl_len length of \a rtp_pl
|
* \param[in] rtp_pl_len length of \a rtp_pl
|
||||||
|
@ -437,34 +438,18 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
|
||||||
* yet, as things like the frame number, etc. are unknown at the time we
|
* yet, as things like the frame number, etc. are unknown at the time we
|
||||||
* pre-fill the primtive.
|
* pre-fill the primtive.
|
||||||
*/
|
*/
|
||||||
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
void l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
|
||||||
unsigned int rtp_pl_len)
|
const uint8_t *rtp_pl, unsigned int rtp_pl_len)
|
||||||
{
|
{
|
||||||
struct gsm_lchan *lchan = rs->priv;
|
|
||||||
struct msgb *msg;
|
|
||||||
GsmL1_Prim_t *l1p;
|
|
||||||
GsmL1_PhDataReq_t *data_req;
|
|
||||||
GsmL1_MsgUnitParam_t *msu_param;
|
|
||||||
uint8_t *payload_type;
|
uint8_t *payload_type;
|
||||||
uint8_t *l1_payload;
|
uint8_t *l1_payload;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* skip processing of incoming RTP frames if we are in loopback mode */
|
DEBUGP(DRTP, "%s RTP IN: %s\n", gsm_lchan_name(lchan),
|
||||||
if (lchan->loopback)
|
osmo_hexdump(rtp_pl, rtp_pl_len));
|
||||||
return;
|
|
||||||
|
|
||||||
msg = l1p_msgb_alloc();
|
payload_type = &data[0];
|
||||||
if (!msg) {
|
l1_payload = &data[1];
|
||||||
LOGP(DRTP, LOGL_ERROR, "%s: Failed to allocate Rx payload.\n",
|
|
||||||
gsm_lchan_name(lchan));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
l1p = msgb_l1prim(msg);
|
|
||||||
data_req = &l1p->u.phDataReq;
|
|
||||||
msu_param = &data_req->msgUnitParam;
|
|
||||||
payload_type = &msu_param->u8Buffer[0];
|
|
||||||
l1_payload = &msu_param->u8Buffer[1];
|
|
||||||
|
|
||||||
switch (lchan->tch_mode) {
|
switch (lchan->tch_mode) {
|
||||||
case GSM48_CMODE_SPEECH_V1:
|
case GSM48_CMODE_SPEECH_V1:
|
||||||
|
@ -499,34 +484,13 @@ void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
LOGP(DRTP, LOGL_ERROR, "%s unable to parse RTP payload\n",
|
LOGP(DRTP, LOGL_ERROR, "%s unable to parse RTP payload\n",
|
||||||
gsm_lchan_name(lchan));
|
gsm_lchan_name(lchan));
|
||||||
msgb_free(msg);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
msu_param->u8Size = rc + 1;
|
*len = rc + 1;
|
||||||
|
|
||||||
|
DEBUGP(DRTP, "%s RTP->L1: %s\n", gsm_lchan_name(lchan),
|
||||||
/* make sure the number of entries in the dl_tch_queue is never
|
osmo_hexdump(data, *len));
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_recv_only(uint8_t speech_mode)
|
static int is_recv_only(uint8_t speech_mode)
|
||||||
|
@ -535,7 +499,7 @@ static int is_recv_only(uint8_t speech_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief receive a traffic L1 primitive for a given lchan */
|
/*! \brief receive a traffic L1 primitive for a given lchan */
|
||||||
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
|
int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg)
|
||||||
{
|
{
|
||||||
GsmL1_Prim_t *l1p = msgb_l1prim(l1p_msg);
|
GsmL1_Prim_t *l1p = msgb_l1prim(l1p_msg);
|
||||||
GsmL1_PhDataInd_t *data_ind = &l1p->u.phDataInd;
|
GsmL1_PhDataInd_t *data_ind = &l1p->u.phDataInd;
|
||||||
|
@ -543,53 +507,18 @@ int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
|
||||||
uint8_t *payload = data_ind->msgUnitParam.u8Buffer + 1;
|
uint8_t *payload = data_ind->msgUnitParam.u8Buffer + 1;
|
||||||
uint8_t payload_len;
|
uint8_t payload_len;
|
||||||
struct msgb *rmsg = NULL;
|
struct msgb *rmsg = NULL;
|
||||||
|
struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)].lchan[l1sap_chan2ss(chan_nr)];
|
||||||
|
|
||||||
if (is_recv_only(lchan->abis_ip.speech_mode))
|
if (is_recv_only(lchan->abis_ip.speech_mode))
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
||||||
if (data_ind->msgUnitParam.u8Size < 1) {
|
if (data_ind->msgUnitParam.u8Size < 1) {
|
||||||
LOGP(DL1C, LOGL_ERROR, "%s Rx Payload size 0\n",
|
LOGP(DL1C, LOGL_ERROR, "chan_nr %d Rx Payload size 0\n",
|
||||||
gsm_lchan_name(lchan));
|
chan_nr);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
payload_len = data_ind->msgUnitParam.u8Size - 1;
|
payload_len = data_ind->msgUnitParam.u8Size - 1;
|
||||||
|
|
||||||
if (lchan->loopback) {
|
|
||||||
GsmL1_Prim_t *rl1p;
|
|
||||||
GsmL1_PhDataReq_t *data_req;
|
|
||||||
GsmL1_MsgUnitParam_t *msu_param;
|
|
||||||
|
|
||||||
struct msgb *tmp;
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
/* generate a new msgb from the paylaod */
|
|
||||||
rmsg = l1p_msgb_alloc();
|
|
||||||
if (!rmsg)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
rl1p = msgb_l1prim(rmsg);
|
|
||||||
data_req = &rl1p->u.phDataReq;
|
|
||||||
msu_param = &data_req->msgUnitParam;
|
|
||||||
|
|
||||||
memcpy(msu_param->u8Buffer,
|
|
||||||
data_ind->msgUnitParam.u8Buffer,
|
|
||||||
data_ind->msgUnitParam.u8Size);
|
|
||||||
msu_param->u8Size = data_ind->msgUnitParam.u8Size;
|
|
||||||
|
|
||||||
/* make sure the queue doesn't get too long */
|
|
||||||
llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
|
|
||||||
count++;
|
|
||||||
while (count >= 1) {
|
|
||||||
tmp = msgb_dequeue(&lchan->dl_tch_queue);
|
|
||||||
msgb_free(tmp);
|
|
||||||
count--;
|
|
||||||
}
|
|
||||||
|
|
||||||
msgb_enqueue(&lchan->dl_tch_queue, rmsg);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (payload_type) {
|
switch (payload_type) {
|
||||||
case GsmL1_TchPlType_Fr:
|
case GsmL1_TchPlType_Fr:
|
||||||
#if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
|
#if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
|
||||||
|
@ -633,11 +562,20 @@ int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rmsg) {
|
if (rmsg) {
|
||||||
/* hand rmsg to RTP code for transmission */
|
struct osmo_phsap_prim *l1sap;
|
||||||
if (lchan->abis_ip.rtp_socket)
|
|
||||||
osmo_rtp_send_frame(lchan->abis_ip.rtp_socket,
|
LOGP(DL1C, LOGL_DEBUG, "%s Rx -> RTP: %s\n",
|
||||||
rmsg->data, rmsg->len, 160);
|
gsm_lchan_name(lchan), osmo_hexdump(rmsg->data, rmsg->len));
|
||||||
msgb_free(rmsg);
|
|
||||||
|
/* add l1sap header */
|
||||||
|
rmsg->l2h = rmsg->data;
|
||||||
|
msgb_push(rmsg, sizeof(*l1sap));
|
||||||
|
rmsg->l1h = rmsg->data;
|
||||||
|
l1sap = msgb_l1sap_prim(rmsg);
|
||||||
|
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_INDICATION, rmsg);
|
||||||
|
l1sap->u.tch.chan_nr = chan_nr;
|
||||||
|
|
||||||
|
return l1sap_up(trx, l1sap);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue