/* Data Types / Encoding / Decoding for OsmocomBB L1CTL interface */ /* (C) 2017 by Harald Welte , derived from l1ctl_proto.h * which is (C) 2010 by Harald Welte + Holger Hans Peter Freyther * All rights reserved. * * Released under the terms of GNU General Public License, Version 2 or * (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later */ module L1CTL_Types { import from General_Types all; import from GSM_Types all; import from GSM_RR_Types all; import from Osmocom_Types all; type uint32_t uint32_le with { variant "BYTEORDER(first)" }; type enumerated L1ctlMsgType { L1CTL_NONE, L1CTL_FBSB_REQ, L1CTL_FBSB_CONF, L1CTL_DATA_IND, L1CTL_RACH_REQ, L1CTL_DM_EST_REQ, L1CTL_DATA_REQ, L1CTL_RESET_IND, L1CTL_PM_REQ, /* power measurement */ L1CTL_PM_CONF, /* power measurement */ L1CTL_ECHO_REQ, L1CTL_ECHO_CONF, L1CTL_RACH_CONF, L1CTL_RESET_REQ, L1CTL_RESET_CONF, L1CTL_DATA_CONF, L1CTL_CCCH_MODE_REQ, L1CTL_CCCH_MODE_CONF, L1CTL_DM_REL_REQ, L1CTL_PARAM_REQ, L1CTL_DM_FREQ_REQ, L1CTL_CRYPTO_REQ, L1CTL_SIM_REQ, L1CTL_SIM_CONF, L1CTL_TCH_MODE_REQ, L1CTL_TCH_MODE_CONF, L1CTL_NEIGH_PM_REQ, L1CTL_NEIGH_PM_IND, L1CTL_TRAFFIC_REQ, 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_EXT_RACH_REQ, L1CTL_DATA_ABS_REQ /*!< FIXME: no such message in OsmocomBB */ } with { variant "FIELDLENGTH(8)" }; type enumerated L1ctlCcchMode { CCCH_MODE_NONE (0), CCCH_MODE_NON_COMBINED, CCCH_MODE_COMBINED, CCCH_MODE_COMBINED_CBCH } with { variant "FIELDLENGTH(8)" }; type enumerated L1ctlNeighMode { NEIGH_MODE_NONE (0), NEIGH_MODE_PM, NEIGH_MODE_SB } with { variant "FIELDLENGTH(8)" }; type enumerated L1ctlGprsCs { L1CTL_CS1 (1), L1CTL_CS2, L1CTL_CS3, L1CTL_CS4, L1CTL_MCS1, L1CTL_MCS2, L1CTL_MCS3, L1CTL_MCS4, L1CTL_MCS5, L1CTL_MCS6, L1CTL_MCS7, L1CTL_MCS8, L1CTL_MCS9 } with { variant "FIELDLENGTH(8)" }; type enumerated L1ctlResetType { L1CTL_RES_T_BOOT (0), L1CTL_RES_T_FULL, L1CTL_RES_T_SCHED } with { variant "FIELDLENGTH(8)" }; type record L1ctlHdrFlags { BIT7 padding, boolean f_done } with { variant "" }; type record L1ctlHeader { L1ctlMsgType msg_type, L1ctlHdrFlags flags, OCT2 padding } with { variant "" }; template L1ctlHeader tr_L1ctlHeader(template L1ctlMsgType msg_type) := { msg_type := msg_type, flags := ?, padding := ? }; template (value) L1ctlHeader ts_L1ctlHeader(L1ctlMsgType msg_type) := { msg_type := msg_type, flags := { padding := '0000000'B, f_done := false }, padding := '0000'O }; type record L1ctlDlInfo { RslChannelNr chan_nr, RslLinkId link_id, Arfcn arfcn, uint32_t frame_nr, GsmRxLev rx_level, uint8_t snr, uint8_t num_biterr, uint8_t fire_crc } with { variant "" }; type record L1ctlFbsbConf { int16_t initial_freq_err, uint8_t result, uint8_t bsic } with { variant "" }; type record L1ctlCcchModeConf { L1ctlCcchMode ccch_mode, OCT3 padding } with { variant "" }; /* gsm48_chan_mode */ type uint8_t L1ctlTchMode; type record L1ctlAudioMode { BIT4 padding, boolean tx_microphone, boolean tx_traffic_req, boolean rx_speaker, boolean rx_traffic_ind } with { variant "" }; template (value) L1ctlAudioMode t_L1CTL_AudioModeNone := { '0000'B, false, false, false, false }; type record L1ctlTchModeConf { L1ctlTchMode tch_mode, L1ctlAudioMode audio_mode, OCT2 padding } with { variant "" }; type record L1ctlDataInd { octetstring payload } with { variant (payload) "BYTEORDER(first)" }; type union L1ctlDlPayload { L1ctlFbsbConf fbsb_conf, L1ctlCcchModeConf ccch_mode_conf, L1ctlTchModeConf tch_mode_conf, L1ctlDataInd data_ind, L1ctlTrafficReq traffic_ind, L1ctlTbfCfgReq tbf_cfg_conf, octetstring other } with { variant (other) "BYTEORDER(first)" }; type record L1ctlDlMessage { L1ctlHeader header, L1ctlDlInfo dl_info optional, L1ctlDlPayload payload optional } with { variant (dl_info) "PRESENCE(header.msg_type = L1CTL_FBSB_CONF, header.msg_type = L1CTL_RACH_CONF, header.msg_type = L1CTL_DATA_IND, header.msg_type = L1CTL_DATA_CONF, header.msg_type = L1CTL_TRAFFIC_IND, header.msg_type = L1CTL_TRAFFIC_CONF)" variant (payload) "CROSSTAG(fbsb_conf, header.msg_type = L1CTL_FBSB_CONF; ccch_mode_conf, header.msg_type = L1CTL_CCCH_MODE_CONF; tch_mode_conf, header.msg_type = L1CTL_TCH_MODE_CONF; data_ind, header.msg_type = L1CTL_DATA_IND; traffic_ind, header.msg_type = L1CTL_TRAFFIC_IND; tbf_cfg_conf, header.msg_type = L1CTL_TBF_CFG_CONF; other, OTHERWISE; )" }; external function enc_L1ctlDlMessage(in L1ctlDlMessage msg) return octetstring with { extension "prototype(convert) encode(RAW)" }; external function dec_L1ctlDlMessage(in octetstring stream) return L1ctlDlMessage with { extension "prototype(convert) decode(RAW)" }; type record L1ctlUlInfo { RslChannelNr chan_nr, RslLinkId link_id, OCT2 padding } with { variant "" }; type record L1ctlUlTbfInfo { uint8_t tbf_nr, L1ctlGprsCs cs, OCT2 padding } with { variant "" }; type record L1ctlUlAbsInfo { uint8_t tbf_nr, L1ctlGprsCs cs, uint8_t ts_nr, OCT1 padding, uint32_le fn, Arfcn arfcn, OCT2 padding2 } with { variant "" }; type record L1ctlFbsbFlags { BIT5 padding, boolean sb, boolean fb1, boolean fb0 } with { variant "FIELDORDER(msb)" }; template (value) L1ctlFbsbFlags t_L1CTL_FBSB_F_ALL := { padding := '00000'B, sb := true, fb1 := true, fb0 := true }; type record L1ctlFbsbReq { Arfcn arfcn, uint16_t timeout_tdma_frames, uint16_t freq_err_thresh1, uint16_t freq_err_thresh2, uint8_t num_freqerr_avg, L1ctlFbsbFlags flags, uint8_t sync_info_idx, L1ctlCcchMode ccch_mode, GsmRxLev rxlev_exp } with { variant "" }; type record L1ctlCcchModeReq { L1ctlCcchMode ccch_mode, OCT3 padding } with { variant "" }; type record L1ctlTchModeReq { L1ctlTchMode tch_mode, L1ctlAudioMode audio_mode, OCT2 padding } with { variant "" }; type record L1ctlRachReq { uint8_t ra, uint8_t combined, uint16_t offset } with { variant "" }; type enumerated L1ctlRachSynchSeq { RACH_SYNCH_SEQ_TS0 (0), RACH_SYNCH_SEQ_TS1, RACH_SYNCH_SEQ_TS2 } with { variant "FIELDLENGTH(8)" }; type record L1ctlExtRachReq { uint16_t ra11, L1ctlRachSynchSeq synch_seq, uint8_t combined, uint16_t offset } with { variant "" }; type record L1ctlParReq { int8_t ta, uint8_t tx_power, OCT2 padding } with { variant "" }; type record L1ctlDataReq { SacchL1Header l1header optional, octetstring l2_payload } with { variant "" }; type record L1ctlH1 { uint8_t hsn, uint8_t maio, uint8_t n, OCT1 padding, bitstring ma length(64) } with { variant "" }; type record L1ctlDmEstReq { GsmTsc tsc, uint8_t h, Arfcn arfcn optional, L1ctlH1 hopping optional, L1ctlTchMode tch_mode, L1ctlAudioMode audio_mode } with { variant (arfcn) "PRESENCE(h = 0)" variant (hopping) "PRESENCE(h = 1)" }; type record L1ctlReset { L1ctlResetType reset_type, OCT3 padding } with { variant "" }; type record L1CtlCryptoReq { uint8_t algo, uint8_t key_len, octetstring key } with { variant (key_len) "LENGTHTO(key)" }; type record L1ctlTrafficReq { octetstring data } with { variant (data) "BYTEORDER(first)" } type record length(8) of uint8_t TfiUsfArr; type record L1ctlTbfCfgReq { uint8_t tbf_nr, boolean is_uplink, OCT2 padding, TfiUsfArr tfi_usf } with { variant (is_uplink) "FIELDLENGTH(8)" }; type union L1ctlUlPayload { L1ctlFbsbReq fbsb_req, L1ctlCcchModeReq ccch_mode_req, L1ctlTchModeReq tch_mode_req, L1ctlRachReq rach_req, L1ctlExtRachReq ext_rach_req, L1ctlParReq par_req, L1ctlDmEstReq dm_est_req, L1ctlReset reset_req, //L1ctlNeighPmReq neigh_pm_req, L1CtlCryptoReq crypto_req, L1ctlTrafficReq traffic_req, L1ctlTbfCfgReq tbf_cfg_req, L1ctlDataReq data_req, octetstring other } with { variant (other) "BYTEORDER(first)" }; type record L1ctlUlMessage { L1ctlHeader header, L1ctlUlInfo ul_info optional, L1ctlUlTbfInfo ul_info_tbf optional, L1ctlUlAbsInfo ul_info_abs optional, L1ctlUlPayload payload } with { variant (ul_info) "PRESENCE(header.msg_type = L1CTL_RACH_REQ, header.msg_type = L1CTL_EXT_RACH_REQ, header.msg_type = L1CTL_PARAM_REQ, header.msg_type = L1CTL_CRYPTO_REQ, header.msg_type = L1CTL_DATA_REQ, header.msg_type = L1CTL_DM_EST_REQ, header.msg_type = L1CTL_DM_FREQ_REQ, header.msg_type = L1CTL_DM_REL_REQ, header.msg_type = L1CTL_TRAFFIC_REQ)" variant (ul_info_tbf) "PRESENCE(header.msg_type = L1CTL_DATA_TBF_REQ)" variant (ul_info_abs) "PRESENCE(header.msg_type = L1CTL_DATA_ABS_REQ)" variant (payload) "CROSSTAG(fbsb_req, header.msg_type = L1CTL_FBSB_REQ; ccch_mode_req, header.msg_type = L1CTL_CCCH_MODE_REQ; tch_mode_req, header.msg_type = L1CTL_TCH_MODE_REQ; rach_req, header.msg_type = L1CTL_RACH_REQ; ext_rach_req, header.msg_type = L1CTL_EXT_RACH_REQ; par_req, header.msg_type = L1CTL_PARAM_REQ; dm_est_req, header.msg_type = L1CTL_DM_EST_REQ; reset_req, header.msg_type = L1CTL_RESET_REQ; crypto_req, header.msg_type = L1CTL_CRYPTO_REQ; traffic_req, header.msg_type = L1CTL_TRAFFIC_REQ; tbf_cfg_req, header.msg_type = L1CTL_TBF_CFG_REQ; data_req, header.msg_type = L1CTL_DATA_REQ; other, OTHERWISE; )" }; external function enc_L1ctlUlMessage(in L1ctlUlMessage msg) return octetstring with { extension "prototype(convert) encode(RAW)" }; external function dec_L1ctlUlMessage(in octetstring stream) return L1ctlUlMessage with { extension "prototype(convert) decode(RAW)" }; type record L1ctlUlMessageLV { uint16_t len, L1ctlUlMessage msg } with { variant (len) "LENGTHTO(msg)" }; external function enc_L1ctlUlMessageLV(in L1ctlUlMessageLV msg) return octetstring with { extension "prototype(convert) encode(RAW)" }; external function dec_L1ctlUlMessageLV(in octetstring stream) return L1ctlUlMessageLV with { extension "prototype(convert) decode(RAW)" }; type record L1ctlDlMessageLV { uint16_t len, L1ctlDlMessage msg } with { variant (len) "LENGTHTO(msg)" }; external function enc_L1ctlDlMessageLV(in L1ctlDlMessageLV msg) return octetstring with { extension "prototype(convert) encode(RAW)" }; external function dec_L1ctlDlMessageLV(in octetstring stream) return L1ctlDlMessageLV with { extension "prototype(convert) decode(RAW)" }; /* for generating RESET_REQ */ template (value) L1ctlUlMessage t_L1ctlResetReq(L1ctlResetType rst_type) := { header := ts_L1ctlHeader(L1CTL_RESET_REQ), ul_info := omit, ul_info_tbf := omit, ul_info_abs := omit, payload := { reset_req := { reset_type := rst_type, padding := '000000'O } } }; /* for generating FBSB_REQ */ template (value) L1ctlUlMessage ts_L1CTL_FBSB_REQ(Arfcn arfcn, L1ctlFbsbFlags flags, uint8_t sync_info_idx, L1ctlCcchMode ccch_mode, GsmRxLev rxlev_exp) := { header := ts_L1ctlHeader(L1CTL_FBSB_REQ), ul_info := omit, ul_info_tbf := omit, ul_info_abs := omit, payload := { fbsb_req := { arfcn := arfcn, timeout_tdma_frames := 250, /* about 1s */ freq_err_thresh1 := 10000, freq_err_thresh2 := 800, num_freqerr_avg := 3, flags := flags, sync_info_idx := sync_info_idx, ccch_mode := ccch_mode, rxlev_exp := rxlev_exp } } }; /* for matching against incoming FBSB_CONF */ template L1ctlDlMessage tr_L1CTL_FBSB_CONF(template uint8_t result) := { header := tr_L1ctlHeader(L1CTL_FBSB_CONF), dl_info := ?, payload := { fbsb_conf := { initial_freq_err := ?, result := result, bsic := ? } } }; template (value) L1ctlUlMessage ts_L1CTL_CCCH_MODE_REQ(L1ctlCcchMode ccch_mode) := { header := ts_L1ctlHeader(L1CTL_CCCH_MODE_REQ), ul_info := omit, ul_info_tbf := omit, ul_info_abs := omit, payload := { ccch_mode_req := { ccch_mode := ccch_mode, padding := '000000'O } } }; template L1ctlDlMessage tr_L1CTL_MsgType(template L1ctlMsgType msg_type) := { header := tr_L1ctlHeader(msg_type), dl_info := *, payload := * } template L1ctlDlMessage tr_L1CTL_CCCH_MODE_CONF := tr_L1CTL_MsgType(L1CTL_CCCH_MODE_CONF); template L1ctlUlMessage ts_L1CTL_RACH_REQ(uint8_t ra, uint8_t combined, uint16_t offset, template (value) RslChannelNr chan_nr := ts_RslChanNr_RACH(0), template (value) RslLinkId link_id := ts_RslLinkID_DCCH(0)) := { header := ts_L1ctlHeader(L1CTL_RACH_REQ), ul_info := { chan_nr := chan_nr, link_id := link_id, padding := '0000'O }, ul_info_tbf := omit, ul_info_abs := omit, payload := { rach_req := { ra := ra, combined := combined, offset := offset } } } template L1ctlUlMessage ts_L1CTL_EXT_RACH_REQ( uint16_t ra11, L1ctlRachSynchSeq seq, uint8_t combined, uint16_t offset ) := { header := ts_L1ctlHeader(L1CTL_EXT_RACH_REQ), ul_info := { /* FIXME: both RSL chan_nr and link_id should be configurable */ chan_nr := t_RslChanNr_RACH(0), link_id := ts_RslLinkID_DCCH(0), padding := '0000'O }, ul_info_tbf := omit, ul_info_abs := omit, payload := { ext_rach_req := { ra11 := ra11, synch_seq := seq, combined := combined, offset := offset } } } template L1ctlUlMessage ts_L1CTL_PAR_REQ(uint8_t ta, uint8_t tx_power) := { header := ts_L1ctlHeader(L1CTL_PARAM_REQ), ul_info := { chan_nr := t_RslChanNr_RACH(0), link_id := ts_RslLinkID_DCCH(0), padding := '0000'O }, ul_info_tbf := omit, ul_info_abs := omit, payload := { par_req := { ta := ta, tx_power := tx_power, padding := '0000'O } } } template L1ctlUlMessage ts_L1CTL_DM_EST_REQ(Arfcn arfcn, RslChannelNr chan_nr, GsmTsc tsc) := { header := ts_L1ctlHeader(L1CTL_DM_EST_REQ), ul_info := { chan_nr := chan_nr, link_id := ts_RslLinkID_DCCH(0), padding := '0000'O }, ul_info_tbf := omit, ul_info_abs := omit, payload := { dm_est_req := { tsc := tsc, h := 0, arfcn := arfcn, hopping := omit, tch_mode := 0, audio_mode := t_L1CTL_AudioModeNone } } } template L1ctlUlMessage ts_L1CTL_DM_REL_REQ(RslChannelNr chan_nr) := { header := ts_L1ctlHeader(L1CTL_DM_REL_REQ), ul_info := { chan_nr := chan_nr, link_id := ts_RslLinkID_DCCH(0), padding := '0000'O }, ul_info_tbf := omit, ul_info_abs := omit, payload := { other := ''O } } template (value) L1ctlUlMessage ts_L1CTL_DATA_REQ(template (value) RslChannelNr chan_nr, template (value) RslLinkId link_id, octetstring l2_data) := { header := ts_L1ctlHeader(L1CTL_DATA_REQ), ul_info := { chan_nr := chan_nr, link_id := link_id, padding := '0000'O }, ul_info_tbf := omit, ul_info_abs := omit, payload := { data_req := { l1header := omit, l2_payload := l2_data } } } template (value) L1ctlUlMessage ts_L1CTL_DATA_REQ_SACCH(template (value) RslChannelNr chan_nr, template (value) RslLinkId link_id, template (value) SacchL1Header l1h, octetstring l2_data) := { header := ts_L1ctlHeader(L1CTL_DATA_REQ), ul_info := { chan_nr := chan_nr, link_id := link_id, padding := '0000'O }, ul_info_tbf := omit, ul_info_abs := omit, payload := { data_req := { l1header := l1h, l2_payload := l2_data } } } template (value) L1ctlUlMessage ts_L1CTL_TRAFFIC_REQ(template (value) RslChannelNr chan_nr, template (value) RslLinkId link_id, octetstring frame) := { header := ts_L1ctlHeader(L1CTL_TRAFFIC_REQ), ul_info := { chan_nr := chan_nr, link_id := link_id, padding := '0000'O }, ul_info_tbf := omit, ul_info_abs := omit, payload := { traffic_req := { data := frame } } }; template (value) L1ctlUlMessage ts_L1CTL_TBF_CFG_REQ(boolean is_uplink, TfiUsfArr tfi_usf) := { header := ts_L1ctlHeader(L1CTL_TBF_CFG_REQ), ul_info := omit, ul_info_tbf := omit, ul_info_abs := omit, payload := { tbf_cfg_req := { tbf_nr := 0, is_uplink := is_uplink, padding := '0000'O, tfi_usf := tfi_usf } } }; template L1ctlDlMessage tr_L1CTL_TBF_CFG_CONF(template boolean is_uplink) := { header := tr_L1ctlHeader(L1CTL_TBF_CFG_CONF), dl_info := omit, payload := { tbf_cfg_conf := { tbf_nr := 0, is_uplink := is_uplink, padding := ?, tfi_usf := ? } } }; template (value) L1ctlUlMessage ts_L1CTL_DATA_TBF_REQ(octetstring l2_data, L1ctlGprsCs cs := L1CTL_CS1, uint8_t tbf_nr := 0) := { header := ts_L1ctlHeader(L1CTL_DATA_TBF_REQ), ul_info := omit, ul_info_tbf := { tbf_nr := tbf_nr, cs := cs, padding := '0000'O }, ul_info_abs := omit, payload := { other := l2_data } } template (value) L1ctlUlMessage ts_L1CTL_DATA_ABS_REQ(octetstring l2_data, Arfcn arfcn, uint8_t ts, GsmFrameNumber fn, L1ctlGprsCs cs := L1CTL_CS1, uint8_t tbf_nr := 0) := { header := ts_L1ctlHeader(L1CTL_DATA_ABS_REQ), ul_info := omit, ul_info_tbf := omit, ul_info_abs := { tbf_nr := tbf_nr, cs := cs, ts_nr := ts, padding := '00'O, fn := fn, arfcn := arfcn, padding2 := '0000'O }, payload := { other := l2_data } } /* for matching against incoming RACH_CONF */ template L1ctlDlMessage tr_L1CTL_RACH_CONF := { header := tr_L1ctlHeader(L1CTL_RACH_CONF), dl_info := ?, payload := * }; /* for matching against incoming DATA_IND */ template L1ctlDlMessage tr_L1CTL_DATA_IND(template RslChannelNr chan_nr, template RslLinkId link_id := ?, template octetstring l2_data := ?, template uint8_t num_biterr := 0, template uint8_t fire_crc := 0) := { header := tr_L1ctlHeader(L1CTL_DATA_IND), dl_info := { chan_nr := chan_nr, link_id := link_id, arfcn := ?, frame_nr := ?, rx_level := ?, snr := ?, num_biterr := num_biterr, fire_crc := fire_crc }, payload := { data_ind := { payload := l2_data } } }; /* for matching against incoming TRAFFIC_IND */ template L1ctlDlMessage tr_L1CTL_TRAFFIC_IND(template RslChannelNr chan_nr, template RslLinkId link_id := ?, template octetstring frame := ?, template uint8_t num_biterr := 0, template uint8_t fire_crc := 0) := { header := tr_L1ctlHeader(L1CTL_TRAFFIC_IND), dl_info := { chan_nr := chan_nr, link_id := link_id, arfcn := ?, frame_nr := ?, rx_level := ?, snr := ?, num_biterr := num_biterr, fire_crc := fire_crc }, payload := { traffic_ind := { data := frame } } }; template (value) L1ctlUlMessage ts_L1CTL_CRYPTO_REQ(RslChannelNr chan_nr, uint8_t algo, octetstring key) := { header := ts_L1ctlHeader(L1CTL_CRYPTO_REQ), ul_info := { chan_nr := chan_nr, link_id := ts_RslLinkID_DCCH(0), padding := '0000'O }, ul_info_tbf := omit, ul_info_abs := omit, payload := { crypto_req := { algo := algo, key_len := 0, /* overwritten */ key := key } } }; const octetstring c_DummyUI := '0303012B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B'O; /* We use "BYTEORDER(last)" so we get little-endian integers. Unfortuantely, this also switches the byte ordering in octet strings, so we need to explicitly annotate them :/ */ } with { encode "RAW" };