trxcon/l1sched: rework the primitive API
The goal is to simplify primitive management, and allow passing data between different components without having to re-allocate memory and copy it over several times. This patch has been tested by running ttcn3-bts-test, no regressions observed. * Use msgb and prim API from libosmocore, * Move l1sched_prim definitions to its own header file, * Move Tx queue from per-timeslot to per-lchan state, * Route prims via l1sched_prim_{to,from}_user() functions, * Remove GSMTAP stuff from sched_lchan_desc[]. Change-Id: I73576bd0ea10a5663ba6254283812c275cc3fa46 Related: OS#5500
This commit is contained in:
parent
1cb78f3114
commit
ff9db9def7
|
@ -1,4 +1,5 @@
|
|||
noinst_HEADERS = \
|
||||
l1sched.h \
|
||||
logging.h \
|
||||
prim.h \
|
||||
$(NULL)
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include <osmocom/bb/l1sched/prim.h>
|
||||
|
||||
#define GPRS_L2_MAX_LEN 54
|
||||
#define EDGE_L2_MAX_LEN 155
|
||||
|
||||
|
@ -49,12 +51,6 @@ enum l1sched_burst_type {
|
|||
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.
|
||||
|
@ -103,29 +99,6 @@ enum l1sched_lchan_type {
|
|||
_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;
|
||||
|
@ -182,10 +155,6 @@ struct l1sched_lchan_desc {
|
|||
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;
|
||||
|
@ -259,8 +228,10 @@ struct l1sched_lchan_state {
|
|||
/*! Burst buffer for TX */
|
||||
ubit_t *tx_bursts;
|
||||
|
||||
/*! A primitive being sent */
|
||||
struct l1sched_ts_prim *prim;
|
||||
/*! Queue of Tx primitives */
|
||||
struct llist_head tx_prims;
|
||||
/*! Tx primitive being sent */
|
||||
struct msgb *prim;
|
||||
|
||||
/*! Mode for TCH channels (see GSM48_CMODE_*) */
|
||||
uint8_t tch_mode;
|
||||
|
@ -340,38 +311,10 @@ struct l1sched_ts {
|
|||
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;
|
||||
/*! TDMA Fn for L1SCHED_{PDTCH,PTCCH} */
|
||||
uint32_t fn;
|
||||
/*! 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;
|
||||
};
|
||||
|
||||
/*! Scheduler configuration */
|
||||
struct l1sched_cfg {
|
||||
/*! Logging context (used as prefix for messages) */
|
||||
|
@ -426,8 +369,6 @@ int l1sched_start_ciphering(struct l1sched_ts *ts, uint8_t algo,
|
|||
|
||||
/* 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,
|
||||
|
@ -441,12 +382,6 @@ struct l1sched_lchan_state *l1sched_find_lchan_by_type(struct l1sched_ts *ts,
|
|||
struct l1sched_lchan_state *l1sched_find_lchan_by_chan_nr(struct l1sched_state *sched,
|
||||
uint8_t chan_nr, uint8_t link_id);
|
||||
|
||||
/* 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 \
|
||||
|
@ -464,31 +399,12 @@ struct l1sched_ts_prim *l1sched_prim_push(struct l1sched_state *sched,
|
|||
#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_lchan_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,
|
||||
struct l1sched_burst_ind *bi);
|
||||
int l1sched_handle_rx_probe(struct l1sched_state *sched,
|
||||
struct l1sched_probe *probe);
|
||||
int l1sched_handle_burst_req(struct l1sched_state *sched,
|
||||
const struct l1sched_burst_req *br);
|
||||
|
||||
/* Shared declarations for lchan handlers */
|
||||
extern const uint8_t l1sched_nb_training_bits[8][26];
|
||||
|
@ -521,17 +437,3 @@ void l1sched_clck_reset(struct l1sched_state *sched);
|
|||
|
||||
void l1sched_pull_burst(struct l1sched_state *sched, struct l1sched_burst_req *br);
|
||||
void l1sched_pull_send_frame(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);
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/prim.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#define l1sched_prim_from_msgb(msg) \
|
||||
((struct l1sched_prim *)(msg)->l1h)
|
||||
|
||||
#define l1sched_prim_data_from_msgb(msg) \
|
||||
((uint8_t *)msgb_l2(msg))
|
||||
|
||||
#define l1sched_prim_type_from_msgb(msg) \
|
||||
l1sched_prim_from_msgb(msg)->oph.primitive
|
||||
|
||||
#define L1SCHED_PRIM_STR_FMT "%s.%s"
|
||||
#define L1SCHED_PRIM_STR_ARGS(prim) \
|
||||
l1sched_prim_type_name((prim)->oph.primitive), \
|
||||
osmo_prim_operation_name((prim)->oph.operation)
|
||||
|
||||
enum l1sched_prim_type {
|
||||
L1SCHED_PRIM_T_DATA, /* Req | Ind | Cnf */
|
||||
L1SCHED_PRIM_T_RACH, /* Req | Cnf */
|
||||
L1SCHED_PRIM_T_SCH, /* Ind */
|
||||
L1SCHED_PRIM_T_PCHAN_COMB, /* Ind */
|
||||
};
|
||||
|
||||
extern const struct value_string l1sched_prim_type_names[];
|
||||
static inline const char *l1sched_prim_type_name(enum l1sched_prim_type val)
|
||||
{
|
||||
return get_value_string(l1sched_prim_type_names, val);
|
||||
}
|
||||
|
||||
/*! Common header for L1SCHED_PRIM_T_{DATA,RACH} */
|
||||
struct l1sched_prim_chdr {
|
||||
/*! TDMA Frame Number */
|
||||
uint32_t frame_nr;
|
||||
/*! RSL Channel Number */
|
||||
uint8_t chan_nr;
|
||||
/*! RSL Link Identifier */
|
||||
uint8_t link_id;
|
||||
/*! Traffic or signalling */
|
||||
bool traffic;
|
||||
};
|
||||
|
||||
/*! Payload of L1SCHED_PRIM_T_DATA | Ind */
|
||||
struct l1sched_prim_data_ind {
|
||||
/*! Common sub-header */
|
||||
struct l1sched_prim_chdr chdr;
|
||||
int16_t toa256;
|
||||
int8_t rssi;
|
||||
int n_errors;
|
||||
int n_bits_total;
|
||||
};
|
||||
|
||||
/*! Payload of L1SCHED_PRIM_T_RACH | {Req,Cnf} */
|
||||
struct l1sched_prim_rach {
|
||||
/*! Common sub-header */
|
||||
struct l1sched_prim_chdr chdr;
|
||||
/*! Training Sequence (only for 11-bit RA) */
|
||||
uint8_t synch_seq;
|
||||
/*! Transmission offset (how many frames to skip) */
|
||||
uint8_t offset;
|
||||
/*! RA value is 11 bit */
|
||||
bool is_11bit;
|
||||
/*! RA value */
|
||||
uint16_t ra;
|
||||
};
|
||||
|
||||
struct l1sched_prim {
|
||||
/*! Primitive header */
|
||||
struct osmo_prim_hdr oph;
|
||||
/*! Type specific header */
|
||||
union {
|
||||
/*! L1SCHED_PRIM_T_DATA | Req */
|
||||
struct l1sched_prim_chdr data_req;
|
||||
/*! L1SCHED_PRIM_T_DATA | Cnf */
|
||||
struct l1sched_prim_chdr data_cnf;
|
||||
/*! L1SCHED_PRIM_T_DATA | Ind */
|
||||
struct l1sched_prim_data_ind data_ind;
|
||||
|
||||
/*! L1SCHED_PRIM_T_RACH | Req */
|
||||
struct l1sched_prim_rach rach_req;
|
||||
/*! L1SCHED_PRIM_T_RACH | Cnf */
|
||||
struct l1sched_prim_rach rach_cnf;
|
||||
|
||||
/*! L1SCHED_PRIM_T_SCH | Ind */
|
||||
struct {
|
||||
/*! TDMA frame number */
|
||||
uint32_t frame_nr;
|
||||
/*! BSIC */
|
||||
uint8_t bsic;
|
||||
} sch_ind;
|
||||
|
||||
/*! L1SCHED_PRIM_T_PCHAN_COMB | Ind */
|
||||
struct {
|
||||
/*! Timeslot number */
|
||||
uint8_t tn;
|
||||
/*! Channel combination for a timeslot */
|
||||
enum gsm_phys_chan_config pchan;
|
||||
} pchan_comb_ind;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
struct l1sched_state;
|
||||
struct l1sched_lchan_state;
|
||||
|
||||
void l1sched_prim_init(struct msgb *msg,
|
||||
enum l1sched_prim_type type,
|
||||
enum osmo_prim_operation op);
|
||||
|
||||
struct msgb *l1sched_prim_alloc(enum l1sched_prim_type type,
|
||||
enum osmo_prim_operation op,
|
||||
size_t extra_size);
|
||||
|
||||
struct msgb *l1sched_lchan_prim_dequeue(struct l1sched_lchan_state *lchan, uint32_t fn);
|
||||
void l1sched_lchan_prim_assign_dummy(struct l1sched_lchan_state *lchan);
|
||||
void l1sched_lchan_prim_drop(struct l1sched_lchan_state *lchan);
|
||||
|
||||
int l1sched_lchan_emit_data_ind(struct l1sched_lchan_state *lchan,
|
||||
const uint8_t *data, size_t data_len,
|
||||
int n_errors, int n_bits_total, bool traffic);
|
||||
int l1sched_lchan_emit_data_cnf(struct l1sched_lchan_state *lchan, uint32_t fn);
|
||||
|
||||
int l1sched_prim_from_user(struct l1sched_state *sched, struct msgb *msg);
|
||||
int l1sched_prim_to_user(struct l1sched_state *sched, struct msgb *msg);
|
|
@ -25,7 +25,6 @@
|
|||
*/
|
||||
|
||||
#include <osmocom/gsm/protocol/gsm_08_58.h>
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
|
||||
#include <osmocom/bb/l1sched/l1sched.h>
|
||||
|
||||
|
@ -84,7 +83,6 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_BCCH] = {
|
||||
.name = "BCCH", /* 3GPP TS 05.02, section 3.3.2.3 */
|
||||
.desc = "Broadcast control channel",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_BCCH,
|
||||
.chan_nr = RSL_CHAN_BCCH,
|
||||
|
||||
/* Rx only, xCCH convolutional coding (3GPP TS 05.03, section 4.4),
|
||||
|
@ -97,7 +95,6 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_RACH] = {
|
||||
.name = "RACH", /* 3GPP TS 05.02, section 3.3.3.1 */
|
||||
.desc = "Random access channel",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_RACH,
|
||||
.chan_nr = RSL_CHAN_RACH,
|
||||
|
||||
/* Tx only, RACH convolutional coding (3GPP TS 05.03, section 4.6). */
|
||||
|
@ -107,7 +104,6 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_CCCH] = {
|
||||
.name = "CCCH", /* 3GPP TS 05.02, section 3.3.3.1 */
|
||||
.desc = "Common control channel",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_CCCH,
|
||||
.chan_nr = RSL_CHAN_PCH_AGCH,
|
||||
|
||||
/* Rx only, xCCH convolutional coding (3GPP TS 05.03, section 4.4),
|
||||
|
@ -120,7 +116,6 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_TCHF] = {
|
||||
.name = "TCH/F", /* 3GPP TS 05.02, section 3.2 */
|
||||
.desc = "Full Rate traffic channel",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_TCH_F,
|
||||
.chan_nr = RSL_CHAN_Bm_ACCHs,
|
||||
.link_id = L1SCHED_CH_LID_DEDIC,
|
||||
|
||||
|
@ -143,10 +138,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_TCHH_0] = {
|
||||
.name = "TCH/H(0)", /* 3GPP TS 05.02, section 3.2 */
|
||||
.desc = "Half Rate traffic channel (sub-channel 0)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H,
|
||||
.chan_nr = RSL_CHAN_Lm_ACCHs + (0 << 3),
|
||||
.link_id = L1SCHED_CH_LID_DEDIC,
|
||||
.ss_nr = 0,
|
||||
|
||||
/* Rx and Tx, multiple convolutional coding types (3GPP TS 05.03,
|
||||
* chapter 3), block diagonal interleaving (3GPP TS 05.02, clause 7):
|
||||
|
@ -172,10 +165,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_TCHH_1] = {
|
||||
.name = "TCH/H(1)", /* 3GPP TS 05.02, section 3.2 */
|
||||
.desc = "Half Rate traffic channel (sub-channel 1)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H,
|
||||
.chan_nr = RSL_CHAN_Lm_ACCHs + (1 << 3),
|
||||
.link_id = L1SCHED_CH_LID_DEDIC,
|
||||
.ss_nr = 1,
|
||||
|
||||
/* Same as for L1SCHED_TCHH_0, see above. */
|
||||
.burst_buf_size = 6 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -186,10 +177,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SDCCH4_0] = {
|
||||
.name = "SDCCH/4(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Stand-alone dedicated control channel (sub-channel 0)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4,
|
||||
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (0 << 3),
|
||||
.link_id = L1SCHED_CH_LID_DEDIC,
|
||||
.ss_nr = 0,
|
||||
|
||||
/* Same as for L1SCHED_BCCH (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -200,10 +189,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SDCCH4_1] = {
|
||||
.name = "SDCCH/4(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Stand-alone dedicated control channel (sub-channel 1)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4,
|
||||
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (1 << 3),
|
||||
.link_id = L1SCHED_CH_LID_DEDIC,
|
||||
.ss_nr = 1,
|
||||
|
||||
/* Same as for L1SCHED_BCCH (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -214,10 +201,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SDCCH4_2] = {
|
||||
.name = "SDCCH/4(2)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Stand-alone dedicated control channel (sub-channel 2)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4,
|
||||
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (2 << 3),
|
||||
.link_id = L1SCHED_CH_LID_DEDIC,
|
||||
.ss_nr = 2,
|
||||
|
||||
/* Same as for L1SCHED_BCCH (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -228,10 +213,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SDCCH4_3] = {
|
||||
.name = "SDCCH/4(3)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Stand-alone dedicated control channel (sub-channel 3)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4,
|
||||
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (3 << 3),
|
||||
.link_id = L1SCHED_CH_LID_DEDIC,
|
||||
.ss_nr = 3,
|
||||
|
||||
/* Same as for L1SCHED_BCCH (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -242,10 +225,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SDCCH8_0] = {
|
||||
.name = "SDCCH/8(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Stand-alone dedicated control channel (sub-channel 0)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
|
||||
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (0 << 3),
|
||||
.link_id = L1SCHED_CH_LID_DEDIC,
|
||||
.ss_nr = 0,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -256,10 +237,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SDCCH8_1] = {
|
||||
.name = "SDCCH/8(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Stand-alone dedicated control channel (sub-channel 1)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
|
||||
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (1 << 3),
|
||||
.link_id = L1SCHED_CH_LID_DEDIC,
|
||||
.ss_nr = 1,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -270,10 +249,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SDCCH8_2] = {
|
||||
.name = "SDCCH/8(2)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Stand-alone dedicated control channel (sub-channel 2)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
|
||||
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (2 << 3),
|
||||
.link_id = L1SCHED_CH_LID_DEDIC,
|
||||
.ss_nr = 2,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -284,10 +261,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SDCCH8_3] = {
|
||||
.name = "SDCCH/8(3)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Stand-alone dedicated control channel (sub-channel 3)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
|
||||
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (3 << 3),
|
||||
.link_id = L1SCHED_CH_LID_DEDIC,
|
||||
.ss_nr = 3,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -298,10 +273,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SDCCH8_4] = {
|
||||
.name = "SDCCH/8(4)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Stand-alone dedicated control channel (sub-channel 4)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
|
||||
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (4 << 3),
|
||||
.link_id = L1SCHED_CH_LID_DEDIC,
|
||||
.ss_nr = 4,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -312,10 +285,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SDCCH8_5] = {
|
||||
.name = "SDCCH/8(5)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Stand-alone dedicated control channel (sub-channel 5)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
|
||||
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (5 << 3),
|
||||
.link_id = L1SCHED_CH_LID_DEDIC,
|
||||
.ss_nr = 5,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -326,10 +297,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SDCCH8_6] = {
|
||||
.name = "SDCCH/8(6)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Stand-alone dedicated control channel (sub-channel 6)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
|
||||
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (6 << 3),
|
||||
.link_id = L1SCHED_CH_LID_DEDIC,
|
||||
.ss_nr = 6,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -340,10 +309,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SDCCH8_7] = {
|
||||
.name = "SDCCH/8(7)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Stand-alone dedicated control channel (sub-channel 7)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
|
||||
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (7 << 3),
|
||||
.link_id = L1SCHED_CH_LID_DEDIC,
|
||||
.ss_nr = 7,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -354,7 +321,6 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SACCHTF] = {
|
||||
.name = "SACCH/TF", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Slow TCH/F associated control channel",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_TCH_F | GSMTAP_CHANNEL_ACCH,
|
||||
.chan_nr = RSL_CHAN_Bm_ACCHs,
|
||||
.link_id = L1SCHED_CH_LID_SACCH,
|
||||
|
||||
|
@ -367,10 +333,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SACCHTH_0] = {
|
||||
.name = "SACCH/TH(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Slow TCH/H associated control channel (sub-channel 0)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H | GSMTAP_CHANNEL_ACCH,
|
||||
.chan_nr = RSL_CHAN_Lm_ACCHs + (0 << 3),
|
||||
.link_id = L1SCHED_CH_LID_SACCH,
|
||||
.ss_nr = 0,
|
||||
|
||||
/* Same as for L1SCHED_BCCH (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -381,10 +345,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SACCHTH_1] = {
|
||||
.name = "SACCH/TH(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Slow TCH/H associated control channel (sub-channel 1)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H | GSMTAP_CHANNEL_ACCH,
|
||||
.chan_nr = RSL_CHAN_Lm_ACCHs + (1 << 3),
|
||||
.link_id = L1SCHED_CH_LID_SACCH,
|
||||
.ss_nr = 1,
|
||||
|
||||
/* Same as for L1SCHED_BCCH (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -395,10 +357,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SACCH4_0] = {
|
||||
.name = "SACCH/4(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Slow SDCCH/4 associated control channel (sub-channel 0)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH,
|
||||
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (0 << 3),
|
||||
.link_id = L1SCHED_CH_LID_SACCH,
|
||||
.ss_nr = 0,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -409,10 +369,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SACCH4_1] = {
|
||||
.name = "SACCH/4(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Slow SDCCH/4 associated control channel (sub-channel 1)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH,
|
||||
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (1 << 3),
|
||||
.link_id = L1SCHED_CH_LID_SACCH,
|
||||
.ss_nr = 1,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -423,10 +381,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SACCH4_2] = {
|
||||
.name = "SACCH/4(2)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Slow SDCCH/4 associated control channel (sub-channel 2)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH,
|
||||
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (2 << 3),
|
||||
.link_id = L1SCHED_CH_LID_SACCH,
|
||||
.ss_nr = 2,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -437,10 +393,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SACCH4_3] = {
|
||||
.name = "SACCH/4(3)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Slow SDCCH/4 associated control channel (sub-channel 3)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH,
|
||||
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (3 << 3),
|
||||
.link_id = L1SCHED_CH_LID_SACCH,
|
||||
.ss_nr = 3,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -451,10 +405,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SACCH8_0] = {
|
||||
.name = "SACCH/8(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Slow SDCCH/8 associated control channel (sub-channel 0)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
|
||||
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (0 << 3),
|
||||
.link_id = L1SCHED_CH_LID_SACCH,
|
||||
.ss_nr = 0,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -465,10 +417,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SACCH8_1] = {
|
||||
.name = "SACCH/8(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Slow SDCCH/8 associated control channel (sub-channel 1)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
|
||||
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (1 << 3),
|
||||
.link_id = L1SCHED_CH_LID_SACCH,
|
||||
.ss_nr = 1,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -479,10 +429,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SACCH8_2] = {
|
||||
.name = "SACCH/8(2)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Slow SDCCH/8 associated control channel (sub-channel 2)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
|
||||
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (2 << 3),
|
||||
.link_id = L1SCHED_CH_LID_SACCH,
|
||||
.ss_nr = 2,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -493,10 +441,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SACCH8_3] = {
|
||||
.name = "SACCH/8(3)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Slow SDCCH/8 associated control channel (sub-channel 3)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
|
||||
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (3 << 3),
|
||||
.link_id = L1SCHED_CH_LID_SACCH,
|
||||
.ss_nr = 3,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -507,10 +453,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SACCH8_4] = {
|
||||
.name = "SACCH/8(4)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Slow SDCCH/8 associated control channel (sub-channel 4)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
|
||||
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (4 << 3),
|
||||
.link_id = L1SCHED_CH_LID_SACCH,
|
||||
.ss_nr = 4,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -521,10 +465,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SACCH8_5] = {
|
||||
.name = "SACCH/8(5)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Slow SDCCH/8 associated control channel (sub-channel 5)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
|
||||
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (5 << 3),
|
||||
.link_id = L1SCHED_CH_LID_SACCH,
|
||||
.ss_nr = 5,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -535,10 +477,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SACCH8_6] = {
|
||||
.name = "SACCH/8(6)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Slow SDCCH/8 associated control channel (sub-channel 6)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
|
||||
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (6 << 3),
|
||||
.link_id = L1SCHED_CH_LID_SACCH,
|
||||
.ss_nr = 6,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -549,10 +489,8 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SACCH8_7] = {
|
||||
.name = "SACCH/8(7)", /* 3GPP TS 05.02, section 3.3.4.1 */
|
||||
.desc = "Slow SDCCH/8 associated control channel (sub-channel 7)",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
|
||||
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (7 << 3),
|
||||
.link_id = L1SCHED_CH_LID_SACCH,
|
||||
.ss_nr = 7,
|
||||
|
||||
/* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -563,7 +501,6 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_PDTCH] = {
|
||||
.name = "PDTCH", /* 3GPP TS 05.02, sections 3.2.4, 3.3.2.4 */
|
||||
.desc = "Packet data traffic & control channel",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_PDTCH,
|
||||
.chan_nr = RSL_CHAN_OSMO_PDCH,
|
||||
|
||||
/* Rx and Tx, multiple coding schemes: CS-1..4 and MCS-1..9 (3GPP TS
|
||||
|
@ -578,7 +515,6 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_PTCCH] = {
|
||||
.name = "PTCCH", /* 3GPP TS 05.02, section 3.3.4.2 */
|
||||
.desc = "Packet Timing advance control channel",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_PTCCH,
|
||||
.chan_nr = RSL_CHAN_OSMO_PDCH,
|
||||
.link_id = L1SCHED_CH_LID_PTCCH,
|
||||
|
||||
|
@ -595,9 +531,7 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SDCCH4_CBCH] = {
|
||||
.name = "SDCCH/4(CBCH)", /* 3GPP TS 05.02, section 3.3.5 */
|
||||
.desc = "Cell Broadcast channel on SDCCH/4",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_CBCH51,
|
||||
.chan_nr = RSL_CHAN_OSMO_CBCH4,
|
||||
.ss_nr = 2,
|
||||
|
||||
/* Same as for L1SCHED_BCCH (xCCH), but Rx only. See above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
@ -607,9 +541,7 @@ const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
|
|||
[L1SCHED_SDCCH8_CBCH] = {
|
||||
.name = "SDCCH/8(CBCH)", /* 3GPP TS 05.02, section 3.3.5 */
|
||||
.desc = "Cell Broadcast channel on SDCCH/8",
|
||||
.gsmtap_chan_type = GSMTAP_CHANNEL_CBCH52,
|
||||
.chan_nr = RSL_CHAN_OSMO_CBCH8,
|
||||
.ss_nr = 2,
|
||||
|
||||
/* Same as for L1SCHED_BCCH (xCCH), but Rx only. See above. */
|
||||
.burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
|
||||
|
|
|
@ -96,7 +96,7 @@ int rx_pdtch_fn(struct l1sched_lchan_state *lchan,
|
|||
l2_len = rc > 0 ? rc : 0;
|
||||
|
||||
/* Send a L2 frame to the higher layers */
|
||||
l1sched_handle_data_ind(lchan, l2, l2_len, n_errors, n_bits_total, L1SCHED_DT_PACKET_DATA);
|
||||
l1sched_lchan_emit_data_ind(lchan, l2, l2_len, n_errors, n_bits_total, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -123,12 +123,10 @@ int tx_pdtch_fn(struct l1sched_lchan_state *lchan,
|
|||
}
|
||||
|
||||
/* Encode payload */
|
||||
rc = gsm0503_pdtch_encode(buffer, lchan->prim->payload,
|
||||
lchan->prim->payload_len);
|
||||
rc = gsm0503_pdtch_encode(buffer, msgb_l2(lchan->prim), msgb_l2len(lchan->prim));
|
||||
if (rc < 0) {
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n",
|
||||
lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
|
||||
lchan->prim->payload_len));
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n",
|
||||
msgb_l2len(lchan->prim), msgb_hexdump_l2(lchan->prim));
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -155,11 +153,8 @@ send_burst:
|
|||
|
||||
/* If we have sent the last (4/4) burst */
|
||||
if ((*mask & 0x0f) == 0x0f) {
|
||||
/* Confirm data / traffic sending */
|
||||
l1sched_handle_data_cnf(lchan, br->fn, L1SCHED_DT_PACKET_DATA);
|
||||
|
||||
/* Forget processed primitive */
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
/* Confirm data / traffic sending (pass ownership of the prim) */
|
||||
l1sched_lchan_emit_data_cnf(lchan, br->fn);
|
||||
|
||||
/* Reset mask */
|
||||
*mask = 0x00;
|
||||
|
|
|
@ -73,56 +73,37 @@ int tx_rach_fn(struct l1sched_lchan_state *lchan,
|
|||
struct l1sched_burst_req *br)
|
||||
{
|
||||
const uint8_t bsic = lchan->ts->sched->bsic;
|
||||
struct l1sched_ts_prim_rach *rach;
|
||||
struct l1sched_prim *prim;
|
||||
uint8_t *burst_ptr = br->burst;
|
||||
uint8_t payload[36];
|
||||
int i, rc;
|
||||
|
||||
rach = (struct l1sched_ts_prim_rach *)lchan->prim->payload;
|
||||
prim = l1sched_prim_from_msgb(lchan->prim);
|
||||
|
||||
/* Delay sending according to offset value */
|
||||
if (rach->offset-- > 0)
|
||||
if (prim->rach_req.offset-- > 0)
|
||||
return 0;
|
||||
|
||||
if (L1SCHED_PRIM_IS_RACH11(lchan->prim)) {
|
||||
/* Check requested synch. sequence */
|
||||
if (rach->synch_seq >= RACH_SYNCH_SEQ_NUM) {
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR,
|
||||
"Unknown RACH synch. sequence=0x%02x\n",
|
||||
rach->synch_seq);
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Encode 11-bit payload */
|
||||
rc = gsm0503_rach_ext_encode(payload, rach->ra, bsic, true);
|
||||
if (rc) {
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR,
|
||||
"Could not encode 11-bit RACH burst (ra=%u bsic=%u)\n",
|
||||
rach->ra, bsic);
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
return rc;
|
||||
}
|
||||
} else if (L1SCHED_PRIM_IS_RACH8(lchan->prim)) {
|
||||
rach->synch_seq = RACH_SYNCH_SEQ_TS0;
|
||||
|
||||
/* Encode 8-bit payload */
|
||||
rc = gsm0503_rach_ext_encode(payload, rach->ra, bsic, false);
|
||||
if (rc) {
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR,
|
||||
"Could not encode RACH burst (ra=%u bsic=%u)\n",
|
||||
rach->ra, bsic);
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
/* Check requested synch. sequence */
|
||||
if (prim->rach_req.synch_seq >= RACH_SYNCH_SEQ_NUM) {
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR,
|
||||
"Primitive has unexpected type=0x%02x\n",
|
||||
lchan->prim->type);
|
||||
"Unknown RACH synch. sequence=0x%02x\n",
|
||||
prim->rach_req.synch_seq);
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
return -EINVAL;
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Encode the payload */
|
||||
rc = gsm0503_rach_ext_encode(payload, prim->rach_req.ra,
|
||||
bsic, prim->rach_req.is_11bit);
|
||||
if (rc) {
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR,
|
||||
"Could not encode %s-bit RACH burst (ra=%u bsic=%u)\n",
|
||||
prim->rach_req.is_11bit ? "11" : "8",
|
||||
prim->rach_req.ra, bsic);
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* BN0-7: extended tail bits */
|
||||
memcpy(burst_ptr, rach_ext_tail_bits, RACH_EXT_TAIL_BITS_LEN);
|
||||
|
@ -130,7 +111,7 @@ int tx_rach_fn(struct l1sched_lchan_state *lchan,
|
|||
|
||||
/* BN8-48: chosen synch. (training) sequence */
|
||||
for (i = 0; i < RACH_SYNCH_SEQ_LEN; i++)
|
||||
*(burst_ptr++) = rach_synch_seq_bits[rach->synch_seq][i] == '1';
|
||||
*(burst_ptr++) = rach_synch_seq_bits[prim->rach_req.synch_seq][i] == '1';
|
||||
|
||||
/* BN49-84: encrypted bits (the payload) */
|
||||
memcpy(burst_ptr, payload, RACH_PAYLOAD_LEN);
|
||||
|
@ -141,14 +122,11 @@ int tx_rach_fn(struct l1sched_lchan_state *lchan,
|
|||
br->burst_len = GSM_NBITS_NB_GMSK_BURST;
|
||||
|
||||
LOGP_LCHAND(lchan, LOGL_NOTICE, "Scheduled %s-bit RACH (%s) at fn=%u\n",
|
||||
L1SCHED_PRIM_IS_RACH11(lchan->prim) ? "11" : "8",
|
||||
get_value_string(rach_synch_seq_names, rach->synch_seq), br->fn);
|
||||
prim->rach_req.is_11bit ? "11" : "8",
|
||||
get_value_string(rach_synch_seq_names, prim->rach_req.synch_seq), br->fn);
|
||||
|
||||
/* Confirm RACH request */
|
||||
l1sched_handle_data_cnf(lchan, br->fn, L1SCHED_DT_OTHER);
|
||||
|
||||
/* Forget processed primitive */
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
/* Confirm RACH request (pass ownership of the prim) */
|
||||
l1sched_lchan_emit_data_cnf(lchan, br->fn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,21 @@ static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info)
|
|||
time->fn = gsm_gsmtime2fn(time);
|
||||
}
|
||||
|
||||
static int handle_sch_ind(struct l1sched_state *sched, uint32_t fn, uint8_t bsic)
|
||||
{
|
||||
struct l1sched_prim *prim;
|
||||
struct msgb *msg;
|
||||
|
||||
msg = l1sched_prim_alloc(L1SCHED_PRIM_T_SCH, PRIM_OP_INDICATION, 0);
|
||||
OSMO_ASSERT(msg != NULL);
|
||||
|
||||
prim = l1sched_prim_from_msgb(msg);
|
||||
prim->sch_ind.frame_nr = fn;
|
||||
prim->sch_ind.bsic = bsic;
|
||||
|
||||
return l1sched_prim_to_user(sched, msg);
|
||||
}
|
||||
|
||||
int rx_sch_fn(struct l1sched_lchan_state *lchan,
|
||||
const struct l1sched_burst_ind *bi)
|
||||
{
|
||||
|
@ -100,8 +115,5 @@ int rx_sch_fn(struct l1sched_lchan_state *lchan,
|
|||
/* Update BSIC value in the scheduler state */
|
||||
lchan->ts->sched->bsic = bsic;
|
||||
|
||||
l1sched_handle_data_ind(lchan, (const uint8_t *)&time, sizeof(time),
|
||||
0, 39 * 2, L1SCHED_DT_OTHER);
|
||||
|
||||
return 0;
|
||||
return handle_sch_ind(lchan->ts->sched, time.fn, bsic);
|
||||
}
|
||||
|
|
|
@ -171,9 +171,8 @@ int rx_tchf_fn(struct l1sched_lchan_state *lchan,
|
|||
goto bfi;
|
||||
} else if (rc == GSM_MACBLOCK_LEN) {
|
||||
/* FACCH received, forward it to the higher layers */
|
||||
l1sched_handle_data_ind(lchan, l2 + amr, GSM_MACBLOCK_LEN,
|
||||
n_errors, n_bits_total,
|
||||
L1SCHED_DT_SIGNALING);
|
||||
l1sched_lchan_emit_data_ind(lchan, l2 + amr, GSM_MACBLOCK_LEN,
|
||||
n_errors, n_bits_total, false);
|
||||
|
||||
/* Send BFI substituting a stolen TCH frame */
|
||||
n_errors = -1; /* ensure fake measurements */
|
||||
|
@ -184,7 +183,8 @@ int rx_tchf_fn(struct l1sched_lchan_state *lchan,
|
|||
}
|
||||
|
||||
/* Send a traffic frame to the higher layers */
|
||||
return l1sched_handle_data_ind(lchan, l2, l2_len, n_errors, n_bits_total, L1SCHED_DT_TRAFFIC);
|
||||
return l1sched_lchan_emit_data_ind(lchan, l2, l2_len,
|
||||
n_errors, n_bits_total, true);
|
||||
|
||||
bfi:
|
||||
/* Didn't try to decode, fake measurements */
|
||||
|
@ -201,18 +201,16 @@ bfi:
|
|||
|
||||
/* BFI is not applicable in signalling mode */
|
||||
if (lchan->tch_mode == GSM48_CMODE_SIGN) {
|
||||
return l1sched_handle_data_ind(lchan, NULL, 0,
|
||||
n_errors, n_bits_total,
|
||||
L1SCHED_DT_TRAFFIC);
|
||||
return l1sched_lchan_emit_data_ind(lchan, NULL, 0,
|
||||
n_errors, n_bits_total, false);
|
||||
}
|
||||
|
||||
/* Bad frame indication */
|
||||
l2_len = l1sched_bad_frame_ind(l2, lchan);
|
||||
|
||||
/* Send a BFI frame to the higher layers */
|
||||
return l1sched_handle_data_ind(lchan, l2, l2_len,
|
||||
n_errors, n_bits_total,
|
||||
L1SCHED_DT_TRAFFIC);
|
||||
return l1sched_lchan_emit_data_ind(lchan, l2, l2_len,
|
||||
n_errors, n_bits_total, true);
|
||||
}
|
||||
|
||||
int tx_tchf_fn(struct l1sched_lchan_state *lchan,
|
||||
|
@ -240,9 +238,9 @@ int tx_tchf_fn(struct l1sched_lchan_state *lchan,
|
|||
memcpy(buffer, buffer + 464, 464);
|
||||
|
||||
/* populate the buffer with bursts */
|
||||
if (L1SCHED_PRIM_IS_FACCH(lchan->prim)) {
|
||||
if (msgb_l2len(lchan->prim) == GSM_MACBLOCK_LEN) {
|
||||
/* Encode payload */
|
||||
rc = gsm0503_tch_fr_encode(buffer, lchan->prim->payload, GSM_MACBLOCK_LEN, 1);
|
||||
rc = gsm0503_tch_fr_encode(buffer, msgb_l2(lchan->prim), GSM_MACBLOCK_LEN, 1);
|
||||
} else if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
|
||||
int len;
|
||||
uint8_t cmr_codec;
|
||||
|
@ -256,12 +254,11 @@ int tx_tchf_fn(struct l1sched_lchan_state *lchan,
|
|||
*/
|
||||
amr_fn_is_cmr = !sched_tchf_ul_amr_cmi_map[br->fn % 26];
|
||||
|
||||
len = osmo_amr_rtp_dec(lchan->prim->payload, lchan->prim->payload_len,
|
||||
&cmr_codec, &cmi, &ft_codec,
|
||||
&bfi, &sti);
|
||||
len = osmo_amr_rtp_dec(msgb_l2(lchan->prim), msgb_l2len(lchan->prim),
|
||||
&cmr_codec, &cmi, &ft_codec, &bfi, &sti);
|
||||
if (len < 0) {
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR, "Cannot send invalid AMR payload (%zu): %s\n",
|
||||
lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload, lchan->prim->payload_len));
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR, "Cannot send invalid AMR payload (%u): %s\n",
|
||||
msgb_l2len(lchan->prim), msgb_hexdump_l2(lchan->prim));
|
||||
goto free_bad_msg;
|
||||
}
|
||||
ft = -1;
|
||||
|
@ -290,11 +287,14 @@ int tx_tchf_fn(struct l1sched_lchan_state *lchan,
|
|||
} else {
|
||||
lchan->amr.ul_cmr = cmr;
|
||||
}
|
||||
rc = gsm0503_tch_afs_encode(buffer, lchan->prim->payload + 2,
|
||||
lchan->prim->payload_len - 2, amr_fn_is_cmr,
|
||||
lchan->amr.codec, lchan->amr.codecs,
|
||||
lchan->amr.ul_ft,
|
||||
lchan->amr.ul_cmr);
|
||||
rc = gsm0503_tch_afs_encode(buffer,
|
||||
msgb_l2(lchan->prim) + 2,
|
||||
msgb_l2len(lchan->prim) - 2,
|
||||
amr_fn_is_cmr,
|
||||
lchan->amr.codec,
|
||||
lchan->amr.codecs,
|
||||
lchan->amr.ul_ft,
|
||||
lchan->amr.ul_cmr);
|
||||
} else {
|
||||
/* Determine and check the payload length */
|
||||
switch (lchan->tch_mode) {
|
||||
|
@ -312,20 +312,19 @@ int tx_tchf_fn(struct l1sched_lchan_state *lchan,
|
|||
l1sched_lchan_prim_drop(lchan);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (lchan->prim->payload_len != l2_len) {
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR, "Primitive has odd length %zu "
|
||||
if (msgb_l2len(lchan->prim) != l2_len) {
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR, "Primitive has odd length %u "
|
||||
"(expected %zu for TCH or %u for FACCH), so dropping...\n",
|
||||
lchan->prim->payload_len, l2_len, GSM_MACBLOCK_LEN);
|
||||
msgb_l2len(lchan->prim), l2_len, GSM_MACBLOCK_LEN);
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
return -EINVAL;
|
||||
}
|
||||
rc = gsm0503_tch_fr_encode(buffer, lchan->prim->payload, l2_len, 1);
|
||||
rc = gsm0503_tch_fr_encode(buffer, msgb_l2(lchan->prim), l2_len, 1);
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n",
|
||||
lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
|
||||
lchan->prim->payload_len));
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n",
|
||||
msgb_l2len(lchan->prim), msgb_hexdump_l2(lchan->prim));
|
||||
free_bad_msg:
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
return -EINVAL;
|
||||
|
@ -353,13 +352,8 @@ send_burst:
|
|||
|
||||
/* If we have sent the last (4/4) burst */
|
||||
if (*mask == 0x0f) {
|
||||
/* Confirm data / traffic sending */
|
||||
enum l1sched_data_type dt = L1SCHED_PRIM_IS_TCH(lchan->prim) ?
|
||||
L1SCHED_DT_TRAFFIC : L1SCHED_DT_SIGNALING;
|
||||
l1sched_handle_data_cnf(lchan, br->fn, dt);
|
||||
|
||||
/* Forget processed primitive */
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
/* Confirm data / traffic sending (pass ownership of the prim) */
|
||||
l1sched_lchan_emit_data_cnf(lchan, br->fn);
|
||||
|
||||
/* Reset mask */
|
||||
*mask = 0x00;
|
||||
|
|
|
@ -366,9 +366,8 @@ int rx_tchh_fn(struct l1sched_lchan_state *lchan,
|
|||
l1sched_lchan_meas_avg(lchan, 6);
|
||||
|
||||
/* FACCH/H received, forward to the higher layers */
|
||||
l1sched_handle_data_ind(lchan, l2 + amr, GSM_MACBLOCK_LEN,
|
||||
n_errors, n_bits_total,
|
||||
L1SCHED_DT_SIGNALING);
|
||||
l1sched_lchan_emit_data_ind(lchan, l2 + amr, GSM_MACBLOCK_LEN,
|
||||
n_errors, n_bits_total, false);
|
||||
|
||||
/* Send BFI substituting 1/2 stolen TCH frames */
|
||||
n_errors = -1; /* ensure fake measurements */
|
||||
|
@ -382,9 +381,8 @@ int rx_tchh_fn(struct l1sched_lchan_state *lchan,
|
|||
}
|
||||
|
||||
/* Send a traffic frame to the higher layers */
|
||||
return l1sched_handle_data_ind(lchan, l2, l2_len,
|
||||
n_errors, n_bits_total,
|
||||
L1SCHED_DT_TRAFFIC);
|
||||
return l1sched_lchan_emit_data_ind(lchan, l2, l2_len,
|
||||
n_errors, n_bits_total, true);
|
||||
|
||||
bfi_shift:
|
||||
/* Shift buffer */
|
||||
|
@ -409,18 +407,16 @@ bfi:
|
|||
|
||||
/* BFI is not applicable in signalling mode */
|
||||
if (lchan->tch_mode == GSM48_CMODE_SIGN) {
|
||||
return l1sched_handle_data_ind(lchan, NULL, 0,
|
||||
n_errors, n_bits_total,
|
||||
L1SCHED_DT_SIGNALING);
|
||||
return l1sched_lchan_emit_data_ind(lchan, NULL, 0,
|
||||
n_errors, n_bits_total, false);
|
||||
}
|
||||
|
||||
/* Bad frame indication */
|
||||
l2_len = l1sched_bad_frame_ind(l2, lchan);
|
||||
|
||||
/* Send a BFI frame to the higher layers */
|
||||
return l1sched_handle_data_ind(lchan, l2, l2_len,
|
||||
n_errors, n_bits_total,
|
||||
L1SCHED_DT_TRAFFIC);
|
||||
return l1sched_lchan_emit_data_ind(lchan, l2, l2_len,
|
||||
n_errors, n_bits_total, true);
|
||||
}
|
||||
|
||||
int tx_tchh_fn(struct l1sched_lchan_state *lchan,
|
||||
|
@ -463,8 +459,8 @@ int tx_tchh_fn(struct l1sched_lchan_state *lchan,
|
|||
}
|
||||
|
||||
/* populate the buffer with bursts */
|
||||
if (L1SCHED_PRIM_IS_FACCH(lchan->prim)) {
|
||||
rc = gsm0503_tch_hr_encode(buffer, lchan->prim->payload, lchan->prim->payload_len);
|
||||
if (msgb_l2len(lchan->prim) == GSM_MACBLOCK_LEN) {
|
||||
rc = gsm0503_tch_hr_encode(buffer, msgb_l2(lchan->prim), GSM_MACBLOCK_LEN);
|
||||
lchan->ul_facch_blocks = 6;
|
||||
} else if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
|
||||
int len;
|
||||
|
@ -479,12 +475,11 @@ int tx_tchh_fn(struct l1sched_lchan_state *lchan,
|
|||
*/
|
||||
amr_fn_is_cmr = !sched_tchh_ul_amr_cmi_map[br->fn % 26];
|
||||
|
||||
len = osmo_amr_rtp_dec(lchan->prim->payload, lchan->prim->payload_len,
|
||||
&cmr_codec, &cmi, &ft_codec,
|
||||
&bfi, &sti);
|
||||
len = osmo_amr_rtp_dec(msgb_l2(lchan->prim), msgb_l2len(lchan->prim),
|
||||
&cmr_codec, &cmi, &ft_codec, &bfi, &sti);
|
||||
if (len < 0) {
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR, "Cannot send invalid AMR payload (%zu): %s\n",
|
||||
lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload, lchan->prim->payload_len));
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR, "Cannot send invalid AMR payload (%u): %s\n",
|
||||
msgb_l2len(lchan->prim), msgb_hexdump_l2(lchan->prim));
|
||||
goto free_bad_msg;
|
||||
}
|
||||
ft = -1;
|
||||
|
@ -513,11 +508,14 @@ int tx_tchh_fn(struct l1sched_lchan_state *lchan,
|
|||
} else {
|
||||
lchan->amr.ul_cmr = cmr;
|
||||
}
|
||||
rc = gsm0503_tch_ahs_encode(buffer, lchan->prim->payload + 2,
|
||||
lchan->prim->payload_len - 2, amr_fn_is_cmr,
|
||||
lchan->amr.codec, lchan->amr.codecs,
|
||||
lchan->amr.ul_ft,
|
||||
lchan->amr.ul_cmr);
|
||||
rc = gsm0503_tch_ahs_encode(buffer,
|
||||
msgb_l2(lchan->prim) + 2,
|
||||
msgb_l2len(lchan->prim) - 2,
|
||||
amr_fn_is_cmr,
|
||||
lchan->amr.codec,
|
||||
lchan->amr.codecs,
|
||||
lchan->amr.ul_ft,
|
||||
lchan->amr.ul_cmr);
|
||||
} else {
|
||||
/* Determine and check the payload length */
|
||||
switch (lchan->tch_mode) {
|
||||
|
@ -532,20 +530,19 @@ int tx_tchh_fn(struct l1sched_lchan_state *lchan,
|
|||
l1sched_lchan_prim_drop(lchan);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (lchan->prim->payload_len != l2_len) {
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR, "Primitive has odd length %zu "
|
||||
if (msgb_l2len(lchan->prim) != l2_len) {
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR, "Primitive has odd length %u "
|
||||
"(expected %zu for TCH or %u for FACCH), so dropping...\n",
|
||||
lchan->prim->payload_len, l2_len, GSM_MACBLOCK_LEN);
|
||||
msgb_l2len(lchan->prim), l2_len, GSM_MACBLOCK_LEN);
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
return -EINVAL;
|
||||
}
|
||||
rc = gsm0503_tch_hr_encode(buffer, lchan->prim->payload, l2_len);
|
||||
rc = gsm0503_tch_hr_encode(buffer, msgb_l2(lchan->prim), l2_len);
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n",
|
||||
lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
|
||||
lchan->prim->payload_len));
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n",
|
||||
msgb_l2len(lchan->prim), msgb_hexdump_l2(lchan->prim));
|
||||
free_bad_msg:
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
return -EINVAL;
|
||||
|
@ -576,18 +573,11 @@ send_burst:
|
|||
lchan->ul_facch_blocks--;
|
||||
|
||||
if ((*mask & 0x0f) == 0x0f) {
|
||||
/**
|
||||
* If no more FACCH/H blocks pending,
|
||||
* confirm data / traffic sending
|
||||
*/
|
||||
if (!lchan->ul_facch_blocks) {
|
||||
enum l1sched_data_type dt = L1SCHED_PRIM_IS_TCH(lchan->prim) ?
|
||||
L1SCHED_DT_TRAFFIC : L1SCHED_DT_SIGNALING;
|
||||
l1sched_handle_data_cnf(lchan, br->fn, dt);
|
||||
}
|
||||
|
||||
/* Forget processed primitive */
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
/* Confirm data / traffic sending (pass ownership of the prim) */
|
||||
if (!lchan->ul_facch_blocks)
|
||||
l1sched_lchan_emit_data_cnf(lchan, br->fn);
|
||||
else /* do not confirm dropped prims */
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -94,9 +94,8 @@ int rx_data_fn(struct l1sched_lchan_state *lchan,
|
|||
}
|
||||
|
||||
/* Send a L2 frame to the higher layers */
|
||||
return l1sched_handle_data_ind(lchan, l2, rc ? 0 : GSM_MACBLOCK_LEN,
|
||||
n_errors, n_bits_total,
|
||||
L1SCHED_DT_SIGNALING);
|
||||
return l1sched_lchan_emit_data_ind(lchan, l2, rc ? 0 : GSM_MACBLOCK_LEN,
|
||||
n_errors, n_bits_total, false);
|
||||
}
|
||||
|
||||
int tx_data_fn(struct l1sched_lchan_state *lchan,
|
||||
|
@ -120,20 +119,19 @@ int tx_data_fn(struct l1sched_lchan_state *lchan,
|
|||
}
|
||||
|
||||
/* Check the prim payload length */
|
||||
if (lchan->prim->payload_len != GSM_MACBLOCK_LEN) {
|
||||
if (msgb_l2len(lchan->prim) != GSM_MACBLOCK_LEN) {
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR,
|
||||
"Primitive has odd length %zu (expected %u), so dropping...\n",
|
||||
lchan->prim->payload_len, GSM_MACBLOCK_LEN);
|
||||
"Primitive has odd length %u (expected %u), so dropping...\n",
|
||||
msgb_l2len(lchan->prim), GSM_MACBLOCK_LEN);
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Encode payload */
|
||||
rc = gsm0503_xcch_encode(buffer, lchan->prim->payload);
|
||||
rc = gsm0503_xcch_encode(buffer, msgb_l2(lchan->prim));
|
||||
if (rc) {
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n",
|
||||
lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
|
||||
lchan->prim->payload_len));
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n",
|
||||
msgb_l2len(lchan->prim), msgb_hexdump_l2(lchan->prim));
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -160,11 +158,8 @@ send_burst:
|
|||
|
||||
/* If we have sent the last (4/4) burst */
|
||||
if ((*mask & 0x0f) == 0x0f) {
|
||||
/* Confirm data sending */
|
||||
l1sched_handle_data_cnf(lchan, br->fn, L1SCHED_DT_SIGNALING);
|
||||
|
||||
/* Forget processed primitive */
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
/* Confirm data sending (pass ownership of the prim) */
|
||||
l1sched_lchan_emit_data_cnf(lchan, br->fn);
|
||||
|
||||
/* Reset mask */
|
||||
*mask = 0x00;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* TDMA scheduler: primitive management
|
||||
*
|
||||
* (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
|
||||
* Contributions by sysmocom - s.f.m.c. GmbH
|
||||
* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
|
@ -25,6 +25,8 @@
|
|||
#include <talloc.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/prim.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
|
@ -33,86 +35,45 @@
|
|||
#include <osmocom/bb/l1sched/l1sched.h>
|
||||
#include <osmocom/bb/l1sched/logging.h>
|
||||
|
||||
/**
|
||||
* Initializes a new primitive by allocating memory
|
||||
* and filling some meta-information (e.g. lchan type).
|
||||
*
|
||||
* @param ctx parent talloc context
|
||||
* @param pl_len prim payload length
|
||||
* @param type prim payload type
|
||||
* @param chan_nr RSL channel description (used to set a proper chan)
|
||||
* @param link_id RSL link description (used to set a proper chan)
|
||||
* @return allocated primitive or NULL
|
||||
*/
|
||||
static struct l1sched_ts_prim *prim_alloc(void *ctx, size_t pl_len,
|
||||
enum l1sched_ts_prim_type type,
|
||||
uint8_t chan_nr, uint8_t link_id)
|
||||
#define L1SCHED_PRIM_HEADROOM 128
|
||||
|
||||
osmo_static_assert(sizeof(struct l1sched_prim) <= L1SCHED_PRIM_HEADROOM, l1sched_prim_size);
|
||||
|
||||
const struct value_string l1sched_prim_type_names[] = {
|
||||
{ L1SCHED_PRIM_T_DATA, "DATA" },
|
||||
{ L1SCHED_PRIM_T_RACH, "RACH" },
|
||||
{ L1SCHED_PRIM_T_SCH, "SCH" },
|
||||
{ L1SCHED_PRIM_T_PCHAN_COMB, "PCHAN_COMB" },
|
||||
{ 0, NULL },
|
||||
};
|
||||
|
||||
void l1sched_prim_init(struct msgb *msg,
|
||||
enum l1sched_prim_type type,
|
||||
enum osmo_prim_operation op)
|
||||
{
|
||||
enum l1sched_lchan_type lchan_type;
|
||||
struct l1sched_ts_prim *prim;
|
||||
struct l1sched_prim *prim;
|
||||
|
||||
/* Determine lchan type */
|
||||
lchan_type = l1sched_chan_nr2lchan_type(chan_nr, link_id);
|
||||
if (!lchan_type) {
|
||||
/* TODO: use proper logging context */
|
||||
LOGP(DLGLOBAL, LOGL_ERROR, "Couldn't determine lchan type "
|
||||
"for chan_nr=%02x and link_id=%02x\n", chan_nr, link_id);
|
||||
return NULL;
|
||||
}
|
||||
msg->l2h = msg->data;
|
||||
msg->l1h = msgb_push(msg, sizeof(*prim));
|
||||
|
||||
/* Allocate a new primitive */
|
||||
prim = talloc_zero_size(ctx, sizeof(*prim) + pl_len);
|
||||
if (prim == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Init primitive header */
|
||||
prim->payload_len = pl_len;
|
||||
prim->chan = lchan_type;
|
||||
prim->type = type;
|
||||
|
||||
return prim;
|
||||
prim = l1sched_prim_from_msgb(msg);
|
||||
osmo_prim_init(&prim->oph, 0, type, op, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a primitive to the end of transmit queue of a particular
|
||||
* timeslot, whose index is parsed from chan_nr.
|
||||
*
|
||||
* @param sched scheduler instance
|
||||
* @param chan_nr RSL channel description
|
||||
* @param link_id RSL link description
|
||||
* @param pl Payload data
|
||||
* @param pl_len Payload length
|
||||
* @return queued primitive or NULL
|
||||
*/
|
||||
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)
|
||||
struct msgb *l1sched_prim_alloc(enum l1sched_prim_type type,
|
||||
enum osmo_prim_operation op,
|
||||
size_t extra_size)
|
||||
{
|
||||
struct l1sched_ts_prim *prim;
|
||||
struct l1sched_ts *ts;
|
||||
uint8_t tn;
|
||||
struct msgb *msg;
|
||||
|
||||
/* Determine TS index */
|
||||
tn = chan_nr & 0x7;
|
||||
|
||||
/* Check whether required timeslot is allocated and configured */
|
||||
ts = sched->ts[tn];
|
||||
if (ts == NULL || ts->mf_layout == NULL) {
|
||||
LOGP_SCHEDC(sched, LOGL_ERROR, "Timeslot %u isn't configured\n", tn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prim = prim_alloc(ts, pl_len, type, chan_nr, link_id);
|
||||
if (prim == NULL)
|
||||
msg = msgb_alloc_headroom(L1SCHED_PRIM_HEADROOM + extra_size,
|
||||
L1SCHED_PRIM_HEADROOM, "l1sched_prim");
|
||||
if (msg == NULL)
|
||||
return NULL;
|
||||
|
||||
memcpy(&prim->payload[0], pl, pl_len);
|
||||
l1sched_prim_init(msg, type, op);
|
||||
|
||||
/* Add primitive to TS transmit queue */
|
||||
llist_add_tail(&prim->list, &ts->tx_prims);
|
||||
|
||||
return prim;
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -121,16 +82,21 @@ struct l1sched_ts_prim *l1sched_prim_push(struct l1sched_state *sched,
|
|||
* @param lchan lchan to assign a primitive
|
||||
* @return SACCH primitive to be transmitted
|
||||
*/
|
||||
static struct l1sched_ts_prim *prim_compose_mr(struct l1sched_lchan_state *lchan)
|
||||
static struct msgb *prim_compose_mr(struct l1sched_lchan_state *lchan)
|
||||
{
|
||||
struct l1sched_ts_prim *prim;
|
||||
struct l1sched_prim *prim;
|
||||
struct msgb *msg;
|
||||
bool cached;
|
||||
|
||||
/* Allocate a new primitive */
|
||||
prim = prim_alloc(lchan, GSM_MACBLOCK_LEN, L1SCHED_PRIM_DATA,
|
||||
l1sched_lchan_desc[lchan->type].chan_nr,
|
||||
L1SCHED_CH_LID_SACCH);
|
||||
OSMO_ASSERT(prim != NULL);
|
||||
msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST, GSM_MACBLOCK_LEN);
|
||||
OSMO_ASSERT(msg != NULL);
|
||||
|
||||
prim = l1sched_prim_from_msgb(msg);
|
||||
prim->data_req = (struct l1sched_prim_chdr) {
|
||||
.chan_nr = l1sched_lchan_desc[lchan->type].chan_nr,
|
||||
.link_id = L1SCHED_CH_LID_SACCH,
|
||||
};
|
||||
|
||||
/* Check if the MR cache is populated (verify LAPDm header) */
|
||||
cached = (lchan->sacch.mr_cache[2] != 0x00
|
||||
|
@ -143,7 +109,9 @@ static struct l1sched_ts_prim *prim_compose_mr(struct l1sched_lchan_state *lchan
|
|||
}
|
||||
|
||||
/* Compose a new Measurement Report primitive */
|
||||
memcpy(&prim->payload[0], &lchan->sacch.mr_cache[0], GSM_MACBLOCK_LEN);
|
||||
memcpy(msgb_put(msg, GSM_MACBLOCK_LEN),
|
||||
&lchan->sacch.mr_cache[0],
|
||||
GSM_MACBLOCK_LEN);
|
||||
|
||||
/* Inform about the cache usage count */
|
||||
if (++lchan->sacch.mr_cache_usage > 5) {
|
||||
|
@ -155,7 +123,7 @@ static struct l1sched_ts_prim *prim_compose_mr(struct l1sched_lchan_state *lchan
|
|||
|
||||
LOGP_LCHAND(lchan, LOGL_NOTICE, "Using cached Measurement Report\n");
|
||||
|
||||
return prim;
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -180,140 +148,101 @@ static struct l1sched_ts_prim *prim_compose_mr(struct l1sched_lchan_state *lchan
|
|||
* between two successive measurement result messages
|
||||
* shall not exceed one L2 frame.
|
||||
*
|
||||
* @param queue transmit queue to take a prim from
|
||||
* @param lchan lchan to assign a primitive
|
||||
* @return SACCH primitive to be transmitted
|
||||
*/
|
||||
static struct l1sched_ts_prim *prim_dequeue_sacch(struct llist_head *queue,
|
||||
struct l1sched_lchan_state *lchan)
|
||||
static struct msgb *prim_dequeue_sacch(struct l1sched_lchan_state *lchan)
|
||||
{
|
||||
struct l1sched_ts_prim *prim_nmr = NULL;
|
||||
struct l1sched_ts_prim *prim_mr = NULL;
|
||||
struct l1sched_ts_prim *prim;
|
||||
struct msgb *msg_nmr = NULL;
|
||||
struct msgb *msg_mr = NULL;
|
||||
struct msgb *msg;
|
||||
bool mr_now;
|
||||
|
||||
/* Shall we transmit MR now? */
|
||||
mr_now = !lchan->sacch.mr_tx_last;
|
||||
|
||||
#define PRIM_IS_MR(prim) \
|
||||
(prim->payload[5] == GSM48_PDISC_RR \
|
||||
&& prim->payload[6] == GSM48_MT_RR_MEAS_REP)
|
||||
#define PRIM_MSGB_IS_MR(msg) \
|
||||
(l1sched_prim_data_from_msgb(msg)[5] == GSM48_PDISC_RR && \
|
||||
l1sched_prim_data_from_msgb(msg)[6] == GSM48_MT_RR_MEAS_REP)
|
||||
|
||||
/* Iterate over all primitives in the queue */
|
||||
llist_for_each_entry(prim, queue, list) {
|
||||
/* We are looking for particular channel */
|
||||
if (prim->chan != lchan->type)
|
||||
continue;
|
||||
|
||||
llist_for_each_entry(msg, &lchan->tx_prims, list) {
|
||||
/* Look for a Measurement Report */
|
||||
if (!prim_mr && PRIM_IS_MR(prim))
|
||||
prim_mr = prim;
|
||||
if (!msg_mr && PRIM_MSGB_IS_MR(msg))
|
||||
msg_mr = msg;
|
||||
|
||||
/* Look for anything else */
|
||||
if (!prim_nmr && !PRIM_IS_MR(prim))
|
||||
prim_nmr = prim;
|
||||
if (!msg_nmr && !PRIM_MSGB_IS_MR(msg))
|
||||
msg_nmr = msg;
|
||||
|
||||
/* Should we look further? */
|
||||
if (mr_now && prim_mr)
|
||||
if (mr_now && msg_mr)
|
||||
break; /* MR was found */
|
||||
else if (!mr_now && prim_nmr)
|
||||
else if (!mr_now && msg_nmr)
|
||||
break; /* something else was found */
|
||||
}
|
||||
|
||||
LOGP_LCHAND(lchan, LOGL_DEBUG,
|
||||
"SACCH MR selection: mr_tx_last=%d prim_mr=%p prim_nmr=%p\n",
|
||||
lchan->sacch.mr_tx_last, prim_mr, prim_nmr);
|
||||
"SACCH MR selection: mr_tx_last=%d msg_mr=%p msg_nmr=%p\n",
|
||||
lchan->sacch.mr_tx_last, msg_mr, msg_nmr);
|
||||
|
||||
/* Prioritize non-MR prim if possible */
|
||||
if (mr_now && prim_mr)
|
||||
prim = prim_mr;
|
||||
else if (!mr_now && prim_nmr)
|
||||
prim = prim_nmr;
|
||||
else if (!mr_now && prim_mr)
|
||||
prim = prim_mr;
|
||||
if (mr_now && msg_mr)
|
||||
msg = msg_mr;
|
||||
else if (!mr_now && msg_nmr)
|
||||
msg = msg_nmr;
|
||||
else if (!mr_now && msg_mr)
|
||||
msg = msg_mr;
|
||||
else /* Nothing was found */
|
||||
prim = NULL;
|
||||
msg = NULL;
|
||||
|
||||
/* Have we found what we were looking for? */
|
||||
if (prim) /* Dequeue if so */
|
||||
llist_del(&prim->list);
|
||||
if (msg) /* Dequeue if so */
|
||||
llist_del(&msg->list);
|
||||
else /* Otherwise compose a new MR */
|
||||
prim = prim_compose_mr(lchan);
|
||||
msg = prim_compose_mr(lchan);
|
||||
|
||||
/* Update the cached report */
|
||||
if (prim == prim_mr) {
|
||||
memcpy(lchan->sacch.mr_cache,
|
||||
prim->payload, GSM_MACBLOCK_LEN);
|
||||
if (msg == msg_mr) {
|
||||
memcpy(lchan->sacch.mr_cache, msgb_l2(msg), GSM_MACBLOCK_LEN);
|
||||
lchan->sacch.mr_cache_usage = 0;
|
||||
|
||||
LOGP_LCHAND(lchan, LOGL_DEBUG, "SACCH MR cache has been updated\n");
|
||||
}
|
||||
|
||||
/* Update the MR transmission state */
|
||||
lchan->sacch.mr_tx_last = PRIM_IS_MR(prim);
|
||||
lchan->sacch.mr_tx_last = PRIM_MSGB_IS_MR(msg);
|
||||
|
||||
LOGP_LCHAND(lchan, LOGL_DEBUG, "SACCH decision: %s\n",
|
||||
PRIM_IS_MR(prim) ? "Measurement Report" : "data frame");
|
||||
PRIM_MSGB_IS_MR(msg) ? "Measurement Report" : "data frame");
|
||||
|
||||
return prim;
|
||||
}
|
||||
|
||||
/* Dequeues a primitive of a given channel type */
|
||||
static struct l1sched_ts_prim *prim_dequeue_one(struct llist_head *queue,
|
||||
enum l1sched_lchan_type lchan_type)
|
||||
{
|
||||
struct l1sched_ts_prim *prim;
|
||||
|
||||
/**
|
||||
* There is no need to use the 'safe' list iteration here
|
||||
* as an item removal is immediately followed by return.
|
||||
*/
|
||||
llist_for_each_entry(prim, queue, list) {
|
||||
if (prim->chan == lchan_type) {
|
||||
llist_del(&prim->list);
|
||||
return prim;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeues either a FACCH, or a speech TCH primitive
|
||||
* of a given channel type (Lm or Bm).
|
||||
*
|
||||
* Note: we could avoid 'lchan_type' parameter and just
|
||||
* check the prim's channel type using L1SCHED_CHAN_IS_TCH(),
|
||||
* but the current approach is a bit more flexible,
|
||||
* and allows one to have both sub-slots of TCH/H
|
||||
* enabled on same timeslot e.g. for testing...
|
||||
*
|
||||
* @param queue transmit queue to take a prim from
|
||||
* @param lchan_type required channel type of a primitive,
|
||||
* e.g. L1SCHED_TCHF, L1SCHED_TCHH_0, or L1SCHED_TCHH_1
|
||||
* @param lchan logical channel state
|
||||
* @param facch FACCH (true) or speech (false) prim?
|
||||
* @return either a FACCH, or a TCH primitive if found,
|
||||
* otherwise NULL
|
||||
*/
|
||||
static struct l1sched_ts_prim *prim_dequeue_tch(struct llist_head *queue,
|
||||
enum l1sched_lchan_type lchan_type, bool facch)
|
||||
static struct msgb *prim_dequeue_tch(struct l1sched_lchan_state *lchan, bool facch)
|
||||
{
|
||||
struct l1sched_ts_prim *prim;
|
||||
struct msgb *msg;
|
||||
|
||||
/**
|
||||
* There is no need to use the 'safe' list iteration here
|
||||
* as an item removal is immediately followed by return.
|
||||
*/
|
||||
llist_for_each_entry(prim, queue, list) {
|
||||
if (prim->chan != lchan_type)
|
||||
llist_for_each_entry(msg, &lchan->tx_prims, list) {
|
||||
bool is_facch = msgb_l2len(msg) == GSM_MACBLOCK_LEN;
|
||||
if (is_facch != facch)
|
||||
continue;
|
||||
|
||||
/* Either FACCH, or not FACCH */
|
||||
if (L1SCHED_PRIM_IS_FACCH(prim) != facch)
|
||||
continue;
|
||||
|
||||
llist_del(&prim->list);
|
||||
return prim;
|
||||
llist_del(&msg->list);
|
||||
return msg;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
@ -324,30 +253,30 @@ static struct l1sched_ts_prim *prim_dequeue_tch(struct llist_head *queue,
|
|||
* If a FACCH/F prim is found, one TCH/F prim is being
|
||||
* dropped (i.e. replaced).
|
||||
*
|
||||
* @param queue a transmit queue to take a prim from
|
||||
* @param lchan logical channel state
|
||||
* @return either a FACCH/F, or a TCH/F primitive,
|
||||
* otherwise NULL
|
||||
*/
|
||||
static struct l1sched_ts_prim *prim_dequeue_tch_f(struct llist_head *queue)
|
||||
static struct msgb *prim_dequeue_tch_f(struct l1sched_lchan_state *lchan)
|
||||
{
|
||||
struct l1sched_ts_prim *facch;
|
||||
struct l1sched_ts_prim *tch;
|
||||
struct msgb *facch;
|
||||
struct msgb *tch;
|
||||
|
||||
/* Attempt to find a pair of both FACCH/F and TCH/F frames */
|
||||
facch = prim_dequeue_tch(queue, L1SCHED_TCHF, true);
|
||||
tch = prim_dequeue_tch(queue, L1SCHED_TCHF, false);
|
||||
facch = prim_dequeue_tch(lchan, true);
|
||||
tch = prim_dequeue_tch(lchan, false);
|
||||
|
||||
/* Prioritize FACCH/F, if found */
|
||||
if (facch) {
|
||||
/* One TCH/F prim is replaced */
|
||||
if (tch)
|
||||
talloc_free(tch);
|
||||
msgb_free(tch);
|
||||
return facch;
|
||||
} else if (tch) {
|
||||
/* Only TCH/F prim was found */
|
||||
return tch;
|
||||
} else {
|
||||
/* Nothing was found, e.g. when only SACCH frames are in queue */
|
||||
/* Nothing was found */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -368,96 +297,95 @@ static struct l1sched_ts_prim *prim_dequeue_tch_f(struct llist_head *queue)
|
|||
*
|
||||
* where the numbers within brackets are fn % 26.
|
||||
*
|
||||
* @param queue transmit queue to take a prim from
|
||||
* @param lchan logical channel state
|
||||
* @param fn the current frame number
|
||||
* @param lchan_type required channel type of a primitive,
|
||||
* @return either a FACCH/H, or a TCH/H primitive,
|
||||
* otherwise NULL
|
||||
*/
|
||||
static struct l1sched_ts_prim *prim_dequeue_tch_h(struct llist_head *queue,
|
||||
uint32_t fn, enum l1sched_lchan_type lchan_type)
|
||||
static struct msgb *prim_dequeue_tch_h(struct l1sched_lchan_state *lchan, uint32_t fn)
|
||||
{
|
||||
struct l1sched_ts_prim *facch;
|
||||
struct l1sched_ts_prim *tch;
|
||||
struct msgb *facch;
|
||||
struct msgb *tch;
|
||||
bool facch_now;
|
||||
|
||||
/* May we initiate an UL FACCH/H frame transmission now? */
|
||||
facch_now = l1sched_tchh_facch_start(lchan_type, fn, true);
|
||||
facch_now = l1sched_tchh_facch_start(lchan->type, fn, true);
|
||||
if (!facch_now) /* Just dequeue a TCH/H prim */
|
||||
goto no_facch;
|
||||
|
||||
/* If there are no FACCH/H prims in the queue */
|
||||
facch = prim_dequeue_tch(queue, lchan_type, true);
|
||||
facch = prim_dequeue_tch(lchan, true);
|
||||
if (!facch) /* Just dequeue a TCH/H prim */
|
||||
goto no_facch;
|
||||
|
||||
/* FACCH/H prim replaces two TCH/F prims */
|
||||
tch = prim_dequeue_tch(queue, lchan_type, false);
|
||||
tch = prim_dequeue_tch(lchan, false);
|
||||
if (tch) {
|
||||
/* At least one TCH/H prim is dropped */
|
||||
talloc_free(tch);
|
||||
msgb_free(tch);
|
||||
|
||||
/* Attempt to find another */
|
||||
tch = prim_dequeue_tch(queue, lchan_type, false);
|
||||
tch = prim_dequeue_tch(lchan, false);
|
||||
if (tch) /* Drop the second TCH/H prim */
|
||||
talloc_free(tch);
|
||||
msgb_free(tch);
|
||||
}
|
||||
|
||||
return facch;
|
||||
|
||||
no_facch:
|
||||
return prim_dequeue_tch(queue, lchan_type, false);
|
||||
return prim_dequeue_tch(lchan, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeues a single primitive of required type
|
||||
* from a specified transmit queue.
|
||||
* Dequeues a primitive from the Tx queue of the given lchan.
|
||||
*
|
||||
* @param queue a transmit queue to take a prim from
|
||||
* @param fn the current frame number (used for FACCH/H)
|
||||
* @param lchan logical channel state
|
||||
* @param fn the current frame number (used for FACCH/H)
|
||||
* @return a primitive or NULL if not found
|
||||
*/
|
||||
struct l1sched_ts_prim *l1sched_prim_dequeue(struct llist_head *queue,
|
||||
uint32_t fn, struct l1sched_lchan_state *lchan)
|
||||
struct msgb *l1sched_lchan_prim_dequeue(struct l1sched_lchan_state *lchan, uint32_t fn)
|
||||
{
|
||||
struct l1sched_ts_prim *prim;
|
||||
|
||||
/* SACCH is unorthodox, see 3GPP TS 04.08, section 3.4.1 */
|
||||
if (L1SCHED_CHAN_IS_SACCH(lchan->type))
|
||||
return prim_dequeue_sacch(queue, lchan);
|
||||
return prim_dequeue_sacch(lchan);
|
||||
|
||||
/* There is nothing to dequeue */
|
||||
if (llist_empty(queue))
|
||||
if (llist_empty(&lchan->tx_prims))
|
||||
return NULL;
|
||||
|
||||
switch (lchan->type) {
|
||||
/* TCH/F requires FACCH/F prioritization */
|
||||
case L1SCHED_TCHF:
|
||||
return prim_dequeue_tch_f(queue);
|
||||
return prim_dequeue_tch_f(lchan);
|
||||
|
||||
/* FACCH/H prioritization is a bit more complex */
|
||||
case L1SCHED_TCHH_0:
|
||||
case L1SCHED_TCHH_1:
|
||||
return prim_dequeue_tch_h(queue, fn, lchan->type);
|
||||
return prim_dequeue_tch_h(lchan, fn);
|
||||
|
||||
/* PDCH is timing critical, we need to check TDMA Fn */
|
||||
case L1SCHED_PDTCH:
|
||||
case L1SCHED_PTCCH:
|
||||
prim = prim_dequeue_one(queue, lchan->type);
|
||||
if (prim == NULL)
|
||||
{
|
||||
struct msgb *msg = msgb_dequeue(&lchan->tx_prims);
|
||||
const struct l1sched_prim *prim;
|
||||
|
||||
if (msg == NULL)
|
||||
return NULL;
|
||||
if (OSMO_LIKELY(prim->fn == fn))
|
||||
return prim;
|
||||
prim = l1sched_prim_from_msgb(msg);
|
||||
|
||||
if (OSMO_LIKELY(prim->data_req.frame_nr == fn))
|
||||
return msg;
|
||||
LOGP_LCHAND(lchan, LOGL_ERROR,
|
||||
"%s(): dropping Tx primitive (current Fn=%u, prim Fn=%u)\n",
|
||||
__func__, fn, prim->fn);
|
||||
talloc_free(prim);
|
||||
__func__, fn, prim->data_req.frame_nr);
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Other kinds of logical channels */
|
||||
default:
|
||||
return prim_dequeue_one(queue, lchan->type);
|
||||
return msgb_dequeue(&lchan->tx_prims);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -468,7 +396,7 @@ struct l1sched_ts_prim *l1sched_prim_dequeue(struct llist_head *queue,
|
|||
*/
|
||||
void l1sched_lchan_prim_drop(struct l1sched_lchan_state *lchan)
|
||||
{
|
||||
talloc_free(lchan->prim);
|
||||
msgb_free(lchan->prim);
|
||||
lchan->prim = NULL;
|
||||
}
|
||||
|
||||
|
@ -480,11 +408,13 @@ void l1sched_lchan_prim_drop(struct l1sched_lchan_state *lchan)
|
|||
* @param lchan lchan to assign a primitive
|
||||
* @return zero in case of success, otherwise a error code
|
||||
*/
|
||||
int l1sched_prim_dummy(struct l1sched_lchan_state *lchan)
|
||||
void l1sched_lchan_prim_assign_dummy(struct l1sched_lchan_state *lchan)
|
||||
{
|
||||
const struct l1sched_lchan_desc *lchan_desc;
|
||||
enum l1sched_lchan_type chan = lchan->type;
|
||||
uint8_t tch_mode = lchan->tch_mode;
|
||||
struct l1sched_ts_prim *prim;
|
||||
struct l1sched_prim *prim;
|
||||
struct msgb *msg;
|
||||
uint8_t prim_buffer[40];
|
||||
size_t prim_len = 0;
|
||||
int i;
|
||||
|
@ -504,6 +434,8 @@ int l1sched_prim_dummy(struct l1sched_lchan_state *lchan)
|
|||
/* Not applicable for SACCH! */
|
||||
OSMO_ASSERT(!L1SCHED_CHAN_IS_SACCH(lchan->type));
|
||||
|
||||
lchan_desc = &l1sched_lchan_desc[lchan->type];
|
||||
|
||||
/**
|
||||
* Determine what actually should be generated:
|
||||
* TCH in GSM48_CMODE_SIGN: LAPDm fill frame;
|
||||
|
@ -515,7 +447,7 @@ int l1sched_prim_dummy(struct l1sched_lchan_state *lchan)
|
|||
prim_len = l1sched_bad_frame_ind(prim_buffer, lchan);
|
||||
} else if (L1SCHED_CHAN_IS_TCH(chan) && L1SCHED_TCH_MODE_IS_DATA(tch_mode)) {
|
||||
/* FIXME: should we do anything for CSD? */
|
||||
return 0;
|
||||
return;
|
||||
} else {
|
||||
/* Copy LAPDm fill frame's header */
|
||||
memcpy(prim_buffer, lapdm_fill_frame, sizeof(lapdm_fill_frame));
|
||||
|
@ -535,39 +467,136 @@ int l1sched_prim_dummy(struct l1sched_lchan_state *lchan)
|
|||
|
||||
/* Nothing to allocate / assign */
|
||||
if (!prim_len)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
/* Allocate a new primitive */
|
||||
prim = talloc_zero_size(lchan, sizeof(struct l1sched_ts_prim) + prim_len);
|
||||
if (prim == NULL)
|
||||
return -ENOMEM;
|
||||
msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST, prim_len);
|
||||
OSMO_ASSERT(msg != NULL);
|
||||
|
||||
/* Init primitive header */
|
||||
prim->payload_len = prim_len;
|
||||
prim->chan = lchan->type;
|
||||
prim = l1sched_prim_from_msgb(msg);
|
||||
prim->data_req = (struct l1sched_prim_chdr) {
|
||||
.chan_nr = lchan_desc->chan_nr | lchan->ts->index,
|
||||
.link_id = lchan_desc->link_id,
|
||||
};
|
||||
|
||||
/* Fill in the payload */
|
||||
memcpy(prim->payload, prim_buffer, prim_len);
|
||||
memcpy(msgb_put(msg, prim_len), &prim_buffer[0], prim_len);
|
||||
|
||||
/* Assign the current prim */
|
||||
lchan->prim = prim;
|
||||
lchan->prim = msg;
|
||||
|
||||
LOGP_LCHAND(lchan, LOGL_DEBUG, "Transmitting a dummy / silence frame\n");
|
||||
}
|
||||
|
||||
int l1sched_lchan_emit_data_ind(struct l1sched_lchan_state *lchan,
|
||||
const uint8_t *data, size_t data_len,
|
||||
int n_errors, int n_bits_total,
|
||||
bool traffic)
|
||||
{
|
||||
const struct l1sched_meas_set *meas = &lchan->meas_avg;
|
||||
const struct l1sched_lchan_desc *lchan_desc;
|
||||
struct l1sched_prim *prim;
|
||||
struct msgb *msg;
|
||||
|
||||
lchan_desc = &l1sched_lchan_desc[lchan->type];
|
||||
|
||||
msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_INDICATION, data_len);
|
||||
OSMO_ASSERT(msg != NULL);
|
||||
|
||||
prim = l1sched_prim_from_msgb(msg);
|
||||
prim->data_ind = (struct l1sched_prim_data_ind) {
|
||||
.chdr = {
|
||||
.frame_nr = meas->fn,
|
||||
.chan_nr = lchan_desc->chan_nr | lchan->ts->index,
|
||||
.link_id = lchan_desc->link_id,
|
||||
.traffic = traffic,
|
||||
},
|
||||
.toa256 = meas->toa256,
|
||||
.rssi = meas->rssi,
|
||||
.n_errors = n_errors,
|
||||
.n_bits_total = n_bits_total,
|
||||
};
|
||||
|
||||
if (data_len > 0)
|
||||
memcpy(msgb_put(msg, data_len), data, data_len);
|
||||
|
||||
return l1sched_prim_to_user(lchan->ts->sched, msg);
|
||||
}
|
||||
|
||||
int l1sched_lchan_emit_data_cnf(struct l1sched_lchan_state *lchan, uint32_t fn)
|
||||
{
|
||||
struct l1sched_prim *prim;
|
||||
struct msgb *msg;
|
||||
|
||||
/* take ownership of the prim */
|
||||
OSMO_ASSERT(lchan->prim != NULL);
|
||||
msg = lchan->prim;
|
||||
lchan->prim = NULL;
|
||||
|
||||
/* convert from DATA.req to DATA.cnf */
|
||||
prim = l1sched_prim_from_msgb(msg);
|
||||
prim->oph.operation = PRIM_OP_CONFIRM;
|
||||
|
||||
switch (prim->oph.primitive) {
|
||||
case L1SCHED_PRIM_T_DATA:
|
||||
prim->data_cnf.frame_nr = fn;
|
||||
break;
|
||||
case L1SCHED_PRIM_T_RACH:
|
||||
prim->rach_cnf.chdr.frame_nr = fn;
|
||||
break;
|
||||
default:
|
||||
/* shall not happen */
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
return l1sched_prim_to_user(lchan->ts->sched, msg);
|
||||
}
|
||||
|
||||
static int prim_enqeue(struct l1sched_state *sched, struct msgb *msg,
|
||||
const struct l1sched_prim_chdr *chdr)
|
||||
{
|
||||
const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
|
||||
struct l1sched_lchan_state *lchan;
|
||||
|
||||
lchan = l1sched_find_lchan_by_chan_nr(sched, chdr->chan_nr, chdr->link_id);
|
||||
if (OSMO_UNLIKELY(lchan == NULL || !lchan->active)) {
|
||||
LOGP_SCHEDD(sched, LOGL_ERROR,
|
||||
"No [active] lchan for primitive " L1SCHED_PRIM_STR_FMT " "
|
||||
"(chan_nr=%02x, link_id=%02x, len=%u): %s\n",
|
||||
L1SCHED_PRIM_STR_ARGS(prim),
|
||||
chdr->chan_nr, chdr->link_id,
|
||||
msgb_l2len(msg), msgb_hexdump_l2(msg));
|
||||
msgb_free(msg);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
LOGP_LCHAND(lchan, LOGL_DEBUG,
|
||||
"Enqueue primitive " L1SCHED_PRIM_STR_FMT " "
|
||||
"(chan_nr=%02x, link_id=%02x, len=%u): %s\n",
|
||||
L1SCHED_PRIM_STR_ARGS(prim),
|
||||
chdr->chan_nr, chdr->link_id,
|
||||
msgb_l2len(msg), msgb_hexdump_l2(msg));
|
||||
|
||||
msgb_enqueue(&lchan->tx_prims, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes a queue of primitives
|
||||
*
|
||||
* @param list list of prims going to be flushed
|
||||
*/
|
||||
void l1sched_prim_flush_queue(struct llist_head *list)
|
||||
int l1sched_prim_from_user(struct l1sched_state *sched, struct msgb *msg)
|
||||
{
|
||||
struct l1sched_ts_prim *prim, *prim_next;
|
||||
const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
|
||||
|
||||
llist_for_each_entry_safe(prim, prim_next, list, list) {
|
||||
llist_del(&prim->list);
|
||||
talloc_free(prim);
|
||||
LOGP_SCHEDD(sched, LOGL_DEBUG,
|
||||
"%s(): Rx " L1SCHED_PRIM_STR_FMT "\n",
|
||||
__func__, L1SCHED_PRIM_STR_ARGS(prim));
|
||||
|
||||
switch (OSMO_PRIM_HDR(&prim->oph)) {
|
||||
case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST):
|
||||
return prim_enqeue(sched, msg, &prim->data_req);
|
||||
case OSMO_PRIM(L1SCHED_PRIM_T_RACH, PRIM_OP_REQUEST):
|
||||
return prim_enqeue(sched, msg, &prim->rach_req.chdr);
|
||||
default:
|
||||
LOGP_SCHEDD(sched, LOGL_ERROR,
|
||||
"%s(): Unhandled primitive " L1SCHED_PRIM_STR_FMT "\n",
|
||||
__func__, L1SCHED_PRIM_STR_ARGS(prim));
|
||||
msgb_free(msg);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,18 +68,20 @@ static const uint8_t meas_rep_dummy[] = {
|
|||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static int l1sched_cfg_pchan_comb_req(struct l1sched_state *sched,
|
||||
static int l1sched_cfg_pchan_comb_ind(struct l1sched_state *sched,
|
||||
uint8_t tn, enum gsm_phys_chan_config pchan)
|
||||
{
|
||||
const struct l1sched_config_req cr = {
|
||||
.type = L1SCHED_CFG_PCHAN_COMB,
|
||||
.pchan_comb = {
|
||||
.tn = tn,
|
||||
.pchan = pchan,
|
||||
},
|
||||
};
|
||||
struct l1sched_prim *prim;
|
||||
struct msgb *msg;
|
||||
|
||||
return l1sched_handle_config_req(sched, &cr);
|
||||
msg = l1sched_prim_alloc(L1SCHED_PRIM_T_PCHAN_COMB, PRIM_OP_INDICATION, 0);
|
||||
OSMO_ASSERT(msg != NULL);
|
||||
|
||||
prim = l1sched_prim_from_msgb(msg);
|
||||
prim->pchan_comb_ind.tn = tn;
|
||||
prim->pchan_comb_ind.pchan = pchan;
|
||||
|
||||
return l1sched_prim_to_user(sched, msg);
|
||||
}
|
||||
|
||||
static void l1sched_a5_burst_enc(struct l1sched_lchan_state *lchan,
|
||||
|
@ -121,11 +123,11 @@ void l1sched_pull_burst(struct l1sched_state *sched, struct l1sched_burst_req *b
|
|||
|
||||
/* If no primitive is being processed, try obtaining one from Tx queue */
|
||||
if (lchan->prim == NULL)
|
||||
lchan->prim = l1sched_prim_dequeue(&ts->tx_prims, br->fn, lchan);
|
||||
lchan->prim = l1sched_lchan_prim_dequeue(lchan, br->fn);
|
||||
if (lchan->prim == NULL) {
|
||||
/* If CBTX (Continuous Burst Transmission) is required */
|
||||
if (l1sched_lchan_desc[chan].flags & L1SCHED_CH_FLAG_CBTX)
|
||||
l1sched_prim_dummy(lchan);
|
||||
l1sched_lchan_prim_assign_dummy(lchan);
|
||||
if (lchan->prim == NULL)
|
||||
return;
|
||||
}
|
||||
|
@ -134,7 +136,8 @@ void l1sched_pull_burst(struct l1sched_state *sched, struct l1sched_burst_req *b
|
|||
|
||||
/* Handover RACH needs to be handled regardless of the
|
||||
* current channel type and the associated handler. */
|
||||
if (L1SCHED_PRIM_IS_RACH(lchan->prim) && lchan->prim->chan != L1SCHED_RACH)
|
||||
if (l1sched_prim_type_from_msgb(lchan->prim) == L1SCHED_PRIM_T_RACH &&
|
||||
lchan->type != L1SCHED_RACH)
|
||||
handler = l1sched_lchan_desc[L1SCHED_RACH].tx_fn;
|
||||
|
||||
/* Poke lchan handler */
|
||||
|
@ -272,15 +275,12 @@ void l1sched_del_ts(struct l1sched_state *sched, int tn)
|
|||
talloc_free(lchan);
|
||||
}
|
||||
|
||||
/* Flush queue primitives for TX */
|
||||
l1sched_prim_flush_queue(&ts->tx_prims);
|
||||
|
||||
/* Remove ts from list and free memory */
|
||||
sched->ts[tn] = NULL;
|
||||
talloc_free(ts);
|
||||
|
||||
/* Notify transceiver about that */
|
||||
l1sched_cfg_pchan_comb_req(sched, tn, GSM_PCHAN_NONE);
|
||||
l1sched_cfg_pchan_comb_ind(sched, tn, GSM_PCHAN_NONE);
|
||||
}
|
||||
|
||||
#define LAYOUT_HAS_LCHAN(layout, lchan) \
|
||||
|
@ -316,8 +316,6 @@ int l1sched_configure_ts(struct l1sched_state *sched, int tn,
|
|||
"(Re)configure TDMA timeslot #%u as %s\n",
|
||||
tn, ts->mf_layout->name);
|
||||
|
||||
/* Init queue primitives for TX */
|
||||
INIT_LLIST_HEAD(&ts->tx_prims);
|
||||
/* Init logical channels list */
|
||||
INIT_LLIST_HEAD(&ts->lchans);
|
||||
|
||||
|
@ -337,6 +335,9 @@ int l1sched_configure_ts(struct l1sched_state *sched, int tn,
|
|||
/* Set channel type */
|
||||
lchan->type = type;
|
||||
|
||||
/* Init the Tx queue */
|
||||
INIT_LLIST_HEAD(&lchan->tx_prims);
|
||||
|
||||
/* Add to the list of channel states */
|
||||
llist_add_tail(&lchan->list, &ts->lchans);
|
||||
|
||||
|
@ -346,7 +347,7 @@ int l1sched_configure_ts(struct l1sched_state *sched, int tn,
|
|||
}
|
||||
|
||||
/* Notify transceiver about TS activation */
|
||||
l1sched_cfg_pchan_comb_req(sched, tn, config);
|
||||
l1sched_cfg_pchan_comb_ind(sched, tn, config);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -364,9 +365,6 @@ int l1sched_reset_ts(struct l1sched_state *sched, int tn)
|
|||
/* Undefine multiframe layout */
|
||||
ts->mf_layout = NULL;
|
||||
|
||||
/* Flush queue primitives for TX */
|
||||
l1sched_prim_flush_queue(&ts->tx_prims);
|
||||
|
||||
/* Deactivate all logical channels */
|
||||
l1sched_deactivate_all_lchans(ts);
|
||||
|
||||
|
@ -377,7 +375,7 @@ int l1sched_reset_ts(struct l1sched_state *sched, int tn)
|
|||
}
|
||||
|
||||
/* Notify transceiver about that */
|
||||
l1sched_cfg_pchan_comb_req(sched, tn, GSM_PCHAN_NONE);
|
||||
l1sched_cfg_pchan_comb_ind(sched, tn, GSM_PCHAN_NONE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -541,6 +539,8 @@ int l1sched_activate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan)
|
|||
|
||||
static void l1sched_reset_lchan(struct l1sched_lchan_state *lchan)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
/* Prevent NULL-pointer deference */
|
||||
OSMO_ASSERT(lchan != NULL);
|
||||
|
||||
|
@ -568,6 +568,10 @@ static void l1sched_reset_lchan(struct l1sched_lchan_state *lchan)
|
|||
/* Forget the current prim */
|
||||
l1sched_lchan_prim_drop(lchan);
|
||||
|
||||
/* Flush the queue of pending Tx prims */
|
||||
while ((msg = msgb_dequeue(&lchan->tx_prims)) != NULL)
|
||||
msgb_free(msg);
|
||||
|
||||
/* Channel specific stuff */
|
||||
if (L1SCHED_CHAN_IS_TCH(lchan->type)) {
|
||||
lchan->dl_ongoing_facch = 0;
|
||||
|
@ -657,20 +661,6 @@ enum gsm_phys_chan_config l1sched_chan_nr2pchan_config(uint8_t chan_nr)
|
|||
return GSM_PCHAN_NONE;
|
||||
}
|
||||
|
||||
enum l1sched_lchan_type l1sched_chan_nr2lchan_type(uint8_t chan_nr,
|
||||
uint8_t link_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Iterate over all known lchan types */
|
||||
for (i = 0; i < _L1SCHED_CHAN_MAX; i++)
|
||||
if (l1sched_lchan_desc[i].chan_nr == (chan_nr & RSL_CHAN_NR_MASK))
|
||||
if (l1sched_lchan_desc[i].link_id == link_id)
|
||||
return i;
|
||||
|
||||
return L1SCHED_IDLE;
|
||||
}
|
||||
|
||||
static void l1sched_a5_burst_dec(struct l1sched_lchan_state *lchan,
|
||||
struct l1sched_burst_ind *bi)
|
||||
{
|
||||
|
|
|
@ -283,21 +283,25 @@ static void handle_tx_access_burst_req(struct osmo_fsm_inst *fi,
|
|||
const struct trxcon_param_tx_access_burst_req *req)
|
||||
{
|
||||
struct trxcon_inst *trxcon = fi->priv;
|
||||
enum l1sched_ts_prim_type prim_type;
|
||||
const struct l1sched_ts_prim *prim;
|
||||
struct l1sched_prim *prim;
|
||||
struct msgb *msg;
|
||||
|
||||
const struct l1sched_ts_prim_rach rach = {
|
||||
msg = l1sched_prim_alloc(L1SCHED_PRIM_T_RACH, PRIM_OP_REQUEST, 0);
|
||||
OSMO_ASSERT(msg != NULL);
|
||||
|
||||
prim = l1sched_prim_from_msgb(msg);
|
||||
prim->rach_req = (struct l1sched_prim_rach) {
|
||||
.chdr = {
|
||||
.chan_nr = req->chan_nr,
|
||||
.link_id = req->link_id,
|
||||
},
|
||||
.synch_seq = req->synch_seq,
|
||||
.offset = req->offset,
|
||||
.is_11bit = req->is_11bit,
|
||||
.ra = req->ra,
|
||||
};
|
||||
|
||||
prim_type = req->is_11bit ? L1SCHED_PRIM_RACH11 : L1SCHED_PRIM_RACH8;
|
||||
prim = l1sched_prim_push(trxcon->sched, prim_type,
|
||||
req->chan_nr, req->link_id,
|
||||
(const uint8_t *)&rach, sizeof(rach));
|
||||
if (prim == NULL)
|
||||
LOGPFSML(fi, LOGL_ERROR, "Failed to enqueue a prim\n");
|
||||
l1sched_prim_from_user(trxcon->sched, msg);
|
||||
}
|
||||
|
||||
static void handle_dch_est_req(struct osmo_fsm_inst *fi,
|
||||
|
@ -485,15 +489,21 @@ static void trxcon_st_dedicated_action(struct osmo_fsm_inst *fi,
|
|||
case TRXCON_EV_TX_DATA_REQ:
|
||||
{
|
||||
const struct trxcon_param_tx_data_req *req = data;
|
||||
struct l1sched_ts_prim *prim;
|
||||
struct l1sched_prim *prim;
|
||||
struct msgb *msg;
|
||||
|
||||
prim = l1sched_prim_push(trxcon->sched, L1SCHED_PRIM_DATA,
|
||||
req->chan_nr, req->link_id,
|
||||
req->data, req->data_len);
|
||||
if (prim == NULL) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Failed to enqueue a prim\n");
|
||||
return;
|
||||
}
|
||||
msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST, req->data_len);
|
||||
OSMO_ASSERT(msg != NULL);
|
||||
|
||||
prim = l1sched_prim_from_msgb(msg);
|
||||
prim->data_req = (struct l1sched_prim_chdr) {
|
||||
.chan_nr = req->chan_nr,
|
||||
.link_id = req->link_id,
|
||||
.traffic = req->traffic,
|
||||
};
|
||||
|
||||
memcpy(msgb_put(msg, req->data_len), req->data, req->data_len);
|
||||
l1sched_prim_from_user(trxcon->sched, msg);
|
||||
break;
|
||||
}
|
||||
case TRXCON_EV_TX_DATA_CNF:
|
||||
|
@ -547,21 +557,24 @@ static void trxcon_st_packet_data_action(struct osmo_fsm_inst *fi,
|
|||
case TRXCON_EV_GPRS_UL_BLOCK_REQ:
|
||||
{
|
||||
struct l1gprs_prim_ul_block_req block_req;
|
||||
const struct msgb *msg = data;
|
||||
struct l1sched_ts_prim *prim;
|
||||
struct l1sched_prim *prim;
|
||||
struct msgb *msg = data;
|
||||
|
||||
if (l1gprs_handle_ul_block_req(trxcon->gprs, &block_req, msg) != 0)
|
||||
return;
|
||||
|
||||
prim = l1sched_prim_push(trxcon->sched, L1SCHED_PRIM_DATA,
|
||||
RSL_CHAN_OSMO_PDCH | block_req.hdr.tn, 0x00,
|
||||
block_req.data, block_req.data_len);
|
||||
if (prim == NULL) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Failed to enqueue a prim\n");
|
||||
return;
|
||||
}
|
||||
msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST, block_req.data_len);
|
||||
OSMO_ASSERT(msg != NULL);
|
||||
|
||||
prim->fn = block_req.hdr.fn;
|
||||
prim = l1sched_prim_from_msgb(msg);
|
||||
prim->data_req = (struct l1sched_prim_chdr) {
|
||||
.frame_nr = block_req.hdr.fn,
|
||||
.chan_nr = RSL_CHAN_OSMO_PDCH | block_req.hdr.tn,
|
||||
.link_id = 0x00,
|
||||
};
|
||||
|
||||
memcpy(msgb_put(msg, block_req.data_len), block_req.data, block_req.data_len);
|
||||
l1sched_prim_from_user(trxcon->sched, msg);
|
||||
break;
|
||||
}
|
||||
case TRXCON_EV_RX_DATA_IND:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* OsmocomBB <-> SDR connection bridge
|
||||
*
|
||||
* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* (C) 2022-2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
|
@ -24,52 +24,33 @@
|
|||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/gsmtap_util.h>
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
#include <osmocom/gsm/rsl.h>
|
||||
|
||||
#include <osmocom/bb/trxcon/trxcon.h>
|
||||
#include <osmocom/bb/trxcon/trxcon_fsm.h>
|
||||
#include <osmocom/bb/trxcon/phyif.h>
|
||||
#include <osmocom/bb/l1sched/l1sched.h>
|
||||
|
||||
static void trxcon_gsmtap_send(struct gsmtap_inst *gi, uint8_t chan_type,
|
||||
uint32_t fn, uint8_t tn, uint8_t ss,
|
||||
uint16_t band_arfcn,
|
||||
int8_t signal_dbm, uint8_t snr,
|
||||
const uint8_t *data, size_t data_len)
|
||||
static void trxcon_gsmtap_send(struct trxcon_inst *trxcon,
|
||||
const struct l1sched_prim_chdr *chdr,
|
||||
const uint8_t *data, size_t data_len,
|
||||
int8_t signal_dbm, uint8_t snr, bool uplink)
|
||||
{
|
||||
/* Omit frames with unknown channel type */
|
||||
if (chan_type == GSMTAP_CHANNEL_UNKNOWN)
|
||||
return;
|
||||
uint16_t band_arfcn = trxcon->l1p.band_arfcn;
|
||||
uint8_t chan_type, ss, tn;
|
||||
|
||||
/* TODO: distinguish GSMTAP_CHANNEL_PCH and GSMTAP_CHANNEL_AGCH */
|
||||
gsmtap_send(gi, band_arfcn, tn, chan_type, ss, fn, signal_dbm, snr, data, data_len);
|
||||
if (uplink)
|
||||
band_arfcn |= ARFCN_UPLINK;
|
||||
if (rsl_dec_chan_nr(chdr->chan_nr, &chan_type, &ss, &tn) != 0)
|
||||
return;
|
||||
chan_type = chantype_rsl2gsmtap2(chan_type, chdr->link_id, chdr->traffic);
|
||||
|
||||
gsmtap_send(trxcon->gsmtap, band_arfcn, tn, chan_type, ss,
|
||||
chdr->frame_nr, signal_dbm, snr,
|
||||
data, data_len);
|
||||
}
|
||||
|
||||
/* External L1 API for the scheduler */
|
||||
int l1sched_handle_config_req(struct l1sched_state *sched,
|
||||
const struct l1sched_config_req *cr)
|
||||
{
|
||||
struct trxcon_inst *trxcon = sched->priv;
|
||||
|
||||
switch (cr->type) {
|
||||
case L1SCHED_CFG_PCHAN_COMB:
|
||||
{
|
||||
struct trxcon_param_set_phy_config_req req = {
|
||||
.type = TRXCON_PHY_CFGT_PCHAN_COMB,
|
||||
.pchan_comb = {
|
||||
.tn = cr->pchan_comb.tn,
|
||||
.pchan = cr->pchan_comb.pchan,
|
||||
},
|
||||
};
|
||||
|
||||
return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_SET_PHY_CONFIG_REQ, &req);
|
||||
}
|
||||
default:
|
||||
LOGPFSML(trxcon->fi, LOGL_ERROR,
|
||||
"Unhandled config request (type 0x%02x)\n", cr->type);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
int l1sched_handle_burst_req(struct l1sched_state *sched,
|
||||
const struct l1sched_burst_req *br)
|
||||
{
|
||||
|
@ -86,137 +67,122 @@ int l1sched_handle_burst_req(struct l1sched_state *sched,
|
|||
}
|
||||
|
||||
/* External L2 API for the scheduler */
|
||||
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)
|
||||
static int handle_prim_data_ind(struct trxcon_inst *trxcon, struct msgb *msg)
|
||||
{
|
||||
const struct l1sched_meas_set *meas = &lchan->meas_avg;
|
||||
const struct l1sched_lchan_desc *lchan_desc;
|
||||
struct l1sched_state *sched = lchan->ts->sched;
|
||||
struct trxcon_inst *trxcon = sched->priv;
|
||||
int rc;
|
||||
|
||||
lchan_desc = &l1sched_lchan_desc[lchan->type];
|
||||
|
||||
const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
|
||||
struct trxcon_param_rx_data_ind ind = {
|
||||
/* .traffic is set below */
|
||||
.chan_nr = lchan_desc->chan_nr | lchan->ts->index,
|
||||
.link_id = lchan_desc->link_id,
|
||||
.traffic = prim->data_ind.chdr.traffic,
|
||||
.chan_nr = prim->data_ind.chdr.chan_nr,
|
||||
.link_id = prim->data_ind.chdr.link_id,
|
||||
.band_arfcn = trxcon->l1p.band_arfcn,
|
||||
.frame_nr = meas->fn,
|
||||
.toa256 = meas->toa256,
|
||||
.rssi = meas->rssi,
|
||||
.n_errors = n_errors,
|
||||
.n_bits_total = n_bits_total,
|
||||
.data_len = data_len,
|
||||
.data = data,
|
||||
.frame_nr = prim->data_ind.chdr.frame_nr,
|
||||
.toa256 = prim->data_ind.toa256,
|
||||
.rssi = prim->data_ind.rssi,
|
||||
.n_errors = prim->data_ind.n_errors,
|
||||
.n_bits_total = prim->data_ind.n_bits_total,
|
||||
.data_len = msgb_l2len(msg),
|
||||
.data = msgb_l2(msg),
|
||||
};
|
||||
|
||||
switch (dt) {
|
||||
case L1SCHED_DT_PACKET_DATA:
|
||||
case L1SCHED_DT_TRAFFIC:
|
||||
ind.traffic = true;
|
||||
/* fall-through */
|
||||
case L1SCHED_DT_SIGNALING:
|
||||
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_RX_DATA_IND, &ind);
|
||||
break;
|
||||
case L1SCHED_DT_OTHER:
|
||||
if (lchan->type == L1SCHED_SCH) {
|
||||
if (trxcon->fi->state != TRXCON_ST_FBSB_SEARCH)
|
||||
return 0;
|
||||
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FBSB_SEARCH_RES, NULL);
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
LOGPFSML(trxcon->fi, LOGL_ERROR,
|
||||
"Unhandled L2 DATA.ind (type 0x%02x)\n", dt);
|
||||
return -ENODEV;
|
||||
if (trxcon->gsmtap != NULL && ind.data_len > 0) {
|
||||
trxcon_gsmtap_send(trxcon, &prim->data_ind.chdr,
|
||||
ind.data, ind.data_len,
|
||||
ind.rssi, 0, false);
|
||||
}
|
||||
|
||||
if (trxcon->gsmtap != NULL && data != NULL && data_len > 0) {
|
||||
trxcon_gsmtap_send(trxcon->gsmtap, lchan_desc->gsmtap_chan_type,
|
||||
meas->fn, lchan->ts->index, lchan_desc->ss_nr,
|
||||
trxcon->l1p.band_arfcn, meas->rssi, 0,
|
||||
data, data_len);
|
||||
}
|
||||
|
||||
return rc;
|
||||
return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_RX_DATA_IND, &ind);
|
||||
}
|
||||
|
||||
int l1sched_handle_data_cnf(struct l1sched_lchan_state *lchan,
|
||||
uint32_t fn, enum l1sched_data_type dt)
|
||||
static int handle_prim_data_cnf(struct trxcon_inst *trxcon, struct msgb *msg)
|
||||
{
|
||||
const struct l1sched_lchan_desc *lchan_desc;
|
||||
struct l1sched_state *sched = lchan->ts->sched;
|
||||
struct trxcon_inst *trxcon = sched->priv;
|
||||
const uint8_t *data;
|
||||
uint8_t ra_buf[2];
|
||||
size_t data_len;
|
||||
int rc;
|
||||
|
||||
lchan_desc = &l1sched_lchan_desc[lchan->type];
|
||||
|
||||
switch (dt) {
|
||||
case L1SCHED_DT_PACKET_DATA:
|
||||
data_len = lchan->prim->payload_len;
|
||||
data = lchan->prim->payload;
|
||||
rc = 0;
|
||||
break; /* do not send DATA.cnf */
|
||||
case L1SCHED_DT_SIGNALING:
|
||||
case L1SCHED_DT_TRAFFIC:
|
||||
{
|
||||
struct trxcon_param_tx_data_cnf cnf = {
|
||||
.traffic = (dt == L1SCHED_DT_TRAFFIC),
|
||||
.chan_nr = lchan_desc->chan_nr | lchan->ts->index,
|
||||
.link_id = lchan_desc->link_id,
|
||||
.band_arfcn = trxcon->l1p.band_arfcn,
|
||||
.frame_nr = fn,
|
||||
};
|
||||
|
||||
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_DATA_CNF, &cnf);
|
||||
data_len = lchan->prim->payload_len;
|
||||
data = lchan->prim->payload;
|
||||
break;
|
||||
}
|
||||
case L1SCHED_DT_OTHER:
|
||||
if (L1SCHED_PRIM_IS_RACH(lchan->prim)) {
|
||||
const struct l1sched_ts_prim_rach *rach;
|
||||
struct trxcon_param_tx_access_burst_cnf cnf = {
|
||||
.band_arfcn = trxcon->l1p.band_arfcn,
|
||||
.frame_nr = fn,
|
||||
};
|
||||
|
||||
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_ACCESS_BURST_CNF, &cnf);
|
||||
|
||||
rach = (struct l1sched_ts_prim_rach *)lchan->prim->payload;
|
||||
if (lchan->prim->type == L1SCHED_PRIM_RACH11) {
|
||||
ra_buf[0] = (uint8_t)(rach->ra >> 3);
|
||||
ra_buf[1] = (uint8_t)(rach->ra & 0x07);
|
||||
data = &ra_buf[0];
|
||||
data_len = 2;
|
||||
} else {
|
||||
ra_buf[0] = (uint8_t)(rach->ra);
|
||||
data = &ra_buf[0];
|
||||
data_len = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
LOGPFSML(trxcon->fi, LOGL_ERROR,
|
||||
"Unhandled L2 DATA.cnf (type 0x%02x)\n", dt);
|
||||
return -ENODEV;
|
||||
}
|
||||
const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
|
||||
struct trxcon_param_tx_data_cnf cnf = {
|
||||
.traffic = prim->data_cnf.traffic,
|
||||
.chan_nr = prim->data_cnf.chan_nr,
|
||||
.link_id = prim->data_cnf.link_id,
|
||||
.band_arfcn = trxcon->l1p.band_arfcn,
|
||||
.frame_nr = prim->data_cnf.frame_nr,
|
||||
};
|
||||
|
||||
if (trxcon->gsmtap != NULL) {
|
||||
trxcon_gsmtap_send(trxcon->gsmtap, lchan_desc->gsmtap_chan_type,
|
||||
fn, lchan->ts->index, lchan_desc->ss_nr,
|
||||
trxcon->l1p.band_arfcn | ARFCN_UPLINK,
|
||||
0, 0, data, data_len);
|
||||
trxcon_gsmtap_send(trxcon, &prim->data_cnf,
|
||||
msgb_l2(msg), msgb_l2len(msg),
|
||||
0, 0, true);
|
||||
}
|
||||
|
||||
/* XXX: do not send for L1SCHED_DT_PACKET_DATA */
|
||||
return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_DATA_CNF, &cnf);
|
||||
}
|
||||
|
||||
static int handle_prim_rach_cnf(struct trxcon_inst *trxcon, struct msgb *msg)
|
||||
{
|
||||
const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
|
||||
struct trxcon_param_tx_access_burst_cnf cnf = {
|
||||
.band_arfcn = trxcon->l1p.band_arfcn,
|
||||
.frame_nr = prim->rach_cnf.chdr.frame_nr,
|
||||
};
|
||||
|
||||
if (trxcon->gsmtap != NULL) {
|
||||
if (prim->rach_cnf.is_11bit) {
|
||||
msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra >> 3));
|
||||
msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra & 0x07));
|
||||
} else {
|
||||
msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra));
|
||||
}
|
||||
|
||||
trxcon_gsmtap_send(trxcon, &prim->rach_cnf.chdr,
|
||||
msgb_l2(msg), msgb_l2len(msg),
|
||||
0, 0, true);
|
||||
}
|
||||
|
||||
return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_ACCESS_BURST_CNF, &cnf);
|
||||
}
|
||||
|
||||
int l1sched_prim_to_user(struct l1sched_state *sched, struct msgb *msg)
|
||||
{
|
||||
const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
|
||||
struct trxcon_inst *trxcon = sched->priv;
|
||||
int rc = 0;
|
||||
|
||||
LOGPFSML(trxcon->fi, LOGL_DEBUG,
|
||||
"%s(): Rx " L1SCHED_PRIM_STR_FMT "\n",
|
||||
__func__, L1SCHED_PRIM_STR_ARGS(prim));
|
||||
|
||||
switch (OSMO_PRIM_HDR(&prim->oph)) {
|
||||
case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_INDICATION):
|
||||
rc = handle_prim_data_ind(trxcon, msg);
|
||||
break;
|
||||
case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_CONFIRM):
|
||||
rc = handle_prim_data_cnf(trxcon, msg);
|
||||
break;
|
||||
case OSMO_PRIM(L1SCHED_PRIM_T_RACH, PRIM_OP_CONFIRM):
|
||||
rc = handle_prim_rach_cnf(trxcon, msg);
|
||||
break;
|
||||
case OSMO_PRIM(L1SCHED_PRIM_T_SCH, PRIM_OP_INDICATION):
|
||||
if (trxcon->fi->state == TRXCON_ST_FBSB_SEARCH)
|
||||
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FBSB_SEARCH_RES, NULL);
|
||||
break;
|
||||
case OSMO_PRIM(L1SCHED_PRIM_T_PCHAN_COMB, PRIM_OP_INDICATION):
|
||||
{
|
||||
struct trxcon_param_set_phy_config_req req = {
|
||||
.type = TRXCON_PHY_CFGT_PCHAN_COMB,
|
||||
.pchan_comb = {
|
||||
.tn = prim->pchan_comb_ind.tn,
|
||||
.pchan = prim->pchan_comb_ind.pchan,
|
||||
},
|
||||
};
|
||||
|
||||
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_SET_PHY_CONFIG_REQ, &req);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGPFSML(trxcon->fi, LOGL_ERROR,
|
||||
"%s(): Unhandled primitive " L1SCHED_PRIM_STR_FMT "\n",
|
||||
__func__, L1SCHED_PRIM_STR_ARGS(prim));
|
||||
rc = -ENOTSUP;
|
||||
}
|
||||
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue