summaryrefslogtreecommitdiffstats
path: root/src/host/trxcon/sched_lchan_xcch.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/host/trxcon/sched_lchan_xcch.c')
-rw-r--r--src/host/trxcon/sched_lchan_xcch.c304
1 files changed, 304 insertions, 0 deletions
diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c
new file mode 100644
index 00000000..12bb6ee5
--- /dev/null
+++ b/src/host/trxcon/sched_lchan_xcch.c
@@ -0,0 +1,304 @@
+/*
+ * 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 <arpa/inet.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/fsm.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/coding/gsm0503_coding.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"
+
+/* GSM 05.02 Chapter 5.2.3 Normal Burst (NB) */
+static const uint8_t nb_training_bits[8][26] = {
+ {
+ 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0,
+ 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1,
+ },
+ {
+ 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1,
+ 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1,
+ },
+ {
+ 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1,
+ 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0,
+ },
+ {
+ 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0,
+ 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0,
+ },
+ {
+ 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0,
+ 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1,
+ },
+ {
+ 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
+ 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0,
+ },
+ {
+ 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1,
+ 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1,
+ },
+ {
+ 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0,
+ 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0,
+ },
+};
+
+int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
+ uint32_t fn, enum trx_lchan_type chan, uint8_t bid,
+ sbit_t *bits, uint16_t nbits, int8_t rssi, float toa)
+{
+ int n_errors, n_bits_total, rc;
+ struct trx_lchan_state *lchan;
+ uint8_t *rssi_num, *toa_num;
+ float *rssi_sum, *toa_sum;
+ sbit_t *buffer, *offset;
+ uint8_t l2[23], *mask;
+ uint32_t *first_fn;
+
+ LOGP(DSCH, LOGL_DEBUG, "Data received on %s: fn=%u ts=%u bid=%u\n",
+ trx_lchan_desc[chan].name, fn, ts->index, bid);
+
+ /* Find required channel state */
+ lchan = sched_trx_find_lchan(ts, chan);
+ if (lchan == NULL)
+ return -EINVAL;
+
+ /* Set up pointers */
+ first_fn = &lchan->rx_first_fn;
+ mask = &lchan->rx_burst_mask;
+ buffer = lchan->rx_bursts;
+
+ rssi_sum = &lchan->rssi_sum;
+ rssi_num = &lchan->rssi_num;
+ toa_sum = &lchan->toa_sum;
+ toa_num = &lchan->toa_num;
+
+ /* Clear buffer & store frame number of first burst */
+ if (bid == 0) {
+ memset(buffer, 0, 464);
+
+ *first_fn = fn;
+ *mask = 0x0;
+
+ *rssi_sum = 0;
+ *rssi_num = 0;
+ *toa_sum = 0;
+ *toa_num = 0;
+ }
+
+ /* Update mask and RSSI */
+ *mask |= (1 << bid);
+ *rssi_sum += rssi;
+ (*rssi_num)++;
+ *toa_sum += toa;
+ (*toa_num)++;
+
+ /* Copy burst to buffer of 4 bursts */
+ offset = buffer + bid * 116;
+ 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(DSCH, LOGL_DEBUG, "Received incomplete data frame at "
+ "fn=%u (%u/%u) for %s\n", *first_fn,
+ (*first_fn) % ts->mf_layout->period,
+ ts->mf_layout->period,
+ trx_lchan_desc[chan].name);
+
+ /* We require first burst to have correct FN */
+ if (!(*mask & 0x1)) {
+ *mask = 0x0;
+ return 0;
+ }
+
+ /* FIXME: return from here? */
+ }
+
+ /* Attempt to decode */
+ rc = gsm0503_xcch_decode(l2, buffer, &n_errors, &n_bits_total);
+ if (rc) {
+ LOGP(DSCH, LOGL_DEBUG, "Received bad data frame at fn=%u "
+ "(%u/%u) for %s\n", *first_fn,
+ (*first_fn) % ts->mf_layout->period,
+ ts->mf_layout->period,
+ trx_lchan_desc[chan].name);
+ return rc;
+ }
+
+ /* Compose a message to the higher layers */
+ struct l1ctl_info_dl *data;
+ data = talloc_zero_size(ts, sizeof(struct l1ctl_info_dl) + 23);
+ if (data == NULL)
+ return -ENOMEM;
+
+ /* Fill in some downlink info */
+ data->chan_nr = trx_lchan_desc[chan].chan_nr | ts->index;
+ data->link_id = trx_lchan_desc[chan].link_id;
+ data->band_arfcn = htons(trx->band_arfcn);
+ data->frame_nr = htonl(*first_fn);
+ data->rx_level = -(*rssi_sum / *rssi_num);
+
+ /* FIXME: set proper values */
+ data->num_biterr = n_errors;
+ data->fire_crc = 0;
+ data->snr = 0;
+
+ /* Fill in decoded payload */
+ memcpy(data->payload, l2, 23);
+
+ /* Put a packet to higher layers */
+ l1ctl_tx_data_ind(trx->l1l, data);
+ talloc_free(data);
+
+ /* TODO: AGC, TA loops */
+ return 0;
+}
+
+int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
+ uint32_t fn, enum trx_lchan_type chan,
+ uint8_t bid, uint16_t *nbits)
+{
+ struct trx_lchan_state *lchan;
+ struct trx_ts_prim *prim;
+ struct l1ctl_info_ul *ul;
+ ubit_t burst[GSM_BURST_LEN];
+ ubit_t *buffer, *offset;
+ uint8_t *mask, *l2;
+ const uint8_t *tsc;
+ int rc;
+
+ /* Find required channel state */
+ lchan = sched_trx_find_lchan(ts, chan);
+ if (lchan == NULL)
+ return -EINVAL;
+
+ /* Set up pointers */
+ mask = &lchan->tx_burst_mask;
+ buffer = lchan->tx_bursts;
+
+ if (bid > 0) {
+ /* If we have encoded bursts */
+ if (*mask)
+ goto send_burst;
+ else
+ return 0;
+ }
+
+ /* Encode payload if not yet */
+
+ /* Get a message from TX queue */
+ prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list);
+ ul = (struct l1ctl_info_ul *) prim->payload;
+ l2 = (uint8_t *) ul->payload;
+
+ /* Encode bursts */
+ rc = gsm0503_xcch_encode(buffer, l2);
+ if (rc) {
+ LOGP(DSCH, 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 = 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 */
+
+ if (nbits)
+ *nbits = GSM_BURST_LEN;
+
+ LOGP(DSCH, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n",
+ trx_lchan_desc[chan].name, fn, ts->index, bid);
+
+ /* Send burst to transceiver */
+ rc = trx_if_tx_burst(trx, ts->index, fn, 10, burst);
+ if (rc) {
+ LOGP(DSCH, 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) == 0x0f) {
+ /* 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;
+
+ /* Confirm data sending */
+ l1ctl_tx_data_conf(trx->l1l);
+ }
+
+ return 0;
+}