fixeria has uploaded this change for review.

View Change

trxcon/l1sched: rework the primitive API

Change-Id: I73576bd0ea10a5663ba6254283812c275cc3fa46
Related: OS#5500
---
M src/host/trxcon/include/osmocom/bb/l1sched/Makefile.am
M src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
A src/host/trxcon/include/osmocom/bb/l1sched/prim.h
M src/host/trxcon/src/sched_lchan_pdtch.c
M src/host/trxcon/src/sched_lchan_rach.c
M src/host/trxcon/src/sched_lchan_sch.c
M src/host/trxcon/src/sched_lchan_tchf.c
M src/host/trxcon/src/sched_lchan_tchh.c
M src/host/trxcon/src/sched_lchan_xcch.c
M src/host/trxcon/src/sched_prim.c
M src/host/trxcon/src/sched_trx.c
M src/host/trxcon/src/trxcon_fsm.c
M src/host/trxcon/src/trxcon_shim.c
13 files changed, 698 insertions(+), 700 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/04/32304/1
diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/Makefile.am b/src/host/trxcon/include/osmocom/bb/l1sched/Makefile.am
index 76f54a7..39c32ba 100644
--- a/src/host/trxcon/include/osmocom/bb/l1sched/Makefile.am
+++ b/src/host/trxcon/include/osmocom/bb/l1sched/Makefile.am
@@ -1,4 +1,5 @@
noinst_HEADERS = \
l1sched.h \
logging.h \
+ prim.h \
$(NULL)
diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h b/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
index 4728f41..c6360b8 100644
--- a/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
+++ b/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
@@ -14,6 +14,8 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>

+#include <osmocom/bb/l1sched/prim.h>
+
#define GPRS_L2_MAX_LEN 54
#define EDGE_L2_MAX_LEN 155

@@ -49,12 +51,6 @@
L1SCHED_BURST_8PSK,
};

-enum l1sched_ts_prim_type {
- L1SCHED_PRIM_DATA,
- L1SCHED_PRIM_RACH8,
- L1SCHED_PRIM_RACH11,
-};
-
/**
* These types define the different channels on a multiframe.
* Each channel has queues and can be activated individually.
@@ -103,29 +99,6 @@
_L1SCHED_CHAN_MAX
};

-enum l1sched_data_type {
- L1SCHED_DT_PACKET_DATA,
- L1SCHED_DT_SIGNALING,
- L1SCHED_DT_TRAFFIC,
- L1SCHED_DT_OTHER, /* SCH and RACH */
-};
-
-enum l1sched_config_type {
- /*! Channel combination for a timeslot */
- L1SCHED_CFG_PCHAN_COMB,
-};
-
-/* Represents a (re)configuration request */
-struct l1sched_config_req {
- enum l1sched_config_type type;
- union {
- struct {
- uint8_t tn;
- enum gsm_phys_chan_config pchan;
- } pchan_comb;
- };
-};
-
/* Represents a burst to be transmitted */
struct l1sched_burst_req {
uint32_t fn;
@@ -259,8 +232,10 @@
/*! Burst buffer for TX */
ubit_t *tx_bursts;

- /*! A primitive being sent */
- struct l1sched_ts_prim *prim;
+ /*! Queue of Tx primitives */
+ struct llist_head tx_prims;
+ /*! Tx primitive being sent */
+ struct msgb *prim;

/*! Mode for TCH channels (see GSM48_CMODE_*) */
uint8_t tch_mode;
@@ -340,38 +315,10 @@
const struct l1sched_tdma_multiframe *mf_layout;
/*! Channel states for logical channels */
struct llist_head lchans;
- /*! Queue primitives for TX */
- struct llist_head tx_prims;
/*! Backpointer to the scheduler */
struct l1sched_state *sched;
};

-/* Represents one TX primitive in the queue of l1sched_ts */
-struct l1sched_ts_prim {
- /*! Link to queue of TS */
- struct llist_head list;
- /*! Type of primitive */
- enum l1sched_ts_prim_type type;
- /*! Logical channel type */
- enum l1sched_lchan_type chan;
- /*! TDMA Fn for L1SCHED_{PDTCH,PTCCH} */
- uint32_t fn;
- /*! Payload length */
- size_t payload_len;
- /*! Payload */
- uint8_t payload[0];
-};
-
-/*! Represents a RACH (8-bit or 11-bit) primitive */
-struct l1sched_ts_prim_rach {
- /*! RA value */
- uint16_t ra;
- /*! Training Sequence (only for 11-bit RA) */
- uint8_t synch_seq;
- /*! Transmission offset (how many frames to skip) */
- uint8_t offset;
-};
-
/*! Scheduler configuration */
struct l1sched_cfg {
/*! Logging context (used as prefix for messages) */
@@ -426,8 +373,6 @@

/* Logical channel management functions */
enum gsm_phys_chan_config l1sched_chan_nr2pchan_config(uint8_t chan_nr);
-enum l1sched_lchan_type l1sched_chan_nr2lchan_type(uint8_t chan_nr,
- uint8_t link_id);

void l1sched_deactivate_all_lchans(struct l1sched_ts *ts);
int l1sched_set_lchans(struct l1sched_ts *ts, uint8_t chan_nr,
@@ -441,12 +386,6 @@
struct l1sched_lchan_state *l1sched_find_lchan_by_chan_nr(struct l1sched_state *sched,
uint8_t chan_nr, uint8_t link_id);

-/* Primitive management functions */
-struct l1sched_ts_prim *l1sched_prim_push(struct l1sched_state *sched,
- enum l1sched_ts_prim_type type,
- uint8_t chan_nr, uint8_t link_id,
- const uint8_t *pl, size_t pl_len);
-
#define L1SCHED_TCH_MODE_IS_SPEECH(mode) \
(mode == GSM48_CMODE_SPEECH_V1 \
|| mode == GSM48_CMODE_SPEECH_EFR \
@@ -464,31 +403,12 @@
#define L1SCHED_CHAN_IS_SACCH(chan) \
(l1sched_lchan_desc[chan].link_id & L1SCHED_CH_LID_SACCH)

-#define L1SCHED_PRIM_IS_RACH11(prim) \
- (prim->type == L1SCHED_PRIM_RACH11)
-
-#define L1SCHED_PRIM_IS_RACH8(prim) \
- (prim->type == L1SCHED_PRIM_RACH8)
-
-#define L1SCHED_PRIM_IS_RACH(prim) \
- (L1SCHED_PRIM_IS_RACH8(prim) || L1SCHED_PRIM_IS_RACH11(prim))
-
-#define L1SCHED_PRIM_IS_TCH(prim) \
- (L1SCHED_CHAN_IS_TCH(prim->chan) && prim->payload_len != GSM_MACBLOCK_LEN)
-
-#define L1SCHED_PRIM_IS_FACCH(prim) \
- (L1SCHED_CHAN_IS_TCH(prim->chan) && prim->payload_len == GSM_MACBLOCK_LEN)
-
-struct l1sched_ts_prim *l1sched_prim_dequeue(struct llist_head *queue,
- uint32_t fn, struct l1sched_lchan_state *lchan);
-int l1sched_prim_dummy(struct l1sched_lchan_state *lchan);
-void l1sched_lchan_prim_drop(struct l1sched_lchan_state *lchan);
-void l1sched_prim_flush_queue(struct llist_head *list);
-
int l1sched_handle_rx_burst(struct l1sched_state *sched,
struct l1sched_burst_ind *bi);
int l1sched_handle_rx_probe(struct l1sched_state *sched,
struct l1sched_probe *probe);
+int l1sched_handle_burst_req(struct l1sched_state *sched,
+ const struct l1sched_burst_req *br);

/* Shared declarations for lchan handlers */
extern const uint8_t l1sched_nb_training_bits[8][26];
@@ -521,17 +441,3 @@

void l1sched_pull_burst(struct l1sched_state *sched, struct l1sched_burst_req *br);
void l1sched_pull_send_frame(struct l1sched_state *sched);
-
-/* External L1 API, must be implemented by the API user */
-int l1sched_handle_config_req(struct l1sched_state *sched,
- const struct l1sched_config_req *cr);
-int l1sched_handle_burst_req(struct l1sched_state *sched,
- const struct l1sched_burst_req *br);
-
-/* External L2 API, must be implemented by the API user */
-int l1sched_handle_data_ind(struct l1sched_lchan_state *lchan,
- const uint8_t *data, size_t data_len,
- int n_errors, int n_bits_total,
- enum l1sched_data_type dt);
-int l1sched_handle_data_cnf(struct l1sched_lchan_state *lchan,
- uint32_t fn, enum l1sched_data_type dt);
diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/prim.h b/src/host/trxcon/include/osmocom/bb/l1sched/prim.h
new file mode 100644
index 0000000..eb96b3b
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/l1sched/prim.h
@@ -0,0 +1,126 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/prim.h>
+#include <osmocom/core/utils.h>
+
+#define l1sched_prim_from_msgb(msg) \
+ ((struct l1sched_prim *)(msg)->l1h)
+
+#define l1sched_prim_data_from_msgb(msg) \
+ ((uint8_t *)msgb_l2(msg))
+
+#define l1sched_prim_type_from_msgb(msg) \
+ l1sched_prim_from_msgb(msg)->oph.primitive
+
+#define L1SCHED_PRIM_STR_FMT "%s.%s"
+#define L1SCHED_PRIM_STR_ARGS(prim) \
+ l1sched_prim_type_name((prim)->oph.primitive), \
+ osmo_prim_operation_name((prim)->oph.operation)
+
+enum l1sched_prim_type {
+ L1SCHED_PRIM_T_DATA, /* Req | Ind | Cnf */
+ L1SCHED_PRIM_T_RACH, /* Req | Cnf */
+ L1SCHED_PRIM_T_SCH, /* Ind */
+ L1SCHED_PRIM_T_PCHAN_COMB, /* Ind */
+};
+
+extern const struct value_string l1sched_prim_type_names[];
+static inline const char *l1sched_prim_type_name(enum l1sched_prim_type val)
+{
+ return get_value_string(l1sched_prim_type_names, val);
+}
+
+/*! Common header for L1SCHED_PRIM_T_{DATA,RACH} */
+struct l1sched_prim_chdr {
+ /*! TDMA Frame Number */
+ uint32_t frame_nr;
+ /*! RSL Channel Number */
+ uint8_t chan_nr;
+ /*! RSL Link Identifier */
+ uint8_t link_id;
+ /*! Traffic or signalling */
+ bool traffic;
+};
+
+/*! Payload of L1SCHED_PRIM_T_DATA | Ind */
+struct l1sched_prim_data_ind {
+ /*! Common sub-header */
+ struct l1sched_prim_chdr chdr;
+ int16_t toa256;
+ int8_t rssi;
+ int n_errors;
+ int n_bits_total;
+};
+
+/*! Payload of L1SCHED_PRIM_T_RACH | {Req,Cnf} */
+struct l1sched_prim_rach {
+ /*! Common sub-header */
+ struct l1sched_prim_chdr chdr;
+ /*! Training Sequence (only for 11-bit RA) */
+ uint8_t synch_seq;
+ /*! Transmission offset (how many frames to skip) */
+ uint8_t offset;
+ /*! RA value is 11 bit */
+ bool is_11bit;
+ /*! RA value */
+ uint16_t ra;
+};
+
+struct l1sched_prim {
+ /*! Primitive header */
+ struct osmo_prim_hdr oph;
+ /*! Type specific header */
+ union {
+ /*! L1SCHED_PRIM_T_DATA | Req */
+ struct l1sched_prim_chdr data_req;
+ /*! L1SCHED_PRIM_T_DATA | Cnf */
+ struct l1sched_prim_chdr data_cnf;
+ /*! L1SCHED_PRIM_T_DATA | Ind */
+ struct l1sched_prim_data_ind data_ind;
+
+ /*! L1SCHED_PRIM_T_RACH | Req */
+ struct l1sched_prim_rach rach_req;
+ /*! L1SCHED_PRIM_T_RACH | Cnf */
+ struct l1sched_prim_rach rach_cnf;
+
+ /*! L1SCHED_PRIM_T_SCH | Ind */
+ struct {
+ /*! TDMA frame number */
+ uint32_t frame_nr;
+ /*! BSIC */
+ uint8_t bsic;
+ } sch_ind;
+
+ /*! L1SCHED_PRIM_T_PCHAN_COMB | Ind */
+ struct {
+ /*! Timeslot number */
+ uint8_t tn;
+ /*! Channel combination for a timeslot */
+ enum gsm_phys_chan_config pchan;
+ } pchan_comb_ind;
+ };
+};
+
+
+struct l1sched_state;
+struct l1sched_lchan_state;
+
+struct msgb *l1sched_prim_alloc(enum l1sched_prim_type type,
+ enum osmo_prim_operation op,
+ size_t extra_size);
+
+struct msgb *l1sched_lchan_prim_dequeue(struct l1sched_lchan_state *lchan, uint32_t fn);
+void l1sched_lchan_prim_assign_dummy(struct l1sched_lchan_state *lchan);
+void l1sched_lchan_prim_drop(struct l1sched_lchan_state *lchan);
+
+int l1sched_lchan_emit_data_ind(struct l1sched_lchan_state *lchan,
+ const uint8_t *data, size_t data_len,
+ int n_errors, int n_bits_total, bool traffic);
+int l1sched_lchan_emit_data_cnf(struct l1sched_lchan_state *lchan, uint32_t fn);
+
+int l1sched_prim_from_user(struct l1sched_state *sched, struct msgb *msg);
+int l1sched_prim_to_user(struct l1sched_state *sched, struct msgb *msg);
diff --git a/src/host/trxcon/src/sched_lchan_pdtch.c b/src/host/trxcon/src/sched_lchan_pdtch.c
index 722800f..6702d9d 100644
--- a/src/host/trxcon/src/sched_lchan_pdtch.c
+++ b/src/host/trxcon/src/sched_lchan_pdtch.c
@@ -96,7 +96,7 @@
l2_len = rc > 0 ? rc : 0;

/* Send a L2 frame to the higher layers */
- l1sched_handle_data_ind(lchan, l2, l2_len, n_errors, n_bits_total, L1SCHED_DT_PACKET_DATA);
+ l1sched_lchan_emit_data_ind(lchan, l2, l2_len, n_errors, n_bits_total, true);

return 0;
}
@@ -123,12 +123,10 @@
}

/* Encode payload */
- rc = gsm0503_pdtch_encode(buffer, lchan->prim->payload,
- lchan->prim->payload_len);
+ rc = gsm0503_pdtch_encode(buffer, msgb_l2(lchan->prim), msgb_l2len(lchan->prim));
if (rc < 0) {
- LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n",
- lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
- lchan->prim->payload_len));
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n",
+ msgb_l2len(lchan->prim), msgb_hexdump_l2(lchan->prim));
l1sched_lchan_prim_drop(lchan);
return -EINVAL;
}
@@ -155,11 +153,8 @@

/* If we have sent the last (4/4) burst */
if ((*mask & 0x0f) == 0x0f) {
- /* Confirm data / traffic sending */
- l1sched_handle_data_cnf(lchan, br->fn, L1SCHED_DT_PACKET_DATA);
-
- /* Forget processed primitive */
- l1sched_lchan_prim_drop(lchan);
+ /* Confirm data / traffic sending (pass ownership of the prim) */
+ l1sched_lchan_emit_data_cnf(lchan, br->fn);

/* Reset mask */
*mask = 0x00;
diff --git a/src/host/trxcon/src/sched_lchan_rach.c b/src/host/trxcon/src/sched_lchan_rach.c
index 7448d83..109d100 100644
--- a/src/host/trxcon/src/sched_lchan_rach.c
+++ b/src/host/trxcon/src/sched_lchan_rach.c
@@ -73,56 +73,37 @@
struct l1sched_burst_req *br)
{
const uint8_t bsic = lchan->ts->sched->bsic;
- struct l1sched_ts_prim_rach *rach;
+ struct l1sched_prim *prim;
uint8_t *burst_ptr = br->burst;
uint8_t payload[36];
int i, rc;

- rach = (struct l1sched_ts_prim_rach *)lchan->prim->payload;
+ prim = l1sched_prim_from_msgb(lchan->prim);

/* Delay sending according to offset value */
- if (rach->offset-- > 0)
+ if (prim->rach_req.offset-- > 0)
return 0;

- if (L1SCHED_PRIM_IS_RACH11(lchan->prim)) {
- /* Check requested synch. sequence */
- if (rach->synch_seq >= RACH_SYNCH_SEQ_NUM) {
- LOGP_LCHAND(lchan, LOGL_ERROR,
- "Unknown RACH synch. sequence=0x%02x\n",
- rach->synch_seq);
- l1sched_lchan_prim_drop(lchan);
- return -ENOTSUP;
- }
-
- /* Encode 11-bit payload */
- rc = gsm0503_rach_ext_encode(payload, rach->ra, bsic, true);
- if (rc) {
- LOGP_LCHAND(lchan, LOGL_ERROR,
- "Could not encode 11-bit RACH burst (ra=%u bsic=%u)\n",
- rach->ra, bsic);
- l1sched_lchan_prim_drop(lchan);
- return rc;
- }
- } else if (L1SCHED_PRIM_IS_RACH8(lchan->prim)) {
- rach->synch_seq = RACH_SYNCH_SEQ_TS0;
-
- /* Encode 8-bit payload */
- rc = gsm0503_rach_ext_encode(payload, rach->ra, bsic, false);
- if (rc) {
- LOGP_LCHAND(lchan, LOGL_ERROR,
- "Could not encode RACH burst (ra=%u bsic=%u)\n",
- rach->ra, bsic);
- l1sched_lchan_prim_drop(lchan);
- return rc;
- }
- } else {
+ /* Check requested synch. sequence */
+ if (prim->rach_req.synch_seq >= RACH_SYNCH_SEQ_NUM) {
LOGP_LCHAND(lchan, LOGL_ERROR,
- "Primitive has unexpected type=0x%02x\n",
- lchan->prim->type);
+ "Unknown RACH synch. sequence=0x%02x\n",
+ prim->rach_req.synch_seq);
l1sched_lchan_prim_drop(lchan);
- return -EINVAL;
+ return -ENOTSUP;
}

+ /* Encode the payload */
+ rc = gsm0503_rach_ext_encode(payload, prim->rach_req.ra,
+ bsic, prim->rach_req.is_11bit);
+ if (rc) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Could not encode %s-bit RACH burst (ra=%u bsic=%u)\n",
+ prim->rach_req.is_11bit ? "11" : "8",
+ prim->rach_req.ra, bsic);
+ l1sched_lchan_prim_drop(lchan);
+ return rc;
+ }

/* BN0-7: extended tail bits */
memcpy(burst_ptr, rach_ext_tail_bits, RACH_EXT_TAIL_BITS_LEN);
@@ -130,7 +111,7 @@

/* BN8-48: chosen synch. (training) sequence */
for (i = 0; i < RACH_SYNCH_SEQ_LEN; i++)
- *(burst_ptr++) = rach_synch_seq_bits[rach->synch_seq][i] == '1';
+ *(burst_ptr++) = rach_synch_seq_bits[prim->rach_req.synch_seq][i] == '1';

/* BN49-84: encrypted bits (the payload) */
memcpy(burst_ptr, payload, RACH_PAYLOAD_LEN);
@@ -141,14 +122,11 @@
br->burst_len = GSM_NBITS_NB_GMSK_BURST;

LOGP_LCHAND(lchan, LOGL_NOTICE, "Scheduled %s-bit RACH (%s) at fn=%u\n",
- L1SCHED_PRIM_IS_RACH11(lchan->prim) ? "11" : "8",
- get_value_string(rach_synch_seq_names, rach->synch_seq), br->fn);
+ prim->rach_req.is_11bit ? "11" : "8",
+ get_value_string(rach_synch_seq_names, prim->rach_req.synch_seq), br->fn);

- /* Confirm RACH request */
- l1sched_handle_data_cnf(lchan, br->fn, L1SCHED_DT_OTHER);
-
- /* Forget processed primitive */
- l1sched_lchan_prim_drop(lchan);
+ /* Confirm RACH request (pass ownership of the prim) */
+ l1sched_lchan_emit_data_cnf(lchan, br->fn);

return 0;
}
diff --git a/src/host/trxcon/src/sched_lchan_sch.c b/src/host/trxcon/src/sched_lchan_sch.c
index de665cb..f433de6 100644
--- a/src/host/trxcon/src/sched_lchan_sch.c
+++ b/src/host/trxcon/src/sched_lchan_sch.c
@@ -61,6 +61,21 @@
time->fn = gsm_gsmtime2fn(time);
}

+static int handle_sch_ind(struct l1sched_state *sched, uint32_t fn, uint8_t bsic)
+{
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_SCH, PRIM_OP_INDICATION, 0);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->sch_ind.frame_nr = fn;
+ prim->sch_ind.bsic = bsic;
+
+ return l1sched_prim_to_user(sched, msg);
+}
+
int rx_sch_fn(struct l1sched_lchan_state *lchan,
const struct l1sched_burst_ind *bi)
{
@@ -100,8 +115,5 @@
/* Update BSIC value in the scheduler state */
lchan->ts->sched->bsic = bsic;

- l1sched_handle_data_ind(lchan, (const uint8_t *)&time, sizeof(time),
- 0, 39 * 2, L1SCHED_DT_OTHER);
-
- return 0;
+ return handle_sch_ind(lchan->ts->sched, time.fn, bsic);
}
diff --git a/src/host/trxcon/src/sched_lchan_tchf.c b/src/host/trxcon/src/sched_lchan_tchf.c
index 8cb996b..326713d 100644
--- a/src/host/trxcon/src/sched_lchan_tchf.c
+++ b/src/host/trxcon/src/sched_lchan_tchf.c
@@ -171,9 +171,8 @@
goto bfi;
} else if (rc == GSM_MACBLOCK_LEN) {
/* FACCH received, forward it to the higher layers */
- l1sched_handle_data_ind(lchan, l2 + amr, GSM_MACBLOCK_LEN,
- n_errors, n_bits_total,
- L1SCHED_DT_SIGNALING);
+ l1sched_lchan_emit_data_ind(lchan, l2 + amr, GSM_MACBLOCK_LEN,
+ n_errors, n_bits_total, false);

/* Send BFI substituting a stolen TCH frame */
n_errors = -1; /* ensure fake measurements */
@@ -184,7 +183,8 @@
}

/* Send a traffic frame to the higher layers */
- return l1sched_handle_data_ind(lchan, l2, l2_len, n_errors, n_bits_total, L1SCHED_DT_TRAFFIC);
+ return l1sched_lchan_emit_data_ind(lchan, l2, l2_len,
+ n_errors, n_bits_total, true);

bfi:
/* Didn't try to decode, fake measurements */
@@ -201,18 +201,16 @@

/* BFI is not applicable in signalling mode */
if (lchan->tch_mode == GSM48_CMODE_SIGN) {
- return l1sched_handle_data_ind(lchan, NULL, 0,
- n_errors, n_bits_total,
- L1SCHED_DT_TRAFFIC);
+ return l1sched_lchan_emit_data_ind(lchan, NULL, 0,
+ n_errors, n_bits_total, false);
}

/* Bad frame indication */
l2_len = l1sched_bad_frame_ind(l2, lchan);

/* Send a BFI frame to the higher layers */
- return l1sched_handle_data_ind(lchan, l2, l2_len,
- n_errors, n_bits_total,
- L1SCHED_DT_TRAFFIC);
+ return l1sched_lchan_emit_data_ind(lchan, l2, l2_len,
+ n_errors, n_bits_total, true);
}

int tx_tchf_fn(struct l1sched_lchan_state *lchan,
@@ -240,9 +238,9 @@
memcpy(buffer, buffer + 464, 464);

/* populate the buffer with bursts */
- if (L1SCHED_PRIM_IS_FACCH(lchan->prim)) {
+ if (msgb_l2len(lchan->prim) == GSM_MACBLOCK_LEN) {
/* Encode payload */
- rc = gsm0503_tch_fr_encode(buffer, lchan->prim->payload, GSM_MACBLOCK_LEN, 1);
+ rc = gsm0503_tch_fr_encode(buffer, msgb_l2(lchan->prim), GSM_MACBLOCK_LEN, 1);
} else if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
int len;
uint8_t cmr_codec;
@@ -256,12 +254,11 @@
*/
amr_fn_is_cmr = !sched_tchf_ul_amr_cmi_map[br->fn % 26];

- len = osmo_amr_rtp_dec(lchan->prim->payload, lchan->prim->payload_len,
- &cmr_codec, &cmi, &ft_codec,
- &bfi, &sti);
+ len = osmo_amr_rtp_dec(msgb_l2(lchan->prim), msgb_l2len(lchan->prim),
+ &cmr_codec, &cmi, &ft_codec, &bfi, &sti);
if (len < 0) {
- LOGP_LCHAND(lchan, LOGL_ERROR, "Cannot send invalid AMR payload (%zu): %s\n",
- lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload, lchan->prim->payload_len));
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Cannot send invalid AMR payload (%u): %s\n",
+ msgb_l2len(lchan->prim), msgb_hexdump_l2(lchan->prim));
goto free_bad_msg;
}
ft = -1;
@@ -290,11 +287,14 @@
} else {
lchan->amr.ul_cmr = cmr;
}
- rc = gsm0503_tch_afs_encode(buffer, lchan->prim->payload + 2,
- lchan->prim->payload_len - 2, amr_fn_is_cmr,
- lchan->amr.codec, lchan->amr.codecs,
- lchan->amr.ul_ft,
- lchan->amr.ul_cmr);
+ rc = gsm0503_tch_afs_encode(buffer,
+ msgb_l2(lchan->prim) + 2,
+ msgb_l2len(lchan->prim) - 2,
+ amr_fn_is_cmr,
+ lchan->amr.codec,
+ lchan->amr.codecs,
+ lchan->amr.ul_ft,
+ lchan->amr.ul_cmr);
} else {
/* Determine and check the payload length */
switch (lchan->tch_mode) {
@@ -312,20 +312,19 @@
l1sched_lchan_prim_drop(lchan);
return -EINVAL;
}
- if (lchan->prim->payload_len != l2_len) {
- LOGP_LCHAND(lchan, LOGL_ERROR, "Primitive has odd length %zu "
+ if (msgb_l2len(lchan->prim) != l2_len) {
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Primitive has odd length %u "
"(expected %zu for TCH or %u for FACCH), so dropping...\n",
- lchan->prim->payload_len, l2_len, GSM_MACBLOCK_LEN);
+ msgb_l2len(lchan->prim), l2_len, GSM_MACBLOCK_LEN);
l1sched_lchan_prim_drop(lchan);
return -EINVAL;
}
- rc = gsm0503_tch_fr_encode(buffer, lchan->prim->payload, l2_len, 1);
+ rc = gsm0503_tch_fr_encode(buffer, msgb_l2(lchan->prim), l2_len, 1);
}

if (rc) {
- LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n",
- lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
- lchan->prim->payload_len));
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n",
+ msgb_l2len(lchan->prim), msgb_hexdump_l2(lchan->prim));
free_bad_msg:
l1sched_lchan_prim_drop(lchan);
return -EINVAL;
@@ -353,13 +352,8 @@

/* If we have sent the last (4/4) burst */
if (*mask == 0x0f) {
- /* Confirm data / traffic sending */
- enum l1sched_data_type dt = L1SCHED_PRIM_IS_TCH(lchan->prim) ?
- L1SCHED_DT_TRAFFIC : L1SCHED_DT_SIGNALING;
- l1sched_handle_data_cnf(lchan, br->fn, dt);
-
- /* Forget processed primitive */
- l1sched_lchan_prim_drop(lchan);
+ /* Confirm data / traffic sending (pass ownership of the prim) */
+ l1sched_lchan_emit_data_cnf(lchan, br->fn);

/* Reset mask */
*mask = 0x00;
diff --git a/src/host/trxcon/src/sched_lchan_tchh.c b/src/host/trxcon/src/sched_lchan_tchh.c
index 7302351..34781d3 100644
--- a/src/host/trxcon/src/sched_lchan_tchh.c
+++ b/src/host/trxcon/src/sched_lchan_tchh.c
@@ -366,9 +366,8 @@
l1sched_lchan_meas_avg(lchan, 6);

/* FACCH/H received, forward to the higher layers */
- l1sched_handle_data_ind(lchan, l2 + amr, GSM_MACBLOCK_LEN,
- n_errors, n_bits_total,
- L1SCHED_DT_SIGNALING);
+ l1sched_lchan_emit_data_ind(lchan, l2 + amr, GSM_MACBLOCK_LEN,
+ n_errors, n_bits_total, false);

/* Send BFI substituting 1/2 stolen TCH frames */
n_errors = -1; /* ensure fake measurements */
@@ -382,9 +381,8 @@
}

/* Send a traffic frame to the higher layers */
- return l1sched_handle_data_ind(lchan, l2, l2_len,
- n_errors, n_bits_total,
- L1SCHED_DT_TRAFFIC);
+ return l1sched_lchan_emit_data_ind(lchan, l2, l2_len,
+ n_errors, n_bits_total, true);

bfi_shift:
/* Shift buffer */
@@ -409,18 +407,16 @@

/* BFI is not applicable in signalling mode */
if (lchan->tch_mode == GSM48_CMODE_SIGN) {
- return l1sched_handle_data_ind(lchan, NULL, 0,
- n_errors, n_bits_total,
- L1SCHED_DT_SIGNALING);
+ return l1sched_lchan_emit_data_ind(lchan, NULL, 0,
+ n_errors, n_bits_total, false);
}

/* Bad frame indication */
l2_len = l1sched_bad_frame_ind(l2, lchan);

/* Send a BFI frame to the higher layers */
- return l1sched_handle_data_ind(lchan, l2, l2_len,
- n_errors, n_bits_total,
- L1SCHED_DT_TRAFFIC);
+ return l1sched_lchan_emit_data_ind(lchan, l2, l2_len,
+ n_errors, n_bits_total, true);
}

int tx_tchh_fn(struct l1sched_lchan_state *lchan,
@@ -463,8 +459,8 @@
}

/* populate the buffer with bursts */
- if (L1SCHED_PRIM_IS_FACCH(lchan->prim)) {
- rc = gsm0503_tch_hr_encode(buffer, lchan->prim->payload, lchan->prim->payload_len);
+ if (msgb_l2len(lchan->prim) == GSM_MACBLOCK_LEN) {
+ rc = gsm0503_tch_hr_encode(buffer, msgb_l2(lchan->prim), GSM_MACBLOCK_LEN);
lchan->ul_facch_blocks = 6;
} else if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
int len;
@@ -479,12 +475,11 @@
*/
amr_fn_is_cmr = !sched_tchh_ul_amr_cmi_map[br->fn % 26];

- len = osmo_amr_rtp_dec(lchan->prim->payload, lchan->prim->payload_len,
- &cmr_codec, &cmi, &ft_codec,
- &bfi, &sti);
+ len = osmo_amr_rtp_dec(msgb_l2(lchan->prim), msgb_l2len(lchan->prim),
+ &cmr_codec, &cmi, &ft_codec, &bfi, &sti);
if (len < 0) {
- LOGP_LCHAND(lchan, LOGL_ERROR, "Cannot send invalid AMR payload (%zu): %s\n",
- lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload, lchan->prim->payload_len));
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Cannot send invalid AMR payload (%u): %s\n",
+ msgb_l2len(lchan->prim), msgb_hexdump_l2(lchan->prim));
goto free_bad_msg;
}
ft = -1;
@@ -513,11 +508,14 @@
} else {
lchan->amr.ul_cmr = cmr;
}
- rc = gsm0503_tch_ahs_encode(buffer, lchan->prim->payload + 2,
- lchan->prim->payload_len - 2, amr_fn_is_cmr,
- lchan->amr.codec, lchan->amr.codecs,
- lchan->amr.ul_ft,
- lchan->amr.ul_cmr);
+ rc = gsm0503_tch_ahs_encode(buffer,
+ msgb_l2(lchan->prim) + 2,
+ msgb_l2len(lchan->prim) - 2,
+ amr_fn_is_cmr,
+ lchan->amr.codec,
+ lchan->amr.codecs,
+ lchan->amr.ul_ft,
+ lchan->amr.ul_cmr);
} else {
/* Determine and check the payload length */
switch (lchan->tch_mode) {
@@ -532,20 +530,19 @@
l1sched_lchan_prim_drop(lchan);
return -EINVAL;
}
- if (lchan->prim->payload_len != l2_len) {
- LOGP_LCHAND(lchan, LOGL_ERROR, "Primitive has odd length %zu "
+ if (msgb_l2len(lchan->prim) != l2_len) {
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Primitive has odd length %u "
"(expected %zu for TCH or %u for FACCH), so dropping...\n",
- lchan->prim->payload_len, l2_len, GSM_MACBLOCK_LEN);
+ msgb_l2len(lchan->prim), l2_len, GSM_MACBLOCK_LEN);
l1sched_lchan_prim_drop(lchan);
return -EINVAL;
}
- rc = gsm0503_tch_hr_encode(buffer, lchan->prim->payload, l2_len);
+ rc = gsm0503_tch_hr_encode(buffer, msgb_l2(lchan->prim), l2_len);
}

if (rc) {
- LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n",
- lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
- lchan->prim->payload_len));
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n",
+ msgb_l2len(lchan->prim), msgb_hexdump_l2(lchan->prim));
free_bad_msg:
l1sched_lchan_prim_drop(lchan);
return -EINVAL;
@@ -576,18 +573,11 @@
lchan->ul_facch_blocks--;

if ((*mask & 0x0f) == 0x0f) {
- /**
- * If no more FACCH/H blocks pending,
- * confirm data / traffic sending
- */
- if (!lchan->ul_facch_blocks) {
- enum l1sched_data_type dt = L1SCHED_PRIM_IS_TCH(lchan->prim) ?
- L1SCHED_DT_TRAFFIC : L1SCHED_DT_SIGNALING;
- l1sched_handle_data_cnf(lchan, br->fn, dt);
- }
-
- /* Forget processed primitive */
- l1sched_lchan_prim_drop(lchan);
+ /* Confirm data / traffic sending (pass ownership of the prim) */
+ if (!lchan->ul_facch_blocks)
+ l1sched_lchan_emit_data_cnf(lchan, br->fn);
+ else /* do not confirm dropped prims */
+ l1sched_lchan_prim_drop(lchan);
}

return 0;
diff --git a/src/host/trxcon/src/sched_lchan_xcch.c b/src/host/trxcon/src/sched_lchan_xcch.c
index 936a899..0dca098 100644
--- a/src/host/trxcon/src/sched_lchan_xcch.c
+++ b/src/host/trxcon/src/sched_lchan_xcch.c
@@ -94,9 +94,8 @@
}

/* Send a L2 frame to the higher layers */
- return l1sched_handle_data_ind(lchan, l2, rc ? 0 : GSM_MACBLOCK_LEN,
- n_errors, n_bits_total,
- L1SCHED_DT_SIGNALING);
+ return l1sched_lchan_emit_data_ind(lchan, l2, rc ? 0 : GSM_MACBLOCK_LEN,
+ n_errors, n_bits_total, false);
}

int tx_data_fn(struct l1sched_lchan_state *lchan,
@@ -120,20 +119,19 @@
}

/* Check the prim payload length */
- if (lchan->prim->payload_len != GSM_MACBLOCK_LEN) {
+ if (msgb_l2len(lchan->prim) != GSM_MACBLOCK_LEN) {
LOGP_LCHAND(lchan, LOGL_ERROR,
- "Primitive has odd length %zu (expected %u), so dropping...\n",
- lchan->prim->payload_len, GSM_MACBLOCK_LEN);
+ "Primitive has odd length %u (expected %u), so dropping...\n",
+ msgb_l2len(lchan->prim), GSM_MACBLOCK_LEN);
l1sched_lchan_prim_drop(lchan);
return -EINVAL;
}

/* Encode payload */
- rc = gsm0503_xcch_encode(buffer, lchan->prim->payload);
+ rc = gsm0503_xcch_encode(buffer, msgb_l2(lchan->prim));
if (rc) {
- LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n",
- lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
- lchan->prim->payload_len));
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n",
+ msgb_l2len(lchan->prim), msgb_hexdump_l2(lchan->prim));
l1sched_lchan_prim_drop(lchan);
return -EINVAL;
}
@@ -160,11 +158,8 @@

/* If we have sent the last (4/4) burst */
if ((*mask & 0x0f) == 0x0f) {
- /* Confirm data sending */
- l1sched_handle_data_cnf(lchan, br->fn, L1SCHED_DT_SIGNALING);
-
- /* Forget processed primitive */
- l1sched_lchan_prim_drop(lchan);
+ /* Confirm data sending (pass ownership of the prim) */
+ l1sched_lchan_emit_data_cnf(lchan, br->fn);

/* Reset mask */
*mask = 0x00;
diff --git a/src/host/trxcon/src/sched_prim.c b/src/host/trxcon/src/sched_prim.c
index b7ebdeb..8602097 100644
--- a/src/host/trxcon/src/sched_prim.c
+++ b/src/host/trxcon/src/sched_prim.c
@@ -3,7 +3,7 @@
* TDMA scheduler: primitive management
*
* (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
- * Contributions by sysmocom - s.f.m.c. GmbH
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
@@ -25,6 +25,8 @@
#include <talloc.h>

#include <osmocom/core/msgb.h>
+#include <osmocom/core/prim.h>
+#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/linuxlist.h>

@@ -33,86 +35,37 @@
#include <osmocom/bb/l1sched/l1sched.h>
#include <osmocom/bb/l1sched/logging.h>

-/**
- * Initializes a new primitive by allocating memory
- * and filling some meta-information (e.g. lchan type).
- *
- * @param ctx parent talloc context
- * @param pl_len prim payload length
- * @param type prim payload type
- * @param chan_nr RSL channel description (used to set a proper chan)
- * @param link_id RSL link description (used to set a proper chan)
- * @return allocated primitive or NULL
- */
-static struct l1sched_ts_prim *prim_alloc(void *ctx, size_t pl_len,
- enum l1sched_ts_prim_type type,
- uint8_t chan_nr, uint8_t link_id)
+#define L1SCHED_PRIM_HEADROOM 128
+
+osmo_static_assert(sizeof(struct l1sched_prim) <= L1SCHED_PRIM_HEADROOM, l1sched_prim_size);
+
+const struct value_string l1sched_prim_type_names[] = {
+ { L1SCHED_PRIM_T_DATA, "DATA" },
+ { L1SCHED_PRIM_T_RACH, "RACH" },
+ { L1SCHED_PRIM_T_SCH, "SCH" },
+ { L1SCHED_PRIM_T_PCHAN_COMB, "PCHAN_COMB" },
+ { 0, NULL },
+};
+
+struct msgb *l1sched_prim_alloc(enum l1sched_prim_type type,
+ enum osmo_prim_operation op,
+ size_t extra_size)
{
- enum l1sched_lchan_type lchan_type;
- struct l1sched_ts_prim *prim;
+ struct l1sched_prim *prim;
+ struct msgb *msg;

- /* Determine lchan type */
- lchan_type = l1sched_chan_nr2lchan_type(chan_nr, link_id);
- if (!lchan_type) {
- /* TODO: use proper logging context */
- LOGP(DLGLOBAL, LOGL_ERROR, "Couldn't determine lchan type "
- "for chan_nr=%02x and link_id=%02x\n", chan_nr, link_id);
- return NULL;
- }
-
- /* Allocate a new primitive */
- prim = talloc_zero_size(ctx, sizeof(*prim) + pl_len);
- if (prim == NULL)
+ msg = msgb_alloc_headroom(L1SCHED_PRIM_HEADROOM + extra_size,
+ L1SCHED_PRIM_HEADROOM, "l1sched_prim");
+ if (msg == NULL)
return NULL;

- /* Init primitive header */
- prim->payload_len = pl_len;
- prim->chan = lchan_type;
- prim->type = type;
+ msg->l2h = msg->data;
+ msg->l1h = msgb_push(msg, sizeof(*prim));

- return prim;
-}
+ prim = l1sched_prim_from_msgb(msg);
+ osmo_prim_init(&prim->oph, 0, type, op, msg);

-/**
- * Adds a primitive to the end of transmit queue of a particular
- * timeslot, whose index is parsed from chan_nr.
- *
- * @param sched scheduler instance
- * @param chan_nr RSL channel description
- * @param link_id RSL link description
- * @param pl Payload data
- * @param pl_len Payload length
- * @return queued primitive or NULL
- */
-struct l1sched_ts_prim *l1sched_prim_push(struct l1sched_state *sched,
- enum l1sched_ts_prim_type type,
- uint8_t chan_nr, uint8_t link_id,
- const uint8_t *pl, size_t pl_len)
-{
- struct l1sched_ts_prim *prim;
- struct l1sched_ts *ts;
- uint8_t tn;
-
- /* Determine TS index */
- tn = chan_nr & 0x7;
-
- /* Check whether required timeslot is allocated and configured */
- ts = sched->ts[tn];
- if (ts == NULL || ts->mf_layout == NULL) {
- LOGP_SCHEDC(sched, LOGL_ERROR, "Timeslot %u isn't configured\n", tn);
- return NULL;
- }
-
- prim = prim_alloc(ts, pl_len, type, chan_nr, link_id);
- if (prim == NULL)
- return NULL;
-
- memcpy(&prim->payload[0], pl, pl_len);
-
- /* Add primitive to TS transmit queue */
- llist_add_tail(&prim->list, &ts->tx_prims);
-
- return prim;
+ return msg;
}

/**
@@ -121,16 +74,21 @@
* @param lchan lchan to assign a primitive
* @return SACCH primitive to be transmitted
*/
-static struct l1sched_ts_prim *prim_compose_mr(struct l1sched_lchan_state *lchan)
+static struct msgb *prim_compose_mr(struct l1sched_lchan_state *lchan)
{
- struct l1sched_ts_prim *prim;
+ struct l1sched_prim *prim;
+ struct msgb *msg;
bool cached;

/* Allocate a new primitive */
- prim = prim_alloc(lchan, GSM_MACBLOCK_LEN, L1SCHED_PRIM_DATA,
- l1sched_lchan_desc[lchan->type].chan_nr,
- L1SCHED_CH_LID_SACCH);
- OSMO_ASSERT(prim != NULL);
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST, GSM_MACBLOCK_LEN);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->data_req = (struct l1sched_prim_chdr) {
+ .chan_nr = l1sched_lchan_desc[lchan->type].chan_nr,
+ .chan_nr = L1SCHED_CH_LID_SACCH,
+ };

/* Check if the MR cache is populated (verify LAPDm header) */
cached = (lchan->sacch.mr_cache[2] != 0x00
@@ -143,7 +101,9 @@
}

/* Compose a new Measurement Report primitive */
- memcpy(&prim->payload[0], &lchan->sacch.mr_cache[0], GSM_MACBLOCK_LEN);
+ memcpy(msgb_put(msg, GSM_MACBLOCK_LEN),
+ &lchan->sacch.mr_cache[0],
+ GSM_MACBLOCK_LEN);

/* Inform about the cache usage count */
if (++lchan->sacch.mr_cache_usage > 5) {
@@ -155,7 +115,7 @@

LOGP_LCHAND(lchan, LOGL_NOTICE, "Using cached Measurement Report\n");

- return prim;
+ return msg;
}

/**
@@ -180,140 +140,101 @@
* between two successive measurement result messages
* shall not exceed one L2 frame.
*
- * @param queue transmit queue to take a prim from
* @param lchan lchan to assign a primitive
* @return SACCH primitive to be transmitted
*/
-static struct l1sched_ts_prim *prim_dequeue_sacch(struct llist_head *queue,
- struct l1sched_lchan_state *lchan)
+static struct msgb *prim_dequeue_sacch(struct l1sched_lchan_state *lchan)
{
- struct l1sched_ts_prim *prim_nmr = NULL;
- struct l1sched_ts_prim *prim_mr = NULL;
- struct l1sched_ts_prim *prim;
+ struct msgb *msg_nmr = NULL;
+ struct msgb *msg_mr = NULL;
+ struct msgb *msg;
bool mr_now;

/* Shall we transmit MR now? */
mr_now = !lchan->sacch.mr_tx_last;

-#define PRIM_IS_MR(prim) \
- (prim->payload[5] == GSM48_PDISC_RR \
- && prim->payload[6] == GSM48_MT_RR_MEAS_REP)
+#define PRIM_MSGB_IS_MR(msg) \
+ (l1sched_prim_data_from_msgb(msg)[5] == GSM48_PDISC_RR && \
+ l1sched_prim_data_from_msgb(msg)[6] == GSM48_MT_RR_MEAS_REP)

/* Iterate over all primitives in the queue */
- llist_for_each_entry(prim, queue, list) {
- /* We are looking for particular channel */
- if (prim->chan != lchan->type)
- continue;
-
+ llist_for_each_entry(msg, &lchan->tx_prims, list) {
/* Look for a Measurement Report */
- if (!prim_mr && PRIM_IS_MR(prim))
- prim_mr = prim;
+ if (!msg_mr && PRIM_MSGB_IS_MR(msg))
+ msg_mr = msg;

/* Look for anything else */
- if (!prim_nmr && !PRIM_IS_MR(prim))
- prim_nmr = prim;
+ if (!msg_nmr && !PRIM_MSGB_IS_MR(msg))
+ msg_nmr = msg;

/* Should we look further? */
- if (mr_now && prim_mr)
+ if (mr_now && msg_mr)
break; /* MR was found */
- else if (!mr_now && prim_nmr)
+ else if (!mr_now && msg_nmr)
break; /* something else was found */
}

LOGP_LCHAND(lchan, LOGL_DEBUG,
- "SACCH MR selection: mr_tx_last=%d prim_mr=%p prim_nmr=%p\n",
- lchan->sacch.mr_tx_last, prim_mr, prim_nmr);
+ "SACCH MR selection: mr_tx_last=%d msg_mr=%p msg_nmr=%p\n",
+ lchan->sacch.mr_tx_last, msg_mr, msg_nmr);

/* Prioritize non-MR prim if possible */
- if (mr_now && prim_mr)
- prim = prim_mr;
- else if (!mr_now && prim_nmr)
- prim = prim_nmr;
- else if (!mr_now && prim_mr)
- prim = prim_mr;
+ if (mr_now && msg_mr)
+ msg = msg_mr;
+ else if (!mr_now && msg_nmr)
+ msg = msg_nmr;
+ else if (!mr_now && msg_mr)
+ msg = msg_mr;
else /* Nothing was found */
- prim = NULL;
+ msg = NULL;

/* Have we found what we were looking for? */
- if (prim) /* Dequeue if so */
- llist_del(&prim->list);
+ if (msg) /* Dequeue if so */
+ llist_del(&msg->list);
else /* Otherwise compose a new MR */
- prim = prim_compose_mr(lchan);
+ msg = prim_compose_mr(lchan);

/* Update the cached report */
- if (prim == prim_mr) {
- memcpy(lchan->sacch.mr_cache,
- prim->payload, GSM_MACBLOCK_LEN);
+ if (msg == msg_mr) {
+ memcpy(lchan->sacch.mr_cache, msgb_l2(msg), GSM_MACBLOCK_LEN);
lchan->sacch.mr_cache_usage = 0;

LOGP_LCHAND(lchan, LOGL_DEBUG, "SACCH MR cache has been updated\n");
}

/* Update the MR transmission state */
- lchan->sacch.mr_tx_last = PRIM_IS_MR(prim);
+ lchan->sacch.mr_tx_last = PRIM_MSGB_IS_MR(msg);

LOGP_LCHAND(lchan, LOGL_DEBUG, "SACCH decision: %s\n",
- PRIM_IS_MR(prim) ? "Measurement Report" : "data frame");
+ PRIM_MSGB_IS_MR(msg) ? "Measurement Report" : "data frame");

- return prim;
-}
-
-/* Dequeues a primitive of a given channel type */
-static struct l1sched_ts_prim *prim_dequeue_one(struct llist_head *queue,
- enum l1sched_lchan_type lchan_type)
-{
- struct l1sched_ts_prim *prim;
-
- /**
- * There is no need to use the 'safe' list iteration here
- * as an item removal is immediately followed by return.
- */
- llist_for_each_entry(prim, queue, list) {
- if (prim->chan == lchan_type) {
- llist_del(&prim->list);
- return prim;
- }
- }
-
- return NULL;
+ return msg;
}

/**
* Dequeues either a FACCH, or a speech TCH primitive
* of a given channel type (Lm or Bm).
*
- * Note: we could avoid 'lchan_type' parameter and just
- * check the prim's channel type using L1SCHED_CHAN_IS_TCH(),
- * but the current approach is a bit more flexible,
- * and allows one to have both sub-slots of TCH/H
- * enabled on same timeslot e.g. for testing...
- *
- * @param queue transmit queue to take a prim from
- * @param lchan_type required channel type of a primitive,
- * e.g. L1SCHED_TCHF, L1SCHED_TCHH_0, or L1SCHED_TCHH_1
+ * @param lchan logical channel state
* @param facch FACCH (true) or speech (false) prim?
* @return either a FACCH, or a TCH primitive if found,
* otherwise NULL
*/
-static struct l1sched_ts_prim *prim_dequeue_tch(struct llist_head *queue,
- enum l1sched_lchan_type lchan_type, bool facch)
+static struct msgb *prim_dequeue_tch(struct l1sched_lchan_state *lchan, bool facch)
{
- struct l1sched_ts_prim *prim;
+ struct msgb *msg;

/**
* There is no need to use the 'safe' list iteration here
* as an item removal is immediately followed by return.
*/
- llist_for_each_entry(prim, queue, list) {
- if (prim->chan != lchan_type)
+ llist_for_each_entry(msg, &lchan->tx_prims, list) {
+ bool is_facch = msgb_l2len(msg) == GSM_MACBLOCK_LEN;
+ if (is_facch != facch)
continue;

- /* Either FACCH, or not FACCH */
- if (L1SCHED_PRIM_IS_FACCH(prim) != facch)
- continue;
-
- llist_del(&prim->list);
- return prim;
+ llist_del(&msg->list);
+ return msg;
}

return NULL;
@@ -324,30 +245,30 @@
* If a FACCH/F prim is found, one TCH/F prim is being
* dropped (i.e. replaced).
*
- * @param queue a transmit queue to take a prim from
+ * @param lchan logical channel state
* @return either a FACCH/F, or a TCH/F primitive,
* otherwise NULL
*/
-static struct l1sched_ts_prim *prim_dequeue_tch_f(struct llist_head *queue)
+static struct msgb *prim_dequeue_tch_f(struct l1sched_lchan_state *lchan)
{
- struct l1sched_ts_prim *facch;
- struct l1sched_ts_prim *tch;
+ struct msgb *facch;
+ struct msgb *tch;

/* Attempt to find a pair of both FACCH/F and TCH/F frames */
- facch = prim_dequeue_tch(queue, L1SCHED_TCHF, true);
- tch = prim_dequeue_tch(queue, L1SCHED_TCHF, false);
+ facch = prim_dequeue_tch(lchan, true);
+ tch = prim_dequeue_tch(lchan, false);

/* Prioritize FACCH/F, if found */
if (facch) {
/* One TCH/F prim is replaced */
if (tch)
- talloc_free(tch);
+ msgb_free(tch);
return facch;
} else if (tch) {
/* Only TCH/F prim was found */
return tch;
} else {
- /* Nothing was found, e.g. when only SACCH frames are in queue */
+ /* Nothing was found */
return NULL;
}
}
@@ -368,96 +289,95 @@
*
* where the numbers within brackets are fn % 26.
*
- * @param queue transmit queue to take a prim from
+ * @param lchan logical channel state
* @param fn the current frame number
- * @param lchan_type required channel type of a primitive,
* @return either a FACCH/H, or a TCH/H primitive,
* otherwise NULL
*/
-static struct l1sched_ts_prim *prim_dequeue_tch_h(struct llist_head *queue,
- uint32_t fn, enum l1sched_lchan_type lchan_type)
+static struct msgb *prim_dequeue_tch_h(struct l1sched_lchan_state *lchan, uint32_t fn)
{
- struct l1sched_ts_prim *facch;
- struct l1sched_ts_prim *tch;
+ struct msgb *facch;
+ struct msgb *tch;
bool facch_now;

/* May we initiate an UL FACCH/H frame transmission now? */
- facch_now = l1sched_tchh_facch_start(lchan_type, fn, true);
+ facch_now = l1sched_tchh_facch_start(lchan->type, fn, true);
if (!facch_now) /* Just dequeue a TCH/H prim */
goto no_facch;

/* If there are no FACCH/H prims in the queue */
- facch = prim_dequeue_tch(queue, lchan_type, true);
+ facch = prim_dequeue_tch(lchan, true);
if (!facch) /* Just dequeue a TCH/H prim */
goto no_facch;

/* FACCH/H prim replaces two TCH/F prims */
- tch = prim_dequeue_tch(queue, lchan_type, false);
+ tch = prim_dequeue_tch(lchan, false);
if (tch) {
/* At least one TCH/H prim is dropped */
- talloc_free(tch);
+ msgb_free(tch);

/* Attempt to find another */
- tch = prim_dequeue_tch(queue, lchan_type, false);
+ tch = prim_dequeue_tch(lchan, false);
if (tch) /* Drop the second TCH/H prim */
- talloc_free(tch);
+ msgb_free(tch);
}

return facch;

no_facch:
- return prim_dequeue_tch(queue, lchan_type, false);
+ return prim_dequeue_tch(lchan, false);
}

/**
- * Dequeues a single primitive of required type
- * from a specified transmit queue.
+ * Dequeues a primitive from the Tx queue of the given lchan.
*
- * @param queue a transmit queue to take a prim from
- * @param fn the current frame number (used for FACCH/H)
* @param lchan logical channel state
+ * @param fn the current frame number (used for FACCH/H)
* @return a primitive or NULL if not found
*/
-struct l1sched_ts_prim *l1sched_prim_dequeue(struct llist_head *queue,
- uint32_t fn, struct l1sched_lchan_state *lchan)
+struct msgb *l1sched_lchan_prim_dequeue(struct l1sched_lchan_state *lchan, uint32_t fn)
{
- struct l1sched_ts_prim *prim;
-
/* SACCH is unorthodox, see 3GPP TS 04.08, section 3.4.1 */
if (L1SCHED_CHAN_IS_SACCH(lchan->type))
- return prim_dequeue_sacch(queue, lchan);
+ return prim_dequeue_sacch(lchan);

/* There is nothing to dequeue */
- if (llist_empty(queue))
+ if (llist_empty(&lchan->tx_prims))
return NULL;

switch (lchan->type) {
/* TCH/F requires FACCH/F prioritization */
case L1SCHED_TCHF:
- return prim_dequeue_tch_f(queue);
+ return prim_dequeue_tch_f(lchan);

/* FACCH/H prioritization is a bit more complex */
case L1SCHED_TCHH_0:
case L1SCHED_TCHH_1:
- return prim_dequeue_tch_h(queue, fn, lchan->type);
+ return prim_dequeue_tch_h(lchan, fn);

/* PDCH is timing critical, we need to check TDMA Fn */
case L1SCHED_PDTCH:
case L1SCHED_PTCCH:
- prim = prim_dequeue_one(queue, lchan->type);
- if (prim == NULL)
+ {
+ struct msgb *msg = msgb_dequeue(&lchan->tx_prims);
+ const struct l1sched_prim *prim;
+
+ if (msg == NULL)
return NULL;
- if (OSMO_LIKELY(prim->fn == fn))
- return prim;
+ prim = l1sched_prim_from_msgb(msg);
+
+ if (OSMO_LIKELY(prim->data_req.frame_nr == fn))
+ return msg;
LOGP_LCHAND(lchan, LOGL_ERROR,
"%s(): dropping Tx primitive (current Fn=%u, prim Fn=%u)\n",
- __func__, fn, prim->fn);
- talloc_free(prim);
+ __func__, fn, prim->data_req.frame_nr);
+ msgb_free(msg);
return NULL;
+ }

/* Other kinds of logical channels */
default:
- return prim_dequeue_one(queue, lchan->type);
+ return msgb_dequeue(&lchan->tx_prims);
}
}

@@ -468,7 +388,7 @@
*/
void l1sched_lchan_prim_drop(struct l1sched_lchan_state *lchan)
{
- talloc_free(lchan->prim);
+ msgb_free(lchan->prim);
lchan->prim = NULL;
}

@@ -480,11 +400,13 @@
* @param lchan lchan to assign a primitive
* @return zero in case of success, otherwise a error code
*/
-int l1sched_prim_dummy(struct l1sched_lchan_state *lchan)
+void l1sched_lchan_prim_assign_dummy(struct l1sched_lchan_state *lchan)
{
+ const struct l1sched_lchan_desc *lchan_desc;
enum l1sched_lchan_type chan = lchan->type;
uint8_t tch_mode = lchan->tch_mode;
- struct l1sched_ts_prim *prim;
+ struct l1sched_prim *prim;
+ struct msgb *msg;
uint8_t prim_buffer[40];
size_t prim_len = 0;
int i;
@@ -504,6 +426,8 @@
/* Not applicable for SACCH! */
OSMO_ASSERT(!L1SCHED_CHAN_IS_SACCH(lchan->type));

+ lchan_desc = &l1sched_lchan_desc[lchan->type];
+
/**
* Determine what actually should be generated:
* TCH in GSM48_CMODE_SIGN: LAPDm fill frame;
@@ -515,7 +439,7 @@
prim_len = l1sched_bad_frame_ind(prim_buffer, lchan);
} else if (L1SCHED_CHAN_IS_TCH(chan) && L1SCHED_TCH_MODE_IS_DATA(tch_mode)) {
/* FIXME: should we do anything for CSD? */
- return 0;
+ return;
} else {
/* Copy LAPDm fill frame's header */
memcpy(prim_buffer, lapdm_fill_frame, sizeof(lapdm_fill_frame));
@@ -535,39 +459,137 @@

/* Nothing to allocate / assign */
if (!prim_len)
- return 0;
+ return;

- /* Allocate a new primitive */
- prim = talloc_zero_size(lchan, sizeof(struct l1sched_ts_prim) + prim_len);
- if (prim == NULL)
- return -ENOMEM;
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST, prim_len);
+ OSMO_ASSERT(msg != NULL);

- /* Init primitive header */
- prim->payload_len = prim_len;
- prim->chan = lchan->type;
+ prim = l1sched_prim_from_msgb(msg);
+ prim->data_req = (struct l1sched_prim_chdr) {
+ .frame_nr = 0, /* dummy */
+ .chan_nr = lchan_desc->chan_nr | lchan->ts->index,
+ .link_id = lchan_desc->link_id,
+ };

- /* Fill in the payload */
- memcpy(prim->payload, prim_buffer, prim_len);
+ memcpy(msgb_put(msg, prim_len), &prim_buffer[0], prim_len);

/* Assign the current prim */
- lchan->prim = prim;
+ lchan->prim = msg;

LOGP_LCHAND(lchan, LOGL_DEBUG, "Transmitting a dummy / silence frame\n");
+}

+int l1sched_lchan_emit_data_ind(struct l1sched_lchan_state *lchan,
+ const uint8_t *data, size_t data_len,
+ int n_errors, int n_bits_total,
+ bool traffic)
+{
+ const struct l1sched_meas_set *meas = &lchan->meas_avg;
+ const struct l1sched_lchan_desc *lchan_desc;
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+
+ lchan_desc = &l1sched_lchan_desc[lchan->type];
+
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_INDICATION, data_len);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->data_ind = (struct l1sched_prim_data_ind) {
+ .chdr = {
+ .frame_nr = meas->fn,
+ .chan_nr = lchan_desc->chan_nr | lchan->ts->index,
+ .link_id = lchan_desc->link_id,
+ .traffic = traffic,
+ },
+ .toa256 = meas->toa256,
+ .rssi = meas->rssi,
+ .n_errors = n_errors,
+ .n_bits_total = n_bits_total,
+ };
+
+ if (data_len > 0)
+ memcpy(msgb_put(msg, data_len), data, data_len);
+
+ return l1sched_prim_to_user(lchan->ts->sched, msg);
+}
+
+int l1sched_lchan_emit_data_cnf(struct l1sched_lchan_state *lchan, uint32_t fn)
+{
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+
+ /* take ownership of the prim */
+ OSMO_ASSERT(msg != NULL);
+ msg = lchan->prim;
+ lchan->prim = NULL;
+
+ /* convert from DATA.req to DATA.cnf */
+ prim = l1sched_prim_from_msgb(lchan->prim);
+ prim->oph.operation = PRIM_OP_CONFIRM;
+
+ switch (prim->oph.primitive) {
+ case L1SCHED_PRIM_T_DATA:
+ prim->data_cnf.frame_nr = fn;
+ break;
+ case L1SCHED_PRIM_T_RACH:
+ prim->rach_cnf.chdr.frame_nr = fn;
+ break;
+ default:
+ /* shall not happen */
+ OSMO_ASSERT(0);
+ }
+
+ return l1sched_prim_to_user(lchan->ts->sched, msg);
+}
+
+static int prim_enqeue(struct l1sched_state *sched, struct msgb *msg,
+ const struct l1sched_prim_chdr *chdr)
+{
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+ struct l1sched_lchan_state *lchan;
+
+ lchan = l1sched_find_lchan_by_chan_nr(sched, chdr->chan_nr, chdr->link_id);
+ if (OSMO_UNLIKELY(lchan == NULL || !lchan->active)) {
+ LOGP_SCHEDD(sched, LOGL_ERROR,
+ "No [active] lchan for primitive " L1SCHED_PRIM_STR_FMT " "
+ "(chan_nr=%02x, link_id=%02x, len=%u): %s\n",
+ L1SCHED_PRIM_STR_ARGS(prim),
+ chdr->chan_nr, chdr->link_id,
+ msgb_l2len(msg), msgb_hexdump_l2(msg));
+ msgb_free(msg);
+ return -ENODEV;
+ }
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG,
+ "Enqueue primitive " L1SCHED_PRIM_STR_FMT " "
+ "(chan_nr=%02x, link_id=%02x, len=%u): %s\n",
+ L1SCHED_PRIM_STR_ARGS(prim),
+ chdr->chan_nr, chdr->link_id,
+ msgb_l2len(msg), msgb_hexdump_l2(msg));
+
+ msgb_enqueue(&lchan->tx_prims, msg);
return 0;
}

-/**
- * Flushes a queue of primitives
- *
- * @param list list of prims going to be flushed
- */
-void l1sched_prim_flush_queue(struct llist_head *list)
+int l1sched_prim_from_user(struct l1sched_state *sched, struct msgb *msg)
{
- struct l1sched_ts_prim *prim, *prim_next;
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);

- llist_for_each_entry_safe(prim, prim_next, list, list) {
- llist_del(&prim->list);
- talloc_free(prim);
+ LOGP_SCHEDD(sched, LOGL_DEBUG,
+ "%s(): Rx " L1SCHED_PRIM_STR_FMT "\n",
+ __func__, L1SCHED_PRIM_STR_ARGS(prim));
+
+ switch (OSMO_PRIM_HDR(&prim->oph)) {
+ case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST):
+ return prim_enqeue(sched, msg, &prim->data_req);
+ case OSMO_PRIM(L1SCHED_PRIM_T_RACH, PRIM_OP_REQUEST):
+ return prim_enqeue(sched, msg, &prim->rach_req.chdr);
+ default:
+ LOGP_SCHEDD(sched, LOGL_ERROR,
+ "%s(): Unhandled primitive " L1SCHED_PRIM_STR_FMT "\n",
+ __func__, L1SCHED_PRIM_STR_ARGS(prim));
+ msgb_free(msg);
+ return -ENOTSUP;
}
}
diff --git a/src/host/trxcon/src/sched_trx.c b/src/host/trxcon/src/sched_trx.c
index c337661..3bbf530 100644
--- a/src/host/trxcon/src/sched_trx.c
+++ b/src/host/trxcon/src/sched_trx.c
@@ -68,18 +68,20 @@
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

-static int l1sched_cfg_pchan_comb_req(struct l1sched_state *sched,
+static int l1sched_cfg_pchan_comb_ind(struct l1sched_state *sched,
uint8_t tn, enum gsm_phys_chan_config pchan)
{
- const struct l1sched_config_req cr = {
- .type = L1SCHED_CFG_PCHAN_COMB,
- .pchan_comb = {
- .tn = tn,
- .pchan = pchan,
- },
- };
+ struct l1sched_prim *prim;
+ struct msgb *msg;

- return l1sched_handle_config_req(sched, &cr);
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_PCHAN_COMB, PRIM_OP_INDICATION, 0);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->pchan_comb_ind.tn = tn;
+ prim->pchan_comb_ind.pchan = pchan;
+
+ return l1sched_prim_to_user(sched, msg);
}

static void l1sched_a5_burst_enc(struct l1sched_lchan_state *lchan,
@@ -121,11 +123,11 @@

/* If no primitive is being processed, try obtaining one from Tx queue */
if (lchan->prim == NULL)
- lchan->prim = l1sched_prim_dequeue(&ts->tx_prims, br->fn, lchan);
+ lchan->prim = l1sched_lchan_prim_dequeue(lchan, br->fn);
if (lchan->prim == NULL) {
/* If CBTX (Continuous Burst Transmission) is required */
if (l1sched_lchan_desc[chan].flags & L1SCHED_CH_FLAG_CBTX)
- l1sched_prim_dummy(lchan);
+ l1sched_lchan_prim_assign_dummy(lchan);
if (lchan->prim == NULL)
return;
}
@@ -134,7 +136,8 @@

/* Handover RACH needs to be handled regardless of the
* current channel type and the associated handler. */
- if (L1SCHED_PRIM_IS_RACH(lchan->prim) && lchan->prim->chan != L1SCHED_RACH)
+ if (l1sched_prim_type_from_msgb(lchan->prim) == L1SCHED_PRIM_T_RACH &&
+ lchan->type != L1SCHED_RACH)
handler = l1sched_lchan_desc[L1SCHED_RACH].tx_fn;

/* Poke lchan handler */
@@ -272,15 +275,12 @@
talloc_free(lchan);
}

- /* Flush queue primitives for TX */
- l1sched_prim_flush_queue(&ts->tx_prims);
-
/* Remove ts from list and free memory */
sched->ts[tn] = NULL;
talloc_free(ts);

/* Notify transceiver about that */
- l1sched_cfg_pchan_comb_req(sched, tn, GSM_PCHAN_NONE);
+ l1sched_cfg_pchan_comb_ind(sched, tn, GSM_PCHAN_NONE);
}

#define LAYOUT_HAS_LCHAN(layout, lchan) \
@@ -316,8 +316,6 @@
"(Re)configure TDMA timeslot #%u as %s\n",
tn, ts->mf_layout->name);

- /* Init queue primitives for TX */
- INIT_LLIST_HEAD(&ts->tx_prims);
/* Init logical channels list */
INIT_LLIST_HEAD(&ts->lchans);

@@ -337,6 +335,9 @@
/* Set channel type */
lchan->type = type;

+ /* Init the Tx queue */
+ INIT_LLIST_HEAD(&lchan->tx_prims);
+
/* Add to the list of channel states */
llist_add_tail(&lchan->list, &ts->lchans);

@@ -346,7 +347,7 @@
}

/* Notify transceiver about TS activation */
- l1sched_cfg_pchan_comb_req(sched, tn, config);
+ l1sched_cfg_pchan_comb_ind(sched, tn, config);

return 0;
}
@@ -364,9 +365,6 @@
/* Undefine multiframe layout */
ts->mf_layout = NULL;

- /* Flush queue primitives for TX */
- l1sched_prim_flush_queue(&ts->tx_prims);
-
/* Deactivate all logical channels */
l1sched_deactivate_all_lchans(ts);

@@ -377,7 +375,7 @@
}

/* Notify transceiver about that */
- l1sched_cfg_pchan_comb_req(sched, tn, GSM_PCHAN_NONE);
+ l1sched_cfg_pchan_comb_ind(sched, tn, GSM_PCHAN_NONE);

return 0;
}
@@ -541,6 +539,8 @@

static void l1sched_reset_lchan(struct l1sched_lchan_state *lchan)
{
+ struct msgb *msg;
+
/* Prevent NULL-pointer deference */
OSMO_ASSERT(lchan != NULL);

@@ -568,6 +568,10 @@
/* Forget the current prim */
l1sched_lchan_prim_drop(lchan);

+ /* Flush the queue of pending Tx prims */
+ while ((msg = msgb_dequeue(&lchan->tx_prims)) != NULL)
+ msgb_free(msg);
+
/* Channel specific stuff */
if (L1SCHED_CHAN_IS_TCH(lchan->type)) {
lchan->dl_ongoing_facch = 0;
@@ -657,20 +661,6 @@
return GSM_PCHAN_NONE;
}

-enum l1sched_lchan_type l1sched_chan_nr2lchan_type(uint8_t chan_nr,
- uint8_t link_id)
-{
- int i;
-
- /* Iterate over all known lchan types */
- for (i = 0; i < _L1SCHED_CHAN_MAX; i++)
- if (l1sched_lchan_desc[i].chan_nr == (chan_nr & 0xf8))
- if (l1sched_lchan_desc[i].link_id == link_id)
- return i;
-
- return L1SCHED_IDLE;
-}
-
static void l1sched_a5_burst_dec(struct l1sched_lchan_state *lchan,
struct l1sched_burst_ind *bi)
{
diff --git a/src/host/trxcon/src/trxcon_fsm.c b/src/host/trxcon/src/trxcon_fsm.c
index 91c807a..74d1a66 100644
--- a/src/host/trxcon/src/trxcon_fsm.c
+++ b/src/host/trxcon/src/trxcon_fsm.c
@@ -283,21 +283,25 @@
const struct trxcon_param_tx_access_burst_req *req)
{
struct trxcon_inst *trxcon = fi->priv;
- enum l1sched_ts_prim_type prim_type;
- const struct l1sched_ts_prim *prim;
+ struct l1sched_prim *prim;
+ struct msgb *msg;

- const struct l1sched_ts_prim_rach rach = {
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_RACH, PRIM_OP_REQUEST, 0);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->rach_req = (struct l1sched_prim_rach) {
+ .chdr = {
+ .chan_nr = req->chan_nr,
+ .link_id = req->link_id,
+ },
.synch_seq = req->synch_seq,
.offset = req->offset,
+ .is_11bit = req->is_11bit,
.ra = req->ra,
};

- prim_type = req->is_11bit ? L1SCHED_PRIM_RACH11 : L1SCHED_PRIM_RACH8;
- prim = l1sched_prim_push(trxcon->sched, prim_type,
- req->chan_nr, req->link_id,
- (const uint8_t *)&rach, sizeof(rach));
- if (prim == NULL)
- LOGPFSML(fi, LOGL_ERROR, "Failed to enqueue a prim\n");
+ l1sched_prim_to_user(trxcon->sched, msg);
}

static void handle_dch_est_req(struct osmo_fsm_inst *fi,
@@ -485,15 +489,21 @@
case TRXCON_EV_TX_DATA_REQ:
{
const struct trxcon_param_tx_data_req *req = data;
- struct l1sched_ts_prim *prim;
+ struct l1sched_prim *prim;
+ struct msgb *msg;

- prim = l1sched_prim_push(trxcon->sched, L1SCHED_PRIM_DATA,
- req->chan_nr, req->link_id,
- req->data, req->data_len);
- if (prim == NULL) {
- LOGPFSML(fi, LOGL_ERROR, "Failed to enqueue a prim\n");
- return;
- }
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST, req->data_len);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->data_req = (struct l1sched_prim_chdr) {
+ .chan_nr = req->chan_nr,
+ .link_id = req->link_id,
+ .traffic = req->traffic,
+ };
+
+ memcpy(msgb_put(msg, req->data_len), req->data, req->data_len);
+ l1sched_prim_to_user(trxcon->sched, msg);
break;
}
case TRXCON_EV_TX_DATA_CNF:
@@ -547,21 +557,24 @@
case TRXCON_EV_GPRS_UL_BLOCK_REQ:
{
struct l1gprs_prim_ul_block_req block_req;
- const struct msgb *msg = data;
- struct l1sched_ts_prim *prim;
+ struct l1sched_prim *prim;
+ struct msgb *msg = data;

if (l1gprs_handle_ul_block_req(trxcon->gprs, &block_req, msg) != 0)
return;

- prim = l1sched_prim_push(trxcon->sched, L1SCHED_PRIM_DATA,
- RSL_CHAN_OSMO_PDCH | block_req.hdr.tn, 0x00,
- block_req.data, block_req.data_len);
- if (prim == NULL) {
- LOGPFSML(fi, LOGL_ERROR, "Failed to enqueue a prim\n");
- return;
- }
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST, block_req.data_len);
+ OSMO_ASSERT(msg != NULL);

- prim->fn = block_req.hdr.fn;
+ prim = l1sched_prim_from_msgb(msg);
+ prim->data_req = (struct l1sched_prim_chdr) {
+ .frame_nr = block_req.hdr.fn,
+ .chan_nr = RSL_CHAN_OSMO_PDCH | block_req.hdr.tn,
+ .link_id = 0x00,
+ };
+
+ memcpy(msgb_put(msg, block_req.data_len), block_req.data, block_req.data_len);
+ l1sched_prim_to_user(trxcon->sched, msg);
break;
}
case TRXCON_EV_RX_DATA_IND:
diff --git a/src/host/trxcon/src/trxcon_shim.c b/src/host/trxcon/src/trxcon_shim.c
index de3de91..4f56d85 100644
--- a/src/host/trxcon/src/trxcon_shim.c
+++ b/src/host/trxcon/src/trxcon_shim.c
@@ -1,7 +1,7 @@
/*
* OsmocomBB <-> SDR connection bridge
*
- * (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * (C) 2022-2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
*
* All Rights Reserved
@@ -24,52 +24,33 @@
#include <osmocom/core/fsm.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/gsmtap.h>
+#include <osmocom/gsm/rsl.h>

#include <osmocom/bb/trxcon/trxcon.h>
#include <osmocom/bb/trxcon/trxcon_fsm.h>
#include <osmocom/bb/trxcon/phyif.h>
#include <osmocom/bb/l1sched/l1sched.h>

-static void trxcon_gsmtap_send(struct gsmtap_inst *gi, uint8_t chan_type,
- uint32_t fn, uint8_t tn, uint8_t ss,
- uint16_t band_arfcn,
- int8_t signal_dbm, uint8_t snr,
- const uint8_t *data, size_t data_len)
+static void trxcon_gsmtap_send(struct trxcon_inst *trxcon,
+ const struct l1sched_prim_chdr *chdr,
+ const uint8_t *data, size_t data_len,
+ int8_t signal_dbm, uint8_t snr, bool uplink)
{
- /* Omit frames with unknown channel type */
- if (chan_type == GSMTAP_CHANNEL_UNKNOWN)
- return;
+ uint16_t band_arfcn = trxcon->l1p.band_arfcn;
+ uint8_t chan_type, ss, tn;

- /* TODO: distinguish GSMTAP_CHANNEL_PCH and GSMTAP_CHANNEL_AGCH */
- gsmtap_send(gi, band_arfcn, tn, chan_type, ss, fn, signal_dbm, snr, data, data_len);
+ if (uplink)
+ band_arfcn |= ARFCN_UPLINK;
+ if (rsl_dec_chan_nr(chdr->chan_nr, &chan_type, &ss, &tn) != 0)
+ return;
+ chan_type = chantype_rsl2gsmtap2(chan_type, chdr->link_id, chdr->traffic);
+
+ gsmtap_send(trxcon->gsmtap, band_arfcn, tn, chan_type, ss,
+ chdr->frame_nr, signal_dbm, snr,
+ data, data_len);
}

/* External L1 API for the scheduler */
-int l1sched_handle_config_req(struct l1sched_state *sched,
- const struct l1sched_config_req *cr)
-{
- struct trxcon_inst *trxcon = sched->priv;
-
- switch (cr->type) {
- case L1SCHED_CFG_PCHAN_COMB:
- {
- struct trxcon_param_set_phy_config_req req = {
- .type = TRXCON_PHY_CFGT_PCHAN_COMB,
- .pchan_comb = {
- .tn = cr->pchan_comb.tn,
- .pchan = cr->pchan_comb.pchan,
- },
- };
-
- return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_SET_PHY_CONFIG_REQ, &req);
- }
- default:
- LOGPFSML(trxcon->fi, LOGL_ERROR,
- "Unhandled config request (type 0x%02x)\n", cr->type);
- return -ENODEV;
- }
-}
-
int l1sched_handle_burst_req(struct l1sched_state *sched,
const struct l1sched_burst_req *br)
{
@@ -86,137 +67,122 @@
}

/* External L2 API for the scheduler */
-int l1sched_handle_data_ind(struct l1sched_lchan_state *lchan,
- const uint8_t *data, size_t data_len,
- int n_errors, int n_bits_total,
- enum l1sched_data_type dt)
+static int handle_prim_data_ind(struct trxcon_inst *trxcon, struct msgb *msg)
{
- const struct l1sched_meas_set *meas = &lchan->meas_avg;
- const struct l1sched_lchan_desc *lchan_desc;
- struct l1sched_state *sched = lchan->ts->sched;
- struct trxcon_inst *trxcon = sched->priv;
- int rc;
-
- lchan_desc = &l1sched_lchan_desc[lchan->type];
-
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
struct trxcon_param_rx_data_ind ind = {
- /* .traffic is set below */
- .chan_nr = lchan_desc->chan_nr | lchan->ts->index,
- .link_id = lchan_desc->link_id,
+ .traffic = prim->data_ind.chdr.traffic,
+ .chan_nr = prim->data_ind.chdr.chan_nr,
+ .link_id = prim->data_ind.chdr.link_id,
.band_arfcn = trxcon->l1p.band_arfcn,
- .frame_nr = meas->fn,
- .toa256 = meas->toa256,
- .rssi = meas->rssi,
- .n_errors = n_errors,
- .n_bits_total = n_bits_total,
- .data_len = data_len,
- .data = data,
+ .frame_nr = prim->data_ind.chdr.frame_nr,
+ .toa256 = prim->data_ind.toa256,
+ .rssi = prim->data_ind.rssi,
+ .n_errors = prim->data_ind.n_errors,
+ .n_bits_total = prim->data_ind.n_bits_total,
+ .data_len = msgb_l2len(msg),
+ .data = msgb_l2(msg),
};

- switch (dt) {
- case L1SCHED_DT_PACKET_DATA:
- case L1SCHED_DT_TRAFFIC:
- ind.traffic = true;
- /* fall-through */
- case L1SCHED_DT_SIGNALING:
- rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_RX_DATA_IND, &ind);
- break;
- case L1SCHED_DT_OTHER:
- if (lchan->type == L1SCHED_SCH) {
- if (trxcon->fi->state != TRXCON_ST_FBSB_SEARCH)
- return 0;
- rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FBSB_SEARCH_RES, NULL);
- break;
- }
- /* fall through */
- default:
- LOGPFSML(trxcon->fi, LOGL_ERROR,
- "Unhandled L2 DATA.ind (type 0x%02x)\n", dt);
- return -ENODEV;
+ if (trxcon->gsmtap != NULL && ind.data_len > 0) {
+ trxcon_gsmtap_send(trxcon, &prim->data_ind.chdr,
+ ind.data, ind.data_len,
+ ind.rssi, 0, false);
}

- if (trxcon->gsmtap != NULL && data != NULL && data_len > 0) {
- trxcon_gsmtap_send(trxcon->gsmtap, lchan_desc->gsmtap_chan_type,
- meas->fn, lchan->ts->index, lchan_desc->ss_nr,
- trxcon->l1p.band_arfcn, meas->rssi, 0,
- data, data_len);
- }
-
- return rc;
+ return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_RX_DATA_IND, &ind);
}

-int l1sched_handle_data_cnf(struct l1sched_lchan_state *lchan,
- uint32_t fn, enum l1sched_data_type dt)
+static int handle_prim_data_cnf(struct trxcon_inst *trxcon, struct msgb *msg)
{
- const struct l1sched_lchan_desc *lchan_desc;
- struct l1sched_state *sched = lchan->ts->sched;
- struct trxcon_inst *trxcon = sched->priv;
- const uint8_t *data;
- uint8_t ra_buf[2];
- size_t data_len;
- int rc;
-
- lchan_desc = &l1sched_lchan_desc[lchan->type];
-
- switch (dt) {
- case L1SCHED_DT_PACKET_DATA:
- data_len = lchan->prim->payload_len;
- data = lchan->prim->payload;
- rc = 0;
- break; /* do not send DATA.cnf */
- case L1SCHED_DT_SIGNALING:
- case L1SCHED_DT_TRAFFIC:
- {
- struct trxcon_param_tx_data_cnf cnf = {
- .traffic = (dt == L1SCHED_DT_TRAFFIC),
- .chan_nr = lchan_desc->chan_nr | lchan->ts->index,
- .link_id = lchan_desc->link_id,
- .band_arfcn = trxcon->l1p.band_arfcn,
- .frame_nr = fn,
- };
-
- rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_DATA_CNF, &cnf);
- data_len = lchan->prim->payload_len;
- data = lchan->prim->payload;
- break;
- }
- case L1SCHED_DT_OTHER:
- if (L1SCHED_PRIM_IS_RACH(lchan->prim)) {
- const struct l1sched_ts_prim_rach *rach;
- struct trxcon_param_tx_access_burst_cnf cnf = {
- .band_arfcn = trxcon->l1p.band_arfcn,
- .frame_nr = fn,
- };
-
- rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_ACCESS_BURST_CNF, &cnf);
-
- rach = (struct l1sched_ts_prim_rach *)lchan->prim->payload;
- if (lchan->prim->type == L1SCHED_PRIM_RACH11) {
- ra_buf[0] = (uint8_t)(rach->ra >> 3);
- ra_buf[1] = (uint8_t)(rach->ra & 0x07);
- data = &ra_buf[0];
- data_len = 2;
- } else {
- ra_buf[0] = (uint8_t)(rach->ra);
- data = &ra_buf[0];
- data_len = 1;
- }
- break;
- }
- /* fall through */
- default:
- LOGPFSML(trxcon->fi, LOGL_ERROR,
- "Unhandled L2 DATA.cnf (type 0x%02x)\n", dt);
- return -ENODEV;
- }
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+ struct trxcon_param_tx_data_cnf cnf = {
+ .traffic = prim->data_cnf.traffic,
+ .chan_nr = prim->data_cnf.chan_nr,
+ .link_id = prim->data_cnf.link_id,
+ .band_arfcn = trxcon->l1p.band_arfcn,
+ .frame_nr = prim->data_cnf.frame_nr,
+ };

if (trxcon->gsmtap != NULL) {
- trxcon_gsmtap_send(trxcon->gsmtap, lchan_desc->gsmtap_chan_type,
- fn, lchan->ts->index, lchan_desc->ss_nr,
- trxcon->l1p.band_arfcn | ARFCN_UPLINK,
- 0, 0, data, data_len);
+ trxcon_gsmtap_send(trxcon, &prim->data_cnf,
+ msgb_l2(msg), msgb_l2len(msg),
+ 0, 0, true);
}

+ /* XXX: do not send for L1SCHED_DT_PACKET_DATA */
+ return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_DATA_CNF, &cnf);
+}
+
+static int handle_prim_rach_cnf(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+ struct trxcon_param_tx_access_burst_cnf cnf = {
+ .band_arfcn = trxcon->l1p.band_arfcn,
+ .frame_nr = prim->rach_cnf.chdr.frame_nr,
+ };
+
+ if (trxcon->gsmtap != NULL) {
+ if (prim->rach_cnf.is_11bit) {
+ msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra >> 3));
+ msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra & 0x07));
+ } else {
+ msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra));
+ }
+
+ trxcon_gsmtap_send(trxcon, &prim->rach_cnf.chdr,
+ msgb_l2(msg), msgb_l2len(msg),
+ 0, 0, true);
+ }
+
+ return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_ACCESS_BURST_CNF, &cnf);
+}
+
+int l1sched_prim_to_user(struct l1sched_state *sched, struct msgb *msg)
+{
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+ struct trxcon_inst *trxcon = sched->priv;
+ int rc = 0;
+
+ LOGPFSML(trxcon->fi, LOGL_DEBUG,
+ "%s(): Rx " L1SCHED_PRIM_STR_FMT "\n",
+ __func__, L1SCHED_PRIM_STR_ARGS(prim));
+
+ switch (OSMO_PRIM_HDR(&prim->oph)) {
+ case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_INDICATION):
+ rc = handle_prim_data_ind(trxcon, msg);
+ break;
+ case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_CONFIRM):
+ rc = handle_prim_data_cnf(trxcon, msg);
+ break;
+ case OSMO_PRIM(L1SCHED_PRIM_T_RACH, PRIM_OP_CONFIRM):
+ rc = handle_prim_rach_cnf(trxcon, msg);
+ break;
+ case OSMO_PRIM(L1SCHED_PRIM_T_SCH, PRIM_OP_INDICATION):
+ if (trxcon->fi->state == TRXCON_ST_FBSB_SEARCH)
+ rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FBSB_SEARCH_RES, NULL);
+ break;
+ case OSMO_PRIM(L1SCHED_PRIM_T_PCHAN_COMB, PRIM_OP_INDICATION):
+ {
+ struct trxcon_param_set_phy_config_req req = {
+ .type = TRXCON_PHY_CFGT_PCHAN_COMB,
+ .pchan_comb = {
+ .tn = prim->pchan_comb_ind.tn,
+ .pchan = prim->pchan_comb_ind.pchan,
+ },
+ };
+
+ rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_SET_PHY_CONFIG_REQ, &req);
+ break;
+ }
+ default:
+ LOGPFSML(trxcon->fi, LOGL_ERROR,
+ "%s(): Unhandled primitive " L1SCHED_PRIM_STR_FMT "\n",
+ __func__, L1SCHED_PRIM_STR_ARGS(prim));
+ rc = -ENOTSUP;
+ }
+
+ msgb_free(msg);
return rc;
}


To view, visit change 32304. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: osmocom-bb
Gerrit-Branch: master
Gerrit-Change-Id: I73576bd0ea10a5663ba6254283812c275cc3fa46
Gerrit-Change-Number: 32304
Gerrit-PatchSet: 1
Gerrit-Owner: fixeria <vyanitskiy@sysmocom.de>
Gerrit-MessageType: newchange