From 2b462dd89db851148c154e30f570a1d2ea4c6567 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 15 Aug 2022 17:59:49 +0700 Subject: [PATCH] {trxcon,virt_phy}: shared GPRS L1 (MAC) implementation Change-Id: I9567d64f9d00262e36147e8d7e541e5e246bda5f Related: OS#5500 --- include/l1ctl_proto.h | 78 +-- include/l1gprs.h | 80 +++ .../trxcon/include/osmocom/bb/Makefile.am | 1 + src/host/trxcon/include/osmocom/bb/l1gprs.h | 1 + .../include/osmocom/bb/trxcon/logging.h | 1 + .../trxcon/include/osmocom/bb/trxcon/trxcon.h | 5 + .../include/osmocom/bb/trxcon/trxcon_fsm.h | 3 + src/host/trxcon/src/Makefile.am | 8 + src/host/trxcon/src/l1ctl.c | 16 +- src/host/trxcon/src/l1gprs.c | 1 + src/host/trxcon/src/logging.c | 7 + src/host/trxcon/src/trxcon_fsm.c | 84 +++- src/host/trxcon/src/trxcon_inst.c | 4 + .../virt_phy/include/osmocom/bb/Makefile.am | 1 + src/host/virt_phy/include/osmocom/bb/l1gprs.h | 1 + .../include/osmocom/bb/virtphy/l1ctl_sap.h | 4 + .../include/osmocom/bb/virtphy/logging.h | 1 + .../osmocom/bb/virtphy/virt_l1_model.h | 12 +- src/host/virt_phy/src/Makefile.am | 2 + src/host/virt_phy/src/gsmtapl1_if.c | 94 +--- src/host/virt_phy/src/l1ctl_sap.c | 163 +------ src/host/virt_phy/src/l1gprs.c | 1 + src/host/virt_phy/src/logging.c | 15 +- src/host/virt_phy/src/virt_prim_pdch.c | 106 ++++ src/host/virt_phy/src/virtphy.c | 4 +- src/shared/l1gprs.c | 457 ++++++++++++++++++ 26 files changed, 858 insertions(+), 292 deletions(-) create mode 100644 include/l1gprs.h create mode 120000 src/host/trxcon/include/osmocom/bb/l1gprs.h create mode 120000 src/host/trxcon/src/l1gprs.c create mode 120000 src/host/virt_phy/include/osmocom/bb/l1gprs.h create mode 120000 src/host/virt_phy/src/l1gprs.c create mode 100644 src/host/virt_phy/src/virt_prim_pdch.c create mode 100644 src/shared/l1gprs.c diff --git a/include/l1ctl_proto.h b/include/l1ctl_proto.h index 7e415e9d7..9bf40d2ad 100644 --- a/include/l1ctl_proto.h +++ b/include/l1ctl_proto.h @@ -53,11 +53,10 @@ enum { L1CTL_TRAFFIC_CONF = 0x1d, L1CTL_TRAFFIC_IND = 0x1e, L1CTL_BURST_IND = 0x1f, - /* configure TBF for uplink/downlink */ - L1CTL_TBF_CFG_REQ = 0x20, - L1CTL_TBF_CFG_CONF = 0x21, - L1CTL_DATA_TBF_REQ = 0x22, - L1CTL_DATA_TBF_CONF = 0x23, + L1CTL_GPRS_UL_TBF_CFG_REQ = 0x20, + L1CTL_GPRS_DL_TBF_CFG_REQ = 0x21, + L1CTL_GPRS_UL_BLOCK_REQ = 0x22, + L1CTL_GPRS_DL_BLOCK_IND = 0x23, /* Extended (11-bit) RACH (see 3GPP TS 05.02, section 5.2.7) */ L1CTL_EXT_RACH_REQ = 0x24, }; @@ -75,23 +74,6 @@ enum neigh_mode { 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, -}; - /* * NOTE: struct size. We do add manual padding out of the believe * that it will avoid some unaligned access. @@ -188,15 +170,6 @@ struct l1ctl_info_ul { 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)); - /* * msg for FBSB_REQ * the l1_info_ul header is in front @@ -372,15 +345,44 @@ struct l1ctl_traffic_req { uint8_t data[0]; } __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; +/* payload of L1CTL_GPRS_UL_TBF_CFG_REQ */ +struct l1ctl_gprs_ul_tbf_cfg_req { + uint8_t tbf_ref; + uint8_t slotmask; uint8_t padding[2]; +} __attribute__((packed)); - /* one USF for each TN, or 255 for invalid/unused */ - uint8_t usf[8]; +/* payload of L1CTL_GPRS_DL_TBF_CFG_REQ */ +struct l1ctl_gprs_dl_tbf_cfg_req { + uint8_t tbf_ref; + uint8_t slotmask; + uint8_t dl_tfi; + uint8_t padding[1]; +} __attribute__((packed)); + +/* part of L1CTL_GPRS_{UL,DL}_BLOCK_{REQ,IND} */ +struct l1ctl_gprs_block_hdr { + uint32_t fn; + uint8_t tn; + uint8_t padding[3]; +} __attribute__((packed)); + +/* payload of L1CTL_GPRS_UL_BLOCK_REQ */ +struct l1ctl_gprs_ul_block_req { + struct l1ctl_gprs_block_hdr hdr; + uint8_t data[0]; +} __attribute__((packed)); + +/* payload of L1CTL_GPRS_DL_BLOCK_IND */ +struct l1ctl_gprs_dl_block_ind { + struct l1ctl_gprs_block_hdr hdr; + struct { + uint16_t ber10k; /* Bit Error Rate */ + int16_t ci_cb; /* C/I in centiBels */ + uint8_t rx_lev; /* RxLev 0..63 */ + } meas; + uint8_t usf; + uint8_t data[0]; } __attribute__((packed)); #endif /* __L1CTL_PROTO_H__ */ diff --git a/include/l1gprs.h b/include/l1gprs.h new file mode 100644 index 000000000..dbeaf2c7e --- /dev/null +++ b/include/l1gprs.h @@ -0,0 +1,80 @@ +#pragma once + +#include + +#include + +struct l1gprs_state; +struct msgb; + +struct l1gprs_tbf { + /*! Item in l1gprs_state->tbf_list */ + struct llist_head list; + /*! Uplink or Downlink */ + bool uplink; + /*! TBF reference number (not index) */ + uint8_t tbf_ref; + /*! PDCH timeslots used by this TBF */ + uint8_t slotmask; + /*! (Downlink only) DL TFI (Temporary Flow Indentity): 0..31 */ + uint8_t dl_tfi; +}; + +struct l1gprs_pdch { + /*! Timeslot number */ + uint8_t tn; + /*! Backpointer to l1gprs_state we belong to */ + struct l1gprs_state *gprs; + /*! UL TBF count */ + uint8_t ul_tbf_count; + /*! DL TBF count */ + uint8_t dl_tbf_count; + /*! DL TFI mask */ + uint32_t dl_tfi_mask; +}; + +struct l1gprs_state { + /*! PDCH state for each timeslot */ + struct l1gprs_pdch pdch[8]; + /*! Uplink and Downlink TBFs */ + struct llist_head tbf_list; + /*! Logging context (used as prefix for messages) */ + char *log_prefix; + /*! Some private data for API user */ + void *priv; +}; + +void l1gprs_logging_init(int logc); +struct l1gprs_state *l1gprs_state_alloc(void *ctx, const char *log_prefix, void *priv); +void l1gprs_state_free(struct l1gprs_state *gprs); + +int l1gprs_handle_ul_tbf_cfg_req(struct l1gprs_state *gprs, const struct msgb *msg); +int l1gprs_handle_dl_tbf_cfg_req(struct l1gprs_state *gprs, const struct msgb *msg); + +struct l1gprs_prim_block_hdr { + uint32_t fn; + uint8_t tn; +}; + +struct l1gprs_prim_ul_block_req { + struct l1gprs_prim_block_hdr hdr; + size_t data_len; + const uint8_t *data; +}; + +struct l1gprs_prim_dl_block_ind { + struct l1gprs_prim_block_hdr hdr; + struct { + uint16_t ber10k; + int16_t ci_cb; + uint8_t rx_lev; + } meas; + size_t data_len; + const uint8_t *data; +}; + +int l1gprs_handle_ul_block_req(struct l1gprs_state *gprs, + struct l1gprs_prim_ul_block_req *req, + const struct msgb *msg); +struct msgb *l1gprs_handle_dl_block_ind(struct l1gprs_state *gprs, + const struct l1gprs_prim_dl_block_ind *ind); diff --git a/src/host/trxcon/include/osmocom/bb/Makefile.am b/src/host/trxcon/include/osmocom/bb/Makefile.am index 127a2c785..4a575ffb2 100644 --- a/src/host/trxcon/include/osmocom/bb/Makefile.am +++ b/src/host/trxcon/include/osmocom/bb/Makefile.am @@ -5,4 +5,5 @@ SUBDIRS = \ noinst_HEADERS = \ l1ctl_proto.h \ + l1gprs.h \ $(NULL) diff --git a/src/host/trxcon/include/osmocom/bb/l1gprs.h b/src/host/trxcon/include/osmocom/bb/l1gprs.h new file mode 120000 index 000000000..3bf85176e --- /dev/null +++ b/src/host/trxcon/include/osmocom/bb/l1gprs.h @@ -0,0 +1 @@ +../../../../../../include/l1gprs.h \ No newline at end of file diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/logging.h b/src/host/trxcon/include/osmocom/bb/trxcon/logging.h index f8521a0e9..ce149926b 100644 --- a/src/host/trxcon/include/osmocom/bb/trxcon/logging.h +++ b/src/host/trxcon/include/osmocom/bb/trxcon/logging.h @@ -10,6 +10,7 @@ enum { DTRXD, DSCH, DSCHD, + DGPRS, }; int trxcon_logging_init(void *tall_ctx, const char *category_mask); diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h index 15e63f718..1d012de25 100644 --- a/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h +++ b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h @@ -4,6 +4,7 @@ struct osmo_fsm_inst; struct l1sched_state; +struct l1gprs_state; struct msgb; struct trxcon_inst { @@ -18,6 +19,9 @@ struct trxcon_inst { /* The L1 scheduler */ struct l1sched_state *sched; + /* GPRS state (MAC layer) */ + struct l1gprs_state *gprs; + /* PHY interface (e.g. TRXC/TRXD) */ void *phyif; /* L2 interface (e.g. L1CTL) */ @@ -46,6 +50,7 @@ enum trxcon_log_cat { TRXCON_LOGC_L1D, /* L1CTL data */ TRXCON_LOGC_SCHC, /* l1sched control */ TRXCON_LOGC_SCHD, /* l1sched data */ + TRXCON_LOGC_GPRS, /* l1gprs logging */ }; void trxcon_set_log_cfg(const int *logc, unsigned int logc_num); diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/trxcon_fsm.h b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon_fsm.h index 49b5e3a50..bbb4eb57d 100644 --- a/src/host/trxcon/include/osmocom/bb/trxcon/trxcon_fsm.h +++ b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon_fsm.h @@ -37,6 +37,9 @@ enum trxcon_fsm_events { TRXCON_EV_TX_DATA_CNF, TRXCON_EV_RX_DATA_IND, TRXCON_EV_CRYPTO_REQ, + TRXCON_EV_GPRS_UL_TBF_CFG_REQ, /* param: L1CTL msgb */ + TRXCON_EV_GPRS_DL_TBF_CFG_REQ, /* param: L1CTL msgb */ + TRXCON_EV_GPRS_UL_BLOCK_REQ, /* param: L1CTL msgb */ }; /* param of TRXCON_EV_FULL_POWER_SCAN_REQ */ diff --git a/src/host/trxcon/src/Makefile.am b/src/host/trxcon/src/Makefile.am index a286f2aeb..c96949a2e 100644 --- a/src/host/trxcon/src/Makefile.am +++ b/src/host/trxcon/src/Makefile.am @@ -29,6 +29,13 @@ libl1sched_la_SOURCES = \ $(NULL) +noinst_LTLIBRARIES += libl1gprs.la + +libl1gprs_la_SOURCES = \ + l1gprs.c \ + $(NULL) + + noinst_LTLIBRARIES += libtrxcon.la libtrxcon_la_SOURCES = \ @@ -51,6 +58,7 @@ trxcon_SOURCES = \ trxcon_LDADD = \ libtrxcon.la \ libl1sched.la \ + libl1gprs.la \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOCODING_LIBS) \ $(LIBOSMOGSM_LIBS) \ diff --git a/src/host/trxcon/src/l1ctl.c b/src/host/trxcon/src/l1ctl.c index ec98f82bd..a45bc2c46 100644 --- a/src/host/trxcon/src/l1ctl.c +++ b/src/host/trxcon/src/l1ctl.c @@ -785,6 +785,7 @@ static int l1ctl_rx_crypto_req(struct trxcon_inst *trxcon, struct msgb *msg) int trxcon_l1ctl_receive(struct trxcon_inst *trxcon, struct msgb *msg) { const struct l1ctl_hdr *l1h; + int rc; l1h = (const struct l1ctl_hdr *)msg->l1h; msg->l1h = (uint8_t *)l1h->data; @@ -818,11 +819,20 @@ int trxcon_l1ctl_receive(struct trxcon_inst *trxcon, struct msgb *msg) return l1ctl_rx_tch_mode_req(trxcon, msg); case L1CTL_CRYPTO_REQ: return l1ctl_rx_crypto_req(trxcon, msg); - + case L1CTL_GPRS_UL_TBF_CFG_REQ: + rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_GPRS_UL_TBF_CFG_REQ, msg); + msgb_free(msg); + return rc; + case L1CTL_GPRS_DL_TBF_CFG_REQ: + rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_GPRS_DL_TBF_CFG_REQ, msg); + msgb_free(msg); + return rc; + case L1CTL_GPRS_UL_BLOCK_REQ: + rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_GPRS_UL_BLOCK_REQ, msg); + msgb_free(msg); + return rc; /* Not (yet) handled messages */ case L1CTL_NEIGH_PM_REQ: - case L1CTL_DATA_TBF_REQ: - case L1CTL_TBF_CFG_REQ: case L1CTL_DM_FREQ_REQ: case L1CTL_SIM_REQ: LOGPFSMSL(trxcon->fi, g_logc_l1c, LOGL_NOTICE, diff --git a/src/host/trxcon/src/l1gprs.c b/src/host/trxcon/src/l1gprs.c new file mode 120000 index 000000000..0185f68b7 --- /dev/null +++ b/src/host/trxcon/src/l1gprs.c @@ -0,0 +1 @@ +../../../shared/l1gprs.c \ No newline at end of file diff --git a/src/host/trxcon/src/logging.c b/src/host/trxcon/src/logging.c index 2222577b0..e94ad2d8f 100644 --- a/src/host/trxcon/src/logging.c +++ b/src/host/trxcon/src/logging.c @@ -67,6 +67,12 @@ static struct log_info_cat trxcon_log_info_cat[] = { .color = "\033[1;36m", .enabled = 1, .loglevel = LOGL_NOTICE, }, + [DGPRS] = { + .name = "DGPRS", + .description = "L1 GPRS (MAC layer)", + .color = "\033[1;36m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, }; static const struct log_info trxcon_log_info = { @@ -80,6 +86,7 @@ static const int trxcon_log_cfg[] = { [TRXCON_LOGC_L1D] = DL1D, [TRXCON_LOGC_SCHC] = DSCH, [TRXCON_LOGC_SCHD] = DSCHD, + [TRXCON_LOGC_GPRS] = DGPRS, }; int trxcon_logging_init(void *tall_ctx, const char *category_mask) diff --git a/src/host/trxcon/src/trxcon_fsm.c b/src/host/trxcon/src/trxcon_fsm.c index 186c04bcc..053ca2f66 100644 --- a/src/host/trxcon/src/trxcon_fsm.c +++ b/src/host/trxcon/src/trxcon_fsm.c @@ -2,7 +2,7 @@ * OsmocomBB <-> SDR connection bridge * The trxcon state machine (see 3GPP TS 44.004, section 5.1) * - * (C) 2022 by sysmocom - s.f.m.c. GmbH + * (C) 2022-2023 by sysmocom - s.f.m.c. GmbH * Author: Vadim Yanitskiy * * All Rights Reserved @@ -29,12 +29,14 @@ #include #include #include +#include #include #include #include #include #include +#include #define S(x) (1 << (x)) @@ -505,6 +507,25 @@ static void trxcon_st_dedicated_action(struct osmo_fsm_inst *fi, } } +static void trxcon_st_packet_data_onenter(struct osmo_fsm_inst *fi, + uint32_t prev_state) +{ + struct trxcon_inst *trxcon = fi->priv; + + OSMO_ASSERT(trxcon->gprs == NULL); + trxcon->gprs = l1gprs_state_alloc(trxcon, trxcon->log_prefix, trxcon); + OSMO_ASSERT(trxcon->gprs != NULL); +} + +static void trxcon_st_packet_data_onleave(struct osmo_fsm_inst *fi, + uint32_t next_state) +{ + struct trxcon_inst *trxcon = fi->priv; + + l1gprs_state_free(trxcon->gprs); + trxcon->gprs = NULL; +} + static void trxcon_st_packet_data_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) { @@ -517,14 +538,58 @@ static void trxcon_st_packet_data_action(struct osmo_fsm_inst *fi, case TRXCON_EV_TX_ACCESS_BURST_CNF: l1ctl_tx_rach_conf(trxcon, (const struct trxcon_param_tx_access_burst_cnf *)data); break; + case TRXCON_EV_GPRS_UL_TBF_CFG_REQ: + l1gprs_handle_ul_tbf_cfg_req(trxcon->gprs, (struct msgb *)data); + break; + case TRXCON_EV_GPRS_DL_TBF_CFG_REQ: + l1gprs_handle_dl_tbf_cfg_req(trxcon->gprs, (struct msgb *)data); + break; + 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; + + 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; + } + break; + } case TRXCON_EV_RX_DATA_IND: { const struct trxcon_param_rx_data_ind *ind = data; + struct l1gprs_prim_dl_block_ind block_ind; + struct msgb *msg; - if (ind->link_id == 0x00) - LOGPFSML(fi, LOGL_NOTICE, "Rx PDTCH/D message\n"); + block_ind = (struct l1gprs_prim_dl_block_ind) { + .hdr = { + .fn = ind->frame_nr, + .tn = ind->chan_nr & 0x07, + }, + .meas = { + /* .ber10k is set below */ + .ci_cb = 180, /* 18 dB */ + .rx_lev = dbm2rxlev(ind->rssi), + }, + .data_len = ind->data_len, + .data = ind->data, + }; + + if (ind->n_bits_total == 0) + block_ind.meas.ber10k = 10000; else - LOGPFSML(fi, LOGL_NOTICE, "Rx PTCCH/D message\n"); + block_ind.meas.ber10k = 10000 * ind->n_errors / ind->n_bits_total; + + msg = l1gprs_handle_dl_block_ind(trxcon->gprs, &block_ind); + if (msg != NULL) + trxcon_l1ctl_send(trxcon, msg); break; } case TRXCON_EV_DCH_EST_REQ: @@ -550,6 +615,9 @@ static void trxcon_fsm_pre_term_cb(struct osmo_fsm_inst *fi, /* Shutdown the scheduler */ if (trxcon->sched != NULL) l1sched_free(trxcon->sched); + /* Clean up GPRS L1 state */ + l1gprs_state_free(trxcon->gprs); + /* Close active connections */ if (trxcon->l2if != NULL) trxcon_l1ctl_close(trxcon); @@ -625,7 +693,12 @@ static const struct osmo_fsm_state trxcon_fsm_states[] = { | S(TRXCON_EV_DCH_EST_REQ) | S(TRXCON_EV_TX_ACCESS_BURST_REQ) | S(TRXCON_EV_TX_ACCESS_BURST_CNF) + | S(TRXCON_EV_GPRS_UL_TBF_CFG_REQ) + | S(TRXCON_EV_GPRS_DL_TBF_CFG_REQ) + | S(TRXCON_EV_GPRS_UL_BLOCK_REQ) | S(TRXCON_EV_RX_DATA_IND), + .onenter = &trxcon_st_packet_data_onenter, + .onleave = &trxcon_st_packet_data_onleave, .action = &trxcon_st_packet_data_action, }, }; @@ -651,6 +724,9 @@ static const struct value_string trxcon_fsm_event_names[] = { OSMO_VALUE_STRING(TRXCON_EV_TX_DATA_CNF), OSMO_VALUE_STRING(TRXCON_EV_RX_DATA_IND), OSMO_VALUE_STRING(TRXCON_EV_CRYPTO_REQ), + OSMO_VALUE_STRING(TRXCON_EV_GPRS_UL_TBF_CFG_REQ), + OSMO_VALUE_STRING(TRXCON_EV_GPRS_DL_TBF_CFG_REQ), + OSMO_VALUE_STRING(TRXCON_EV_GPRS_UL_BLOCK_REQ), { 0, NULL } }; diff --git a/src/host/trxcon/src/trxcon_inst.c b/src/host/trxcon/src/trxcon_inst.c index da4d61800..65e63b3af 100644 --- a/src/host/trxcon/src/trxcon_inst.c +++ b/src/host/trxcon/src/trxcon_inst.c @@ -27,6 +27,7 @@ #include #include #include +#include extern int g_logc_l1c; extern int g_logc_l1d; @@ -53,6 +54,9 @@ void trxcon_set_log_cfg(const int *logc, unsigned int logc_num) case TRXCON_LOGC_SCHD: schd = logc[i]; break; + case TRXCON_LOGC_GPRS: + l1gprs_logging_init(logc[i]); + break; } } diff --git a/src/host/virt_phy/include/osmocom/bb/Makefile.am b/src/host/virt_phy/include/osmocom/bb/Makefile.am index c52fb9371..9a4b939ad 100644 --- a/src/host/virt_phy/include/osmocom/bb/Makefile.am +++ b/src/host/virt_phy/include/osmocom/bb/Makefile.am @@ -4,4 +4,5 @@ SUBDIRS = \ noinst_HEADERS = \ l1ctl_proto.h \ + l1gprs.h \ $(NULL) diff --git a/src/host/virt_phy/include/osmocom/bb/l1gprs.h b/src/host/virt_phy/include/osmocom/bb/l1gprs.h new file mode 120000 index 000000000..3bf85176e --- /dev/null +++ b/src/host/virt_phy/include/osmocom/bb/l1gprs.h @@ -0,0 +1 @@ +../../../../../../include/l1gprs.h \ No newline at end of file diff --git a/src/host/virt_phy/include/osmocom/bb/virtphy/l1ctl_sap.h b/src/host/virt_phy/include/osmocom/bb/virtphy/l1ctl_sap.h index 724d238b0..e756c140f 100644 --- a/src/host/virt_phy/include/osmocom/bb/virtphy/l1ctl_sap.h +++ b/src/host/virt_phy/include/osmocom/bb/virtphy/l1ctl_sap.h @@ -57,6 +57,8 @@ void l1ctl_rx_tch_mode_req(struct l1_model_ms *ms, struct msgb *msg); void l1ctl_rx_neigh_pm_req(struct l1_model_ms *ms, struct msgb *msg); void l1ctl_rx_traffic_req(struct l1_model_ms *ms, struct msgb *msg); void l1ctl_rx_sim_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_gprs_uldl_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_gprs_ul_block_req(struct l1_model_ms *ms, struct msgb *msg); /* transmit routines */ void l1ctl_tx_reset(struct l1_model_ms *ms, uint8_t msg_type, uint8_t reset_type); @@ -75,6 +77,8 @@ void l1ctl_tx_pm_conf(struct l1_model_ms *ms, struct l1ctl_pm_req *pm_req); void l1ctl_tx_fbsb_conf(struct l1_model_ms *ms, uint8_t res, uint16_t arfcn); void l1ctl_tx_ccch_mode_conf(struct l1_model_ms *ms, uint8_t ccch_mode); void l1ctl_tx_tch_mode_conf(struct l1_model_ms *ms, uint8_t tch_mode, uint8_t audio_mode); +void l1ctl_tx_gprs_dl_block_ind(struct l1_model_ms *ms, const struct msgb *msg, + uint32_t fn, uint8_t tn, uint8_t rxlev); /* scheduler functions */ uint32_t sched_fn_ul(struct gsm_time cur_time, uint8_t chan_nr, diff --git a/src/host/virt_phy/include/osmocom/bb/virtphy/logging.h b/src/host/virt_phy/include/osmocom/bb/virtphy/logging.h index b22db9923..48f7ed556 100644 --- a/src/host/virt_phy/include/osmocom/bb/virtphy/logging.h +++ b/src/host/virt_phy/include/osmocom/bb/virtphy/logging.h @@ -7,6 +7,7 @@ enum virtphy_log_cat { DL1C, DL1P, DVIRPHY, + DGPRS, DMAIN }; diff --git a/src/host/virt_phy/include/osmocom/bb/virtphy/virt_l1_model.h b/src/host/virt_phy/include/osmocom/bb/virtphy/virt_l1_model.h index b729c4438..94581f616 100644 --- a/src/host/virt_phy/include/osmocom/bb/virtphy/virt_l1_model.h +++ b/src/host/virt_phy/include/osmocom/bb/virtphy/virt_l1_model.h @@ -16,7 +16,6 @@ enum ms_state { MS_STATE_IDLE_SYNCING, MS_STATE_IDLE_CAMPING, MS_STATE_DEDICATED, - MS_STATE_TBF }; @@ -77,15 +76,6 @@ struct l1_state_ms { uint8_t tsc; // training sequence code (unused in virtual um) uint8_t h; // hopping enabled flag (unused 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 { @@ -116,6 +106,8 @@ struct l1_model_ms { struct l1ctl_sock_client *lsc; /* pointer to the (shared) GSMTAP/VirtUM socket to talk to BTS(s) */ struct virt_um_inst *vui; + /* GPRS state (MAC layer) */ + struct l1gprs_state *gprs; /* actual per-MS state */ struct l1_state_ms state; }; diff --git a/src/host/virt_phy/src/Makefile.am b/src/host/virt_phy/src/Makefile.am index 902284d09..f10639f52 100644 --- a/src/host/virt_phy/src/Makefile.am +++ b/src/host/virt_phy/src/Makefile.am @@ -5,6 +5,7 @@ bin_PROGRAMS = virtphy virtphy_SOURCES = \ virtphy.c \ + l1gprs.c \ logging.c \ gsmtapl1_if.c \ l1ctl_sock.c \ @@ -13,6 +14,7 @@ virtphy_SOURCES = \ virt_prim_fbsb.c \ virt_prim_rach.c \ virt_prim_data.c \ + virt_prim_pdch.c \ virt_prim_traffic.c \ virt_l1_sched_simple.c \ virt_l1_model.c \ diff --git a/src/host/virt_phy/src/gsmtapl1_if.c b/src/host/virt_phy/src/gsmtapl1_if.c index f5c49af42..ef8d68406 100644 --- a/src/host/virt_phy/src/gsmtapl1_if.c +++ b/src/host/virt_phy/src/gsmtapl1_if.c @@ -94,23 +94,16 @@ void gsmtapl1_tx_to_virt_um_inst(struct l1_model_ms *ms, uint32_t fn, uint8_t tn uint8_t timeslot; /* tdma timeslot to send in (0-7) */ uint8_t gsmtap_chan; /* the gsmtap channel */ - switch (ms->state.state) { - case MS_STATE_DEDICATED: - case MS_STATE_TBF: + if (ms->state.state == MS_STATE_DEDICATED) arfcn = ms->state.dedicated.band_arfcn; - break; - default: + else arfcn = ms->state.serving_cell.arfcn; - break; - } switch (l1h->msg_type) { - case L1CTL_DATA_TBF_REQ: - ul = NULL; - rsl_chantype = RSL_CHAN_OSMO_PDCH; + case L1CTL_GPRS_UL_BLOCK_REQ: + gsmtap_chan = GSMTAP_CHANNEL_PDCH; timeslot = tn; subslot = 0; - gsmtap_chan = chantype_rsl2gsmtap2(rsl_chantype, 0, false); break; case L1CTL_TRAFFIC_REQ: ul = (struct l1ctl_info_ul *)l1h->data; @@ -165,77 +158,6 @@ extern void prim_fbsb_sync(struct l1_model_ms *ms, struct msgb *msg); */ 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 addressed - * 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, @@ -243,7 +165,6 @@ static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg, { struct l1_model_ms *ms = lsc->priv; uint8_t rxlev = dbm2rxlev(prim_pm_set_sig_strength(ms, arfcn & GSMTAP_ARFCN_MASK, MAX_SIG_LEV_DBM)); - uint8_t usf; gsm_fn2gsmtime(&ms->state.downlink_time, fn); @@ -310,16 +231,13 @@ static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg, case GSMTAP_CHANNEL_RACH: LOGPMS(DVIRPHY, LOGL_NOTICE, ms, "Ignoring unexpected RACH in downlink ?!?\n"); break; + case GSMTAP_CHANNEL_PTCCH: 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, rxlev, 0, 0); - usf = get_usf_from_block(msg); - ms_ul_tbf_may_transmit(ms, arfcn, timeslot, fn, usf); + l1ctl_tx_gprs_dl_block_ind(ms, msg, fn, timeslot, rxlev); 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)); break; diff --git a/src/host/virt_phy/src/l1ctl_sap.c b/src/host/virt_phy/src/l1ctl_sap.c index ab917c6a2..7e4540df4 100644 --- a/src/host/virt_phy/src/l1ctl_sap.c +++ b/src/host/virt_phy/src/l1ctl_sap.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -40,9 +39,7 @@ #include #include #include - -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); +#include static void l1_model_tch_mode_set(struct l1_model_ms *ms, uint8_t tch_mode) { @@ -60,7 +57,6 @@ static void l1_model_tch_mode_set(struct l1_model_ms *ms, uint8_t tch_mode) 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); } @@ -163,8 +159,8 @@ static bool is_l1ctl_control(uint8_t msg_type) switch (msg_type) { case L1CTL_DATA_REQ: case L1CTL_DATA_CONF: - case L1CTL_DATA_TBF_REQ: - case L1CTL_DATA_TBF_CONF: + case L1CTL_GPRS_UL_BLOCK_REQ: + case L1CTL_GPRS_DL_BLOCK_IND: case L1CTL_TRAFFIC_REQ: case L1CTL_TRAFFIC_CONF: case L1CTL_TRAFFIC_IND: @@ -247,11 +243,12 @@ void l1ctl_sap_handler(struct l1_model_ms *ms, struct msgb *msg) case L1CTL_SIM_REQ: l1ctl_rx_sim_req(ms, msg); break; - case L1CTL_TBF_CFG_REQ: - l1ctl_rx_tbf_cfg_req(ms, msg); + case L1CTL_GPRS_UL_TBF_CFG_REQ: + case L1CTL_GPRS_DL_TBF_CFG_REQ: + l1ctl_rx_gprs_uldl_tbf_cfg_req(ms, msg); break; - case L1CTL_DATA_TBF_REQ: - l1ctl_rx_data_tbf_req(ms, msg); + case L1CTL_GPRS_UL_BLOCK_REQ: + l1ctl_rx_gprs_ul_block_req(ms, msg); goto exit_nofree; } @@ -297,6 +294,12 @@ void l1ctl_rx_dm_est_req(struct l1_model_ms *ms, struct msgb *msg) ms->state.dedicated.subslot = subslot; ms->state.state = MS_STATE_DEDICATED; + if (rsl_chantype == RSL_CHAN_OSMO_PDCH) { + OSMO_ASSERT(ms->gprs == NULL); + ms->gprs = l1gprs_state_alloc(ms, NULL, ms); + OSMO_ASSERT(ms->gprs != NULL); + } + /* TCH config */ if (rsl_chantype == RSL_CHAN_Bm_ACCHs || rsl_chantype == RSL_CHAN_Lm_ACCHs) { ms->state.tch_mode = est_req->tch_mode; @@ -381,6 +384,9 @@ void l1ctl_rx_dm_rel_req(struct l1_model_ms *ms, struct msgb *msg) ms->state.tch_mode = GSM48_CMODE_SIGN; ms->state.state = MS_STATE_IDLE_CAMPING; + l1gprs_state_free(ms->gprs); + ms->gprs = NULL; + /* TODO: disable ciphering */ /* TODO: disable audio recording / playing */ } @@ -429,6 +435,8 @@ void l1ctl_rx_reset_req(struct l1_model_ms *ms, struct msgb *msg) DEBUGPMS(DL1C, ms, "Rx L1CTL_RESET_REQ (type=FULL)\n"); ms->state.state = MS_STATE_IDLE_SEARCHING; virt_l1_sched_stop(ms); + l1gprs_state_free(ms->gprs); + ms->gprs = NULL; l1ctl_tx_reset(ms, L1CTL_RESET_CONF, reset_req->type); break; case L1CTL_RES_T_SCHED: @@ -547,145 +555,12 @@ void l1ctl_rx_sim_req(struct l1_model_ms *ms, struct msgb *msg) } -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, "Hard termination 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); -} - -#if 0 -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]; -} -#endif - -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. * diff --git a/src/host/virt_phy/src/l1gprs.c b/src/host/virt_phy/src/l1gprs.c new file mode 120000 index 000000000..0185f68b7 --- /dev/null +++ b/src/host/virt_phy/src/l1gprs.c @@ -0,0 +1 @@ +../../../shared/l1gprs.c \ No newline at end of file diff --git a/src/host/virt_phy/src/logging.c b/src/host/virt_phy/src/logging.c index 04667d1fe..0a1b2a418 100644 --- a/src/host/virt_phy/src/logging.c +++ b/src/host/virt_phy/src/logging.c @@ -54,10 +54,10 @@ static const char* l1ctlPrimNames[] = { "L1CTL_TRAFFIC_CONF", "L1CTL_TRAFFIC_IND", "L1CTL_BURST_IND", - "L1CTL_TBF_CFG_REQ", - "L1CTL_TBF_CFG_CONF", - "L1CTL_DATA_TBF_REQ", - "L1CTL_DATA_TBF_CONF" + "L1CTL_GPRS_UL_TBF_CFG_REQ", + "L1CTL_GPRS_DL_TBF_CFG_REQ", + "L1CTL_GPRS_UL_BLOCK_REQ", + "L1CTL_GPRS_DL_BLOCK_IND", }; static const struct log_info_cat default_categories[] = { @@ -82,6 +82,13 @@ static const struct log_info_cat default_categories[] = { .enabled = 1, .loglevel = LOGL_NOTICE, }, + [DGPRS] = { + .name = "DGPRS", + .description = "L1 GPRS (MAC leyer)", + .color = "\033[1;31m", + .enabled = 1, + .loglevel = LOGL_NOTICE, + }, [DMAIN] = { .name = "DMAIN", .description = "Main Program / Data Structures", diff --git a/src/host/virt_phy/src/virt_prim_pdch.c b/src/host/virt_phy/src/virt_prim_pdch.c new file mode 100644 index 000000000..7125a70bd --- /dev/null +++ b/src/host/virt_phy/src/virt_prim_pdch.c @@ -0,0 +1,106 @@ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH + * Author: Vadim Yanitskiy + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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. + * + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +void l1ctl_rx_gprs_uldl_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg) +{ + const struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data; + + if (OSMO_UNLIKELY(ms->gprs == NULL)) { + LOGPMS(DL1C, LOGL_ERROR, ms, "l1gprs is not initialized\n"); + return; + } + + msg->l1h = msgb_pull(msg, sizeof(*l1h)); + + if (l1h->msg_type == L1CTL_GPRS_UL_TBF_CFG_REQ) + l1gprs_handle_ul_tbf_cfg_req(ms->gprs, msg); + else + l1gprs_handle_dl_tbf_cfg_req(ms->gprs, msg); +} + +void l1ctl_rx_gprs_ul_block_req(struct l1_model_ms *ms, struct msgb *msg) +{ + const struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data; + struct l1gprs_prim_ul_block_req req; + uint32_t fn_sched; + + if (OSMO_UNLIKELY(ms->gprs == NULL)) { + LOGPMS(DL1P, LOGL_ERROR, ms, "l1gprs is not initialized\n"); + return; + } + + msg->l1h = (void *)l1h->data; + if (l1gprs_handle_ul_block_req(ms->gprs, &req, msg) != 0) + return; + msg->l2h = (void *)&req.data[0]; + + fn_sched = sched_fn_ul(ms->state.current_time, + RSL_CHAN_OSMO_PDCH | req.hdr.tn, 0x00); + if (OSMO_UNLIKELY(fn_sched != req.hdr.fn)) { + LOGPMS(DL1P, LOGL_ERROR, ms, + "GPRS UL BLOCK.req: fn_sched(%u) != fn_req(%u)\n", + fn_sched, req.hdr.fn); + /* FIXME: return; */ + } + + virt_l1_sched_schedule(ms, msg, fn_sched, req.hdr.tn, + &gsmtapl1_tx_to_virt_um_inst); +} + +void l1ctl_tx_gprs_dl_block_ind(struct l1_model_ms *ms, const struct msgb *msg, + uint32_t fn, uint8_t tn, uint8_t rxlev) +{ + struct l1gprs_prim_dl_block_ind ind; + struct msgb *nmsg; + + if (ms->gprs == NULL) + return; + + ind = (struct l1gprs_prim_dl_block_ind) { + .hdr = { + .fn = fn, + .tn = tn, + }, + .meas = { + .ber10k = 0, /* perfect Um, no errors */ + .ci_cb = 180, /* 18 dB */ + .rx_lev = rxlev, + }, + .data = msgb_data(msg), + .data_len = msgb_length(msg), + }; + + nmsg = l1gprs_handle_dl_block_ind(ms->gprs, &ind); + if (nmsg != NULL) + l1ctl_sap_tx_to_l23_inst(ms, nmsg); +} diff --git a/src/host/virt_phy/src/virtphy.c b/src/host/virt_phy/src/virtphy.c index ad09a53c1..005ae2dd5 100644 --- a/src/host/virt_phy/src/virtphy.c +++ b/src/host/virt_phy/src/virtphy.c @@ -40,8 +40,9 @@ #include #include #include +#include -#define DEFAULT_LOG_MASK "DL1C,2:DL1P,2:DVIRPHY,2:DMAIN,1" +#define DEFAULT_LOG_MASK "DL1C,2:DL1P,2:DVIRPHY,2:DGPRS,1:DMAIN,1" /* this exists once in the program, and contains the state that we * only keep once: L1CTL server socket, GSMTAP/VirtUM socket */ @@ -242,6 +243,7 @@ int main(int argc, char *argv[]) handle_options(argc, argv); ms_log_init(log_mask); + l1gprs_logging_init(DGPRS); LOGP(DVIRPHY, LOGL_INFO, "Virtual physical layer starting up...\n"); diff --git a/src/shared/l1gprs.c b/src/shared/l1gprs.c new file mode 100644 index 000000000..3babd2b5e --- /dev/null +++ b/src/shared/l1gprs.c @@ -0,0 +1,457 @@ +/* + * l1gprs - GPRS layer 1 implementation + * + * (C) 2022-2023 by sysmocom - s.f.m.c. GmbH + * Author: Vadim Yanitskiy + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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. + * + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define LOGP_GPRS(gprs, level, fmt, args...) \ + LOGP(l1gprs_log_cat, level, "%s" fmt, \ + (gprs)->log_prefix, ## args) + +#define LOGP_PDCH(pdch, level, fmt, args...) \ + LOGP_GPRS((pdch)->gprs, level, "(PDCH-%u) " fmt, \ + (pdch)->tn, ## args) + +#define LOG_TBF_FMT "%cL-TBF#%03d" +#define LOG_TBF_ARGS(tbf) \ + (tbf)->uplink ? 'U' : 'D', tbf->tbf_ref + +static int l1gprs_log_cat = DLGLOBAL; + +enum gprs_rlcmac_block_type { + GPRS_RLCMAC_DATA_BLOCK = 0x00, + GPRS_RLCMAC_CONTROL_BLOCK = 0x01, + GPRS_RLCMAC_CONTROL_BLOCK_OPT = 0x02, + GPRS_RLCMAC_RESERVED = 0x03, +}; + +static struct l1gprs_tbf *l1gprs_tbf_alloc(struct l1gprs_state *gprs, + bool uplink, uint8_t tbf_ref, + uint8_t slotmask) +{ + struct l1gprs_tbf *tbf; + + tbf = talloc(gprs, struct l1gprs_tbf); + OSMO_ASSERT(tbf != NULL); + + tbf->uplink = uplink; + tbf->tbf_ref = tbf_ref; + tbf->slotmask = slotmask; + + return tbf; +} + +static void l1gprs_tbf_free(struct l1gprs_tbf *tbf) +{ + if (tbf == NULL) + return; + llist_del(&tbf->list); + talloc_free(tbf); +} + +static struct l1gprs_tbf *l1gprs_find_tbf(struct l1gprs_state *gprs, + bool uplink, uint8_t tbf_ref) +{ + struct l1gprs_tbf *tbf; + + llist_for_each_entry(tbf, &gprs->tbf_list, list) { + if (tbf->uplink != uplink) + continue; + if (tbf->tbf_ref != tbf_ref) + continue; + return tbf; + } + + return NULL; +} + +static void l1gprs_register_tbf(struct l1gprs_state *gprs, + struct l1gprs_tbf *tbf) +{ + OSMO_ASSERT(tbf->slotmask != 0x00); + + /* Update the PDCH states */ + for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) { + struct l1gprs_pdch *pdch = &gprs->pdch[tn]; + + if (~tbf->slotmask & (1 << pdch->tn)) + continue; + + if (tbf->uplink) { + pdch->ul_tbf_count++; + } else { + pdch->dl_tbf_count++; + pdch->dl_tfi_mask |= (1 << tbf->dl_tfi); + } + + LOGP_PDCH(pdch, LOGL_DEBUG, + "Linked " LOG_TBF_FMT "\n", + LOG_TBF_ARGS(tbf)); + } + + llist_add_tail(&tbf->list, &gprs->tbf_list); + + LOGP_GPRS(gprs, LOGL_INFO, + LOG_TBF_FMT " is registered\n", + LOG_TBF_ARGS(tbf)); +} + +static void l1gprs_unregister_tbf(struct l1gprs_state *gprs, + bool uplink, uint8_t tbf_ref) +{ + struct l1gprs_tbf *tbf; + + tbf = l1gprs_find_tbf(gprs, uplink, tbf_ref); + if (tbf == NULL) { + LOGP_GPRS(gprs, LOGL_ERROR, + "%s(): " LOG_TBF_FMT " not found\n", + __func__, LOG_TBF_ARGS(tbf)); + return; + } + + /* Update the PDCH states */ + for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) { + struct l1gprs_pdch *pdch = &gprs->pdch[tn]; + + if (~tbf->slotmask & (1 << pdch->tn)) + continue; + + if (tbf->uplink) { + OSMO_ASSERT(pdch->ul_tbf_count > 0); + pdch->ul_tbf_count--; + } else { + OSMO_ASSERT(pdch->dl_tbf_count > 0); + pdch->dl_tbf_count--; + pdch->dl_tfi_mask &= ~(1 << tbf->dl_tfi); + } + + LOGP_PDCH(pdch, LOGL_DEBUG, + "Unlinked " LOG_TBF_FMT "\n", + LOG_TBF_ARGS(tbf)); + } + + LOGP_GPRS(gprs, LOGL_INFO, + LOG_TBF_FMT " is unregistered and free()d\n", + LOG_TBF_ARGS(tbf)); + + l1gprs_tbf_free(tbf); +} + +#define L1GPRS_L1CTL_MSGB_SIZE 256 +#define L1GPRS_L1CTL_MSGB_HEADROOM 32 + +static struct msgb *l1gprs_l1ctl_msgb_alloc(uint8_t msg_type) +{ + struct l1ctl_hdr *l1h; + struct msgb *msg; + + msg = msgb_alloc_headroom(L1GPRS_L1CTL_MSGB_SIZE, + L1GPRS_L1CTL_MSGB_HEADROOM, + "l1gprs_l1ctl_msg"); + if (msg == NULL) + return NULL; + + msg->l1h = msgb_put(msg, sizeof(*l1h)); + l1h = (struct l1ctl_hdr *)msg->l1h; + l1h->msg_type = msg_type; + + return msg; +} + +static bool l1gprs_pdch_filter_dl_block(const struct l1gprs_pdch *pdch, + const uint8_t *data) +{ + enum gprs_rlcmac_block_type block_type = data[0] >> 6; + uint8_t dl_tfi; + + switch (block_type) { + case GPRS_RLCMAC_DATA_BLOCK: + /* see 3GPP TS 44.060, section 10.2.1 */ + dl_tfi = (data[1] >> 1) & 0x1f; + break; + case GPRS_RLCMAC_CONTROL_BLOCK_OPT: + /* see 3GPP TS 44.060, section 10.3.1 */ + dl_tfi = (data[2] >> 1) & 0x1f; + break; + case GPRS_RLCMAC_CONTROL_BLOCK: + /* no optional octets */ + return true; + default: + LOGP_PDCH(pdch, LOGL_NOTICE, + "Rx Downlink block with unknown payload (0x%0x)\n", + block_type); + return false; + } + + if (pdch->dl_tfi_mask & (1 << dl_tfi)) + return true; + return false; +} + +/////////////////////////////////////////////////////////////////////////////////// + +void l1gprs_logging_init(int logc) +{ + l1gprs_log_cat = logc; +} + +struct l1gprs_state *l1gprs_state_alloc(void *ctx, const char *log_prefix, void *priv) +{ + struct l1gprs_state *gprs; + + gprs = talloc_zero(ctx, struct l1gprs_state); + if (gprs == NULL) + return NULL; + + for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) { + struct l1gprs_pdch *pdch = &gprs->pdch[tn]; + + pdch->tn = tn; + pdch->gprs = gprs; + } + + INIT_LLIST_HEAD(&gprs->tbf_list); + + if (log_prefix == NULL) + gprs->log_prefix = talloc_asprintf(gprs, "l1gprs[0x%p]: ", gprs); + else + gprs->log_prefix = talloc_strdup(gprs, log_prefix); + gprs->priv = priv; + + return gprs; +} + +void l1gprs_state_free(struct l1gprs_state *gprs) +{ + if (gprs == NULL) + return; + + while (!llist_empty(&gprs->tbf_list)) { + struct l1gprs_tbf *tbf; + + tbf = llist_first_entry(&gprs->tbf_list, struct l1gprs_tbf, list); + LOGP_GPRS(gprs, LOGL_DEBUG, + "%s(): " LOG_TBF_FMT " is free()d\n", + __func__, LOG_TBF_ARGS(tbf)); + l1gprs_tbf_free(tbf); + } + + talloc_free(gprs->log_prefix); + talloc_free(gprs); +} + +int l1gprs_handle_ul_tbf_cfg_req(struct l1gprs_state *gprs, const struct msgb *msg) +{ + const struct l1ctl_gprs_ul_tbf_cfg_req *req = (void *)msg->l1h; + struct l1gprs_tbf *tbf = NULL; + + OSMO_ASSERT(req != NULL); + + if (msgb_l1len(msg) < sizeof(*req)) { + LOGP_GPRS(gprs, LOGL_ERROR, + "Rx malformed Uplink TBF config (len=%u < %zu)\n", + msgb_l1len(msg), sizeof(*req)); + return -EINVAL; + } + + LOGP_GPRS(gprs, LOGL_INFO, + "Rx Uplink TBF config: tbf_ref=%u, slotmask=0x%02x\n", + req->tbf_ref, req->slotmask); + + if (req->slotmask != 0x00) { + tbf = l1gprs_tbf_alloc(gprs, true, req->tbf_ref, req->slotmask); + l1gprs_register_tbf(gprs, tbf); + } else { + l1gprs_unregister_tbf(gprs, true, req->tbf_ref); + } + + return 0; +} + +int l1gprs_handle_dl_tbf_cfg_req(struct l1gprs_state *gprs, const struct msgb *msg) +{ + const struct l1ctl_gprs_dl_tbf_cfg_req *req = (void *)msg->l1h; + struct l1gprs_tbf *tbf = NULL; + + OSMO_ASSERT(req != NULL); + + if (msgb_l1len(msg) < sizeof(*req)) { + LOGP_GPRS(gprs, LOGL_ERROR, + "Rx malformed Downlink TBF config (len=%u < %zu)\n", + msgb_l1len(msg), sizeof(*req)); + return -EINVAL; + } + + LOGP_GPRS(gprs, LOGL_INFO, + "Rx Downlink TBF config: tbf_ref=%u, slotmask=0x%02x, dl_tfi=%u\n", + req->tbf_ref, req->slotmask, req->dl_tfi); + + if (req->dl_tfi > 31) { + LOGP_GPRS(gprs, LOGL_ERROR, + "Invalid DL TFI %u (shall be in range 0..31)\n", + req->dl_tfi); + return -EINVAL; + } + + if (req->slotmask != 0x00) { + tbf = l1gprs_tbf_alloc(gprs, false, req->tbf_ref, req->slotmask); + tbf->dl_tfi = req->dl_tfi; + l1gprs_register_tbf(gprs, tbf); + } else { + l1gprs_unregister_tbf(gprs, false, req->tbf_ref); + } + + return 0; +} + +int l1gprs_handle_ul_block_req(struct l1gprs_state *gprs, + struct l1gprs_prim_ul_block_req *req, + const struct msgb *msg) +{ + const struct l1ctl_gprs_ul_block_req *l1br = (void *)msg->l1h; + const struct l1gprs_pdch *pdch = NULL; + size_t data_len; + + OSMO_ASSERT(l1br != NULL); + + if (OSMO_UNLIKELY(msgb_l1len(msg) < sizeof(*l1br))) { + LOGP_GPRS(gprs, LOGL_ERROR, + "Rx malformed UL BLOCK.req (len=%u < %zu)\n", + msgb_l1len(msg), sizeof(*l1br)); + return -EINVAL; + } + if (OSMO_UNLIKELY(l1br->hdr.tn >= ARRAY_SIZE(gprs->pdch))) { + LOGP_GPRS(gprs, LOGL_ERROR, + "Rx malformed UL BLOCK.req (tn=%u)\n", + l1br->hdr.tn); + return -EINVAL; + } + + pdch = &gprs->pdch[l1br->hdr.tn]; + data_len = msgb_l1len(msg) - sizeof(*l1br); + + LOGP_PDCH(pdch, LOGL_DEBUG, + "Rx UL BLOCK.req (fn=%u, len=%zu): %s\n", + ntohl(l1br->hdr.fn), data_len, osmo_hexdump(l1br->data, data_len)); + + if ((pdch->ul_tbf_count == 0) && (pdch->dl_tbf_count == 0)) { + LOGP_PDCH(pdch, LOGL_ERROR, + "Rx UL BLOCK.req, but this PDCH has no configured TBFs\n"); + return -EINVAL; + } + + *req = (struct l1gprs_prim_ul_block_req) { + .hdr = { + .fn = ntohl(l1br->hdr.fn), + .tn = l1br->hdr.tn, + }, + .data = &l1br->data[0], + .data_len = data_len, + }; + + return 0; +} + +struct msgb *l1gprs_handle_dl_block_ind(struct l1gprs_state *gprs, + const struct l1gprs_prim_dl_block_ind *ind) +{ + const struct l1gprs_pdch *pdch = NULL; + struct l1ctl_gprs_dl_block_ind *l1bi; + enum osmo_gprs_cs cs; + struct msgb *msg; + + if (OSMO_UNLIKELY(ind->hdr.tn >= ARRAY_SIZE(gprs->pdch))) { + LOGP_GPRS(gprs, LOGL_ERROR, + "Rx malformed DL BLOCK.ind (tn=%u)\n", + ind->hdr.tn); + return NULL; + } + + pdch = &gprs->pdch[ind->hdr.tn]; + + LOGP_PDCH(pdch, LOGL_DEBUG, + "Rx DL BLOCK.ind (fn=%u, len=%zu): %s\n", + ind->hdr.fn, ind->data_len, osmo_hexdump(ind->data, ind->data_len)); + + if ((pdch->ul_tbf_count == 0) && (pdch->dl_tbf_count == 0)) { + LOGP_PDCH(pdch, LOGL_ERROR, + "Rx DL BLOCK.ind, but this PDCH has no configured TBFs\n"); + return NULL; + } + + msg = l1gprs_l1ctl_msgb_alloc(L1CTL_GPRS_DL_BLOCK_IND); + if (OSMO_UNLIKELY(msg == NULL)) { + LOGP_GPRS(gprs, LOGL_ERROR, "l1gprs_l1ctl_msgb_alloc() failed\n"); + return NULL; + } + + l1bi = (void *)msgb_put(msg, sizeof(*l1bi)); + *l1bi = (struct l1ctl_gprs_dl_block_ind) { + .hdr = { + .fn = htonl(ind->hdr.fn), + .tn = ind->hdr.tn, + }, + .meas = { + .ber10k = htons(ind->meas.ber10k), + .ci_cb = htons(ind->meas.ci_cb), + .rx_lev = ind->meas.rx_lev, + }, + .usf = 0xff, + }; + + if (ind->data_len == 0) + return msg; + + cs = osmo_gprs_dl_cs_by_block_bytes(ind->data_len); + switch (cs) { + case OSMO_GPRS_CS1: + case OSMO_GPRS_CS2: + case OSMO_GPRS_CS3: + case OSMO_GPRS_CS4: + l1bi->usf = ind->data[0] & 0x07; + /* Determine whether to include the payload or not */ + if (l1gprs_pdch_filter_dl_block(pdch, ind->data)) + memcpy(msgb_put(msg, ind->data_len), ind->data, ind->data_len); + break; + case OSMO_GPRS_CS_NONE: + LOGP_PDCH(pdch, LOGL_ERROR, + "Failed to determine Coding Scheme (len=%zu)\n", ind->data_len); + break; + default: + LOGP_PDCH(pdch, LOGL_NOTICE, "Coding Scheme %d is not supported\n", cs); + break; + } + + return msg; +}