Review at  https://gerrit.osmocom.org/3569

VIRT-PHY: Add support for GPRS / TBF mode

we add a new STATE_TBF to vthe MS model and add some L1CTL primitives
to configure that TBF mode as well as to enqueue transmissions for
blocks at a given USF+TS.

Change-Id: Ie6f37090bd45f463aa25d9e00bc06f563be5264a
---
M include/l1ctl_proto.h
M src/host/virt_phy/include/virtphy/virt_l1_model.h
M src/host/virt_phy/src/gsmtapl1_if.c
M src/host/virt_phy/src/l1ctl_sap.c
4 files changed, 293 insertions(+), 5 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/69/3569/1

diff --git a/include/l1ctl_proto.h b/include/l1ctl_proto.h
index 771bf1c..37d3d87 100644
--- a/include/l1ctl_proto.h
+++ b/include/l1ctl_proto.h
@@ -56,6 +56,13 @@
        L1CTL_TRAFFIC_REQ,
        L1CTL_TRAFFIC_CONF,
        L1CTL_TRAFFIC_IND,
+
+       /* configure TBF for uplink/downlink */
+       L1CTL_TBF_CFG_REQ,
+       L1CTL_TBF_CFG_CONF,
+
+       L1CTL_DATA_TBF_REQ,
+       L1CTL_DATA_TBF_CONF,
 };
 
 enum ccch_mode {
@@ -68,6 +75,23 @@
        NEIGH_MODE_NONE = 0,
        NEIGH_MODE_PM,
        NEIGH_MODE_SB,
+};
+
+enum l1ctl_coding_scheme {
+       L1CTL_CS_NONE,
+       L1CTL_CS1,
+       L1CTL_CS2,
+       L1CTL_CS3,
+       L1CTL_CS4,
+       L1CTL_MCS1,
+       L1CTL_MCS2,
+       L1CTL_MCS3,
+       L1CTL_MCS4,
+       L1CTL_MCS5,
+       L1CTL_MCS6,
+       L1CTL_MCS7,
+       L1CTL_MCS8,
+       L1CTL_MCS9,
 };
 
 #define TRAFFIC_DATA_LEN       40
@@ -149,6 +173,15 @@
        uint8_t link_id;
        uint8_t padding[2];
 
+       uint8_t payload[0];
+} __attribute__((packed));
+
+struct l1ctl_info_ul_tbf {
+       /* references l1ctl_tbf_cfg_req.tbf_nr */
+       uint8_t tbf_nr;
+       uint8_t coding_scheme;
+       uint8_t padding[2];
+       /* RLC/MAC block, size determines CS */
        uint8_t payload[0];
 } __attribute__((packed));
 
@@ -300,4 +333,15 @@
        uint8_t data[TRAFFIC_DATA_LEN];
 } __attribute__((packed));
 
+struct l1ctl_tbf_cfg_req {
+       /* future support for multiple concurrent TBFs. 0 for now */
+       uint8_t tbf_nr;
+       /* is this about an UL TBF (1) or DL (0) */
+       uint8_t is_uplink;
+       uint8_t padding[2];
+
+       /* one USF for each TN, or 255 for invalid/unused */
+       uint8_t usf[8];
+} __attribute__((packed));
+
 #endif /* __L1CTL_PROTO_H__ */
diff --git a/src/host/virt_phy/include/virtphy/virt_l1_model.h 
b/src/host/virt_phy/include/virtphy/virt_l1_model.h
index fa79127..67c24bb 100644
--- a/src/host/virt_phy/include/virtphy/virt_l1_model.h
+++ b/src/host/virt_phy/include/virtphy/virt_l1_model.h
@@ -15,6 +15,7 @@
        MS_STATE_IDLE_SYNCING,
        MS_STATE_IDLE_CAMPING,
        MS_STATE_DEDICATED,
+       MS_STATE_TBF
 };
 
 
@@ -74,6 +75,15 @@
                uint8_t tsc; // training sequence code (ununsed in virtual um)
                uint8_t h; // hopping enabled flag (ununsed in virtual um)
        } dedicated;
+       struct {
+               struct {
+                       uint8_t usf[8];
+                       struct llist_head tx_queue;
+               } ul;
+               struct {
+                       uint8_t tfi[8];
+               } dl;
+       } tbf;
 
        /* fbsb state */
        struct {
diff --git a/src/host/virt_phy/src/gsmtapl1_if.c 
b/src/host/virt_phy/src/gsmtapl1_if.c
index 8202046..2cf9d2d 100644
--- a/src/host/virt_phy/src/gsmtapl1_if.c
+++ b/src/host/virt_phy/src/gsmtapl1_if.c
@@ -54,7 +54,7 @@
 void gsmtapl1_tx_to_virt_um_inst(struct l1_model_ms *ms, uint32_t fn, uint8_t 
tn, struct msgb *msg)
 {
        struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data;
-       struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *)l1h->data;
+       struct l1ctl_info_ul *ul;
        struct gsmtap_hdr *gh;
        struct msgb *outmsg;    /* msg to send with gsmtap header prepended */
        uint16_t arfcn = ms->state.serving_cell.arfcn;  /* arfcn of the cell we 
currently camp on */
@@ -68,8 +68,20 @@
        uint8_t timeslot;       /* tdma timeslot to send in (0-7) */
        uint8_t gsmtap_chan;    /* the gsmtap channel */
 
-       rsl_dec_chan_nr(ul->chan_nr, &rsl_chantype, &subslot, &timeslot);
-       gsmtap_chan = chantype_rsl2gsmtap(rsl_chantype, ul->link_id);
+       switch (l1h->msg_type) {
+       case L1CTL_DATA_TBF_REQ:
+               ul = NULL;
+               rsl_chantype = RSL_CHAN_OSMO_PDCH;
+               timeslot = tn;
+               subslot = 0;
+               gsmtap_chan = chantype_rsl2gsmtap(rsl_chantype, 0);
+               break;
+       default:
+               ul = (struct l1ctl_info_ul *)l1h->data;
+               rsl_dec_chan_nr(ul->chan_nr, &rsl_chantype, &subslot, 
&timeslot);
+               gsmtap_chan = chantype_rsl2gsmtap(rsl_chantype, ul->link_id);
+               break;
+       }
 
        /* arfcn needs to be flagged to be able to distinguish between uplink 
and downlink */
        outmsg = gsmtap_makemsg(arfcn | GSMTAP_ARFCN_F_UPLINK, timeslot,
@@ -104,6 +116,77 @@
  */
 extern uint16_t prim_pm_set_sig_strength(struct l1_model_ms *ms, uint16_t 
arfcn, int16_t sig_lev);
 
+/* determine if a received Downlink RLC/MAC block matches the current MS 
configuration */
+static bool gprs_dl_block_matches_ms(struct l1_model_ms *ms, struct msgb *msg, 
uint8_t timeslot)
+{
+       uint8_t payload_type;
+       uint8_t tfi;
+
+       if (msgb_length(msg) < 1)
+               return false;
+
+       /* FIXME: Ensure this will also work for EGPRS!  */
+       payload_type = msg->data[0] >> 6;
+       switch (payload_type) {
+       case 0: /* RLC Data Block */
+               /* forward all RLD Data Blocks destined for TFI of MS */
+               tfi = (msg->data[1] >> 1) & 0x1f;
+               if (ms->state.state == MS_STATE_TBF && 
ms->state.tbf.dl.tfi[timeslot] == tfi)
+                       return true;
+               break;
+       case 1: /* RLC/MAC Control without optional octets */
+               /* forward all RLC/MAC control blocks without optional octets, 
i.e. not adressed
+                * to a specific TFI */
+               return true;
+       case 2: /* RLC/MAC with optional control octets */
+               /* forward all RLD Control Blocks destined for TFI of MS */
+               tfi = (msg->data[2] >> 1) & 0x1f;
+               if (ms->state.state == MS_STATE_TBF && 
ms->state.tbf.dl.tfi[timeslot] == tfi)
+                       return true;
+               break;
+       default:
+               break;
+       }
+       return false;
+}
+
+/* determine if given USF at given timeslot is relevant to given MS or not */
+static bool usf_matches_ms(struct l1_model_ms *ms, uint8_t usf, uint8_t 
timeslot)
+{
+       if (ms->state.state == MS_STATE_TBF && ms->state.tbf.ul.usf[timeslot] 
== usf)
+               return true;
+
+       return false;
+}
+
+/* extract USF from (E)GPRS RLC/MAC block */
+static uint8_t get_usf_from_block(struct msgb *msg)
+{
+       /* FIXME: Ensure this will also work for EGPRS!  */
+       return msg->data[0] & 0x7;
+}
+
+/* MS is authorized to transmit a block in uplink for given USF on 
timeslot+arfcn at FN */
+static void ms_ul_tbf_may_transmit(struct l1_model_ms *ms, uint16_t arfcn, 
uint8_t timeslot,
+                                  uint32_t fn, uint8_t usf)
+{
+       struct msgb *msg;
+
+       /* If USF is not for us, bail out */
+       if (!usf_matches_ms(ms, usf, timeslot))
+               return;
+
+       /* attempt to de-queue pending msgb for this UL TBF and transmit it */
+       msg = msgb_dequeue(&ms->state.tbf.ul.tx_queue);
+       if (!msg) {
+               printf("FN=%u, TN=%u, USF=%u: empty tx_queue, not 
transmitting\n", fn, timeslot, usf);
+               /* FIXME: send some dummy control frame? */
+       } else {
+               printf("FN=%u, TN=%u, USF=%u: transmitting queued msg\n", fn, 
timeslot, usf);
+               gsmtapl1_tx_to_virt_um_inst(ms, fn, timeslot, msg);
+       }
+}
+
 static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb 
*msg, uint32_t fn,
                                uint16_t arfcn, uint8_t timeslot, uint8_t 
subslot,
                                uint8_t gsmtap_chantype, uint8_t chan_nr, 
uint8_t link_id,
@@ -111,6 +194,7 @@
 {
        struct l1_model_ms *ms = lsc->priv;
        uint8_t signal_dbm = dbm2rxlev(prim_pm_set_sig_strength(ms, arfcn & 
GSMTAP_ARFCN_MASK, MAX_SIG_LEV_DBM));       /* Power measurement with each 
received massage */
+       uint8_t usf;
 
        gsm_fn2gsmtime(&ms->state.downlink_time, fn);
 
@@ -163,10 +247,15 @@
        case GSMTAP_CHANNEL_RACH:
                LOGPMS(DVIRPHY, LOGL_NOTICE, ms, "Ignoring unexpected RACH in 
downlink ?!?\n");
                break;
-       case GSMTAP_CHANNEL_SDCCH:
-       case GSMTAP_CHANNEL_CCCH:
        case GSMTAP_CHANNEL_PACCH:
        case GSMTAP_CHANNEL_PDCH:
+               if (gprs_dl_block_matches_ms(ms, msg, timeslot))
+                       l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, 
snr_db, signal_dbm, 0, 0);
+               usf = get_usf_from_block(msg);
+               ms_ul_tbf_may_transmit(ms, arfcn, timeslot, fn, usf);
+               break;
+       case GSMTAP_CHANNEL_SDCCH:
+       case GSMTAP_CHANNEL_CCCH:
        case GSMTAP_CHANNEL_PTCCH:
                LOGPMS(DVIRPHY, LOGL_NOTICE, ms, "Ignoring unsupported channel 
type %s\n",
                        get_value_string(gsmtap_gsm_channel_names, 
gsmtap_chantype));
diff --git a/src/host/virt_phy/src/l1ctl_sap.c 
b/src/host/virt_phy/src/l1ctl_sap.c
index d4b33f1..aac49bf 100644
--- a/src/host/virt_phy/src/l1ctl_sap.c
+++ b/src/host/virt_phy/src/l1ctl_sap.c
@@ -27,6 +27,7 @@
 #include <osmocom/gsm/protocol/gsm_08_58.h>
 #include <osmocom/gsm/protocol/gsm_04_08.h>
 #include <osmocom/gsm/rsl.h>
+#include <osmocom/gprs/gprs_rlc.h>
 #include <stdio.h>
 #include <l1ctl_proto.h>
 #include <netinet/in.h>
@@ -38,6 +39,9 @@
 #include <virtphy/gsmtapl1_if.h>
 #include <virtphy/logging.h>
 #include <virtphy/virt_l1_sched.h>
+
+static void l1ctl_rx_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg);
+static void l1ctl_rx_data_tbf_req(struct l1_model_ms *ms, struct msgb *msg);
 
 static void l1_model_tch_mode_set(struct l1_model_ms *ms, uint8_t tch_mode)
 {
@@ -55,6 +59,8 @@
 void l1ctl_sap_init(struct l1_model_ms *model)
 {
        INIT_LLIST_HEAD(&model->state.sched.mframe_items);
+       INIT_LLIST_HEAD(&model->state.tbf.ul.tx_queue);
+
        prim_pm_init(model);
 }
 
@@ -156,6 +162,8 @@
        switch (msg_type) {
        case L1CTL_DATA_REQ:
        case L1CTL_DATA_CONF:
+       case L1CTL_DATA_TBF_REQ:
+       case L1CTL_DATA_TBF_CONF:
        case L1CTL_TRAFFIC_REQ:
        case L1CTL_TRAFFIC_CONF:
        case L1CTL_TRAFFIC_IND:
@@ -238,6 +246,12 @@
        case L1CTL_SIM_REQ:
                l1ctl_rx_sim_req(ms, msg);
                break;
+       case L1CTL_TBF_CFG_REQ:
+               l1ctl_rx_tbf_cfg_req(ms, msg);
+               break;
+       case L1CTL_DATA_TBF_REQ:
+               l1ctl_rx_data_tbf_req(ms, msg);
+               goto exit_nofree;
        }
 
 exit_msgbfree:
@@ -529,12 +543,143 @@
 
 }
 
+static void l1ctl_tx_tbf_cfg_conf(struct l1_model_ms *ms, const struct 
l1ctl_tbf_cfg_req *in);
+
+static void l1ctl_rx_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg)
+{
+       struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
+       struct l1ctl_tbf_cfg_req *cfg_req = (struct l1ctl_tbf_cfg_req *) 
l1h->data;
+       unsigned int i;
+
+       LOGPMS(DL1C, LOGL_INFO, ms, "Rx L1CTL_TBF_CFG_REQ (tbf_id=%u, dir=%s, "
+               "usf=[%d,%d,%d,%d,%d,%d,%d,%d]\n", cfg_req->tbf_nr,
+               cfg_req->is_uplink ? "UL" : "DL", cfg_req->usf[0], 
cfg_req->usf[1],
+               cfg_req->usf[2], cfg_req->usf[3], cfg_req->usf[4], 
cfg_req->usf[5],
+               cfg_req->usf[6], cfg_req->usf[7]);
+
+       if (cfg_req->tbf_nr != 0) {
+               LOGPMS(DL1C, LOGL_ERROR, ms, "TBF_NR != 0 not supported 
yet!\n");
+               return;
+       }
+
+       if (ms->state.state == MS_STATE_DEDICATED)
+               LOGPMS(DL1C, LOGL_NOTICE, ms, "Harrd termiation of DEDICATED 
mode, fix L23!\n");
+
+       if (cfg_req->is_uplink) {
+               for (i = 0; i < 8; i++)
+                       ms->state.tbf.ul.usf[i] = cfg_req->usf[i];
+       } else {
+               for (i = 0; i < 8; i++)
+                       ms->state.tbf.dl.tfi[i] = cfg_req->usf[i];
+       }
+       ms->state.state = MS_STATE_TBF;
+
+       l1ctl_tx_tbf_cfg_conf(ms, cfg_req);
+}
+
+static const enum osmo_gprs_cs osmo_cs_by_l1ctl[] = {
+       [L1CTL_CS_NONE] = OSMO_GPRS_CS_NONE,
+       [L1CTL_CS1]     = OSMO_GPRS_CS1,
+       [L1CTL_CS2]     = OSMO_GPRS_CS2,
+       [L1CTL_CS3]     = OSMO_GPRS_CS3,
+       [L1CTL_CS4]     = OSMO_GPRS_CS4,
+       [L1CTL_MCS1]    = OSMO_GPRS_MCS1,
+       [L1CTL_MCS2]    = OSMO_GPRS_MCS2,
+       [L1CTL_MCS3]    = OSMO_GPRS_MCS3,
+       [L1CTL_MCS4]    = OSMO_GPRS_MCS4,
+       [L1CTL_MCS5]    = OSMO_GPRS_MCS5,
+       [L1CTL_MCS6]    = OSMO_GPRS_MCS6,
+       [L1CTL_MCS7]    = OSMO_GPRS_MCS7,
+       [L1CTL_MCS8]    = OSMO_GPRS_MCS8,
+       [L1CTL_MCS9]    = OSMO_GPRS_MCS9,
+};
+
+static int get_osmo_cs_by_l1ctl(enum l1ctl_coding_scheme l1)
+{
+       if (l1 >= ARRAY_SIZE(osmo_cs_by_l1ctl))
+               return -1;
+       return osmo_cs_by_l1ctl[l1];
+}
+
+static const enum l1ctl_coding_scheme l1ctl_cs_by_osmo[] = {
+       [OSMO_GPRS_CS_NONE]     = L1CTL_CS_NONE,
+       [OSMO_GPRS_CS1]         = L1CTL_CS1,
+       [OSMO_GPRS_CS2]         = L1CTL_CS2,
+       [OSMO_GPRS_CS3]         = L1CTL_CS3,
+       [OSMO_GPRS_CS4]         = L1CTL_CS4,
+       [OSMO_GPRS_MCS1]        = L1CTL_MCS1,
+       [OSMO_GPRS_MCS2]        = L1CTL_MCS2,
+       [OSMO_GPRS_MCS3]        = L1CTL_MCS3,
+       [OSMO_GPRS_MCS4]        = L1CTL_MCS4,
+       [OSMO_GPRS_MCS5]        = L1CTL_MCS5,
+       [OSMO_GPRS_MCS6]        = L1CTL_MCS6,
+       [OSMO_GPRS_MCS7]        = L1CTL_MCS7,
+       [OSMO_GPRS_MCS8]        = L1CTL_MCS8,
+       [OSMO_GPRS_MCS9]        = L1CTL_MCS9,
+};
+
+static int get_l1ctl_cs_by_osmo(enum osmo_gprs_cs in)
+{
+       if (in >= ARRAY_SIZE(l1ctl_cs_by_osmo))
+               return -1;
+       return l1ctl_cs_by_osmo[in];
+}
+
+static void l1ctl_rx_data_tbf_req(struct l1_model_ms *ms, struct msgb *msg)
+{
+       struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
+       struct l1ctl_info_ul_tbf *udt = (struct l1ctl_info_ul_tbf *) l1h->data;
+       enum osmo_gprs_cs osmo_cs;
+       int block_size;
+
+       msg->l2h = udt->payload;
+
+       LOGPMS(DL1P, LOGL_ERROR, ms, "Rx L1CTL_DATA_TBF_REQ (tbf_id=%d, 
data=%s)\n",
+               udt->tbf_nr, osmo_hexdump(msg->l2h, msgb_l2len(msg)));
+       if (udt->tbf_nr != 0) {
+               LOGPMS(DL1C, LOGL_ERROR, ms, "TBF_NR != 0 not supported 
yet!\n");
+               return;
+       }
+
+       if (ms->state.state != MS_STATE_TBF) {
+               LOGPMS(DL1P, LOGL_ERROR, ms, "DATA_TBF_REQ in state != TBF\n");
+               return;
+       }
+
+       osmo_cs = get_l1ctl_cs_by_osmo(udt->coding_scheme);
+       if (osmo_cs < 0) {
+               LOGPMS(DL1P, LOGL_ERROR, ms, "DATA_RBF_REQ with invalid CS\n");
+               return;
+       }
+       block_size = osmo_gprs_ul_block_size_bytes(osmo_cs);
+
+       if (msgb_l2len(msg) < block_size) {
+               int pad_len = block_size - msgb_l2len(msg);
+               uint8_t *pad = msgb_put(msg, pad_len);
+               memset(pad, GSM_MACBLOCK_PADDING, pad_len);
+       }
+
+       msgb_enqueue(&ms->state.tbf.ul.tx_queue, msg);
+}
+
 /***************************************************************
  * L1CTL TX ROUTINES *******************************************
  * For more routines check the respective handler classes ******
  * like virt_prim_rach.c ***************************************
  ***************************************************************/
 
+static void l1ctl_tx_tbf_cfg_conf(struct l1_model_ms *ms, const struct 
l1ctl_tbf_cfg_req *in)
+{
+       struct msgb *msg = l1ctl_msgb_alloc(L1CTL_TBF_CFG_CONF);
+       struct l1ctl_tbf_cfg_req *out;
+
+       /* copy over the data from the request */
+       out = (struct l1ctl_tbf_cfg_req *) msgb_put(msg, sizeof(*out));
+       *out = *in;
+
+       l1ctl_sap_tx_to_l23_inst(ms, msg);
+}
+
 /**
  * @brief Transmit L1CTL_RESET_IND or L1CTL_RESET_CONF to layer 23.
  *

-- 
To view, visit https://gerrit.osmocom.org/3569
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie6f37090bd45f463aa25d9e00bc06f563be5264a
Gerrit-PatchSet: 1
Gerrit-Project: osmocom-bb
Gerrit-Branch: master
Gerrit-Owner: Harald Welte <[email protected]>

Reply via email to