diff options
-rw-r--r-- | src/host/trxcon/Makefile.am | 1 | ||||
-rw-r--r-- | src/host/trxcon/sched_lchan_common.c | 80 | ||||
-rw-r--r-- | src/host/trxcon/sched_lchan_desc.c | 9 | ||||
-rw-r--r-- | src/host/trxcon/sched_lchan_tchf.c | 292 | ||||
-rw-r--r-- | src/host/trxcon/sched_trx.h | 6 |
5 files changed, 386 insertions, 2 deletions
diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am index 90c7a3c1..88ff2bee 100644 --- a/src/host/trxcon/Makefile.am +++ b/src/host/trxcon/Makefile.am @@ -34,6 +34,7 @@ trxcon_SOURCES += \ sched_lchan_common.c \ sched_lchan_desc.c \ sched_lchan_xcch.c \ + sched_lchan_tchf.c \ sched_lchan_rach.c \ sched_lchan_sch.c \ sched_mframe.c \ diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c index 925a4415..6d75533d 100644 --- a/src/host/trxcon/sched_lchan_common.c +++ b/src/host/trxcon/sched_lchan_common.c @@ -33,6 +33,7 @@ #include <osmocom/core/bits.h> #include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> #include "l1ctl_proto.h" #include "scheduler.h" @@ -145,3 +146,82 @@ int sched_send_data_conf(struct trx_instance *trx, struct trx_ts *ts, return 0; } + +/** + * Composes a bad frame indication message + * according to the current tch_mode. + * + * @param l2 Pointer to allocated byte array + * @param tch_mode Current TCH mode + * @return How much bytes were written + */ +size_t sched_bad_frame_ind(uint8_t *l2, uint8_t rsl_cmode, uint8_t tch_mode) +{ + /* BFI is only required for speech */ + if (rsl_cmode != RSL_CMOD_SPD_SPEECH) + return 0; + + switch (tch_mode) { + case GSM48_CMODE_SIGN: + case GSM48_CMODE_SPEECH_V1: /* Full Rate */ + memset(l2, 0x00, GSM_FR_BYTES); + l2[0] = 0xd0; + return GSM_FR_BYTES; + case GSM48_CMODE_SPEECH_EFR: /* Enhanced Full Rate */ + memset(l2, 0x00, GSM_EFR_BYTES); + l2[0] = 0xc0; + return GSM_EFR_BYTES; + case GSM48_CMODE_SPEECH_AMR: /* Adaptive Multi Rate */ + /* FIXME: AMR is not implemented yet */ + return 0; + default: + LOGP(DSCH, LOGL_ERROR, "Invalid TCH mode: %u\n", tch_mode); + return 0; + } +} + +#define PRIM_IS_FACCH(prim) \ + prim->payload_len == GSM_MACBLOCK_LEN + +#define PRIM_IS_TCH(prim) \ + prim->payload_len != GSM_MACBLOCK_LEN + +struct trx_ts_prim *sched_dequeue_tch_prim(struct llist_head *queue) +{ + struct trx_ts_prim *a, *b; + + /* Obtain the first prim from TX queue */ + a = llist_entry(queue->next, struct trx_ts_prim, list); + + /* If this is the only one => do nothing... */ + if (queue->next->next == queue) + return a; + + /* Obtain the second prim from TX queue */ + b = llist_entry(queue->next->next, struct trx_ts_prim, list); + + /* Find and prioritize FACCH */ + if (PRIM_IS_FACCH(a) && PRIM_IS_TCH(b)) { + /** + * Case 1: first is FACCH, second is TCH: + * Prioritize FACCH, dropping TCH + */ + llist_del(&b->list); + talloc_free(b); + return a; + } else if (PRIM_IS_TCH(a) && PRIM_IS_FACCH(b)) { + /** + * Case 2: first is TCH, second is FACCH: + * Prioritize FACCH, dropping TCH + */ + llist_del(&a->list); + talloc_free(a); + return b; + } else { + /** + * Otherwise: both are TCH or FACCH frames: + * Nothing to prioritize, return the first one + */ + return a; + } +} diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/sched_lchan_desc.c index c483db8b..390b1ef1 100644 --- a/src/host/trxcon/sched_lchan_desc.c +++ b/src/host/trxcon/sched_lchan_desc.c @@ -30,11 +30,9 @@ /* TODO: implement */ #define tx_pdtch_fn NULL -#define tx_tchf_fn NULL #define tx_tchh_fn NULL #define rx_pdtch_fn NULL -#define rx_tchf_fn NULL #define rx_tchh_fn NULL /* Forward declaration of handlers */ @@ -52,6 +50,13 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid); +int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, + sbit_t *bits, int8_t rssi, float toa); + +int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid); + const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { { TRXC_IDLE, "IDLE", diff --git a/src/host/trxcon/sched_lchan_tchf.c b/src/host/trxcon/sched_lchan_tchf.c new file mode 100644 index 00000000..237e5388 --- /dev/null +++ b/src/host/trxcon/sched_lchan_tchf.c @@ -0,0 +1,292 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: handlers for DL / UL bursts on logical channels + * + * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <errno.h> +#include <string.h> +#include <talloc.h> +#include <stdint.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/bits.h> + +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> +#include <osmocom/gsm/gsm_utils.h> + +#include <osmocom/coding/gsm0503_coding.h> +#include <osmocom/codec/codec.h> + +#include "l1ctl_proto.h" +#include "scheduler.h" +#include "sched_trx.h" +#include "logging.h" +#include "trx_if.h" +#include "trxcon.h" +#include "l1ctl.h" + +int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, + sbit_t *bits, int8_t rssi, float toa) +{ + const struct trx_lchan_desc *lchan_desc; + uint8_t rsl_cmode, tch_mode, mode; + int n_errors, n_bits_total, rc; + sbit_t *buffer, *offset; + uint8_t l2[128], *mask; + uint32_t *first_fn; + size_t l2_len; + + /* Set up pointers */ + lchan_desc = &trx_lchan_desc[lchan->type]; + first_fn = &lchan->rx_first_fn; + mask = &lchan->rx_burst_mask; + buffer = lchan->rx_bursts; + + LOGP(DSCHD, LOGL_DEBUG, "Traffic received on %s: fn=%u ts=%u bid=%u\n", + lchan_desc->name, fn, ts->index, bid); + + /* Reset internal state */ + if (bid == 0) { + /* Clean up old measurements */ + memset(&lchan->meas, 0x00, sizeof(lchan->meas)); + + *first_fn = fn; + *mask = 0x00; + } + + /* Update mask */ + *mask |= (1 << bid); + + /* Update mask and RSSI */ + lchan->meas.rssi_sum += rssi; + lchan->meas.toa_sum += toa; + lchan->meas.rssi_num++; + lchan->meas.toa_num++; + + /* Copy burst to end of buffer of 8 bursts */ + offset = buffer + bid * 116 + 464; + memcpy(offset, bits + 3, 58); + memcpy(offset + 58, bits + 87, 58); + + /* Wait until complete set of bursts */ + if (bid != 3) + return 0; + + /* Check for complete set of bursts */ + if ((*mask & 0xf) != 0xf) { + LOGP(DSCHD, LOGL_DEBUG, "Received incomplete traffic frame at " + "fn=%u (%u/%u) for %s\n", *first_fn, + (*first_fn) % ts->mf_layout->period, + ts->mf_layout->period, + lchan_desc->name); + return -EINVAL; + } + + /** + * Get current RSL / TCH modes + * + * FIXME: we do support speech only, and + * CSD support may be implemented latter. + */ + rsl_cmode = RSL_CMOD_SPD_SPEECH; + tch_mode = lchan->tch_mode; + + mode = rsl_cmode != RSL_CMOD_SPD_SPEECH ? + GSM48_CMODE_SPEECH_V1 : tch_mode; + + switch (mode) { + case GSM48_CMODE_SIGN: + case GSM48_CMODE_SPEECH_V1: /* FR */ + rc = gsm0503_tch_fr_decode(l2, buffer, + 1, 0, &n_errors, &n_bits_total); + break; + case GSM48_CMODE_SPEECH_EFR: /* EFR */ + rc = gsm0503_tch_fr_decode(l2, buffer, + 1, 1, &n_errors, &n_bits_total); + break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + /** + * TODO: AMR requires a dedicated loop, + * which will be implemented later... + */ + LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet\n"); + return -ENOTSUP; + default: + LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u\n", tch_mode); + return -EINVAL; + } + + /* Shift buffer by 4 bursts for interleaving */ + memcpy(buffer, buffer + 464, 464); + + /* Check decoding result */ + if (rc < 4) { + LOGP(DSCHD, LOGL_DEBUG, "Received bad TCH frame ending at " + "fn=%u for %s\n", fn, lchan_desc->name); + + l2_len = sched_bad_frame_ind(l2, rsl_cmode, tch_mode); + } else if (rc == GSM_MACBLOCK_LEN) { + /* FACCH received, forward it to the higher layers */ + sched_send_data_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN); + /* Send BFI instead of stolen TCH frame */ + l2_len = sched_bad_frame_ind(l2, rsl_cmode, tch_mode); + } else { + /* A good TCH frame received */ + l2_len = rc; + } + + /* Send a traffic frame to the higher layers */ + if (l2_len > 0) + sched_send_data_ind(trx, ts, lchan, l2, l2_len); + + return 0; +} + +int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid) +{ + const struct trx_lchan_desc *lchan_desc; + struct trx_ts_prim *prim; + ubit_t burst[GSM_BURST_LEN]; + ubit_t *buffer, *offset; + uint8_t *mask, *l2; + const uint8_t *tsc; + size_t l2_len; + int rc; + + /* Set up pointers */ + lchan_desc = &trx_lchan_desc[lchan->type]; + mask = &lchan->tx_burst_mask; + buffer = lchan->tx_bursts; + + /* If we have encoded bursts */ + if (*mask) + goto send_burst; + + /* Wait until a first burst in period */ + if (bid > 0) + return 0; + + /* Check the current TCH mode */ + switch (lchan->tch_mode) { + case GSM48_CMODE_SIGN: + case GSM48_CMODE_SPEECH_V1: /* FR */ + l2_len = GSM_FR_BYTES; + break; + case GSM48_CMODE_SPEECH_EFR: /* EFR */ + l2_len = GSM_EFR_BYTES; + break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + /** + * TODO: AMR requires a dedicated loop, + * which will be implemented later... + */ + /* TODO: drop prim here */ + LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet\n"); + return -ENOTSUP; + default: + /* TODO: drop prim here */ + LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u\n", + lchan->tch_mode); + return -EINVAL; + } + + /* Get a message from TX queue */ + prim = sched_dequeue_tch_prim(&ts->tx_prims); + l2 = (uint8_t *) prim->payload; + + /* Determine payload length */ + if (prim->payload_len == GSM_MACBLOCK_LEN) + l2_len = GSM_MACBLOCK_LEN; + + /* Shift buffer by 4 bursts back for interleaving */ + memcpy(buffer, buffer + 464, 464); + + /* Encode payload */ + rc = gsm0503_tch_fr_encode(buffer, l2, l2_len, 1); + if (rc) { + LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload\n"); + + /* Remove primitive from queue and free memory */ + llist_del(&prim->list); + talloc_free(prim); + + return -EINVAL; + } + +send_burst: + /* Determine which burst should be sent */ + offset = buffer + bid * 116; + + /* Update mask */ + *mask |= (1 << bid); + + /* Choose proper TSC */ + tsc = sched_nb_training_bits[trx->tsc]; + + /* Compose a new burst */ + memset(burst, 0, 3); /* TB */ + memcpy(burst + 3, offset, 58); /* Payload 1/2 */ + memcpy(burst + 61, tsc, 26); /* TSC */ + memcpy(burst + 87, offset + 58, 58); /* Payload 2/2 */ + memset(burst + 145, 0, 3); /* TB */ + + LOGP(DSCHD, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n", + lchan_desc->name, fn, ts->index, bid); + + /* Send burst to transceiver */ + rc = trx_if_tx_burst(trx, ts->index, fn, trx->tx_power, burst); + if (rc) { + LOGP(DSCHD, LOGL_ERROR, "Could not send burst to transceiver\n"); + + /* Remove primitive from queue and free memory */ + prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); + llist_del(&prim->list); + talloc_free(prim); + + /* Reset mask */ + *mask = 0x00; + + return rc; + } + + /* If we have sent the last (4/4) burst */ + if (*mask == 0x0f) { + /* Get pointer to a prim which was sent */ + prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); + + /* Confirm data / traffic sending */ + sched_send_data_conf(trx, ts, lchan, fn, prim->payload_len); + + /* Remove primitive from queue and free memory */ + llist_del(&prim->list); + talloc_free(prim); + + /* Reset mask */ + *mask = 0x00; + } + + return 0; +} diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index de7e0734..268c6b70 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -22,6 +22,11 @@ #define MAX_A5_KEY_LEN (128 / 8) +/* TS 101318 Chapter 5.1: 260 bits + 4bit sig */ +#define GSM_FR_BYTES 33 +/* TS 101318 Chapter 5.3: 244 bits + 4bit sig */ +#define GSM_EFR_BYTES 31 + /* Forward declaration to avoid mutual include */ struct trx_lchan_state; struct trx_instance; @@ -262,6 +267,7 @@ int sched_trx_init_prim(struct trx_instance *trx, struct trx_ts_prim **prim, size_t pl_len, uint8_t chan_nr, uint8_t link_id); int sched_trx_push_prim(struct trx_instance *trx, struct trx_ts_prim *prim, uint8_t chan_nr); +struct trx_ts_prim *sched_dequeue_tch_prim(struct llist_head *queue); int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, uint32_t burst_fn, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); |