/* * (C) 2023 by sysmocom - s.f.m.c. GmbH * Author: Vadim Yanitskiy * * 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 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 /* key is enum gsm48_chan_mode, so assuming a value in range 0..255 */ const struct csd_v110_lchan_desc csd_v110_lchan_desc[256] = { #if 0 [GSM48_CMODE_DATA_14k5] = { /* TCH/F14.4: 290 bits every 20 ms (14.5 kbit/s) */ .fr = { .num_blocks = 1, .num_bits = 290 }, }, #endif [GSM48_CMODE_DATA_12k0] = { /* TCH/F9.6: 4 * 60 bits every 20 ms (12.0 kbit/s) */ .fr = { .num_blocks = 4, .num_bits = 60 }, }, [GSM48_CMODE_DATA_6k0] = { /* TCH/F4.8: 2 * 60 bits every 20 ms (6.0 kbit/s) */ .fr = { .num_blocks = 2, .num_bits = 60 }, /* TCH/H4.8: 4 * 60 bits every 40 ms (6.0 kbit/s) */ .hr = { .num_blocks = 4, .num_bits = 60 }, }, [GSM48_CMODE_DATA_3k6] = { /* TCH/F2.4: 2 * 36 bits every 20 ms (3.6 kbit/s) */ .fr = { .num_blocks = 2, .num_bits = 36 }, /* TCH/H2.4: 4 * 36 bits every 40 ms (3.6 kbit/s) */ .hr = { .num_blocks = 4, .num_bits = 36 }, }, }; int csd_v110_rtp_encode(const struct gsm_lchan *lchan, uint8_t *rtp, const uint8_t *data, size_t data_len) { const struct csd_v110_frame_desc *desc; ubit_t ra_bits[80 * 4]; OSMO_ASSERT(lchan->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc)); if (lchan->type == GSM_LCHAN_TCH_F) desc = &csd_v110_lchan_desc[lchan->tch_mode].fr; else desc = &csd_v110_lchan_desc[lchan->tch_mode].hr; if (OSMO_UNLIKELY(desc->num_blocks == 0)) return -ENOTSUP; /* handle empty/incomplete frames gracefully */ if (OSMO_UNLIKELY(data_len < (desc->num_blocks * desc->num_bits))) return -ENODATA; /* RA1'/RA1: convert to an intermediate data rate */ for (unsigned int i = 0; i < desc->num_blocks; i++) { struct osmo_v110_decoded_frame df; /* convert modified V.110 frames to normal V.110 frames */ if (desc->num_bits == 60) osmo_csd_12k_6k_decode_frame(&df, &data[i * 60], 60); else /* desc->num_bits == 36 */ osmo_csd_3k6_decode_frame(&df, &data[i * 36], 36); /* FIXME: E1 .. E3 must be set by out-of-band knowledge! */ memset(&df.e_bits[0], 0, 3); osmo_v110_encode_frame(&ra_bits[i * 80], 80, &df); } /* RA1/RA2: convert from an intermediate rate to 64 kbit/s */ if (desc->num_blocks == 4) { /* 4 * 80 bits (16 kbit/s) => 2 bits per octet */ for (unsigned int i = 0, j = 0; i < RFC4040_RTP_PLEN; i++) { rtp[i] = (0xff >> 2); rtp[i] |= (ra_bits[j++] << 7); rtp[i] |= (ra_bits[j++] << 6); } } else { /* 2 * 80 bits (8 kbit/s) => 1 bit per octet */ for (unsigned int i = 0; i < RFC4040_RTP_PLEN; i++) { rtp[i] = (0xff >> 1); rtp[i] |= (ra_bits[i] << 7); } } return RFC4040_RTP_PLEN; } int csd_v110_rtp_decode(const struct gsm_lchan *lchan, uint8_t *data, const uint8_t *rtp, size_t rtp_len) { const struct csd_v110_frame_desc *desc; ubit_t ra_bits[80 * 4]; OSMO_ASSERT(lchan->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc)); if (lchan->type == GSM_LCHAN_TCH_F) desc = &csd_v110_lchan_desc[lchan->tch_mode].fr; else desc = &csd_v110_lchan_desc[lchan->tch_mode].hr; if (OSMO_UNLIKELY(desc->num_blocks == 0)) return -ENOTSUP; if (OSMO_UNLIKELY(rtp_len != RFC4040_RTP_PLEN)) return -EINVAL; /* RA1/RA2: convert from 64 kbit/s to an intermediate rate */ if (desc->num_blocks == 4) { /* 4 * 80 bits (16 kbit/s) => 2 bits per octet */ for (unsigned int i = 0, j = 0; i < RFC4040_RTP_PLEN; i++) { ra_bits[j++] = (rtp[i] >> 7); ra_bits[j++] = (rtp[i] >> 6) & 0x01; } } else { /* 2 * 80 bits (8 kbit/s) => 1 bit per octet */ for (unsigned int i = 0; i < RFC4040_RTP_PLEN; i++) ra_bits[i] = (rtp[i] >> 7); } /* RA1'/RA1: convert to an intermediate data rate */ for (unsigned int i = 0; i < desc->num_blocks; i++) { struct osmo_v110_decoded_frame df; /* convert modified V.110 frames to normal V.110 frames */ osmo_v110_decode_frame(&df, &ra_bits[i * 80], 80); if (desc->num_bits == 60) osmo_csd_12k_6k_encode_frame(&data[i * 60], 60, &df); else /* desc->num_bits == 36 */ osmo_csd_3k6_encode_frame(&data[i * 36], 36, &df); } return desc->num_blocks * desc->num_bits; }