/* Traffic Channel (TCH) part of osmo-bts OCTPHY integration */ /* Copyright (c) 2014 Octasic Inc. All rights reserved. * Copyright (c) 2015 Harald Welte * * based on a copy of osmo-bts-sysmo/l1_tch.c, which is * Copyright (C) 2011-2013 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include "l1_if.h" struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len) { struct msgb *msg; uint8_t *cur; msg = msgb_alloc_headroom(1024, 128, "L1P-to-RTP"); if (!msg) return NULL; /* 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 */ int rtppayload_to_l1_fr(uint8_t *l1_payload, const 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; } static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len) { struct msgb *msg; uint8_t *cur; msg = msgb_alloc_headroom(1024, 128, "L1P-to-RTP"); if (!msg) return NULL; if (payload_len != GSM_HR_BYTES) { LOGP(DL1P, LOGL_ERROR, "L1 HR frame length %u != expected %u\n", payload_len, GSM_HR_BYTES); return NULL; } cur = msgb_put(msg, GSM_HR_BYTES); memcpy(cur, l1_payload, GSM_HR_BYTES); /* reverse the bit-order of each payload byte */ osmo_revbytebits_buf(cur, 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, const uint8_t *rtp_payload, unsigned int payload_len) { if (payload_len != GSM_HR_BYTES) { LOGP(DL1P, LOGL_ERROR, "RTP HR frame length %u != expected %u\n", payload_len, GSM_HR_BYTES); return 0; } memcpy(l1_payload, rtp_payload, GSM_HR_BYTES); /* reverse the bit-order of each payload byte */ osmo_revbytebits_buf(l1_payload, GSM_HR_BYTES); return GSM_HR_BYTES; } /* brief receive a traffic L1 primitive for a given lchan */ int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, tOCTVC1_GSM_MSG_TRX_LOGICAL_CHANNEL_DATA_INDICATION_EVT * data_ind) { uint32_t payload_type = data_ind->Data.ulPayloadType; uint8_t *payload = data_ind->Data.abyDataContent; uint32_t fn = data_ind->Data.ulFrameNumber; uint16_t b_total = data_ind->MeasurementInfo.usBERTotalBitCnt; uint16_t b_error = data_ind->MeasurementInfo.usBERCnt; uint16_t ber10k = b_total ? BER_10K * b_error / b_total : 0; int16_t lqual_cb = 0; /* FIXME: check min_qual_norm! */ uint8_t payload_len; struct msgb *rmsg = NULL; struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)].lchan[l1sap_chan2ss(chan_nr)]; if (data_ind->Data.ulDataLength < 1) { LOGPLCFN(lchan, fn, DL1P, LOGL_DEBUG, "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->Data.ulFrameNumber, ber10k, lqual_cb, 0, 0, 0); } payload_len = data_ind->Data.ulDataLength; switch (payload_type) { case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_FULL_RATE: case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_ENH_FULL_RATE: if (lchan->type != GSM_LCHAN_TCH_F) goto err_payload_match; break; case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_HALF_RATE: if (lchan->type != GSM_LCHAN_TCH_H) goto err_payload_match; break; case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_AMR_FULL_RATE: case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_AMR_HALF_RATE: if (lchan->type != GSM_LCHAN_TCH_H && lchan->type != GSM_LCHAN_TCH_F) goto err_payload_match; break; default: LOGPLCFN(lchan, fn, DL1P, LOGL_NOTICE, "%s Rx Payload Type %d is unsupported\n", gsm_lchan_name(lchan), payload_type); break; } LOGPLCFN(lchan, fn, DL1P, LOGL_DEBUG, "%s Rx codec frame (%u): %s\n", gsm_lchan_name(lchan), payload_len, osmo_hexdump(payload, payload_len)); switch (payload_type) { case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_FULL_RATE: rmsg = l1_to_rtppayload_fr(payload, payload_len); break; case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_HALF_RATE: rmsg = l1_to_rtppayload_hr(payload, payload_len); break; case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_ENH_FULL_RATE: /* Currently not supported */ #if 0 rmsg = l1_to_rtppayload_efr(payload, payload_len); break; #endif case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_AMR_FULL_RATE: case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_AMR_HALF_RATE: /* Currently not supported */ #if 0 rmsg = l1_to_rtppayload_amr(payload, payload_len, &lchan->tch.amr_mr); #else LOGPLCFN(lchan, fn, DL1P, LOGL_ERROR, "OctPHY only supports FR!\n"); return -1; #endif break; } if (rmsg) return add_l1sap_header(trx, rmsg, lchan, chan_nr, data_ind->Data.ulFrameNumber, ber10k, lqual_cb, 0, 0, 0); return 0; err_payload_match: LOGPLCFN(lchan, fn, DL1P, LOGL_ERROR, "%s Rx Payload Type %d incompatible with lchan\n", gsm_lchan_name(lchan), payload_type); return -EINVAL; } #define RTP_MSGB_ALLOC_SIZE 512 /*! \brief function for incoming RTP via TCH.req * \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 l1if_tch_encode(struct gsm_lchan *lchan, uint32_t *payload_type, uint8_t *data, uint32_t *len, const uint8_t *rtp_pl, unsigned int rtp_pl_len) { uint8_t *l1_payload; int rc = -1; LOGPLCHAN(lchan, DRTP, LOGL_DEBUG, "RTP IN: %s\n", osmo_hexdump(rtp_pl, rtp_pl_len)); l1_payload = &data[0]; switch (lchan->tch_mode) { case GSM48_CMODE_SPEECH_V1: if (lchan->type == GSM_LCHAN_TCH_F) { *payload_type = cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_FULL_RATE; rc = rtppayload_to_l1_fr(l1_payload, rtp_pl, rtp_pl_len); } else { *payload_type = cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_HALF_RATE; /* Not supported currently */ rc = rtppayload_to_l1_hr(l1_payload, rtp_pl, rtp_pl_len); } break; case GSM48_CMODE_SPEECH_EFR: /* Not supported currently */ case GSM48_CMODE_SPEECH_AMR: /* Not supported currently */ LOGP(DRTP, LOGL_ERROR, "OctPHY only supports FR!\n"); default: /* we don't support CSD modes */ rc = -1; break; } if (rc < 0) { LOGPLCHAN(lchan, DRTP, LOGL_ERROR, "unable to parse RTP payload\n"); return; } *len = rc; LOGPLCHAN(lchan, DRTP, LOGL_DEBUG, "RTP->L1: %s\n", osmo_hexdump(data, *len)); }