#pragma once #include #include #include #include #include #include #include #include #include #include #define GSM_BURST_LEN 148 #define GSM_BURST_PL_LEN 116 #define GPRS_BURST_LEN GSM_BURST_LEN #define EDGE_BURST_LEN 444 #define GPRS_L2_MAX_LEN 54 #define EDGE_L2_MAX_LEN 155 #define L1SCHED_CH_LID_DEDIC 0x00 #define L1SCHED_CH_LID_SACCH 0x40 /* Osmocom-specific extension for PTCCH (see 3GPP TS 45.002, section 3.3.4.2). * Shall be used to distinguish PTCCH and PDTCH channels on a PDCH time-slot. */ #define L1SCHED_CH_LID_PTCCH 0x80 /* Is a channel related to PDCH (GPRS) */ #define L1SCHED_CH_FLAG_PDCH (1 << 0) /* Should a channel be activated automatically */ #define L1SCHED_CH_FLAG_AUTO (1 << 1) /* Is continuous burst transmission assumed */ #define L1SCHED_CH_FLAG_CBTX (1 << 2) #define MAX_A5_KEY_LEN (128 / 8) #define TRX_TS_COUNT 8 struct l1sched_lchan_state; struct l1sched_meas_set; struct l1sched_state; struct l1sched_ts; enum l1sched_clck_state { L1SCHED_CLCK_ST_WAIT, L1SCHED_CLCK_ST_OK, }; enum l1sched_burst_type { L1SCHED_BURST_GMSK, L1SCHED_BURST_8PSK, }; enum l1sched_ts_prim_type { L1SCHED_PRIM_DATA, L1SCHED_PRIM_RACH8, L1SCHED_PRIM_RACH11, }; /** * These types define the different channels on a multiframe. * Each channel has queues and can be activated individually. */ enum l1sched_lchan_type { L1SCHED_IDLE = 0, L1SCHED_FCCH, L1SCHED_SCH, L1SCHED_BCCH, L1SCHED_RACH, L1SCHED_CCCH, L1SCHED_TCHF, L1SCHED_TCHH_0, L1SCHED_TCHH_1, L1SCHED_SDCCH4_0, L1SCHED_SDCCH4_1, L1SCHED_SDCCH4_2, L1SCHED_SDCCH4_3, L1SCHED_SDCCH8_0, L1SCHED_SDCCH8_1, L1SCHED_SDCCH8_2, L1SCHED_SDCCH8_3, L1SCHED_SDCCH8_4, L1SCHED_SDCCH8_5, L1SCHED_SDCCH8_6, L1SCHED_SDCCH8_7, L1SCHED_SACCHTF, L1SCHED_SACCHTH_0, L1SCHED_SACCHTH_1, L1SCHED_SACCH4_0, L1SCHED_SACCH4_1, L1SCHED_SACCH4_2, L1SCHED_SACCH4_3, L1SCHED_SACCH8_0, L1SCHED_SACCH8_1, L1SCHED_SACCH8_2, L1SCHED_SACCH8_3, L1SCHED_SACCH8_4, L1SCHED_SACCH8_5, L1SCHED_SACCH8_6, L1SCHED_SACCH8_7, L1SCHED_PDTCH, L1SCHED_PTCCH, L1SCHED_SDCCH4_CBCH, L1SCHED_SDCCH8_CBCH, _L1SCHED_CHAN_MAX }; enum l1sched_data_type { L1SCHED_DT_PACKET_DATA, L1SCHED_DT_SIGNALING, L1SCHED_DT_TRAFFIC, L1SCHED_DT_OTHER, /* SCH and RACH */ }; enum l1sched_config_type { /*! Channel combination for a timeslot */ L1SCHED_CFG_PCHAN_COMB, }; /* Represents a (re)configuration request */ struct l1sched_config_req { enum l1sched_config_type type; union { struct { uint8_t tn; enum gsm_phys_chan_config pchan; } pchan_comb; }; }; /* Represents a burst to be transmitted */ struct l1sched_burst_req { uint32_t fn; uint8_t tn; uint8_t pwr; /* Internally used by the scheduler */ uint8_t bid; ubit_t burst[EDGE_BURST_LEN]; size_t burst_len; }; typedef int l1sched_lchan_rx_func(struct l1sched_lchan_state *lchan, uint32_t fn, uint8_t bid, const sbit_t *bits, const struct l1sched_meas_set *meas); typedef int l1sched_lchan_tx_func(struct l1sched_lchan_state *lchan, struct l1sched_burst_req *br); struct l1sched_lchan_desc { /*! Human-readable name */ const char *name; /*! Human-readable description */ const char *desc; /*! Channel Number (like in RSL) */ uint8_t chan_nr; /*! Link ID (like in RSL) */ uint8_t link_id; /*! Sub-slot number (for SDCCH and TCH/H) */ uint8_t ss_nr; /*! GSMTAP channel type (see GSMTAP_CHANNEL_*) */ uint8_t gsmtap_chan_type; /*! How much memory do we need to store bursts */ size_t burst_buf_size; /*! Channel specific flags */ uint8_t flags; /*! Function to call when burst received from PHY */ l1sched_lchan_rx_func *rx_fn; /*! Function to call when data received from L2 */ l1sched_lchan_tx_func *tx_fn; }; struct l1sched_tdma_frame { /*! Downlink channel (slot) type */ enum l1sched_lchan_type dl_chan; /*! Downlink block ID */ uint8_t dl_bid; /*! Uplink channel (slot) type */ enum l1sched_lchan_type ul_chan; /*! Uplink block ID */ uint8_t ul_bid; }; struct l1sched_tdma_multiframe { /*! Channel combination */ enum gsm_phys_chan_config chan_config; /*! Human-readable name */ const char *name; /*! Repeats how many frames */ uint8_t period; /*! Applies to which timeslots */ uint8_t slotmask; /*! Contains which lchans */ uint64_t lchan_mask; /*! Pointer to scheduling structure */ const struct l1sched_tdma_frame *frames; }; struct l1sched_meas_set { /*! TDMA frame number of the first burst this set belongs to */ uint32_t fn; /*! ToA256 (Timing of Arrival, 1/256 of a symbol) */ int16_t toa256; /*! RSSI (Received Signal Strength Indication) */ int8_t rssi; }; /* Simple ring buffer (up to 8 unique measurements) */ struct l1sched_lchan_meas_hist { struct l1sched_meas_set buf[8]; struct l1sched_meas_set *head; }; /* States each channel on a multiframe */ struct l1sched_lchan_state { /*! Channel type */ enum l1sched_lchan_type type; /*! Channel status */ uint8_t active; /*! Link to a list of channels */ struct llist_head list; /*! Burst type: GMSK or 8PSK */ enum l1sched_burst_type burst_type; /*! Mask of received bursts */ uint8_t rx_burst_mask; /*! Mask of transmitted bursts */ uint8_t tx_burst_mask; /*! Burst buffer for RX */ sbit_t *rx_bursts; /*! Burst buffer for TX */ ubit_t *tx_bursts; /*! A primitive being sent */ struct l1sched_ts_prim *prim; /*! Mode for TCH channels (see GSM48_CMODE_*) */ uint8_t tch_mode; /*! Training Sequence Code */ uint8_t tsc; /*! FACCH/H on downlink */ bool dl_ongoing_facch; /*! pending FACCH/H blocks on Uplink */ uint8_t ul_facch_blocks; /*! Downlink measurements history */ struct l1sched_lchan_meas_hist meas_hist; /*! AVG measurements of the last received block */ struct l1sched_meas_set meas_avg; /*! TDMA loss detection state */ struct { /*! Last processed TDMA frame number */ uint32_t last_proc; /*! Number of processed TDMA frames */ unsigned long num_proc; /*! Number of lost TDMA frames */ unsigned long num_lost; } tdma; /*! SACCH state */ struct { /*! Cached measurement report (last received) */ uint8_t mr_cache[GSM_MACBLOCK_LEN]; /*! Cache usage counter */ uint8_t mr_cache_usage; /*! Was a MR transmitted last time? */ bool mr_tx_last; } sacch; /* AMR specific */ struct { /*! 4 possible codecs for AMR */ uint8_t codec[4]; /*! Number of possible codecs */ uint8_t codecs; /*! Current uplink FT index */ uint8_t ul_ft; /*! Current downlink FT index */ uint8_t dl_ft; /*! Current uplink CMR index */ uint8_t ul_cmr; /*! Current downlink CMR index */ uint8_t dl_cmr; /*! If AMR loop is enabled */ uint8_t amr_loop; /*! Number of bit error rates */ uint8_t ber_num; /*! Sum of bit error rates */ float ber_sum; } amr; /*! A5/X encryption state */ struct { uint8_t key[MAX_A5_KEY_LEN]; uint8_t key_len; uint8_t algo; } a5; /* TS that this lchan belongs to */ struct l1sched_ts *ts; }; struct l1sched_ts { /*! Timeslot index within a frame (0..7) */ uint8_t index; /*! Pointer to multiframe layout */ const struct l1sched_tdma_multiframe *mf_layout; /*! Channel states for logical channels */ struct llist_head lchans; /*! Queue primitives for TX */ struct llist_head tx_prims; /*! Backpointer to the scheduler */ struct l1sched_state *sched; }; /* Represents one TX primitive in the queue of l1sched_ts */ struct l1sched_ts_prim { /*! Link to queue of TS */ struct llist_head list; /*! Type of primitive */ enum l1sched_ts_prim_type type; /*! Logical channel type */ enum l1sched_lchan_type chan; /*! Payload length */ size_t payload_len; /*! Payload */ uint8_t payload[0]; }; /*! Represents a RACH (8-bit or 11-bit) primitive */ struct l1sched_ts_prim_rach { /*! RA value */ uint16_t ra; /*! Training Sequence (only for 11-bit RA) */ uint8_t synch_seq; /*! Transmission offset (how many frames to skip) */ uint8_t offset; }; /*! One scheduler instance */ struct l1sched_state { /*! Clock state */ enum l1sched_clck_state clck_state; /*! Local clock source */ struct timespec clock; /*! Count of processed frames */ uint32_t fn_counter_proc; /*! Local frame counter advance */ uint32_t fn_counter_advance; /*! Count of lost frames */ uint32_t fn_counter_lost; /*! Frame callback timer */ struct osmo_timer_list clock_timer; /*! Frame callback */ void (*clock_cb)(struct l1sched_state *sched); /*! List of timeslots maintained by this scheduler */ struct l1sched_ts *ts[TRX_TS_COUNT]; /*! BSIC value learned from SCH bursts */ uint8_t bsic; /*! Some private data */ void *priv; }; extern const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX]; const struct l1sched_tdma_multiframe *l1sched_mframe_layout( enum gsm_phys_chan_config config, int tn); /* Scheduler management functions */ struct l1sched_state *l1sched_alloc(void *ctx, uint32_t fn_advance, void *priv); void l1sched_reset(struct l1sched_state *sched, bool reset_clock); void l1sched_free(struct l1sched_state *sched); /* Timeslot management functions */ struct l1sched_ts *l1sched_add_ts(struct l1sched_state *sched, int tn); void l1sched_del_ts(struct l1sched_state *sched, int tn); int l1sched_reset_ts(struct l1sched_state *sched, int tn); int l1sched_configure_ts(struct l1sched_state *sched, int tn, enum gsm_phys_chan_config config); int l1sched_start_ciphering(struct l1sched_ts *ts, uint8_t algo, uint8_t *key, uint8_t key_len); /* Logical channel management functions */ enum gsm_phys_chan_config l1sched_chan_nr2pchan_config(uint8_t chan_nr); enum l1sched_lchan_type l1sched_chan_nr2lchan_type(uint8_t chan_nr, uint8_t link_id); void l1sched_deactivate_all_lchans(struct l1sched_ts *ts); int l1sched_set_lchans(struct l1sched_ts *ts, uint8_t chan_nr, int active, uint8_t tch_mode, uint8_t tsc); int l1sched_activate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan); int l1sched_deactivate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan); struct l1sched_lchan_state *l1sched_find_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan); /* Primitive management functions */ struct l1sched_ts_prim *l1sched_prim_push(struct l1sched_state *sched, enum l1sched_ts_prim_type type, uint8_t chan_nr, uint8_t link_id, const uint8_t *pl, size_t pl_len); #define L1SCHED_TCH_MODE_IS_SPEECH(mode) \ (mode == GSM48_CMODE_SPEECH_V1 \ || mode == GSM48_CMODE_SPEECH_EFR \ || mode == GSM48_CMODE_SPEECH_AMR) #define L1SCHED_TCH_MODE_IS_DATA(mode) \ (mode == GSM48_CMODE_DATA_14k5 \ || mode == GSM48_CMODE_DATA_12k0 \ || mode == GSM48_CMODE_DATA_6k0 \ || mode == GSM48_CMODE_DATA_3k6) #define L1SCHED_CHAN_IS_TCH(chan) \ (chan == L1SCHED_TCHF || chan == L1SCHED_TCHH_0 || chan == L1SCHED_TCHH_1) #define L1SCHED_CHAN_IS_SACCH(chan) \ (l1sched_lchan_desc[chan].link_id & L1SCHED_CH_LID_SACCH) #define L1SCHED_PRIM_IS_RACH11(prim) \ (prim->type == L1SCHED_PRIM_RACH11) #define L1SCHED_PRIM_IS_RACH8(prim) \ (prim->type == L1SCHED_PRIM_RACH8) #define L1SCHED_PRIM_IS_RACH(prim) \ (L1SCHED_PRIM_IS_RACH8(prim) || L1SCHED_PRIM_IS_RACH11(prim)) #define L1SCHED_PRIM_IS_TCH(prim) \ (L1SCHED_CHAN_IS_TCH(prim->chan) && prim->payload_len != GSM_MACBLOCK_LEN) #define L1SCHED_PRIM_IS_FACCH(prim) \ (L1SCHED_CHAN_IS_TCH(prim->chan) && prim->payload_len == GSM_MACBLOCK_LEN) struct l1sched_ts_prim *l1sched_prim_dequeue(struct llist_head *queue, uint32_t fn, struct l1sched_lchan_state *lchan); int l1sched_prim_dummy(struct l1sched_lchan_state *lchan); void l1sched_prim_drop(struct l1sched_lchan_state *lchan); void l1sched_prim_flush_queue(struct llist_head *list); int l1sched_handle_rx_burst(struct l1sched_state *sched, uint8_t tn, uint32_t fn, sbit_t *bits, uint16_t nbits, const struct l1sched_meas_set *meas); /* Shared declarations for lchan handlers */ extern const uint8_t l1sched_nb_training_bits[8][26]; const char *l1sched_burst_mask2str(const uint8_t *mask, int bits); size_t l1sched_bad_frame_ind(uint8_t *l2, struct l1sched_lchan_state *lchan); /* Interleaved TCH/H block TDMA frame mapping */ uint32_t l1sched_tchh_block_dl_first_fn(enum l1sched_lchan_type chan, uint32_t last_fn, bool facch); bool l1sched_tchh_block_map_fn(enum l1sched_lchan_type chan, uint32_t fn, bool ul, bool facch, bool start); #define l1sched_tchh_traffic_start(chan, fn, ul) \ l1sched_tchh_block_map_fn(chan, fn, ul, 0, 1) #define l1sched_tchh_traffic_end(chan, fn, ul) \ l1sched_tchh_block_map_fn(chan, fn, ul, 0, 0) #define l1sched_tchh_facch_start(chan, fn, ul) \ l1sched_tchh_block_map_fn(chan, fn, ul, 1, 1) #define l1sched_tchh_facch_end(chan, fn, ul) \ l1sched_tchh_block_map_fn(chan, fn, ul, 1, 0) /* Measurement history */ void l1sched_lchan_meas_push(struct l1sched_lchan_state *lchan, const struct l1sched_meas_set *meas); void l1sched_lchan_meas_avg(struct l1sched_lchan_state *lchan, unsigned int n); int l1sched_clck_handle(struct l1sched_state *sched, uint32_t fn); void l1sched_clck_reset(struct l1sched_state *sched); /* External L1 API, must be implemented by the API user */ int l1sched_handle_config_req(struct l1sched_state *sched, const struct l1sched_config_req *cr); int l1sched_handle_burst_req(struct l1sched_state *sched, const struct l1sched_burst_req *br); /* External L2 API, must be implemented by the API user */ int l1sched_handle_data_ind(struct l1sched_lchan_state *lchan, const uint8_t *data, size_t data_len, int n_errors, int n_bits_total, enum l1sched_data_type dt); int l1sched_handle_data_cnf(struct l1sched_lchan_state *lchan, uint32_t fn, enum l1sched_data_type dt);