void lchan_set_state(struct gsm_lchan *lchan, enum gsm_lchan_state state) { DEBUGP(DL1C, "%s state %s -> %s\n", gsm_lchan_name(lchan), gsm_lchans_name(lchan->state), gsm_lchans_name(state)); lchan->state = state; }
/* receive a L1 uplink measurement from L1 */ int lchan_new_ul_meas(struct gsm_lchan *lchan, struct bts_ul_meas *ulm) { DEBUGP(DMEAS, "%s adding measurement, num_ul_meas=%d\n", gsm_lchan_name(lchan), lchan->meas.num_ul_meas); if (lchan->state != LCHAN_S_ACTIVE) { LOGP(DMEAS, LOGL_NOTICE, "%s measurement during state: %s\n", gsm_lchan_name(lchan), gsm_lchans_name(lchan->state)); } if (lchan->meas.num_ul_meas >= ARRAY_SIZE(lchan->meas.uplink)) { LOGP(DMEAS, LOGL_NOTICE, "%s no space for uplink measurement\n", gsm_lchan_name(lchan)); return -ENOSPC; } memcpy(&lchan->meas.uplink[lchan->meas.num_ul_meas++], ulm, sizeof(*ulm)); return 0; }
/* obtain the next to-be transmitted dowlink SACCH frame (L2 hdr + L3); returns pointer to lchan->si buffer */ uint8_t *lchan_sacch_get(struct gsm_lchan *lchan) { uint32_t tmp, i; for (i = 0; i < _MAX_SYSINFO_TYPE; i++) { tmp = (lchan->si.last + 1 + i) % _MAX_SYSINFO_TYPE; if (!(lchan->si.valid & (1 << tmp))) continue; lchan->si.last = tmp; return GSM_LCHAN_SI(lchan, tmp); } LOGP(DL1P, LOGL_NOTICE, "%s SACCH no SI available\n", gsm_lchan_name(lchan)); return NULL; }
/*! \brief receive a traffic L1 primitive for a given lchan */ 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_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, sid_first[7] = {0}; 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)) return -EAGAIN; if (data_ind->msgUnitParam.u8Size < 1) { LOGP(DL1C, LOGL_ERROR, "chan_nr %d Rx Payload size 0\n", chan_nr); return -EINVAL; } payload_len = data_ind->msgUnitParam.u8Size - 1; switch (payload_type) { case GsmL1_TchPlType_Fr: #if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE) case GsmL1_TchPlType_Efr: #endif if (lchan->type != GSM_LCHAN_TCH_F) goto err_payload_match; break; case GsmL1_TchPlType_Hr: if (lchan->type != GSM_LCHAN_TCH_H) goto err_payload_match; break; case GsmL1_TchPlType_Amr: if (lchan->type != GSM_LCHAN_TCH_H && lchan->type != GSM_LCHAN_TCH_F) goto err_payload_match; break; case GsmL1_TchPlType_Amr_Onset: if (lchan->type != GSM_LCHAN_TCH_H && lchan->type != GSM_LCHAN_TCH_F) goto err_payload_match; /* according to 3GPP TS 26.093 ONSET frames precede the first speech frame of a speech burst - set the marker for next RTP frame */ lchan->rtp_tx_marker = true; break; case GsmL1_TchPlType_Amr_SidFirstP1: if (lchan->type != GSM_LCHAN_TCH_H) goto err_payload_match; LOGP(DL1C, LOGL_DEBUG, "DTX: received SID_FIRST_P1 from L1 " "(%d bytes)\n", payload_len); break; case GsmL1_TchPlType_Amr_SidFirstP2: if (lchan->type != GSM_LCHAN_TCH_H) goto err_payload_match; LOGP(DL1C, LOGL_DEBUG, "DTX: received SID_FIRST_P2 from L1 " "(%d bytes)\n", payload_len); break; case GsmL1_TchPlType_Amr_SidFirstInH: if (lchan->type != GSM_LCHAN_TCH_H) goto err_payload_match; lchan->rtp_tx_marker = true; LOGP(DL1C, LOGL_DEBUG, "DTX: received SID_FIRST_INH from L1 " "(%d bytes)\n", payload_len); break; case GsmL1_TchPlType_Amr_SidUpdateInH: if (lchan->type != GSM_LCHAN_TCH_H) goto err_payload_match; lchan->rtp_tx_marker = true; LOGP(DL1C, LOGL_DEBUG, "DTX: received SID_UPDATE_INH from L1 " "(%d bytes)\n", payload_len); break; default: LOGP(DL1C, LOGL_NOTICE, "%s Rx Payload Type %s is unsupported\n", gsm_lchan_name(lchan), get_value_string(femtobts_tch_pl_names, payload_type)); break; } switch (payload_type) { case GsmL1_TchPlType_Fr: rmsg = l1_to_rtppayload_fr(payload, payload_len, lchan); break; case GsmL1_TchPlType_Hr: rmsg = l1_to_rtppayload_hr(payload, payload_len, lchan); break; #if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE) case GsmL1_TchPlType_Efr: rmsg = l1_to_rtppayload_efr(payload, payload_len, lchan); break; #endif case GsmL1_TchPlType_Amr: rmsg = l1_to_rtppayload_amr(payload, payload_len, lchan); break; case GsmL1_TchPlType_Amr_SidFirstP1: memcpy(sid_first, payload, payload_len); int len = osmo_amr_rtp_enc(sid_first, 0, AMR_SID, AMR_GOOD); if (len < 0) return 0; rmsg = l1_to_rtppayload_amr(sid_first, len, lchan); break; } if (rmsg) return add_l1sap_header(trx, rmsg, lchan, chan_nr, data_ind->u32Fn); return 0; err_payload_match: LOGP(DL1C, LOGL_ERROR, "%s Rx Payload Type %s incompatible with lchan\n", gsm_lchan_name(lchan), get_value_string(femtobts_tch_pl_names, payload_type)); return -EINVAL; }
/*! \brief function for incoming RTP via TCH.req * \param[in] rtp_pl buffer containing RTP payload * \param[in] rtp_pl_len length of \a rtp_pl * \param[in] use_cache Use cached payload instead of parsing RTP * \param[in] marker RTP header Marker bit (indicates speech onset) * \returns 0 if encoding result can be sent further to L1 without extra actions * positive value if data is ready AND extra actions are required * negative value otherwise (no data for L1 encoded) * * 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. */ int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len, const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn, bool use_cache, bool marker) { uint8_t *payload_type; uint8_t *l1_payload, ft; int rc = 0; bool is_sid = false; DEBUGP(DRTP, "%s RTP IN: %s\n", gsm_lchan_name(lchan), osmo_hexdump(rtp_pl, rtp_pl_len)); payload_type = &data[0]; l1_payload = &data[1]; 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); if (rc && lchan->ts->trx->bts->dtxd) is_sid = osmo_fr_check_sid(rtp_pl, rtp_pl_len); } else{ *payload_type = GsmL1_TchPlType_Hr; rc = rtppayload_to_l1_hr(l1_payload, rtp_pl, rtp_pl_len); if (rc && lchan->ts->trx->bts->dtxd) is_sid = osmo_hr_check_sid(rtp_pl, rtp_pl_len); } if (is_sid) dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, -1); break; #if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE) case GSM48_CMODE_SPEECH_EFR: *payload_type = GsmL1_TchPlType_Efr; rc = rtppayload_to_l1_efr(l1_payload, rtp_pl, rtp_pl_len); /* FIXME: detect and save EFR SID */ break; #endif case GSM48_CMODE_SPEECH_AMR: if (use_cache) { *payload_type = GsmL1_TchPlType_Amr; rtppayload_to_l1_amr(l1_payload, lchan->tch.dtx.cache, lchan->tch.dtx.len, ft); *len = lchan->tch.dtx.len + 1; return 0; } rc = dtx_dl_amr_fsm_step(lchan, rtp_pl, rtp_pl_len, fn, l1_payload, marker, len, &ft); if (rc < 0) return rc; if (!dtx_dl_amr_enabled(lchan)) { *payload_type = GsmL1_TchPlType_Amr; rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len, ft); return 0; } /* DTX DL-specific logic below: */ switch (lchan->tch.dtx.dl_amr_fsm->state) { case ST_ONSET_V: *payload_type = GsmL1_TchPlType_Amr_Onset; dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0); *len = 3; return 1; case ST_VOICE: *payload_type = GsmL1_TchPlType_Amr; rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len, ft); return 0; case ST_SID_F1: if (lchan->type == GSM_LCHAN_TCH_H) { /* AMR HR */ *payload_type = GsmL1_TchPlType_Amr_SidFirstP1; rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len, ft); return 0; } /* AMR FR */ *payload_type = GsmL1_TchPlType_Amr; rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len, ft); return 0; case ST_SID_F2: *payload_type = GsmL1_TchPlType_Amr; rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len, ft); return 0; case ST_F1_INH: *payload_type = GsmL1_TchPlType_Amr_SidFirstInH; *len = 3; dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0); return 1; case ST_U_INH: *payload_type = GsmL1_TchPlType_Amr_SidUpdateInH; *len = 3; dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0); return 1; case ST_SID_U: return -EAGAIN; case ST_FACCH: return -EBADMSG; default: LOGP(DRTP, LOGL_ERROR, "Unhandled DTX DL AMR FSM state " "%d\n", lchan->tch.dtx.dl_amr_fsm->state); return -EINVAL; } break; default: /* we don't support CSD modes */ rc = -1; break; } if (rc < 0) { LOGP(DRTP, LOGL_ERROR, "%s unable to parse RTP payload\n", gsm_lchan_name(lchan)); return -EBADMSG; } *len = rc + 1; DEBUGP(DRTP, "%s RTP->L1: %s\n", gsm_lchan_name(lchan), osmo_hexdump(data, *len)); return 0; }
static int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn) { uint32_t ber_full_sum = 0; uint32_t irssi_full_sum = 0; uint32_t ber_sub_sum = 0; uint32_t irssi_sub_sum = 0; int32_t taqb_sum = 0; unsigned int num_meas_sub = 0; int i; /* if measurement period is not complete, abort */ if (!is_meas_complete(lchan->ts->pchan, lchan->ts->nr, lchan->nr, fn)) return 0; /* if there are no measurements, skip computation */ if (lchan->meas.num_ul_meas == 0) return 0; /* compute the actual measurements */ /* step 1: add up */ for (i = 0; i < lchan->meas.num_ul_meas; i++) { struct bts_ul_meas *m = &lchan->meas.uplink[i]; ber_full_sum += m->ber10k; irssi_full_sum += m->inv_rssi; taqb_sum += m->ta_offs_qbits; if (m->is_sub) { num_meas_sub++; ber_sub_sum += m->ber10k; irssi_sub_sum += m->inv_rssi; } } /* step 2: divide */ ber_full_sum = ber_full_sum / lchan->meas.num_ul_meas; irssi_full_sum = irssi_full_sum / lchan->meas.num_ul_meas; taqb_sum = taqb_sum / lchan->meas.num_ul_meas; if (num_meas_sub) { ber_sub_sum = ber_sub_sum / num_meas_sub; irssi_sub_sum = irssi_sub_sum / num_meas_sub; } DEBUGP(DMEAS, "%s Computed TA(% 4dqb) BER-FULL(%2u.%02u%%), RSSI-FULL(-%3udBm), " "BER-SUB(%2u.%02u%%), RSSI-SUB(-%3udBm)\n", gsm_lchan_name(lchan), taqb_sum, ber_full_sum/100, ber_full_sum%100, irssi_full_sum, ber_sub_sum/100, ber_sub_sum%100, irssi_sub_sum); /* store results */ lchan->meas.res.rxlev_full = dbm2rxlev((int)irssi_full_sum * -1); lchan->meas.res.rxlev_sub = dbm2rxlev((int)irssi_sub_sum * -1); lchan->meas.res.rxqual_full = ber10k_to_rxqual(ber_full_sum); lchan->meas.res.rxqual_sub = ber10k_to_rxqual(ber_sub_sum); lchan->meas.flags |= LC_UL_M_F_RES_VALID; lchan->meas.num_ul_meas = 0; /* send a signal indicating computation is complete */ return 1; }
/*! \brief receive a traffic L1 primitive for a given lchan */ 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_PhDataInd_t *data_ind = &l1p->u.phDataInd; uint8_t *payload, payload_type, payload_len, sid_first[9] = { 0 }; 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)) return -EAGAIN; if (data_ind->msgUnitParam.u8Size < 1) { LOGPFN(DL1P, LOGL_DEBUG, data_ind->u32Fn, "chan_nr %d Rx Payload size 0\n", chan_nr); /* Push empty payload to upper layers */ rmsg = msgb_alloc_headroom(256, 128, "L1P-to-RTP"); return add_l1sap_header(trx, rmsg, lchan, chan_nr, data_ind->u32Fn, data_ind->measParam.fBer * 10000, data_ind->measParam.fLinkQuality * 10); } payload_type = data_ind->msgUnitParam.u8Buffer[0]; payload = data_ind->msgUnitParam.u8Buffer + 1; payload_len = data_ind->msgUnitParam.u8Size - 1; /* clear RTP marker if the marker has previously sent */ if (!lchan->tch.dtx.is_speech_resume) lchan->rtp_tx_marker = false; switch (payload_type) { case GsmL1_TchPlType_Fr: case GsmL1_TchPlType_Efr: if (lchan->type != GSM_LCHAN_TCH_F) goto err_payload_match; break; case GsmL1_TchPlType_Hr: if (lchan->type != GSM_LCHAN_TCH_H) goto err_payload_match; break; case GsmL1_TchPlType_Amr: if (lchan->type != GSM_LCHAN_TCH_H && lchan->type != GSM_LCHAN_TCH_F) goto err_payload_match; break; case GsmL1_TchPlType_Amr_Onset: if (lchan->type != GSM_LCHAN_TCH_H && lchan->type != GSM_LCHAN_TCH_F) goto err_payload_match; /* according to 3GPP TS 26.093 ONSET frames precede the first speech frame of a speech burst - set the marker for next RTP frame */ lchan->tch.dtx.is_speech_resume = true; lchan->rtp_tx_marker = true; break; case GsmL1_TchPlType_Amr_SidFirstP1: if (lchan->type != GSM_LCHAN_TCH_H) goto err_payload_match; LOGPFN(DL1P, LOGL_DEBUG, data_ind->u32Fn, "DTX: received SID_FIRST_P1 from L1 " "(%d bytes)\n", payload_len); break; case GsmL1_TchPlType_Amr_SidFirstP2: if (lchan->type != GSM_LCHAN_TCH_H) goto err_payload_match; LOGPFN(DL1P, LOGL_DEBUG, data_ind->u32Fn, "DTX: received SID_FIRST_P2 from L1 " "(%d bytes)\n", payload_len); break; case GsmL1_TchPlType_Amr_SidFirstInH: if (lchan->type != GSM_LCHAN_TCH_H) goto err_payload_match; lchan->tch.dtx.is_speech_resume = true; lchan->rtp_tx_marker = true; LOGPFN(DL1P, LOGL_DEBUG, data_ind->u32Fn, "DTX: received SID_FIRST_INH from L1 " "(%d bytes)\n", payload_len); break; case GsmL1_TchPlType_Amr_SidUpdateInH: if (lchan->type != GSM_LCHAN_TCH_H) goto err_payload_match; lchan->tch.dtx.is_speech_resume = true; lchan->rtp_tx_marker = true; LOGPFN(DL1P, LOGL_DEBUG, data_ind->u32Fn, "DTX: received SID_UPDATE_INH from L1 " "(%d bytes)\n", payload_len); break; default: LOGPFN(DL1P, LOGL_NOTICE, data_ind->u32Fn, "%s Rx Payload Type %s is unsupported\n", gsm_lchan_name(lchan), get_value_string(oc2gbts_tch_pl_names, payload_type)); break; } LOGP(DL1P, LOGL_DEBUG, "%s %s lchan->rtp_tx_marker = %s, len=%u\n", gsm_lchan_name(lchan), get_value_string(oc2gbts_tch_pl_names, payload_type), lchan->rtp_tx_marker ? "true" : "false", payload_len); switch (payload_type) { case GsmL1_TchPlType_Fr: rmsg = l1_to_rtppayload_fr(payload, payload_len, lchan); break; case GsmL1_TchPlType_Hr: rmsg = l1_to_rtppayload_hr(payload, payload_len, lchan); break; case GsmL1_TchPlType_Efr: rmsg = l1_to_rtppayload_efr(payload, payload_len, lchan); break; case GsmL1_TchPlType_Amr: rmsg = l1_to_rtppayload_amr(payload, payload_len, lchan); break; case GsmL1_TchPlType_Amr_SidFirstP1: memcpy(sid_first, payload, payload_len); int len = osmo_amr_rtp_enc(sid_first, 0, AMR_SID, AMR_GOOD); if (len < 0) return 0; rmsg = l1_to_rtppayload_amr(sid_first, len, lchan); break; } if (rmsg) return add_l1sap_header(trx, rmsg, lchan, chan_nr, data_ind->u32Fn, data_ind->measParam.fBer * 10000, data_ind->measParam.fLinkQuality * 10); return 0; err_payload_match: LOGPFN(DL1P, LOGL_ERROR, data_ind->u32Fn, "%s Rx Payload Type %s incompatible with lchan\n", gsm_lchan_name(lchan), get_value_string(oc2gbts_tch_pl_names, payload_type)); return -EINVAL; }