pespin has submitted this change. ( https://gerrit.osmocom.org/c/libosmo-gprs/+/31099 )
Change subject: rlcmac: Initial implementation of UL TBF assignment and scheduler ......................................................................
rlcmac: Initial implementation of UL TBF assignment and scheduler
This patch is another step towards a working RLC/MAC implementation. It adds: * An initial data model with MS (gprs_rlcmac_entity) and ul_tbf. * A UL_TBF state FSM from initial to FLOW status * A UL_TBF assignemnt FSM, covering both 1phase and 2phase assignments. * Triggering of UL_TBF allocation and assignment FSM when new LLC data is pushed from upper layers. * A scheduler generating some ctrl messages (PktResReq and UlDummyCtrlBlk) when indicated by the network.
This patch is pushed as a WIP state since it already contains a considerable amount of code and lots of new files, which can be used/extended at a later point, making parallel contribution easier.
Related: OS#5500 Change-Id: I420c57a9d0b63f9c2805a7c2ae8ce85532a48eef --- M include/osmocom/gprs/rlcmac/Makefile.am M include/osmocom/gprs/rlcmac/rlcmac.h A include/osmocom/gprs/rlcmac/rlcmac_enc.h M include/osmocom/gprs/rlcmac/rlcmac_private.h A include/osmocom/gprs/rlcmac/sched.h A include/osmocom/gprs/rlcmac/tbf.h A include/osmocom/gprs/rlcmac/tbf_ul.h A include/osmocom/gprs/rlcmac/tbf_ul_ass_fsm.h A include/osmocom/gprs/rlcmac/tbf_ul_fsm.h A include/osmocom/gprs/rlcmac/types_private.h M src/rlcmac/Makefile.am M src/rlcmac/gre.c M src/rlcmac/misc.c M src/rlcmac/rlcmac.c A src/rlcmac/rlcmac_enc.c M src/rlcmac/rlcmac_prim.c A src/rlcmac/sched.c A src/rlcmac/tbf.c A src/rlcmac/tbf_ul.c A src/rlcmac/tbf_ul_ass_fsm.c A src/rlcmac/tbf_ul_fsm.c M tests/rlcmac/rlcmac_prim_test.c M tests/rlcmac/rlcmac_prim_test.err M tests/rlcmac/rlcmac_prim_test.ok 24 files changed, 1,549 insertions(+), 10 deletions(-)
Approvals: Jenkins Builder: Verified pespin: Looks good to me, approved
diff --git a/include/osmocom/gprs/rlcmac/Makefile.am b/include/osmocom/gprs/rlcmac/Makefile.am index e797480..c529a4e 100644 --- a/include/osmocom/gprs/rlcmac/Makefile.am +++ b/include/osmocom/gprs/rlcmac/Makefile.am @@ -2,7 +2,14 @@ codel.h \ gre.h \ llc_queue.h \ + rlcmac_enc.h \ rlcmac_private.h \ + sched.h \ + tbf.h \ + tbf_ul.h \ + tbf_ul_fsm.h \ + tbf_ul_ass_fsm.h \ + types_private.h \ $(NULL)
rlcmac_HEADERS = \ diff --git a/include/osmocom/gprs/rlcmac/rlcmac.h b/include/osmocom/gprs/rlcmac/rlcmac.h index d7bfb0a..2de5687 100644 --- a/include/osmocom/gprs/rlcmac/rlcmac.h +++ b/include/osmocom/gprs/rlcmac/rlcmac.h @@ -17,6 +17,7 @@
enum osmo_gprs_rlcmac_log_cat { OSMO_GPRS_RLCMAC_LOGC_RLCMAC, + OSMO_GPRS_RLCMAC_LOGC_TBFUL, _OSMO_GPRS_RLCMAC_LOGC_MAX, };
diff --git a/include/osmocom/gprs/rlcmac/rlcmac_enc.h b/include/osmocom/gprs/rlcmac/rlcmac_enc.h new file mode 100644 index 0000000..67448c1 --- /dev/null +++ b/include/osmocom/gprs/rlcmac/rlcmac_enc.h @@ -0,0 +1,15 @@ +#pragma once + +/* RLCMAC encoding support functions */ + +#include <stdint.h> + +#include <osmocom/gprs/rlcmac/csn1_defs.h> +#include <osmocom/gprs/rlcmac/types_private.h> +#include <osmocom/gprs/rlcmac/tbf_ul.h> + +#define GPRS_RLCMAC_DUMMY_VEC "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b" + +void gprs_rlcmac_enc_prepare_pkt_ul_dummy_block(RlcMacUplink_t *block, uint32_t tlli); + +void gprs_rlcmac_enc_prepare_pkt_resource_req(RlcMacUplink_t *block, struct gprs_rlcmac_ul_tbf *ul_tbf, enum gprs_rlcmac_access_type acc_type); diff --git a/include/osmocom/gprs/rlcmac/rlcmac_private.h b/include/osmocom/gprs/rlcmac/rlcmac_private.h index c10b159..e838c32 100644 --- a/include/osmocom/gprs/rlcmac/rlcmac_private.h +++ b/include/osmocom/gprs/rlcmac/rlcmac_private.h @@ -6,10 +6,27 @@ #include <stddef.h>
#include <osmocom/core/msgb.h> +#include <osmocom/core/tdef.h>
#include <osmocom/gprs/rlcmac/rlcmac_prim.h> #include <osmocom/gprs/rlcmac/rlcmac.h>
+/* 3GPP TS 44.064 § 8.3 TLLI assignment procedures */ +#define GPRS_RLCMAC_TLLI_UNASSIGNED (0xffffffff) + +#define GPRS_RLCMAC_USF_UNUSED 0x07 + +struct gprs_rlcmac_ul_tbf_allocation_ts { + bool allocated; + uint8_t usf; +}; + +struct gprs_rlcmac_ul_tbf_allocation { + uint8_t ul_tfi; + uint8_t num_ts; /* number of allocated TS */ + struct gprs_rlcmac_ul_tbf_allocation_ts ts[8]; +}; + extern int g_rlcmac_log_cat[_OSMO_GPRS_RLCMAC_LOGC_MAX];
#define LOGRLCMAC(lvl, fmt, args...) LOGP(g_rlcmac_log_cat[OSMO_GPRS_RLCMAC_LOGC_RLCMAC], lvl, fmt, ## args) @@ -31,13 +48,18 @@ osmo_gprs_rlcmac_prim_down_cb rlcmac_down_cb; void *rlcmac_down_cb_user_data;
+ struct osmo_tdef *T_defs; /* timers controlled by RLC/MAC layer */ + struct llist_head gre_list; /* contains (struct gprs_rlcmac_entity)->entry */ + + uint8_t next_ul_tbf_nr; };
extern struct gprs_rlcmac_ctx *g_ctx;
/* rlcmac.c */ struct gprs_rlcmac_entity *gprs_rlcmac_find_entity_by_tlli(uint32_t tlli); +int gprs_rlcmac_handle_ccch_imm_ass(const struct gsm48_imm_ass *ia);
/* rlcmac_prim.c */ int gprs_rlcmac_prim_call_up_cb(struct osmo_gprs_rlcmac_prim *rlcmac_prim); diff --git a/include/osmocom/gprs/rlcmac/sched.h b/include/osmocom/gprs/rlcmac/sched.h new file mode 100644 index 0000000..d344aea --- /dev/null +++ b/include/osmocom/gprs/rlcmac/sched.h @@ -0,0 +1,12 @@ +/* RLC/MAC scheduler, 3GPP TS 44.060 */ +#pragma once + +#include <stdint.h> + +struct gprs_rlcmac_rts_block_ind { + uint8_t ts; + uint32_t fn; + uint8_t usf; +}; + +int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_rts_block_ind *bi); diff --git a/include/osmocom/gprs/rlcmac/tbf.h b/include/osmocom/gprs/rlcmac/tbf.h new file mode 100644 index 0000000..df5698f --- /dev/null +++ b/include/osmocom/gprs/rlcmac/tbf.h @@ -0,0 +1,25 @@ +/* TBF, 3GPP TS 44.060 */ +#pragma once + +#include <stdint.h> + +struct gprs_rlcmac_entity; + +enum gprs_rlcmac_tbf_direction { + GPRS_RLCMAC_TBF_DIR_DL, + GPRS_RLCMAC_TBF_DIR_UL +}; + +struct gprs_rlcmac_tbf { + struct gprs_rlcmac_entity *gre; /* backpointer */ + enum gprs_rlcmac_tbf_direction direction; + const char *name; + uint8_t nr; /* TBF number, separate address space for DL and UL, used to identify TBF. */ +}; + +void gprs_rlcmac_tbf_constructor(struct gprs_rlcmac_tbf *tbf, + enum gprs_rlcmac_tbf_direction direction, + struct gprs_rlcmac_entity *gre); +void gprs_rlcmac_tbf_destructor(struct gprs_rlcmac_tbf *tbf); + +void gprs_rlcmac_tbf_free(struct gprs_rlcmac_tbf *tbf); diff --git a/include/osmocom/gprs/rlcmac/tbf_ul.h b/include/osmocom/gprs/rlcmac/tbf_ul.h new file mode 100644 index 0000000..8d122ae --- /dev/null +++ b/include/osmocom/gprs/rlcmac/tbf_ul.h @@ -0,0 +1,57 @@ +/* Uplink TBF, 3GPP TS 44.060 */ +#pragma once + +#include <inttypes.h> + +#include <osmocom/core/msgb.h> + +#include <osmocom/gprs/rlcmac/tbf.h> +#include <osmocom/gprs/rlcmac/tbf_ul_fsm.h> +#include <osmocom/gprs/rlcmac/tbf_ul_ass_fsm.h> +#include <osmocom/gprs/rlcmac/sched.h> +#include <osmocom/gprs/rlcmac/rlcmac_private.h> + +struct gprs_rlcmac_ul_tbf { + struct gprs_rlcmac_tbf tbf; + struct gprs_rlcmac_tbf_ul_fsm_ctx state_fsm; + struct gprs_rlcmac_tbf_ul_ass_fsm_ctx ul_ass_fsm; + + struct gprs_rlcmac_ul_tbf_allocation cur_alloc; +}; + +struct gprs_rlcmac_ul_tbf *gprs_rlcmac_ul_tbf_alloc(struct gprs_rlcmac_entity *gre); +void gprs_rlcmac_ul_tbf_free(struct gprs_rlcmac_ul_tbf *ul_tbf); + +bool gprs_rlcmac_ul_tbf_data_rts(const struct gprs_rlcmac_ul_tbf *ul_tbf, const struct gprs_rlcmac_rts_block_ind *bi); +bool gprs_rlcmac_ul_tbf_dummy_rts(const struct gprs_rlcmac_ul_tbf *ul_tbf, const struct gprs_rlcmac_rts_block_ind *bi); + +struct msgb *gprs_rlcmac_ul_tbf_data_create(const struct gprs_rlcmac_ul_tbf *ul_tbf, const struct gprs_rlcmac_rts_block_ind *bi); +struct msgb *gprs_rlcmac_ul_tbf_dummy_create(const struct gprs_rlcmac_ul_tbf *ul_tbf); + + +static inline struct gprs_rlcmac_tbf *ul_tbf_as_tbf(struct gprs_rlcmac_ul_tbf *ul_tbf) +{ + return &ul_tbf->tbf; +} + +static inline const struct gprs_rlcmac_tbf *ul_tbf_as_tbf_const(const struct gprs_rlcmac_ul_tbf *ul_tbf) +{ + return &ul_tbf->tbf; +} + +static inline struct gprs_rlcmac_ul_tbf *tbf_as_ul_tbf(struct gprs_rlcmac_tbf *tbf) +{ + OSMO_ASSERT(tbf->direction == GPRS_RLCMAC_TBF_DIR_UL); + return (struct gprs_rlcmac_ul_tbf *)tbf; +} + +static inline const struct gprs_rlcmac_ul_tbf *tbf_as_ul_tbf_const(struct gprs_rlcmac_tbf *tbf) +{ + OSMO_ASSERT(tbf->direction == GPRS_RLCMAC_TBF_DIR_UL); + return (const struct gprs_rlcmac_ul_tbf *)tbf; +} + +#define LOGPTBFUL(ul_tbf, lvl, fmt, args...) \ + LOGP(g_rlcmac_log_cat[OSMO_GPRS_RLCMAC_LOGC_TBFUL], lvl, "TBF(UL:NR-%" PRIu8 ":TLLI-%08x) " fmt, \ + (ul_tbf)->tbf.nr, (ul_tbf)->tbf.gre->tlli, \ + ## args) diff --git a/include/osmocom/gprs/rlcmac/tbf_ul_ass_fsm.h b/include/osmocom/gprs/rlcmac/tbf_ul_ass_fsm.h new file mode 100644 index 0000000..28a51af --- /dev/null +++ b/include/osmocom/gprs/rlcmac/tbf_ul_ass_fsm.h @@ -0,0 +1,78 @@ +/* UL TBF Assignment FSM, 3GPP TS 44.060 */ +#pragma once + +#include <stdint.h> + +#include <osmocom/core/fsm.h> +#include <osmocom/core/msgb.h> + +#include <osmocom/gprs/rlcmac/csn1_defs.h> +#include <osmocom/gprs/rlcmac/rlcmac_private.h> + +struct gprs_rlcmac_ul_tbf; +struct gprs_rlcmac_rts_block_ind; + +enum gprs_rlcmac_tbf_ul_ass_type { + GPRS_RLCMAC_TBF_UL_ASS_TYPE_1PHASE, + GPRS_RLCMAC_TBF_UL_ASS_TYPE_2PHASE, +}; + +enum gprs_rlcmac_tbf_ul_ass_fsm_states { + GPRS_RLCMAC_TBF_UL_ASS_ST_IDLE = 0, /* new created TBF */ + GPRS_RLCMAC_TBF_UL_ASS_ST_WAIT_CCCH_IMM_ASS, /* wait for Immediate Assignment */ + GPRS_RLCMAC_TBF_UL_ASS_ST_SCHED_PKT_RES_REQ, /* wait PDCH sched (USF) */ + GPRS_RLCMAC_TBF_UL_ASS_ST_WAIT_PKT_UL_ASS, /* Wait for PCU to send the new assignment */ + GPRS_RLCMAC_TBF_UL_ASS_ST_SCHED_PKT_CTRL_ACK, /* Wait for scheduler to send PKT CTRL ACK */ + GPRS_RLCMAC_TBF_UL_ASS_ST_COMPL, /* Completed, will update TBF and return to IDLE state */ +}; + +struct gprs_rlcmac_tbf_ul_ass_fsm_ctx { + struct osmo_fsm_inst *fi; + union { /* back pointer. union used to easily access superclass from ctx */ + struct gprs_rlcmac_tbf *tbf; + struct gprs_rlcmac_ul_tbf *ul_tbf; + }; + enum gprs_rlcmac_tbf_ul_ass_type ass_type; + uint8_t rach_req_ra; + struct gprs_rlcmac_ul_tbf_allocation phase1_alloc; + struct gprs_rlcmac_ul_tbf_allocation phase2_alloc; + struct { /* Filled when we receive the poll; exact time here the response PKT CTL ACK is to be transmitted: */ + uint8_t ts; + uint32_t fn; + } sched_pkt_ctrl_ack; +}; + +enum tbf_ul_ass_fsm_event { + GPRS_RLCMAC_TBF_UL_ASS_EV_START, /* Start Uplink assignment (data: enum gprs_rlcmac_tbf_ul_ass_type) */ + GPRS_RLCMAC_TBF_UL_ASS_EV_RX_CCCH_IMM_ASS, /* (data: struct tbf_ul_ass_ev_rx_ccch_imm_ass_ctx *) */ + GPRS_RLCMAC_TBF_UL_ASS_EV_CREATE_RLCMAC_MSG, /* Generate RLC/MAC block (data: struct tbf_ul_ass_ev_create_rlcmac_msg_ctx) */ + GPRS_RLCMAC_TBF_UL_ASS_EV_RX_PKT_UL_ASS, /* (data: decoded PktUlAss) */ + GPRS_RLCMAC_TBF_UL_ASS_EV_FOOBAR, +}; + +struct tbf_ul_ass_ev_rx_ccch_imm_ass_ctx { + uint8_t ts_nr; + const struct gsm48_imm_ass *ia; + const IA_RestOctets_t *iaro; +}; + +struct tbf_ul_ass_ev_create_rlcmac_msg_ctx { + uint8_t ts; /* TS where the created UL ctrl block is to be sent */ + uint32_t fn; /* FN where the created UL ctrl block is to be sent */ + struct msgb *msg; /* to be filled by FSM during event processing */ +}; + +int gprs_rlcmac_tbf_ul_ass_fsm_init(void); +void gprs_rlcmac_tbf_ul_ass_fsm_set_log_cat(int logcat); + +int gprs_rlcmac_tbf_ul_ass_fsm_constructor(struct gprs_rlcmac_ul_tbf *ul_tbf); +void gprs_rlcmac_tbf_ul_ass_fsm_destructor(struct gprs_rlcmac_ul_tbf *ul_tbf); + +int gprs_rlcmac_tbf_ul_ass_start(struct gprs_rlcmac_ul_tbf *ul_tbf, enum gprs_rlcmac_tbf_ul_ass_type type); +bool gprs_rlcmac_tbf_ul_ass_pending(struct gprs_rlcmac_ul_tbf *ul_tbf); +bool gprs_rlcmac_tbf_ul_ass_match_rach_req(struct gprs_rlcmac_ul_tbf *ul_tbf, uint8_t ra); +bool gprs_rlcmac_tbf_ul_ass_rts(const struct gprs_rlcmac_ul_tbf *ul_tbf, const struct gprs_rlcmac_rts_block_ind *bi); +struct msgb *gprs_rlcmac_tbf_ul_ass_create_rlcmac_msg(const struct gprs_rlcmac_ul_tbf *ul_tbf, + const struct gprs_rlcmac_rts_block_ind *bi); + +enum gprs_rlcmac_tbf_ul_ass_fsm_states gprs_rlcmac_tbf_ul_ass_state(const struct gprs_rlcmac_ul_tbf *ul_tbf); diff --git a/include/osmocom/gprs/rlcmac/tbf_ul_fsm.h b/include/osmocom/gprs/rlcmac/tbf_ul_fsm.h new file mode 100644 index 0000000..7aa4b1e --- /dev/null +++ b/include/osmocom/gprs/rlcmac/tbf_ul_fsm.h @@ -0,0 +1,36 @@ +/* Uplink TBF, 3GPP TS 44.060 */ +#pragma once + +#include <osmocom/core/fsm.h> + +#include <osmocom/gprs/rlcmac/rlcmac_private.h> + +struct gprs_rlcmac_ul_tbf; + +enum gprs_rlcmac_tbf_ul_fsm_states { + GPRS_RLCMAC_TBF_UL_ST_NEW = 0, /* new created TBF */ + GPRS_RLCMAC_TBF_UL_ST_WAIT_ASSIGN, /* wait for Immediate Assignment */ + GPRS_RLCMAC_TBF_UL_ST_FLOW, /* RLC/MAC flow, resource needed */ +}; + +struct gprs_rlcmac_tbf_ul_fsm_ctx { + struct osmo_fsm_inst *fi; + union { /* back pointer. union used to easily access superclass from ctx */ + struct gprs_rlcmac_tbf *tbf; + struct gprs_rlcmac_ul_tbf *ul_tbf; + }; +}; + +enum tbf_ul_fsm_event { + GPRS_RLCMAC_TBF_UL_EV_UL_ASS_START, + GPRS_RLCMAC_TBF_UL_EV_UL_ASS_COMPL, + GPRS_RLCMAC_TBF_UL_EV_FOOBAR, +}; + +int gprs_rlcmac_tbf_ul_fsm_init(void); +void gprs_rlcmac_tbf_ul_fsm_set_log_cat(int logcat); + +int gprs_rlcmac_tbf_ul_fsm_constructor(struct gprs_rlcmac_ul_tbf *ul_tbf); +void gprs_rlcmac_tbf_ul_fsm_destructor(struct gprs_rlcmac_ul_tbf *ul_tbf); + +enum gprs_rlcmac_tbf_ul_fsm_states gprs_rlcmac_tbf_ul_state(const struct gprs_rlcmac_ul_tbf *ul_tbf); diff --git a/include/osmocom/gprs/rlcmac/types_private.h b/include/osmocom/gprs/rlcmac/types_private.h new file mode 100644 index 0000000..10852cf --- /dev/null +++ b/include/osmocom/gprs/rlcmac/types_private.h @@ -0,0 +1,32 @@ +/* Types & defines from TS 44.060, TS 44.064, private extensions */ +#pragma once + +#include <osmocom/gprs/rlcmac/types.h> + +/* TS 44.060 Table 11.2.16.2 "ACCESS_TYPE" */ +enum gprs_rlcmac_access_type { + GPRS_RLCMAC_ACCESS_TYPE_2PHASE_ACC_REQ = 0, /* Two Phase Access Request */ + GPRS_RLCMAC_ACCESS_TYPE_PAGE_RESP = 1, /* Page Response */ + GPRS_RLCMAC_ACCESS_TYPE_CELL_UPD = 2, /* Cell Update */ + GPRS_RLCMAC_ACCESS_TYPE_MM = 3, /* Mobility Management procedure */ +}; + +/* TS 44.060 Table Table 11.2.5. "Radio Priority" */ +enum gprs_rlcmac_radio_priority { + GPRS_RLCMAC_RADIO_PRIORITY_1 = 0, /* Radio Priority 1 (Highest priority) */ + GPRS_RLCMAC_RADIO_PRIORITY_2 = 1, /* Radio Priority 2 */ + GPRS_RLCMAC_RADIO_PRIORITY_3 = 2, /* Radio Priority 3 */ + GPRS_RLCMAC_RADIO_PRIORITY_4 = 3, /* Radio Priority 4 (Lower priority) */ +}; + +/* TS 44.060 Table Table 12.7.2 "RLC_MODE" */ +enum gprs_rlcmac_rlc_mode { + GPRS_RLCMAC_RLC_MODE_ACKNOWLEDGED = 0, + GPRS_RLCMAC_RLC_MODE_UNACKNOWLEDGED = 1, +}; + +/* TS 44.060 Table Table Table 12.7.2 "LLC_PDU_TYPE" */ +enum gprs_rlcmac_llc_pdu_type { + GPRS_RLCMAC_LLC_PDU_TYPE_ACKNOWLEDGED = 0, + GPRS_RLCMAC_LLC_PDU_TYPE_UNACKNOWLEDGED = 1, +}; diff --git a/src/rlcmac/Makefile.am b/src/rlcmac/Makefile.am index 676abe0..fe221a2 100644 --- a/src/rlcmac/Makefile.am +++ b/src/rlcmac/Makefile.am @@ -10,10 +10,12 @@
AM_CFLAGS = \ -Wall \ + $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOCORE_CFLAGS) \ $(NULL)
AM_LDFLAGS = \ + $(LIBOSMOGSM_LIBS) \ $(LIBOSMOCORE_LIBS) \ $(NULL)
@@ -29,7 +31,13 @@ gre.c \ llc_queue.c \ rlcmac.c \ + rlcmac_enc.c \ rlcmac_prim.c \ + sched.c \ + tbf.c \ + tbf_ul.c \ + tbf_ul_fsm.c \ + tbf_ul_ass_fsm.c \ ts_44_060.c \ ts_44_064.c \ misc.c \ @@ -43,5 +51,6 @@
libosmo_gprs_rlcmac_la_LIBADD = \ $(top_builddir)/src/csn1/libosmo-csn1.la \ + $(LIBOSMOGSM_LIBS) \ $(LIBOSMOCORE_LIBS) \ $(NULL) diff --git a/src/rlcmac/gre.c b/src/rlcmac/gre.c index 7acd4f6..3d8ba08 100644 --- a/src/rlcmac/gre.c +++ b/src/rlcmac/gre.c @@ -24,6 +24,8 @@ #include <osmocom/gprs/rlcmac/rlcmac.h> #include <osmocom/gprs/rlcmac/rlcmac_prim.h> #include <osmocom/gprs/rlcmac/rlcmac_private.h> +#include <osmocom/gprs/rlcmac/tbf_ul_fsm.h> +#include <osmocom/gprs/rlcmac/tbf_ul.h> #include <osmocom/gprs/rlcmac/gre.h>
struct gprs_rlcmac_entity *gprs_rlcmac_entity_alloc(uint32_t tlli) @@ -55,6 +57,8 @@ { if (!gre) return; + + gprs_rlcmac_ul_tbf_free(gre->ul_tbf); gprs_rlcmac_llc_queue_free(gre->llc_queue); llist_del(&gre->entry); talloc_free(gre); @@ -69,7 +73,14 @@ if (rc < 0) return rc;
- /* TODO: here a new UL TBF will be created if not available yet */ + if (!gre->ul_tbf) { + /* We have new data in the queue but we have no ul_tbf. Allocate one and start UL Assignment. */ + gre->ul_tbf = gprs_rlcmac_ul_tbf_alloc(gre); + if (!gre->ul_tbf) + return -ENOMEM; + /* We always use 1phase for now... */ + rc = gprs_rlcmac_tbf_ul_ass_start(gre->ul_tbf, GPRS_RLCMAC_TBF_UL_ASS_TYPE_1PHASE); + }
return rc; } diff --git a/src/rlcmac/misc.c b/src/rlcmac/misc.c index 4885bbf..4edc2b9 100644 --- a/src/rlcmac/misc.c +++ b/src/rlcmac/misc.c @@ -18,6 +18,8 @@ #include <osmocom/core/utils.h> #include <osmocom/core/logging.h> #include <osmocom/gprs/rlcmac/rlcmac.h> +#include <osmocom/gprs/rlcmac/tbf_ul_fsm.h> +#include <osmocom/gprs/rlcmac/tbf_ul_ass_fsm.h>
int g_rlcmac_log_cat[_OSMO_GPRS_RLCMAC_LOGC_MAX] = { [0 ... _OSMO_GPRS_RLCMAC_LOGC_MAX - 1] = DLGLOBAL @@ -27,4 +29,9 @@ { OSMO_ASSERT(logc < _OSMO_GPRS_RLCMAC_LOGC_MAX); g_rlcmac_log_cat[logc] = logc_num; + + if (logc == OSMO_GPRS_RLCMAC_LOGC_TBFUL) { + gprs_rlcmac_tbf_ul_fsm_set_log_cat(logc_num); + gprs_rlcmac_tbf_ul_ass_fsm_set_log_cat(logc_num); + } } diff --git a/src/rlcmac/rlcmac.c b/src/rlcmac/rlcmac.c index 13a3e84..97516d5 100644 --- a/src/rlcmac/rlcmac.c +++ b/src/rlcmac/rlcmac.c @@ -21,28 +21,58 @@
#include <stdbool.h>
+#include <osmocom/gsm/rsl.h> + #include <osmocom/gprs/rlcmac/rlcmac.h> #include <osmocom/gprs/rlcmac/rlcmac_prim.h> #include <osmocom/gprs/rlcmac/rlcmac_private.h> +#include <osmocom/gprs/rlcmac/tbf_ul_fsm.h> +#include <osmocom/gprs/rlcmac/tbf_ul_ass_fsm.h> #include <osmocom/gprs/rlcmac/gre.h> +#include <osmocom/gprs/rlcmac/tbf_ul.h> +#include <osmocom/gprs/rlcmac/csn1_defs.h>
#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
struct gprs_rlcmac_ctx *g_ctx;
+static struct osmo_tdef T_defs_rlcmac[] = { + { 0 } /* empty item at the end */ +}; + int osmo_gprs_rlcmac_init(enum osmo_gprs_rlcmac_location location) { + bool first_init = true; + int rc; OSMO_ASSERT(location == OSMO_GPRS_RLCMAC_LOCATION_MS || location == OSMO_GPRS_RLCMAC_LOCATION_PCU)
- if (g_ctx) + if (g_ctx) { talloc_free(g_ctx); + first_init = false; + }
g_ctx = talloc_zero(NULL, struct gprs_rlcmac_ctx); g_ctx->cfg.location = location; g_ctx->cfg.codel.use = true; g_ctx->cfg.codel.interval_msec = GPRS_CODEL_SLOW_INTERVAL_MS; + g_ctx->T_defs = T_defs_rlcmac; INIT_LLIST_HEAD(&g_ctx->gre_list);
+ osmo_tdefs_reset(g_ctx->T_defs); + + if (first_init) { + rc = gprs_rlcmac_tbf_ul_fsm_init(); + if (rc != 0) { + TALLOC_FREE(g_ctx); + return rc; + } + rc = gprs_rlcmac_tbf_ul_ass_fsm_init(); + if (rc != 0) { + TALLOC_FREE(g_ctx); + return rc; + } + } + return 0; }
@@ -71,3 +101,106 @@ } return NULL; } + +static int gprs_rlcmac_handle_ccch_imm_ass_ul_tbf(uint8_t ts_nr, const struct gsm48_imm_ass *ia, const IA_RestOctets_t *iaro) +{ + int rc = -ENOENT; + struct gprs_rlcmac_entity *gre; + struct gprs_rlcmac_ul_tbf *ul_tbf; + struct tbf_ul_ass_ev_rx_ccch_imm_ass_ctx d = { + .ts_nr = ts_nr, + .ia = ia, + .iaro = iaro + }; + + llist_for_each_entry(gre, &g_ctx->gre_list, entry) { + ul_tbf = gre->ul_tbf; + if (!ul_tbf) + continue; + if (!gprs_rlcmac_tbf_ul_ass_match_rach_req(ul_tbf, ia->req_ref.ra)) + continue; + rc = osmo_fsm_inst_dispatch(ul_tbf->ul_ass_fsm.fi, + GPRS_RLCMAC_TBF_UL_ASS_EV_RX_CCCH_IMM_ASS, + &d); + break; + } + return rc; +} + +int gprs_rlcmac_handle_ccch_imm_ass(const struct gsm48_imm_ass *ia) +{ + int rc; + uint8_t ch_type, ch_subch, ch_ts; + IA_RestOctets_t iaro; + const uint8_t *iaro_raw = ((uint8_t *)ia) + sizeof(*ia) + ia->mob_alloc_len; + size_t iaro_raw_len = GSM_MACBLOCK_LEN - (sizeof(*ia) + ia->mob_alloc_len); + + rc = rsl_dec_chan_nr(ia->chan_desc.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rc != 0) { + LOGRLCMAC(LOGL_ERROR, "rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + ia->chan_desc.chan_nr); + return rc; + } + + rc = osmo_gprs_rlcmac_decode_imm_ass_ro(&iaro, iaro_raw, iaro_raw_len); + if (rc != 0) { + LOGRLCMAC(LOGL_ERROR, "Failed to decode IA Rest Octets IE\n"); + return rc; + } + + switch (iaro.UnionType) { + case 0: /* iaro.u.ll.* (IA_RestOctetsLL_t) */ + /* TODO: iaro.u.ll.Compressed_Inter_RAT_HO_INFO_IND */ + /* TODO: iaro.u.ll.AdditionsR13.* (IA_AdditionsR13_t) */ + break; + case 1: /* iaro.u.lh.* (IA_RestOctetsLH_t) */ + switch (iaro.u.lh.lh0x.UnionType) { + case 0: /* iaro.u.ll.lh0x.EGPRS_PktUlAss.* (IA_EGPRS_PktUlAss_t) */ + rc = gprs_rlcmac_handle_ccch_imm_ass_ul_tbf(ch_ts, ia, &iaro); + break; + case 1: /* iaro.u.ll.lh0x.MultiBlock_PktDlAss.* (IA_MultiBlock_PktDlAss_t) */ + /* TODO: Alloc DL TBF */ + break; + } + /* TODO: iaro.u.lh.AdditionsR13.* (IA_AdditionsR13_t) */ + break; + case 2: /* iaro.u.hl.* (IA_RestOctetsHL_t) */ + /* TODO: iaro.u.hl.IA_FrequencyParams (IA_FreqParamsBeforeTime_t) */ + /* TODO: iaro.u.hl.Compressed_Inter_RAT_HO_INFO_IND */ + /* TODO: iaro.u.hl.AdditionsR13.* (IA_AdditionsR13_t) */ + break; + case 3: /* iaro.u.hh.* (IA_RestOctetsHH_t) */ + switch (iaro.u.hh.UnionType) { + case 0: /* iaro.u.hh.u.UplinkDownlinkAssignment.* (IA_PacketAssignment_UL_DL_t) */ + switch (iaro.u.hh.u.UplinkDownlinkAssignment.UnionType) { + case 0: /* iaro.u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Uplink_ImmAssignment.* (Packet_Uplink_ImmAssignment_t) */ + switch (iaro.u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Uplink_ImmAssignment.UnionType) { + case 0: /* iaro.u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Uplink_ImmAssignment.Access.SingleBlockAllocation.* (GPRS_SingleBlockAllocation_t) */ + /* TODO: 2phase access support: Schedule transmit of PKT_RES_REQ on FN=(GPRS_SingleBlockAllocation_t).TBF_STARTING_TIME */ + LOGRLCMAC(LOGL_ERROR, "ImmAss SingleBlock (2phase access) not yet supported!\n"); + break; + case 1: /* iaro.u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Uplink_ImmAssignment.Access.DynamicOrFixedAllocation.* (GPRS_DynamicOrFixedAllocation_t) */ + switch (iaro.u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Uplink_ImmAssignment.Access.DynamicOrFixedAllocation.UnionType) { + case 0: /* iaro.u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Uplink_ImmAssignment.Access.DynamicOrFixedAllocation.Allocation.DynamicAllocation (DynamicAllocation_t) */ + rc = gprs_rlcmac_handle_ccch_imm_ass_ul_tbf(ch_ts, ia, &iaro); + break; + case 1: /* iaro.u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Uplink_ImmAssignment.Access.DynamicOrFixedAllocation.Allocation.FixedAllocationDummy (guint8) */ + rc = gprs_rlcmac_handle_ccch_imm_ass_ul_tbf(ch_ts, ia, &iaro); + break; + } + break; + } + break; + case 1: /* iaro.u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Downlink_ImmAssignment* (Packet_Downlink_ImmAssignment_t) */ + /* TODO: Alloc DL TBF */ + break; + } + break; + case 1: /* iaro.u.hh.u.SecondPartPacketAssignment.* (Second_Part_Packet_Assignment_t) */ + break; + } + break; + } + + return rc; +} diff --git a/src/rlcmac/rlcmac_enc.c b/src/rlcmac/rlcmac_enc.c new file mode 100644 index 0000000..ef22f05 --- /dev/null +++ b/src/rlcmac/rlcmac_enc.c @@ -0,0 +1,95 @@ +/* RLC/MAC encoding helpers, 3GPP TS 44.060 */ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH info@sysmocom.de + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ +#include <osmocom/gprs/rlcmac/csn1_defs.h> +#include <osmocom/gprs/rlcmac/rlcmac_enc.h> +#include <osmocom/gprs/rlcmac/gre.h> +#include <osmocom/gprs/rlcmac/tbf_ul.h> + +void gprs_rlcmac_enc_prepare_pkt_ul_dummy_block(RlcMacUplink_t *block, uint32_t tlli) +{ + Packet_Uplink_Dummy_Control_Block_t *dummy; + + memset(block, 0, sizeof(*block)); + + dummy = &block->u.Packet_Uplink_Dummy_Control_Block; + dummy->MESSAGE_TYPE = OSMO_GPRS_RLCMAC_UL_MSGT_PACKET_UPLINK_DUMMY_CONTROL_BLOCK; + /* 10.4.7: RLC/MAC control block that does not include the optional octets of the RLC/MAC control header: */ + dummy->PayloadType = 0x1; + dummy->R = 0; /* MS sent channel request message once */ + dummy->TLLI = tlli; +} + +/* 11.2.16 Packet Resource Request */ +void gprs_rlcmac_enc_prepare_pkt_resource_req(RlcMacUplink_t *block, + struct gprs_rlcmac_ul_tbf *ul_tbf, + enum gprs_rlcmac_access_type acc_type) +{ + Packet_Resource_Request_t *req; + struct gprs_rlcmac_entity *gre = ul_tbf->tbf.gre; + + memset(block, 0, sizeof(*block)); + + req = &block->u.Packet_Resource_Request; + req->MESSAGE_TYPE = OSMO_GPRS_RLCMAC_UL_MSGT_PACKET_RESOURCE_REQUEST; + /* 10.4.7: RLC/MAC control block that does not include the optional octets of the RLC/MAC control header: */ + req->PayloadType = 0x1; + req->R = 0; /* MS sent channel request message once */ + + req->Exist_ACCESS_TYPE = 1; + req->ACCESS_TYPE = acc_type; + + req->ID.UnionType = 1; /* Use TLLI */ + req->ID.u.TLLI = gre->tlli; /* Use TLLI */ + req->Exist_MS_Radio_Access_capability2 = 1; + + req->MS_Radio_Access_capability2.Count_MS_RA_capability_value = 1; + /* TODO: fill Content_t: */ + /* req->MS_Radio_Access_capability2.MS_RA_capability_value[0].Content.* */ + + /* 3GPP TS 24.008 Peak Throughput Class, range 1..9 */ + req->Channel_Request_Description.PEAK_THROUGHPUT_CLASS = 1; + req->Channel_Request_Description.RADIO_PRIORITY = GPRS_RLCMAC_RADIO_PRIORITY_4; + req->Channel_Request_Description.RLC_MODE = GPRS_RLCMAC_RLC_MODE_ACKNOWLEDGED; + req->Channel_Request_Description.LLC_PDU_TYPE = GPRS_RLCMAC_LLC_PDU_TYPE_ACKNOWLEDGED; + req->Channel_Request_Description.RLC_OCTET_COUNT = gprs_rlcmac_llc_queue_octets(gre->llc_queue); + + /* "this field contains the SI13_CHANGE_MARK value stored by the mobile station. + * If the mobile station does not have a valid PSI2 or SI13 change mark for the current cell, + * the mobile station shall omit this field." */ + req->Exist_CHANGE_MARK = 0; + /* req->CHANGE_MARK; */ + + /* TODO: binary representation of the C value as specified in 3GPP TS 45.008. */ + req->C_VALUE = 0; + + /* SIGN_VAR: "This field is not present for TBF establishment using two phase access or for + * a TBF in EGPRS mode" (see 3GPP TS 45.008) */ + if (acc_type != GPRS_RLCMAC_ACCESS_TYPE_2PHASE_ACC_REQ) { + req->Exist_SIGN_VAR = 1; + req->SIGN_VAR = 0; /* TODO: calculate */ + } + + /* For element definition see sub-clause 11.2.6 - Packet Downlink Ack/Nack. */ + /* TODO: req->I_LEVEL_TN[8]; */ + + req->Exist_AdditionsR99 = 0; + /* TODO: no req->AdditionsR99 yet */ +} diff --git a/src/rlcmac/rlcmac_prim.c b/src/rlcmac/rlcmac_prim.c index a2f2576..ef0dd22 100644 --- a/src/rlcmac/rlcmac_prim.c +++ b/src/rlcmac/rlcmac_prim.c @@ -31,11 +31,14 @@ #include <osmocom/core/logging.h> #include <osmocom/crypt/gprs_cipher.h> #include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gprs/rlcmac/rlcmac.h> #include <osmocom/gprs/rlcmac/rlcmac_prim.h> #include <osmocom/gprs/rlcmac/rlcmac_private.h> #include <osmocom/gprs/rlcmac/gre.h> +#include <osmocom/gprs/rlcmac/tbf_ul.h> +#include <osmocom/gprs/rlcmac/tbf_ul_ass_fsm.h>
#define RLCMAC_MSGB_HEADROOM 0
@@ -437,8 +440,14 @@
static int rlcmac_prim_handle_l1ctl_pdch_rts_ind(struct osmo_gprs_rlcmac_prim *rlcmac_prim) { - int rc = gprs_rlcmac_prim_handle_unsupported(rlcmac_prim); - rc = 1; /* msg owned (freed) */ + int rc; + struct gprs_rlcmac_rts_block_ind bi = { + .ts = rlcmac_prim->l1ctl.pdch_rts_ind.ts_nr, + .fn = rlcmac_prim->l1ctl.pdch_rts_ind.fn, + .usf = rlcmac_prim->l1ctl.pdch_rts_ind.usf, + }; + + rc = gprs_rlcmac_rcv_rts_block(&bi); return rc; }
@@ -451,8 +460,13 @@
static int rlcmac_prim_handle_l1ctl_ccch_data_ind(struct osmo_gprs_rlcmac_prim *rlcmac_prim) { - int rc = gprs_rlcmac_prim_handle_unsupported(rlcmac_prim); - rc = 1; /* msg owned (freed) */ + /* TODO: check if it's IMM_ASS: */ + int rc; + + if (rlcmac_prim->l1ctl.ccch_data_ind.data[2] == GSM48_MT_RR_IMM_ASS) + rc = gprs_rlcmac_handle_ccch_imm_ass((struct gsm48_imm_ass *)rlcmac_prim->l1ctl.ccch_data_ind.data); + else + rc = -ENOTSUP; return rc; }
diff --git a/src/rlcmac/sched.c b/src/rlcmac/sched.c new file mode 100644 index 0000000..93dc50f --- /dev/null +++ b/src/rlcmac/sched.c @@ -0,0 +1,136 @@ +/* RLC/MAC scheduler, 3GPP TS 44.060 */ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH info@sysmocom.de + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/msgb.h> + +#include <osmocom/gprs/rlcmac/rlcmac_private.h> +#include <osmocom/gprs/rlcmac/sched.h> +#include <osmocom/gprs/rlcmac/gre.h> +#include <osmocom/gprs/rlcmac/tbf_ul.h> +#include <osmocom/gprs/rlcmac/tbf_ul_ass_fsm.h> + +struct tbf_sched_ctrl_candidates { + struct gprs_rlcmac_ul_tbf *ul_ass; +}; + +static void get_ctrl_msg_tbf_candidates(const struct gprs_rlcmac_rts_block_ind *bi, + struct tbf_sched_ctrl_candidates *tbfs) +{ + + struct gprs_rlcmac_entity *gre; + struct gprs_rlcmac_ul_tbf *ul_tbf; + + /* Iterate over UL TBFs: */ + llist_for_each_entry(gre, &g_ctx->gre_list, entry) { + if (!gre->ul_tbf) + continue; + ul_tbf = gre->ul_tbf; + if (gprs_rlcmac_tbf_ul_ass_rts(ul_tbf, bi)) + tbfs->ul_ass = ul_tbf; + } + + /* TODO: Iterate over DL TBFs: */ +} + +static struct gprs_rlcmac_ul_tbf *find_requested_ul_tbf_for_data(const struct gprs_rlcmac_rts_block_ind *bi) +{ + struct gprs_rlcmac_entity *gre; + llist_for_each_entry(gre, &g_ctx->gre_list, entry) { + if (!gre->ul_tbf) + continue; + if (gprs_rlcmac_ul_tbf_data_rts(gre->ul_tbf, bi)) + return gre->ul_tbf; + } + return NULL; +} + +static struct gprs_rlcmac_ul_tbf *find_requested_ul_tbf_for_dummy(const struct gprs_rlcmac_rts_block_ind *bi) +{ + struct gprs_rlcmac_entity *gre; + llist_for_each_entry(gre, &g_ctx->gre_list, entry) { + if (!gre->ul_tbf) + continue; + if (gprs_rlcmac_ul_tbf_dummy_rts(gre->ul_tbf, bi)) + return gre->ul_tbf; + } + return NULL; +} + +static struct msgb *sched_select_ctrl_msg(const struct gprs_rlcmac_rts_block_ind *bi, + struct tbf_sched_ctrl_candidates *tbfs) +{ + struct msgb *msg = NULL; + if (tbfs->ul_ass) + msg = gprs_rlcmac_tbf_ul_ass_create_rlcmac_msg(tbfs->ul_ass, bi); + return msg; +} + +static struct msgb *sched_select_ul_data_msg(const struct gprs_rlcmac_rts_block_ind *bi) +{ + struct gprs_rlcmac_ul_tbf *ul_tbf; + + ul_tbf = find_requested_ul_tbf_for_data(bi); + if (!ul_tbf) { + LOGRLCMAC(LOGL_DEBUG, "(ts=%u,fn=%u,usf=%u) No Uplink TBF available to transmit RLC/MAC Ul Data Block\n", + bi->ts, bi->fn, bi->usf); + return NULL; + } + return gprs_rlcmac_ul_tbf_data_create(ul_tbf, bi); +} + +static struct msgb *sched_select_ul_dummy_ctrl_blk(const struct gprs_rlcmac_rts_block_ind *bi) +{ + struct gprs_rlcmac_ul_tbf *ul_tbf; + + ul_tbf = find_requested_ul_tbf_for_dummy(bi); + if (!ul_tbf) { + LOGRLCMAC(LOGL_DEBUG, "(ts=%u,fn=%u,usf=%u) No Uplink TBF available to transmit RLC/MAC Ul Dummy Ctrl Block\n", + bi->ts, bi->fn, bi->usf); + return NULL; + } + + return gprs_rlcmac_ul_tbf_dummy_create(ul_tbf); +} + +int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_rts_block_ind *bi) +{ + struct msgb *msg = NULL; + struct tbf_sched_ctrl_candidates tbf_cand = {0}; + + get_ctrl_msg_tbf_candidates(bi, &tbf_cand); + + if ((msg = sched_select_ctrl_msg(bi, &tbf_cand))) + goto tx_msg; + + if ((msg = sched_select_ul_data_msg(bi))) + goto tx_msg; + + /* Prio 3: send dummy control message (or nothing depending on EXT_UTBF_NODATA) */ + if ((msg = sched_select_ul_dummy_ctrl_blk(bi))) + goto tx_msg; + + /* Nothing to transmit */ + return 0; +tx_msg: + /* TODO: transmit msg to lower layer (L1CTL?) */ + return 0; +} diff --git a/src/rlcmac/tbf.c b/src/rlcmac/tbf.c new file mode 100644 index 0000000..ea15d69 --- /dev/null +++ b/src/rlcmac/tbf.c @@ -0,0 +1,43 @@ +/* TBF as per 3GPP TS 44.064 */ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH info@sysmocom.de + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +#include <osmocom/gprs/rlcmac/tbf.h> +#include <osmocom/gprs/rlcmac/tbf_ul.h> + +void gprs_rlcmac_tbf_constructor(struct gprs_rlcmac_tbf *tbf, + enum gprs_rlcmac_tbf_direction direction, + struct gprs_rlcmac_entity *gre) +{ + tbf->gre = gre; + tbf->direction = direction; +} + +void gprs_rlcmac_tbf_destructor(struct gprs_rlcmac_tbf *tbf) +{ + +} + +void gprs_rlcmac_tbf_free(struct gprs_rlcmac_tbf *tbf) +{ + if (tbf->direction == GPRS_RLCMAC_TBF_DIR_UL) + gprs_rlcmac_ul_tbf_free(tbf_as_ul_tbf(tbf)); + /* else: TODO dl_tbf not yet implemented */ +} diff --git a/src/rlcmac/tbf_ul.c b/src/rlcmac/tbf_ul.c new file mode 100644 index 0000000..54ecd34 --- /dev/null +++ b/src/rlcmac/tbf_ul.c @@ -0,0 +1,134 @@ +/* Uplink TBF as per 3GPP TS 44.064 */ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH info@sysmocom.de + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +#include <osmocom/core/bitvec.h> + +#include <osmocom/gprs/rlcmac/tbf_ul.h> +#include <osmocom/gprs/rlcmac/rlcmac_enc.h> +#include <osmocom/gprs/rlcmac/gre.h> + +struct gprs_rlcmac_ul_tbf *gprs_rlcmac_ul_tbf_alloc(struct gprs_rlcmac_entity *gre) +{ + struct gprs_rlcmac_ul_tbf *ul_tbf; + int rc; + + ul_tbf = talloc_zero(gre, struct gprs_rlcmac_ul_tbf); + if (!ul_tbf) + return NULL; + + gprs_rlcmac_tbf_constructor(ul_tbf_as_tbf(ul_tbf), GPRS_RLCMAC_TBF_DIR_UL, gre); + + rc = gprs_rlcmac_tbf_ul_fsm_constructor(ul_tbf); + if (rc < 0) + goto err_tbf_destruct; + + rc = gprs_rlcmac_tbf_ul_ass_fsm_constructor(ul_tbf); + if (rc < 0) + goto err_state_fsm_destruct; + + ul_tbf->tbf.nr = g_ctx->next_ul_tbf_nr++; + + return ul_tbf; + +err_state_fsm_destruct: + gprs_rlcmac_tbf_destructor(ul_tbf_as_tbf(ul_tbf)); +err_tbf_destruct: + gprs_rlcmac_tbf_destructor(ul_tbf_as_tbf(ul_tbf)); + talloc_free(ul_tbf); + return NULL; +} + +void gprs_rlcmac_ul_tbf_free(struct gprs_rlcmac_ul_tbf *ul_tbf) +{ + if (!ul_tbf) + return; + + gprs_rlcmac_tbf_ul_ass_fsm_destructor(ul_tbf); + gprs_rlcmac_tbf_ul_fsm_destructor(ul_tbf); + + gprs_rlcmac_tbf_destructor(ul_tbf_as_tbf(ul_tbf)); + talloc_free(ul_tbf); +} + +/* Used by the scheduler to find out whether an Uplink Dummy Control Block can be transmitted. If + * true, it will potentially call gprs_rlcmac_ul_tbf_dummy_create() to generate a new dummy message to transmit. */ +bool gprs_rlcmac_ul_tbf_dummy_rts(const struct gprs_rlcmac_ul_tbf *ul_tbf, const struct gprs_rlcmac_rts_block_ind *bi) +{ + if (!ul_tbf->cur_alloc.ts[bi->ts].allocated) + return false; + if (ul_tbf->cur_alloc.ts[bi->ts].usf != bi->usf) + return false; + return true; +} + +/* Used by the scheduler to find out whether there's data to be transmitted at the requested time. If + * true, it will potentially call gprs_rlcmac_ul_tbf_data_create() to generate a new data message to transmit. */ +bool gprs_rlcmac_ul_tbf_data_rts(const struct gprs_rlcmac_ul_tbf *ul_tbf, const struct gprs_rlcmac_rts_block_ind *bi) +{ + enum gprs_rlcmac_tbf_ul_fsm_states st; + + if (!gprs_rlcmac_ul_tbf_dummy_rts(ul_tbf, bi)) + return false; + + st = gprs_rlcmac_tbf_ul_state(ul_tbf); + return (st == GPRS_RLCMAC_TBF_UL_ST_FLOW); +} + +struct msgb *gprs_rlcmac_ul_tbf_dummy_create(const struct gprs_rlcmac_ul_tbf *ul_tbf) +{ + struct msgb *msg; + struct bitvec bv; + RlcMacUplink_t ul_block; + int rc; + + OSMO_ASSERT(ul_tbf); + + msg = msgb_alloc(GSM_MACBLOCK_LEN, "pkt_ul_dummy_ctrl_blk"); + if (!msg) + return NULL; + + /* Initialize a bit vector that uses allocated msgb as the data buffer. */ + bv = (struct bitvec){ + .data = msgb_put(msg, GSM_MACBLOCK_LEN), + .data_len = GSM_MACBLOCK_LEN, + }; + bitvec_unhex(&bv, GPRS_RLCMAC_DUMMY_VEC); + + gprs_rlcmac_enc_prepare_pkt_ul_dummy_block(&ul_block, ul_tbf->tbf.gre->tlli); + rc = osmo_gprs_rlcmac_encode_uplink(&bv, &ul_block); + if (rc < 0) { + LOGPTBFUL(ul_tbf, LOGL_ERROR, "Encoding of Packet Uplink Dummy Control Block failed (%d)\n", rc); + goto free_ret; + } + + return msg; + +free_ret: + msgb_free(msg); + return NULL; +} + +struct msgb *gprs_rlcmac_ul_tbf_data_create(const struct gprs_rlcmac_ul_tbf *ul_tbf, const struct gprs_rlcmac_rts_block_ind *bi) +{ + LOGPTBFUL(ul_tbf, LOGL_ERROR, "(ts=%u,fn=%u,usf=%u) TODO: implement dequeue from LLC\n", + bi->ts, bi->fn, bi->usf); + return NULL; +} diff --git a/src/rlcmac/tbf_ul_ass_fsm.c b/src/rlcmac/tbf_ul_ass_fsm.c new file mode 100644 index 0000000..1130187 --- /dev/null +++ b/src/rlcmac/tbf_ul_ass_fsm.c @@ -0,0 +1,445 @@ +/* TBF as per 3GPP TS 44.064 */ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH info@sysmocom.de + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +#include <errno.h> +#include <talloc.h> +#include <osmocom/core/tdef.h> +#include <osmocom/core/fsm.h> +#include <osmocom/core/bitvec.h> + +#include <osmocom/gprs/rlcmac/types.h> +#include <osmocom/gprs/rlcmac/tbf_ul_ass_fsm.h> +#include <osmocom/gprs/rlcmac/tbf_ul.h> +#include <osmocom/gprs/rlcmac/gre.h> +#include <osmocom/gprs/rlcmac/sched.h> +#include <osmocom/gprs/rlcmac/csn1_defs.h> +#include <osmocom/gprs/rlcmac/rlcmac_enc.h> + +#define X(s) (1 << (s)) + +static const struct value_string tbf_ul_ass_fsm_event_names[] = { + { GPRS_RLCMAC_TBF_UL_ASS_EV_START, "START" }, + { GPRS_RLCMAC_TBF_UL_ASS_EV_RX_CCCH_IMM_ASS, "RX_CCCH_IMM_ASS" }, + { GPRS_RLCMAC_TBF_UL_ASS_EV_CREATE_RLCMAC_MSG, "CREATE_RLCMAC_MSG" }, + { GPRS_RLCMAC_TBF_UL_ASS_EV_RX_PKT_UL_ASS, "RX_PKT_UL_ASS" }, + { GPRS_RLCMAC_TBF_UL_ASS_EV_FOOBAR, "FOOBAR" }, + { 0, NULL } +}; + +static const struct osmo_tdef_state_timeout tbf_ul_ass_fsm_timeouts[32] = { + [GPRS_RLCMAC_TBF_UL_ASS_ST_IDLE] = { }, + [GPRS_RLCMAC_TBF_UL_ASS_ST_WAIT_CCCH_IMM_ASS] = { }, + [GPRS_RLCMAC_TBF_UL_ASS_ST_SCHED_PKT_RES_REQ] = { }, + [GPRS_RLCMAC_TBF_UL_ASS_ST_WAIT_PKT_UL_ASS] = { }, + [GPRS_RLCMAC_TBF_UL_ASS_ST_SCHED_PKT_CTRL_ACK] = { }, + [GPRS_RLCMAC_TBF_UL_ASS_ST_COMPL] = { }, +}; + +/* Transition to a state, using the T timer defined in tbf_fsm_timeouts. + * The actual timeout value is in turn obtained from conn->T_defs. + * Assumes local variable fi exists. */ + #define tbf_ul_ass_fsm_state_chg(fi, NEXT_STATE) \ + osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \ + tbf_ul_ass_fsm_timeouts, \ + g_ctx->T_defs, \ + -1) + +static struct msgb *create_pkt_resource_req(const struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *ctx, + struct tbf_ul_ass_ev_create_rlcmac_msg_ctx *d) +{ + struct msgb *msg; + struct bitvec bv; + RlcMacUplink_t ul_block; + int rc; + + msg = msgb_alloc(GSM_MACBLOCK_LEN, "pkt_res_req"); + if (!msg) + return NULL; + + /* Initialize a bit vector that uses allocated msgb as the data buffer. */ + bv = (struct bitvec){ + .data = msgb_put(msg, GSM_MACBLOCK_LEN), + .data_len = GSM_MACBLOCK_LEN, + }; + bitvec_unhex(&bv, GPRS_RLCMAC_DUMMY_VEC); + + gprs_rlcmac_enc_prepare_pkt_resource_req(&ul_block, ctx->ul_tbf, GPRS_RLCMAC_ACCESS_TYPE_2PHASE_ACC_REQ); + rc = osmo_gprs_rlcmac_encode_uplink(&bv, &ul_block); + if (rc < 0) { + LOGPTBFUL(ctx->ul_tbf, LOGL_ERROR, "Encoding of Packet Resource Req failed (%d)\n", rc); + goto free_ret; + } + + return msg; + +free_ret: + msgb_free(msg); + return NULL; +} + +/* Generate a 8-bit CHANNEL REQUEST message as per 3GPP TS 44.018, 9.1.8 */ +static uint8_t gen_chan_req(bool single_block) +{ + uint8_t rnd = (uint8_t)rand(); + + if (single_block) /* 01110xxx */ + return 0x70 | (rnd & 0x07); + + /* 011110xx or 01111x0x or 01111xx0 */ + if ((rnd & 0x07) == 0x07) + return 0x78; + return 0x78 | (rnd & 0x07); +} + +static int submit_rach_req(struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *ctx) +{ + struct osmo_gprs_rlcmac_prim *rlcmac_prim; + ctx->rach_req_ra = gen_chan_req(ctx->ass_type == GPRS_RLCMAC_TBF_UL_ASS_TYPE_2PHASE); + + LOGPFSML(ctx->fi, LOGL_INFO, "Send RACH.req ra=0x%02x\n", ctx->rach_req_ra); + rlcmac_prim = gprs_rlcmac_prim_alloc_l1ctl_rach8_req(ctx->rach_req_ra); + return gprs_rlcmac_prim_call_down_cb(rlcmac_prim); +} + +static int handle_imm_ass(struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *ctx, const struct tbf_ul_ass_ev_rx_ccch_imm_ass_ctx *d) +{ + switch (d->iaro->UnionType) { + case 1: /* d->iaro->u.lh.* (IA_RestOctetsLH_t) */ + switch (d->iaro->u.lh.lh0x.UnionType) { + case 0: /* d->iaro->u.ll.lh0x.EGPRS_PktUlAss.* (IA_EGPRS_PktUlAss_t) */ + return -ENOTSUP; /* TODO */ + } + case 3: /* d->iaro->u.hh.* (IA_RestOctetsHH_t) */ + switch (d->iaro->u.hh.UnionType) { + case 0: /* d->iaro->u.hh.u.UplinkDownlinkAssignment.* (IA_PacketAssignment_UL_DL_t) */ + switch (d->iaro->u.hh.u.UplinkDownlinkAssignment.UnionType) { + case 0: /* d->iaro->u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Uplink_ImmAssignment.* (Packet_Uplink_ImmAssignment_t) */ + switch (d->iaro->u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Uplink_ImmAssignment.UnionType) { + case 0: /* d->iaro->u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Uplink_ImmAssignment.Access.SingleBlockAllocation.* (GPRS_SingleBlockAllocation_t) */ + /* TODO: 2phase access support: Schedule transmit of PKT_RES_REQ on FN=(GPRS_SingleBlockAllocation_t).TBF_STARTING_TIME */ + LOGPFSML(ctx->fi, LOGL_ERROR, "ImmAss SingleBlock (2phase access) not yet supported!\n"); + return -ENOTSUP; + case 1: /* d->iaro->u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Uplink_ImmAssignment.Access.DynamicOrFixedAllocation.* (GPRS_DynamicOrFixedAllocation_t) */ + //ctx->ul_tbf->tx_cs = d->iaro->u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Uplink_ImmAssignment.Access.DynamicOrFixedAllocation.CHANNEL_CODING_COMMAND + 1; + //LOGPFSML(ctx->fi, LOGL_INFO, "ImmAss initial CS=%s\n", gprs_rlcmac_mcs_name(ctx->ul_tbf->tx_cs)); + switch (d->iaro->u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Uplink_ImmAssignment.Access.DynamicOrFixedAllocation.UnionType) { + case 0: /* d->iaro->u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Uplink_ImmAssignment.Access.DynamicOrFixedAllocation.Allocation.DynamicAllocation (DynamicAllocation_t) */ + /* TODO: 2phase access support: Schedule transmit of PKT_RES_REQ on FN=(GPRS_SingleBlockAllocation_t).TBF_STARTING_TIME */ + ctx->phase1_alloc.ts[d->ts_nr].allocated = true; + ctx->phase1_alloc.ts[d->ts_nr].usf = d->iaro->u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Uplink_ImmAssignment.Access.DynamicOrFixedAllocation.Allocation.DynamicAllocation.USF; + ctx->phase1_alloc.num_ts = 1; + LOGPFSML(ctx->fi, LOGL_INFO, "ImmAss DynamicAlloc (1phase access) ts_nr=%u usf=%u\n", d->ts_nr, ctx->phase1_alloc.ts[d->ts_nr].usf); + return 0; + case 1: /* d->iaro->u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Uplink_ImmAssignment.Access.DynamicOrFixedAllocation.Allocation.FixedAllocationDummy (guint8) */ + return -ENOTSUP; + } + break; + } + break; + } + break; + } + break; + } + + OSMO_ASSERT(0); + return -EFAULT; +} + +static void st_idle_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *)fi->priv; + + /* Reset state: */ + memset(&ctx->phase1_alloc, 0, sizeof(ctx->phase1_alloc)); + memset(&ctx->phase2_alloc, 0, sizeof(ctx->phase2_alloc)); + memset(&ctx->sched_pkt_ctrl_ack, 0, sizeof(ctx->sched_pkt_ctrl_ack)); +} + +static void st_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *)fi->priv; + switch (event) { + case GPRS_RLCMAC_TBF_UL_ASS_EV_START: + /* Inform the main TBF state about the assignment starting: */ + osmo_fsm_inst_dispatch(ctx->ul_tbf->state_fsm.fi, GPRS_RLCMAC_TBF_UL_EV_UL_ASS_START, NULL); + ctx->ass_type = *(enum gprs_rlcmac_tbf_ul_ass_type *)data; + submit_rach_req(ctx); + tbf_ul_ass_fsm_state_chg(fi, GPRS_RLCMAC_TBF_UL_ASS_ST_WAIT_CCCH_IMM_ASS); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_wait_ccch_imm_ass(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *)fi->priv; + const struct tbf_ul_ass_ev_rx_ccch_imm_ass_ctx *ev_rx_ccch_imm_ass_ctx; + + switch (event) { + case GPRS_RLCMAC_TBF_UL_ASS_EV_RX_CCCH_IMM_ASS: + ev_rx_ccch_imm_ass_ctx = data; + if (handle_imm_ass(ctx, ev_rx_ccch_imm_ass_ctx) < 0) + return; + if (ctx->ass_type == GPRS_RLCMAC_TBF_UL_ASS_TYPE_1PHASE) + tbf_ul_ass_fsm_state_chg(fi, GPRS_RLCMAC_TBF_UL_ASS_ST_COMPL); + else + tbf_ul_ass_fsm_state_chg(fi, GPRS_RLCMAC_TBF_UL_ASS_ST_SCHED_PKT_RES_REQ); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_sched_pkt_res_req(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *)fi->priv; + struct tbf_ul_ass_ev_create_rlcmac_msg_ctx *data_ctx; + switch (event) { + case GPRS_RLCMAC_TBF_UL_ASS_EV_CREATE_RLCMAC_MSG: + data_ctx = (struct tbf_ul_ass_ev_create_rlcmac_msg_ctx *)data; + LOGPFSML(fi, LOGL_ERROR, "TODO: create PKT RES REQ...\n"); + data_ctx->msg = create_pkt_resource_req(ctx, data_ctx); + if (!data_ctx->msg) + return; + tbf_ul_ass_fsm_state_chg(fi, GPRS_RLCMAC_TBF_UL_ASS_ST_WAIT_PKT_UL_ASS); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_wait_pkt_ul_ass(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + //struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *)fi->priv; + switch (event) { + case GPRS_RLCMAC_TBF_UL_ASS_EV_RX_PKT_UL_ASS: + // TODO: fill ctx->phase2_alloc with contents from pkt_ul_ass + // TODO: what to do if Pkt_ul_ass is "reject"? need to check spec, depending on cause. + tbf_ul_ass_fsm_state_chg(fi, GPRS_RLCMAC_TBF_UL_ASS_ST_SCHED_PKT_CTRL_ACK); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_sched_pkt_ctrl_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + //struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *)fi->priv; + struct tbf_ul_ass_ev_create_rlcmac_msg_ctx *data_ctx; + + switch (event) { + case GPRS_RLCMAC_TBF_UL_ASS_EV_CREATE_RLCMAC_MSG: + data_ctx = (struct tbf_ul_ass_ev_create_rlcmac_msg_ctx *)data; + LOGPFSML(fi, LOGL_ERROR, "TODO: create PKT CTRL ACK...\n"); + //data_ctx->msg = create_packet_ctrl_ack(ctx, data_ctx); + data_ctx->msg = NULL; + if (!data_ctx->msg) + return; + tbf_ul_ass_fsm_state_chg(fi, GPRS_RLCMAC_TBF_UL_ASS_ST_COMPL); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_compl_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *)fi->priv; + + /* Update TBF with allocated content: */ + if (ctx->ass_type == GPRS_RLCMAC_TBF_UL_ASS_TYPE_1PHASE) + memcpy(&ctx->ul_tbf->cur_alloc, &ctx->phase1_alloc, sizeof(ctx->phase1_alloc)); + else + memcpy(&ctx->ul_tbf->cur_alloc, &ctx->phase2_alloc, sizeof(ctx->phase1_alloc)); + /* Inform the main TBF state about the assignment completed: */ + osmo_fsm_inst_dispatch(ctx->ul_tbf->state_fsm.fi, GPRS_RLCMAC_TBF_UL_EV_UL_ASS_COMPL, NULL); + /* Go back to IDLE state. */ + tbf_ul_ass_fsm_state_chg(fi, GPRS_RLCMAC_TBF_UL_ASS_ST_IDLE); +} + +static struct osmo_fsm_state tbf_ul_ass_fsm_states[] = { + [GPRS_RLCMAC_TBF_UL_ASS_ST_IDLE] = { + .in_event_mask = + X(GPRS_RLCMAC_TBF_UL_ASS_EV_START), + .out_state_mask = + X(GPRS_RLCMAC_TBF_UL_ASS_ST_WAIT_CCCH_IMM_ASS), + .name = "IDLE", + .onenter = st_idle_on_enter, + .action = st_idle, + }, + [GPRS_RLCMAC_TBF_UL_ASS_ST_WAIT_CCCH_IMM_ASS] = { + .in_event_mask = + X(GPRS_RLCMAC_TBF_UL_ASS_EV_RX_CCCH_IMM_ASS), + .out_state_mask = + X(GPRS_RLCMAC_TBF_UL_ASS_ST_SCHED_PKT_RES_REQ) | + X(GPRS_RLCMAC_TBF_UL_ASS_ST_COMPL), + .name = "WAIT_CCCH_IMM_ASS", + .action = st_wait_ccch_imm_ass, + }, + [GPRS_RLCMAC_TBF_UL_ASS_ST_SCHED_PKT_RES_REQ] = { + .in_event_mask = + X(GPRS_RLCMAC_TBF_UL_ASS_EV_CREATE_RLCMAC_MSG), + .out_state_mask = + X(GPRS_RLCMAC_TBF_UL_ASS_ST_WAIT_PKT_UL_ASS), + .name = "SCHED_PKT_RES_REQ", + .action = st_sched_pkt_res_req, + }, + [GPRS_RLCMAC_TBF_UL_ASS_ST_WAIT_PKT_UL_ASS] = { + .in_event_mask = + X(GPRS_RLCMAC_TBF_UL_ASS_EV_RX_PKT_UL_ASS), + .out_state_mask = + X(GPRS_RLCMAC_TBF_UL_ASS_ST_SCHED_PKT_CTRL_ACK), + .name = "WAIT_PKT_UL_ASS", + .action = st_wait_pkt_ul_ass, + }, + [GPRS_RLCMAC_TBF_UL_ASS_ST_SCHED_PKT_CTRL_ACK] = { + .in_event_mask = + X(GPRS_RLCMAC_TBF_UL_ASS_EV_CREATE_RLCMAC_MSG), + .out_state_mask = + X(GPRS_RLCMAC_TBF_UL_ASS_ST_COMPL), + .name = "SCHED_PKT_CTRL_ACK", + .action = st_sched_pkt_ctrl_ack, + }, + [GPRS_RLCMAC_TBF_UL_ASS_ST_COMPL] = { + .in_event_mask = 0, + .out_state_mask = + X(GPRS_RLCMAC_TBF_UL_ASS_ST_IDLE), + .name = "COMPLETED", + .onenter = st_compl_on_enter, + }, +}; + + +static int tbf_ul_ass_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *)fi->priv; + switch (fi->T) { + case -2001: + LOGPTBFUL(ctx->ul_tbf, LOGL_NOTICE, "releasing due to PACCH assignment timeout.\n"); + /* fall-through */ + case 3169: + case 3195: + gprs_rlcmac_ul_tbf_free(ctx->ul_tbf); + break; + default: + OSMO_ASSERT(0); + } + return 0; +} + +static struct osmo_fsm tbf_ul_ass_fsm = { + .name = "UL_TBF_ASS", + .states = tbf_ul_ass_fsm_states, + .num_states = ARRAY_SIZE(tbf_ul_ass_fsm_states), + .timer_cb = tbf_ul_ass_fsm_timer_cb, + .log_subsys = DLGLOBAL, /* updated dynamically through gprs_rlcmac_tbf_ul_ass_fsm_set_log_cat() */ + .event_names = tbf_ul_ass_fsm_event_names, +}; + +int gprs_rlcmac_tbf_ul_ass_fsm_init(void) +{ + return osmo_fsm_register(&tbf_ul_ass_fsm); +} + +void gprs_rlcmac_tbf_ul_ass_fsm_set_log_cat(int logcat) +{ + tbf_ul_ass_fsm.log_subsys = logcat; +} + +int gprs_rlcmac_tbf_ul_ass_fsm_constructor(struct gprs_rlcmac_ul_tbf *ul_tbf) +{ + struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *ctx = &ul_tbf->ul_ass_fsm; + ctx->ul_tbf = ul_tbf; + ctx->fi = osmo_fsm_inst_alloc(&tbf_ul_ass_fsm, ul_tbf, ctx, LOGL_INFO, NULL); + if (!ctx->fi) + return -ENODATA; + + return 0; +} + +void gprs_rlcmac_tbf_ul_ass_fsm_destructor(struct gprs_rlcmac_ul_tbf *ul_tbf) +{ + struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *ctx = &ul_tbf->ul_ass_fsm; + osmo_fsm_inst_free(ctx->fi); + ctx->fi = NULL; +} + +int gprs_rlcmac_tbf_ul_ass_start(struct gprs_rlcmac_ul_tbf *ul_tbf, enum gprs_rlcmac_tbf_ul_ass_type type) +{ + int rc; + rc = osmo_fsm_inst_dispatch(ul_tbf->ul_ass_fsm.fi, + GPRS_RLCMAC_TBF_UL_ASS_EV_START, + &type); + return rc; +} + +bool gprs_rlcmac_tbf_ul_ass_pending(struct gprs_rlcmac_ul_tbf *ul_tbf) +{ + return ul_tbf->ul_ass_fsm.fi->state != GPRS_RLCMAC_TBF_UL_ASS_ST_IDLE; +} + +bool gprs_rlcmac_tbf_ul_ass_match_rach_req(struct gprs_rlcmac_ul_tbf *ul_tbf, uint8_t ra) +{ + return ul_tbf->ul_ass_fsm.fi->state == GPRS_RLCMAC_TBF_UL_ASS_ST_WAIT_CCCH_IMM_ASS && + ul_tbf->ul_ass_fsm.rach_req_ra == ra; +} + +enum gprs_rlcmac_tbf_ul_ass_fsm_states gprs_rlcmac_tbf_ul_ass_state(const struct gprs_rlcmac_ul_tbf *ul_tbf) +{ + const struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *ctx = &ul_tbf->ul_ass_fsm; + return ctx->fi->state; +} + +bool gprs_rlcmac_tbf_ul_ass_rts(const struct gprs_rlcmac_ul_tbf *ul_tbf, const struct gprs_rlcmac_rts_block_ind *bi) +{ + const struct gprs_rlcmac_tbf_ul_ass_fsm_ctx *ctx = &ul_tbf->ul_ass_fsm; + + switch (ctx->fi->state) { + case GPRS_RLCMAC_TBF_UL_ASS_ST_SCHED_PKT_RES_REQ: + return (ctx->phase1_alloc.ts[bi->ts].allocated && + ctx->phase1_alloc.ts[bi->ts].usf == bi->usf); + case GPRS_RLCMAC_TBF_UL_ASS_ST_SCHED_PKT_CTRL_ACK: + return (ctx->sched_pkt_ctrl_ack.ts == bi->ts && + ctx->sched_pkt_ctrl_ack.fn == bi->fn); + default: + return false; + }; +} + +struct msgb *gprs_rlcmac_tbf_ul_ass_create_rlcmac_msg(const struct gprs_rlcmac_ul_tbf *ul_tbf, + const struct gprs_rlcmac_rts_block_ind *bi) +{ + int rc; + struct tbf_ul_ass_ev_create_rlcmac_msg_ctx data_ctx = { + .ts = bi->ts, + .fn = bi->fn, + .msg = NULL, + }; + + rc = osmo_fsm_inst_dispatch(ul_tbf->ul_ass_fsm.fi, + GPRS_RLCMAC_TBF_UL_ASS_EV_CREATE_RLCMAC_MSG, + &data_ctx); + if (rc != 0 || !data_ctx.msg) + return NULL; + return data_ctx.msg; +} diff --git a/src/rlcmac/tbf_ul_fsm.c b/src/rlcmac/tbf_ul_fsm.c new file mode 100644 index 0000000..0078846 --- /dev/null +++ b/src/rlcmac/tbf_ul_fsm.c @@ -0,0 +1,198 @@ +/* TBF as per 3GPP TS 44.064 */ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH info@sysmocom.de + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +#include <talloc.h> +#include <osmocom/core/tdef.h> +#include <osmocom/core/fsm.h> + +#include <osmocom/gprs/rlcmac/tbf_ul_fsm.h> +#include <osmocom/gprs/rlcmac/tbf_ul.h> +#include <osmocom/gprs/rlcmac/gre.h> + +#define X(s) (1 << (s)) + +static const struct value_string tbf_ul_fsm_event_names[] = { + { GPRS_RLCMAC_TBF_UL_EV_UL_ASS_START, "UL_ASS_START" }, + { GPRS_RLCMAC_TBF_UL_EV_UL_ASS_COMPL, "UL_ASS_COMPL" }, + { GPRS_RLCMAC_TBF_UL_EV_FOOBAR, "FOOBAR" }, + { 0, NULL } +}; + +static const struct osmo_tdef_state_timeout tbf_ul_fsm_timeouts[32] = { + [GPRS_RLCMAC_TBF_UL_ST_NEW] = { }, + [GPRS_RLCMAC_TBF_UL_ST_WAIT_ASSIGN] = { }, + [GPRS_RLCMAC_TBF_UL_ST_FLOW] = { }, +}; + +/* Transition to a state, using the T timer defined in tbf_fsm_timeouts. + * The actual timeout value is in turn obtained from conn->T_defs. + * Assumes local variable fi exists. */ + #define tbf_ul_fsm_state_chg(fi, NEXT_STATE) \ + osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \ + tbf_ul_fsm_timeouts, \ + g_ctx->T_defs, \ + -1) + +static uint8_t ul_tbf_ul_slotmask(struct gprs_rlcmac_ul_tbf *ul_tbf) +{ + uint8_t i; + uint8_t ul_slotmask = 0; + + for (i = 0; i < 8; i++) { + if (ul_tbf->cur_alloc.ts[i].allocated) + ul_slotmask |= (1 << i); + } + + return ul_slotmask; +} + +static int configure_ul_tbf(struct gprs_rlcmac_tbf_ul_fsm_ctx *ctx) +{ + struct osmo_gprs_rlcmac_prim *rlcmac_prim; + uint8_t ul_slotmask = ul_tbf_ul_slotmask(ctx->ul_tbf); + + LOGPFSML(ctx->fi, LOGL_INFO, "Send L1CTL-CF_UL_TBF.req ul_slotmask=0x%02x\n", ul_slotmask); + rlcmac_prim = gprs_rlcmac_prim_alloc_l1ctl_cfg_ul_tbf_req(ctx->tbf->nr, ul_slotmask); + return gprs_rlcmac_prim_call_down_cb(rlcmac_prim); +} + +static void st_new(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + //struct gprs_rlcmac_tbf_ul_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_ul_fsm_ctx *)fi->priv; + switch (event) { + case GPRS_RLCMAC_TBF_UL_EV_UL_ASS_START: + tbf_ul_fsm_state_chg(fi, GPRS_RLCMAC_TBF_UL_ST_WAIT_ASSIGN); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_wait_assign(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gprs_rlcmac_tbf_ul_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_ul_fsm_ctx *)fi->priv; + switch (event) { + case GPRS_RLCMAC_TBF_UL_EV_UL_ASS_COMPL: + /* Configure UL TBF on the lower MAC side: */ + configure_ul_tbf(ctx); + tbf_ul_fsm_state_chg(fi, GPRS_RLCMAC_TBF_UL_ST_FLOW); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_flow(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + //struct gprs_rlcmac_tbf_ul_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_ul_fsm_ctx *)fi->priv; + switch (event) { + default: + OSMO_ASSERT(0); + } +} + +static struct osmo_fsm_state tbf_ul_fsm_states[] = { + [GPRS_RLCMAC_TBF_UL_ST_NEW] = { + .in_event_mask = + X(GPRS_RLCMAC_TBF_UL_EV_UL_ASS_START), + .out_state_mask = + X(GPRS_RLCMAC_TBF_UL_ST_WAIT_ASSIGN) | + X(GPRS_RLCMAC_TBF_UL_ST_FLOW), + .name = "NEW", + .action = st_new, + }, + [GPRS_RLCMAC_TBF_UL_ST_WAIT_ASSIGN] = { + .in_event_mask = + X(GPRS_RLCMAC_TBF_UL_EV_UL_ASS_COMPL), + .out_state_mask = + X(GPRS_RLCMAC_TBF_UL_ST_FLOW), + .name = "ASSIGN", + .action = st_wait_assign, + }, + [GPRS_RLCMAC_TBF_UL_ST_FLOW] = { + .in_event_mask = + X(GPRS_RLCMAC_TBF_UL_EV_FOOBAR), + .out_state_mask = + X(GPRS_RLCMAC_TBF_UL_ST_WAIT_ASSIGN), + .name = "FLOW", + .action = st_flow, + }, +}; + +static int tbf_ul_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + struct gprs_rlcmac_tbf_ul_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_ul_fsm_ctx *)fi->priv; + switch (fi->T) { + case -2001: + LOGPTBFUL(ctx->ul_tbf, LOGL_NOTICE, "releasing due to PACCH assignment timeout.\n"); + /* fall-through */ + case 3169: + case 3195: + gprs_rlcmac_ul_tbf_free(ctx->ul_tbf); + break; + default: + OSMO_ASSERT(0); + } + return 0; +} + +static struct osmo_fsm tbf_ul_fsm = { + .name = "UL_TBF", + .states = tbf_ul_fsm_states, + .num_states = ARRAY_SIZE(tbf_ul_fsm_states), + .timer_cb = tbf_ul_fsm_timer_cb, + .log_subsys = DLGLOBAL, /* updated dynamically through gprs_rlcmac_tbf_ul_fsm_set_log_cat() */ + .event_names = tbf_ul_fsm_event_names, +}; + +int gprs_rlcmac_tbf_ul_fsm_init(void) +{ + return osmo_fsm_register(&tbf_ul_fsm); +} + +void gprs_rlcmac_tbf_ul_fsm_set_log_cat(int logcat) +{ + tbf_ul_fsm.log_subsys = logcat; +} + +int gprs_rlcmac_tbf_ul_fsm_constructor(struct gprs_rlcmac_ul_tbf *ul_tbf) +{ + struct gprs_rlcmac_tbf_ul_fsm_ctx *ctx = &ul_tbf->state_fsm; + ctx->ul_tbf = ul_tbf; + ctx->fi = osmo_fsm_inst_alloc(&tbf_ul_fsm, ul_tbf, ctx, LOGL_INFO, NULL); + if (!ctx->fi) + return -ENODATA; + + return 0; +} + +void gprs_rlcmac_tbf_ul_fsm_destructor(struct gprs_rlcmac_ul_tbf *ul_tbf) +{ + struct gprs_rlcmac_tbf_ul_fsm_ctx *ctx = &ul_tbf->state_fsm; + osmo_fsm_inst_free(ctx->fi); + ctx->fi = NULL; +} + +enum gprs_rlcmac_tbf_ul_fsm_states gprs_rlcmac_tbf_ul_state(const struct gprs_rlcmac_ul_tbf *ul_tbf) +{ + const struct gprs_rlcmac_tbf_ul_fsm_ctx *ctx = &ul_tbf->state_fsm; + return ctx->fi->state; +} diff --git a/tests/rlcmac/rlcmac_prim_test.c b/tests/rlcmac/rlcmac_prim_test.c index 9347b01..fb12f05 100644 --- a/tests/rlcmac/rlcmac_prim_test.c +++ b/tests/rlcmac/rlcmac_prim_test.c @@ -27,6 +27,8 @@
static void *tall_ctx = NULL;
+uint8_t last_rach_req_ra = 0; + /** MS-SGSN LLC (Mobile Station - Serving GPRS Support Node Logical Link Control) SAPI: GPRS Mobility Management Address field SAPI: LLGMM @@ -143,7 +145,19 @@
switch (rlcmac_prim->oph.sap) { case OSMO_GPRS_RLCMAC_SAP_L1CTL: - printf("%s(): Rx %s\n", __func__, pdu_name); + switch (OSMO_PRIM_HDR(&rlcmac_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_RACH, PRIM_OP_REQUEST): + last_rach_req_ra = rlcmac_prim->l1ctl.rach_req.ra; + printf("%s(): Rx %s ra=0x%02x\n", __func__, pdu_name, last_rach_req_ra); + break; + case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_CFG_UL_TBF, PRIM_OP_REQUEST): + printf("%s(): Rx %s ul_tbf_nr=%u ul_slotmask=0x%02x\n", __func__, pdu_name, + rlcmac_prim->l1ctl.cfg_ul_tbf_req.ul_tbf_nr, + rlcmac_prim->l1ctl.cfg_ul_tbf_req.ul_slotmask); + break; + default: + printf("%s(): Rx %s\n", __func__, pdu_name); + } break; default: printf("%s(): Unexpected Rx %s\n", __func__, pdu_name); @@ -177,11 +191,11 @@ rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim);
OSMO_ASSERT(sizeof(ccch_imm_ass_pkt_ul_tbf_normal) == GSM_MACBLOCK_LEN); + ccch_imm_ass_pkt_ul_tbf_normal[7] = last_rach_req_ra; /* Update RA to match */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_ul_tbf_normal); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim);
- /* This now fails because it's not yet implemented: */ - OSMO_ASSERT(rc != 0); + OSMO_ASSERT(rc == 0); printf("=== %s end ===\n", __func__); }
diff --git a/tests/rlcmac/rlcmac_prim_test.err b/tests/rlcmac/rlcmac_prim_test.err index f9268b1..7e75164 100644 --- a/tests/rlcmac/rlcmac_prim_test.err +++ b/tests/rlcmac/rlcmac_prim_test.err @@ -1,4 +1,17 @@ DLGLOBAL INFO Rx from upper layers: GRR-UNITDATA.request DLGLOBAL INFO TLLI=0x00002342 not found, creating entity on the fly +DLGLOBAL INFO UL_TBF{NEW}: Allocated +DLGLOBAL INFO UL_TBF_ASS{IDLE}: Allocated +DLGLOBAL INFO UL_TBF_ASS{IDLE}: Received Event START +DLGLOBAL INFO UL_TBF{NEW}: Received Event UL_ASS_START +DLGLOBAL INFO UL_TBF{NEW}: state_chg to ASSIGN +DLGLOBAL INFO UL_TBF_ASS{IDLE}: Send RACH.req ra=0x7e +DLGLOBAL INFO UL_TBF_ASS{IDLE}: state_chg to WAIT_CCCH_IMM_ASS DLGLOBAL INFO Rx from lower layers: L1CTL-CCCH_DATA.indication -DLGLOBAL ERROR Unsupported rlcmac_prim! L1CTL-CCCH_DATA.indication +DLGLOBAL INFO UL_TBF_ASS{WAIT_CCCH_IMM_ASS}: Received Event RX_CCCH_IMM_ASS +DLGLOBAL INFO UL_TBF_ASS{WAIT_CCCH_IMM_ASS}: ImmAss DynamicAlloc (1phase access) ts_nr=7 usf=0 +DLGLOBAL INFO UL_TBF_ASS{WAIT_CCCH_IMM_ASS}: state_chg to COMPLETED +DLGLOBAL INFO UL_TBF{ASSIGN}: Received Event UL_ASS_COMPL +DLGLOBAL INFO UL_TBF{ASSIGN}: Send L1CTL-CF_UL_TBF.req ul_slotmask=0x80 +DLGLOBAL INFO UL_TBF{ASSIGN}: state_chg to FLOW +DLGLOBAL INFO UL_TBF_ASS{COMPLETED}: state_chg to IDLE diff --git a/tests/rlcmac/rlcmac_prim_test.ok b/tests/rlcmac/rlcmac_prim_test.ok index 3deb5e9..030743e 100644 --- a/tests/rlcmac/rlcmac_prim_test.ok +++ b/tests/rlcmac/rlcmac_prim_test.ok @@ -1,2 +1,4 @@ === test_ul_tbf_attach start === +test_rlcmac_prim_down_cb(): Rx L1CTL-RACH.request ra=0x7e +test_rlcmac_prim_down_cb(): Rx L1CTL-CFG_UL_TBF.request ul_tbf_nr=0 ul_slotmask=0x80 === test_ul_tbf_attach end ===