{trxcon,virt_phy}: shared GPRS L1 (MAC) implementation
Change-Id: I9567d64f9d00262e36147e8d7e541e5e246bda5f Related: OS#5500
This commit is contained in:
parent
e15996378f
commit
2b462dd89d
|
@ -53,11 +53,10 @@ enum {
|
||||||
L1CTL_TRAFFIC_CONF = 0x1d,
|
L1CTL_TRAFFIC_CONF = 0x1d,
|
||||||
L1CTL_TRAFFIC_IND = 0x1e,
|
L1CTL_TRAFFIC_IND = 0x1e,
|
||||||
L1CTL_BURST_IND = 0x1f,
|
L1CTL_BURST_IND = 0x1f,
|
||||||
/* configure TBF for uplink/downlink */
|
L1CTL_GPRS_UL_TBF_CFG_REQ = 0x20,
|
||||||
L1CTL_TBF_CFG_REQ = 0x20,
|
L1CTL_GPRS_DL_TBF_CFG_REQ = 0x21,
|
||||||
L1CTL_TBF_CFG_CONF = 0x21,
|
L1CTL_GPRS_UL_BLOCK_REQ = 0x22,
|
||||||
L1CTL_DATA_TBF_REQ = 0x22,
|
L1CTL_GPRS_DL_BLOCK_IND = 0x23,
|
||||||
L1CTL_DATA_TBF_CONF = 0x23,
|
|
||||||
/* Extended (11-bit) RACH (see 3GPP TS 05.02, section 5.2.7) */
|
/* Extended (11-bit) RACH (see 3GPP TS 05.02, section 5.2.7) */
|
||||||
L1CTL_EXT_RACH_REQ = 0x24,
|
L1CTL_EXT_RACH_REQ = 0x24,
|
||||||
};
|
};
|
||||||
|
@ -75,23 +74,6 @@ enum neigh_mode {
|
||||||
NEIGH_MODE_SB,
|
NEIGH_MODE_SB,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum l1ctl_coding_scheme {
|
|
||||||
L1CTL_CS_NONE,
|
|
||||||
L1CTL_CS1,
|
|
||||||
L1CTL_CS2,
|
|
||||||
L1CTL_CS3,
|
|
||||||
L1CTL_CS4,
|
|
||||||
L1CTL_MCS1,
|
|
||||||
L1CTL_MCS2,
|
|
||||||
L1CTL_MCS3,
|
|
||||||
L1CTL_MCS4,
|
|
||||||
L1CTL_MCS5,
|
|
||||||
L1CTL_MCS6,
|
|
||||||
L1CTL_MCS7,
|
|
||||||
L1CTL_MCS8,
|
|
||||||
L1CTL_MCS9,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NOTE: struct size. We do add manual padding out of the believe
|
* NOTE: struct size. We do add manual padding out of the believe
|
||||||
* that it will avoid some unaligned access.
|
* that it will avoid some unaligned access.
|
||||||
|
@ -188,15 +170,6 @@ struct l1ctl_info_ul {
|
||||||
uint8_t payload[0];
|
uint8_t payload[0];
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct l1ctl_info_ul_tbf {
|
|
||||||
/* references l1ctl_tbf_cfg_req.tbf_nr */
|
|
||||||
uint8_t tbf_nr;
|
|
||||||
uint8_t coding_scheme;
|
|
||||||
uint8_t padding[2];
|
|
||||||
/* RLC/MAC block, size determines CS */
|
|
||||||
uint8_t payload[0];
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* msg for FBSB_REQ
|
* msg for FBSB_REQ
|
||||||
* the l1_info_ul header is in front
|
* the l1_info_ul header is in front
|
||||||
|
@ -372,15 +345,44 @@ struct l1ctl_traffic_req {
|
||||||
uint8_t data[0];
|
uint8_t data[0];
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct l1ctl_tbf_cfg_req {
|
/* payload of L1CTL_GPRS_UL_TBF_CFG_REQ */
|
||||||
/* future support for multiple concurrent TBFs. 0 for now */
|
struct l1ctl_gprs_ul_tbf_cfg_req {
|
||||||
uint8_t tbf_nr;
|
uint8_t tbf_ref;
|
||||||
/* is this about an UL TBF (1) or DL (0) */
|
uint8_t slotmask;
|
||||||
uint8_t is_uplink;
|
|
||||||
uint8_t padding[2];
|
uint8_t padding[2];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
/* one USF for each TN, or 255 for invalid/unused */
|
/* payload of L1CTL_GPRS_DL_TBF_CFG_REQ */
|
||||||
uint8_t usf[8];
|
struct l1ctl_gprs_dl_tbf_cfg_req {
|
||||||
|
uint8_t tbf_ref;
|
||||||
|
uint8_t slotmask;
|
||||||
|
uint8_t dl_tfi;
|
||||||
|
uint8_t padding[1];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* part of L1CTL_GPRS_{UL,DL}_BLOCK_{REQ,IND} */
|
||||||
|
struct l1ctl_gprs_block_hdr {
|
||||||
|
uint32_t fn;
|
||||||
|
uint8_t tn;
|
||||||
|
uint8_t padding[3];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* payload of L1CTL_GPRS_UL_BLOCK_REQ */
|
||||||
|
struct l1ctl_gprs_ul_block_req {
|
||||||
|
struct l1ctl_gprs_block_hdr hdr;
|
||||||
|
uint8_t data[0];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* payload of L1CTL_GPRS_DL_BLOCK_IND */
|
||||||
|
struct l1ctl_gprs_dl_block_ind {
|
||||||
|
struct l1ctl_gprs_block_hdr hdr;
|
||||||
|
struct {
|
||||||
|
uint16_t ber10k; /* Bit Error Rate */
|
||||||
|
int16_t ci_cb; /* C/I in centiBels */
|
||||||
|
uint8_t rx_lev; /* RxLev 0..63 */
|
||||||
|
} meas;
|
||||||
|
uint8_t usf;
|
||||||
|
uint8_t data[0];
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
#endif /* __L1CTL_PROTO_H__ */
|
#endif /* __L1CTL_PROTO_H__ */
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <osmocom/core/linuxlist.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct l1gprs_state;
|
||||||
|
struct msgb;
|
||||||
|
|
||||||
|
struct l1gprs_tbf {
|
||||||
|
/*! Item in l1gprs_state->tbf_list */
|
||||||
|
struct llist_head list;
|
||||||
|
/*! Uplink or Downlink */
|
||||||
|
bool uplink;
|
||||||
|
/*! TBF reference number (not index) */
|
||||||
|
uint8_t tbf_ref;
|
||||||
|
/*! PDCH timeslots used by this TBF */
|
||||||
|
uint8_t slotmask;
|
||||||
|
/*! (Downlink only) DL TFI (Temporary Flow Indentity): 0..31 */
|
||||||
|
uint8_t dl_tfi;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct l1gprs_pdch {
|
||||||
|
/*! Timeslot number */
|
||||||
|
uint8_t tn;
|
||||||
|
/*! Backpointer to l1gprs_state we belong to */
|
||||||
|
struct l1gprs_state *gprs;
|
||||||
|
/*! UL TBF count */
|
||||||
|
uint8_t ul_tbf_count;
|
||||||
|
/*! DL TBF count */
|
||||||
|
uint8_t dl_tbf_count;
|
||||||
|
/*! DL TFI mask */
|
||||||
|
uint32_t dl_tfi_mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct l1gprs_state {
|
||||||
|
/*! PDCH state for each timeslot */
|
||||||
|
struct l1gprs_pdch pdch[8];
|
||||||
|
/*! Uplink and Downlink TBFs */
|
||||||
|
struct llist_head tbf_list;
|
||||||
|
/*! Logging context (used as prefix for messages) */
|
||||||
|
char *log_prefix;
|
||||||
|
/*! Some private data for API user */
|
||||||
|
void *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
void l1gprs_logging_init(int logc);
|
||||||
|
struct l1gprs_state *l1gprs_state_alloc(void *ctx, const char *log_prefix, void *priv);
|
||||||
|
void l1gprs_state_free(struct l1gprs_state *gprs);
|
||||||
|
|
||||||
|
int l1gprs_handle_ul_tbf_cfg_req(struct l1gprs_state *gprs, const struct msgb *msg);
|
||||||
|
int l1gprs_handle_dl_tbf_cfg_req(struct l1gprs_state *gprs, const struct msgb *msg);
|
||||||
|
|
||||||
|
struct l1gprs_prim_block_hdr {
|
||||||
|
uint32_t fn;
|
||||||
|
uint8_t tn;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct l1gprs_prim_ul_block_req {
|
||||||
|
struct l1gprs_prim_block_hdr hdr;
|
||||||
|
size_t data_len;
|
||||||
|
const uint8_t *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct l1gprs_prim_dl_block_ind {
|
||||||
|
struct l1gprs_prim_block_hdr hdr;
|
||||||
|
struct {
|
||||||
|
uint16_t ber10k;
|
||||||
|
int16_t ci_cb;
|
||||||
|
uint8_t rx_lev;
|
||||||
|
} meas;
|
||||||
|
size_t data_len;
|
||||||
|
const uint8_t *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
int l1gprs_handle_ul_block_req(struct l1gprs_state *gprs,
|
||||||
|
struct l1gprs_prim_ul_block_req *req,
|
||||||
|
const struct msgb *msg);
|
||||||
|
struct msgb *l1gprs_handle_dl_block_ind(struct l1gprs_state *gprs,
|
||||||
|
const struct l1gprs_prim_dl_block_ind *ind);
|
|
@ -5,4 +5,5 @@ SUBDIRS = \
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
l1ctl_proto.h \
|
l1ctl_proto.h \
|
||||||
|
l1gprs.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../../../include/l1gprs.h
|
|
@ -10,6 +10,7 @@ enum {
|
||||||
DTRXD,
|
DTRXD,
|
||||||
DSCH,
|
DSCH,
|
||||||
DSCHD,
|
DSCHD,
|
||||||
|
DGPRS,
|
||||||
};
|
};
|
||||||
|
|
||||||
int trxcon_logging_init(void *tall_ctx, const char *category_mask);
|
int trxcon_logging_init(void *tall_ctx, const char *category_mask);
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
struct osmo_fsm_inst;
|
struct osmo_fsm_inst;
|
||||||
struct l1sched_state;
|
struct l1sched_state;
|
||||||
|
struct l1gprs_state;
|
||||||
struct msgb;
|
struct msgb;
|
||||||
|
|
||||||
struct trxcon_inst {
|
struct trxcon_inst {
|
||||||
|
@ -18,6 +19,9 @@ struct trxcon_inst {
|
||||||
|
|
||||||
/* The L1 scheduler */
|
/* The L1 scheduler */
|
||||||
struct l1sched_state *sched;
|
struct l1sched_state *sched;
|
||||||
|
/* GPRS state (MAC layer) */
|
||||||
|
struct l1gprs_state *gprs;
|
||||||
|
|
||||||
/* PHY interface (e.g. TRXC/TRXD) */
|
/* PHY interface (e.g. TRXC/TRXD) */
|
||||||
void *phyif;
|
void *phyif;
|
||||||
/* L2 interface (e.g. L1CTL) */
|
/* L2 interface (e.g. L1CTL) */
|
||||||
|
@ -46,6 +50,7 @@ enum trxcon_log_cat {
|
||||||
TRXCON_LOGC_L1D, /* L1CTL data */
|
TRXCON_LOGC_L1D, /* L1CTL data */
|
||||||
TRXCON_LOGC_SCHC, /* l1sched control */
|
TRXCON_LOGC_SCHC, /* l1sched control */
|
||||||
TRXCON_LOGC_SCHD, /* l1sched data */
|
TRXCON_LOGC_SCHD, /* l1sched data */
|
||||||
|
TRXCON_LOGC_GPRS, /* l1gprs logging */
|
||||||
};
|
};
|
||||||
|
|
||||||
void trxcon_set_log_cfg(const int *logc, unsigned int logc_num);
|
void trxcon_set_log_cfg(const int *logc, unsigned int logc_num);
|
||||||
|
|
|
@ -37,6 +37,9 @@ enum trxcon_fsm_events {
|
||||||
TRXCON_EV_TX_DATA_CNF,
|
TRXCON_EV_TX_DATA_CNF,
|
||||||
TRXCON_EV_RX_DATA_IND,
|
TRXCON_EV_RX_DATA_IND,
|
||||||
TRXCON_EV_CRYPTO_REQ,
|
TRXCON_EV_CRYPTO_REQ,
|
||||||
|
TRXCON_EV_GPRS_UL_TBF_CFG_REQ, /* param: L1CTL msgb */
|
||||||
|
TRXCON_EV_GPRS_DL_TBF_CFG_REQ, /* param: L1CTL msgb */
|
||||||
|
TRXCON_EV_GPRS_UL_BLOCK_REQ, /* param: L1CTL msgb */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* param of TRXCON_EV_FULL_POWER_SCAN_REQ */
|
/* param of TRXCON_EV_FULL_POWER_SCAN_REQ */
|
||||||
|
|
|
@ -29,6 +29,13 @@ libl1sched_la_SOURCES = \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
|
||||||
|
noinst_LTLIBRARIES += libl1gprs.la
|
||||||
|
|
||||||
|
libl1gprs_la_SOURCES = \
|
||||||
|
l1gprs.c \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
|
||||||
noinst_LTLIBRARIES += libtrxcon.la
|
noinst_LTLIBRARIES += libtrxcon.la
|
||||||
|
|
||||||
libtrxcon_la_SOURCES = \
|
libtrxcon_la_SOURCES = \
|
||||||
|
@ -51,6 +58,7 @@ trxcon_SOURCES = \
|
||||||
trxcon_LDADD = \
|
trxcon_LDADD = \
|
||||||
libtrxcon.la \
|
libtrxcon.la \
|
||||||
libl1sched.la \
|
libl1sched.la \
|
||||||
|
libl1gprs.la \
|
||||||
$(LIBOSMOCORE_LIBS) \
|
$(LIBOSMOCORE_LIBS) \
|
||||||
$(LIBOSMOCODING_LIBS) \
|
$(LIBOSMOCODING_LIBS) \
|
||||||
$(LIBOSMOGSM_LIBS) \
|
$(LIBOSMOGSM_LIBS) \
|
||||||
|
|
|
@ -785,6 +785,7 @@ static int l1ctl_rx_crypto_req(struct trxcon_inst *trxcon, struct msgb *msg)
|
||||||
int trxcon_l1ctl_receive(struct trxcon_inst *trxcon, struct msgb *msg)
|
int trxcon_l1ctl_receive(struct trxcon_inst *trxcon, struct msgb *msg)
|
||||||
{
|
{
|
||||||
const struct l1ctl_hdr *l1h;
|
const struct l1ctl_hdr *l1h;
|
||||||
|
int rc;
|
||||||
|
|
||||||
l1h = (const struct l1ctl_hdr *)msg->l1h;
|
l1h = (const struct l1ctl_hdr *)msg->l1h;
|
||||||
msg->l1h = (uint8_t *)l1h->data;
|
msg->l1h = (uint8_t *)l1h->data;
|
||||||
|
@ -818,11 +819,20 @@ int trxcon_l1ctl_receive(struct trxcon_inst *trxcon, struct msgb *msg)
|
||||||
return l1ctl_rx_tch_mode_req(trxcon, msg);
|
return l1ctl_rx_tch_mode_req(trxcon, msg);
|
||||||
case L1CTL_CRYPTO_REQ:
|
case L1CTL_CRYPTO_REQ:
|
||||||
return l1ctl_rx_crypto_req(trxcon, msg);
|
return l1ctl_rx_crypto_req(trxcon, msg);
|
||||||
|
case L1CTL_GPRS_UL_TBF_CFG_REQ:
|
||||||
|
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_GPRS_UL_TBF_CFG_REQ, msg);
|
||||||
|
msgb_free(msg);
|
||||||
|
return rc;
|
||||||
|
case L1CTL_GPRS_DL_TBF_CFG_REQ:
|
||||||
|
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_GPRS_DL_TBF_CFG_REQ, msg);
|
||||||
|
msgb_free(msg);
|
||||||
|
return rc;
|
||||||
|
case L1CTL_GPRS_UL_BLOCK_REQ:
|
||||||
|
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_GPRS_UL_BLOCK_REQ, msg);
|
||||||
|
msgb_free(msg);
|
||||||
|
return rc;
|
||||||
/* Not (yet) handled messages */
|
/* Not (yet) handled messages */
|
||||||
case L1CTL_NEIGH_PM_REQ:
|
case L1CTL_NEIGH_PM_REQ:
|
||||||
case L1CTL_DATA_TBF_REQ:
|
|
||||||
case L1CTL_TBF_CFG_REQ:
|
|
||||||
case L1CTL_DM_FREQ_REQ:
|
case L1CTL_DM_FREQ_REQ:
|
||||||
case L1CTL_SIM_REQ:
|
case L1CTL_SIM_REQ:
|
||||||
LOGPFSMSL(trxcon->fi, g_logc_l1c, LOGL_NOTICE,
|
LOGPFSMSL(trxcon->fi, g_logc_l1c, LOGL_NOTICE,
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
../../../shared/l1gprs.c
|
|
@ -67,6 +67,12 @@ static struct log_info_cat trxcon_log_info_cat[] = {
|
||||||
.color = "\033[1;36m",
|
.color = "\033[1;36m",
|
||||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||||
},
|
},
|
||||||
|
[DGPRS] = {
|
||||||
|
.name = "DGPRS",
|
||||||
|
.description = "L1 GPRS (MAC layer)",
|
||||||
|
.color = "\033[1;36m",
|
||||||
|
.enabled = 1, .loglevel = LOGL_DEBUG,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct log_info trxcon_log_info = {
|
static const struct log_info trxcon_log_info = {
|
||||||
|
@ -80,6 +86,7 @@ static const int trxcon_log_cfg[] = {
|
||||||
[TRXCON_LOGC_L1D] = DL1D,
|
[TRXCON_LOGC_L1D] = DL1D,
|
||||||
[TRXCON_LOGC_SCHC] = DSCH,
|
[TRXCON_LOGC_SCHC] = DSCH,
|
||||||
[TRXCON_LOGC_SCHD] = DSCHD,
|
[TRXCON_LOGC_SCHD] = DSCHD,
|
||||||
|
[TRXCON_LOGC_GPRS] = DGPRS,
|
||||||
};
|
};
|
||||||
|
|
||||||
int trxcon_logging_init(void *tall_ctx, const char *category_mask)
|
int trxcon_logging_init(void *tall_ctx, const char *category_mask)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* OsmocomBB <-> SDR connection bridge
|
* OsmocomBB <-> SDR connection bridge
|
||||||
* The trxcon state machine (see 3GPP TS 44.004, section 5.1)
|
* The trxcon state machine (see 3GPP TS 44.004, section 5.1)
|
||||||
*
|
*
|
||||||
* (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>
|
* Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
|
||||||
*
|
*
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
|
@ -29,12 +29,14 @@
|
||||||
#include <osmocom/core/talloc.h>
|
#include <osmocom/core/talloc.h>
|
||||||
#include <osmocom/core/logging.h>
|
#include <osmocom/core/logging.h>
|
||||||
#include <osmocom/gsm/gsm0502.h>
|
#include <osmocom/gsm/gsm0502.h>
|
||||||
|
#include <osmocom/gsm/protocol/gsm_08_58.h>
|
||||||
|
|
||||||
#include <osmocom/bb/trxcon/trxcon.h>
|
#include <osmocom/bb/trxcon/trxcon.h>
|
||||||
#include <osmocom/bb/trxcon/trxcon_fsm.h>
|
#include <osmocom/bb/trxcon/trxcon_fsm.h>
|
||||||
#include <osmocom/bb/trxcon/phyif.h>
|
#include <osmocom/bb/trxcon/phyif.h>
|
||||||
#include <osmocom/bb/trxcon/l1ctl.h>
|
#include <osmocom/bb/trxcon/l1ctl.h>
|
||||||
#include <osmocom/bb/l1sched/l1sched.h>
|
#include <osmocom/bb/l1sched/l1sched.h>
|
||||||
|
#include <osmocom/bb/l1gprs.h>
|
||||||
|
|
||||||
#define S(x) (1 << (x))
|
#define S(x) (1 << (x))
|
||||||
|
|
||||||
|
@ -505,6 +507,25 @@ static void trxcon_st_dedicated_action(struct osmo_fsm_inst *fi,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void trxcon_st_packet_data_onenter(struct osmo_fsm_inst *fi,
|
||||||
|
uint32_t prev_state)
|
||||||
|
{
|
||||||
|
struct trxcon_inst *trxcon = fi->priv;
|
||||||
|
|
||||||
|
OSMO_ASSERT(trxcon->gprs == NULL);
|
||||||
|
trxcon->gprs = l1gprs_state_alloc(trxcon, trxcon->log_prefix, trxcon);
|
||||||
|
OSMO_ASSERT(trxcon->gprs != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void trxcon_st_packet_data_onleave(struct osmo_fsm_inst *fi,
|
||||||
|
uint32_t next_state)
|
||||||
|
{
|
||||||
|
struct trxcon_inst *trxcon = fi->priv;
|
||||||
|
|
||||||
|
l1gprs_state_free(trxcon->gprs);
|
||||||
|
trxcon->gprs = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void trxcon_st_packet_data_action(struct osmo_fsm_inst *fi,
|
static void trxcon_st_packet_data_action(struct osmo_fsm_inst *fi,
|
||||||
uint32_t event, void *data)
|
uint32_t event, void *data)
|
||||||
{
|
{
|
||||||
|
@ -517,14 +538,58 @@ static void trxcon_st_packet_data_action(struct osmo_fsm_inst *fi,
|
||||||
case TRXCON_EV_TX_ACCESS_BURST_CNF:
|
case TRXCON_EV_TX_ACCESS_BURST_CNF:
|
||||||
l1ctl_tx_rach_conf(trxcon, (const struct trxcon_param_tx_access_burst_cnf *)data);
|
l1ctl_tx_rach_conf(trxcon, (const struct trxcon_param_tx_access_burst_cnf *)data);
|
||||||
break;
|
break;
|
||||||
|
case TRXCON_EV_GPRS_UL_TBF_CFG_REQ:
|
||||||
|
l1gprs_handle_ul_tbf_cfg_req(trxcon->gprs, (struct msgb *)data);
|
||||||
|
break;
|
||||||
|
case TRXCON_EV_GPRS_DL_TBF_CFG_REQ:
|
||||||
|
l1gprs_handle_dl_tbf_cfg_req(trxcon->gprs, (struct msgb *)data);
|
||||||
|
break;
|
||||||
|
case TRXCON_EV_GPRS_UL_BLOCK_REQ:
|
||||||
|
{
|
||||||
|
struct l1gprs_prim_ul_block_req block_req;
|
||||||
|
const struct msgb *msg = data;
|
||||||
|
struct l1sched_ts_prim *prim;
|
||||||
|
|
||||||
|
if (l1gprs_handle_ul_block_req(trxcon->gprs, &block_req, msg) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
prim = l1sched_prim_push(trxcon->sched, L1SCHED_PRIM_DATA,
|
||||||
|
RSL_CHAN_OSMO_PDCH | block_req.hdr.tn, 0x00,
|
||||||
|
block_req.data, block_req.data_len);
|
||||||
|
if (prim == NULL) {
|
||||||
|
LOGPFSML(fi, LOGL_ERROR, "Failed to enqueue a prim\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case TRXCON_EV_RX_DATA_IND:
|
case TRXCON_EV_RX_DATA_IND:
|
||||||
{
|
{
|
||||||
const struct trxcon_param_rx_data_ind *ind = data;
|
const struct trxcon_param_rx_data_ind *ind = data;
|
||||||
|
struct l1gprs_prim_dl_block_ind block_ind;
|
||||||
|
struct msgb *msg;
|
||||||
|
|
||||||
if (ind->link_id == 0x00)
|
block_ind = (struct l1gprs_prim_dl_block_ind) {
|
||||||
LOGPFSML(fi, LOGL_NOTICE, "Rx PDTCH/D message\n");
|
.hdr = {
|
||||||
|
.fn = ind->frame_nr,
|
||||||
|
.tn = ind->chan_nr & 0x07,
|
||||||
|
},
|
||||||
|
.meas = {
|
||||||
|
/* .ber10k is set below */
|
||||||
|
.ci_cb = 180, /* 18 dB */
|
||||||
|
.rx_lev = dbm2rxlev(ind->rssi),
|
||||||
|
},
|
||||||
|
.data_len = ind->data_len,
|
||||||
|
.data = ind->data,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ind->n_bits_total == 0)
|
||||||
|
block_ind.meas.ber10k = 10000;
|
||||||
else
|
else
|
||||||
LOGPFSML(fi, LOGL_NOTICE, "Rx PTCCH/D message\n");
|
block_ind.meas.ber10k = 10000 * ind->n_errors / ind->n_bits_total;
|
||||||
|
|
||||||
|
msg = l1gprs_handle_dl_block_ind(trxcon->gprs, &block_ind);
|
||||||
|
if (msg != NULL)
|
||||||
|
trxcon_l1ctl_send(trxcon, msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TRXCON_EV_DCH_EST_REQ:
|
case TRXCON_EV_DCH_EST_REQ:
|
||||||
|
@ -550,6 +615,9 @@ static void trxcon_fsm_pre_term_cb(struct osmo_fsm_inst *fi,
|
||||||
/* Shutdown the scheduler */
|
/* Shutdown the scheduler */
|
||||||
if (trxcon->sched != NULL)
|
if (trxcon->sched != NULL)
|
||||||
l1sched_free(trxcon->sched);
|
l1sched_free(trxcon->sched);
|
||||||
|
/* Clean up GPRS L1 state */
|
||||||
|
l1gprs_state_free(trxcon->gprs);
|
||||||
|
|
||||||
/* Close active connections */
|
/* Close active connections */
|
||||||
if (trxcon->l2if != NULL)
|
if (trxcon->l2if != NULL)
|
||||||
trxcon_l1ctl_close(trxcon);
|
trxcon_l1ctl_close(trxcon);
|
||||||
|
@ -625,7 +693,12 @@ static const struct osmo_fsm_state trxcon_fsm_states[] = {
|
||||||
| S(TRXCON_EV_DCH_EST_REQ)
|
| S(TRXCON_EV_DCH_EST_REQ)
|
||||||
| S(TRXCON_EV_TX_ACCESS_BURST_REQ)
|
| S(TRXCON_EV_TX_ACCESS_BURST_REQ)
|
||||||
| S(TRXCON_EV_TX_ACCESS_BURST_CNF)
|
| S(TRXCON_EV_TX_ACCESS_BURST_CNF)
|
||||||
|
| S(TRXCON_EV_GPRS_UL_TBF_CFG_REQ)
|
||||||
|
| S(TRXCON_EV_GPRS_DL_TBF_CFG_REQ)
|
||||||
|
| S(TRXCON_EV_GPRS_UL_BLOCK_REQ)
|
||||||
| S(TRXCON_EV_RX_DATA_IND),
|
| S(TRXCON_EV_RX_DATA_IND),
|
||||||
|
.onenter = &trxcon_st_packet_data_onenter,
|
||||||
|
.onleave = &trxcon_st_packet_data_onleave,
|
||||||
.action = &trxcon_st_packet_data_action,
|
.action = &trxcon_st_packet_data_action,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -651,6 +724,9 @@ static const struct value_string trxcon_fsm_event_names[] = {
|
||||||
OSMO_VALUE_STRING(TRXCON_EV_TX_DATA_CNF),
|
OSMO_VALUE_STRING(TRXCON_EV_TX_DATA_CNF),
|
||||||
OSMO_VALUE_STRING(TRXCON_EV_RX_DATA_IND),
|
OSMO_VALUE_STRING(TRXCON_EV_RX_DATA_IND),
|
||||||
OSMO_VALUE_STRING(TRXCON_EV_CRYPTO_REQ),
|
OSMO_VALUE_STRING(TRXCON_EV_CRYPTO_REQ),
|
||||||
|
OSMO_VALUE_STRING(TRXCON_EV_GPRS_UL_TBF_CFG_REQ),
|
||||||
|
OSMO_VALUE_STRING(TRXCON_EV_GPRS_DL_TBF_CFG_REQ),
|
||||||
|
OSMO_VALUE_STRING(TRXCON_EV_GPRS_UL_BLOCK_REQ),
|
||||||
{ 0, NULL }
|
{ 0, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <osmocom/bb/trxcon/trxcon_fsm.h>
|
#include <osmocom/bb/trxcon/trxcon_fsm.h>
|
||||||
#include <osmocom/bb/l1sched/l1sched.h>
|
#include <osmocom/bb/l1sched/l1sched.h>
|
||||||
#include <osmocom/bb/l1sched/logging.h>
|
#include <osmocom/bb/l1sched/logging.h>
|
||||||
|
#include <osmocom/bb/l1gprs.h>
|
||||||
|
|
||||||
extern int g_logc_l1c;
|
extern int g_logc_l1c;
|
||||||
extern int g_logc_l1d;
|
extern int g_logc_l1d;
|
||||||
|
@ -53,6 +54,9 @@ void trxcon_set_log_cfg(const int *logc, unsigned int logc_num)
|
||||||
case TRXCON_LOGC_SCHD:
|
case TRXCON_LOGC_SCHD:
|
||||||
schd = logc[i];
|
schd = logc[i];
|
||||||
break;
|
break;
|
||||||
|
case TRXCON_LOGC_GPRS:
|
||||||
|
l1gprs_logging_init(logc[i]);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,4 +4,5 @@ SUBDIRS = \
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
l1ctl_proto.h \
|
l1ctl_proto.h \
|
||||||
|
l1gprs.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../../../include/l1gprs.h
|
|
@ -57,6 +57,8 @@ void l1ctl_rx_tch_mode_req(struct l1_model_ms *ms, struct msgb *msg);
|
||||||
void l1ctl_rx_neigh_pm_req(struct l1_model_ms *ms, struct msgb *msg);
|
void l1ctl_rx_neigh_pm_req(struct l1_model_ms *ms, struct msgb *msg);
|
||||||
void l1ctl_rx_traffic_req(struct l1_model_ms *ms, struct msgb *msg);
|
void l1ctl_rx_traffic_req(struct l1_model_ms *ms, struct msgb *msg);
|
||||||
void l1ctl_rx_sim_req(struct l1_model_ms *ms, struct msgb *msg);
|
void l1ctl_rx_sim_req(struct l1_model_ms *ms, struct msgb *msg);
|
||||||
|
void l1ctl_rx_gprs_uldl_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg);
|
||||||
|
void l1ctl_rx_gprs_ul_block_req(struct l1_model_ms *ms, struct msgb *msg);
|
||||||
|
|
||||||
/* transmit routines */
|
/* transmit routines */
|
||||||
void l1ctl_tx_reset(struct l1_model_ms *ms, uint8_t msg_type, uint8_t reset_type);
|
void l1ctl_tx_reset(struct l1_model_ms *ms, uint8_t msg_type, uint8_t reset_type);
|
||||||
|
@ -75,6 +77,8 @@ void l1ctl_tx_pm_conf(struct l1_model_ms *ms, struct l1ctl_pm_req *pm_req);
|
||||||
void l1ctl_tx_fbsb_conf(struct l1_model_ms *ms, uint8_t res, uint16_t arfcn);
|
void l1ctl_tx_fbsb_conf(struct l1_model_ms *ms, uint8_t res, uint16_t arfcn);
|
||||||
void l1ctl_tx_ccch_mode_conf(struct l1_model_ms *ms, uint8_t ccch_mode);
|
void l1ctl_tx_ccch_mode_conf(struct l1_model_ms *ms, uint8_t ccch_mode);
|
||||||
void l1ctl_tx_tch_mode_conf(struct l1_model_ms *ms, uint8_t tch_mode, uint8_t audio_mode);
|
void l1ctl_tx_tch_mode_conf(struct l1_model_ms *ms, uint8_t tch_mode, uint8_t audio_mode);
|
||||||
|
void l1ctl_tx_gprs_dl_block_ind(struct l1_model_ms *ms, const struct msgb *msg,
|
||||||
|
uint32_t fn, uint8_t tn, uint8_t rxlev);
|
||||||
|
|
||||||
/* scheduler functions */
|
/* scheduler functions */
|
||||||
uint32_t sched_fn_ul(struct gsm_time cur_time, uint8_t chan_nr,
|
uint32_t sched_fn_ul(struct gsm_time cur_time, uint8_t chan_nr,
|
||||||
|
|
|
@ -7,6 +7,7 @@ enum virtphy_log_cat {
|
||||||
DL1C,
|
DL1C,
|
||||||
DL1P,
|
DL1P,
|
||||||
DVIRPHY,
|
DVIRPHY,
|
||||||
|
DGPRS,
|
||||||
DMAIN
|
DMAIN
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ enum ms_state {
|
||||||
MS_STATE_IDLE_SYNCING,
|
MS_STATE_IDLE_SYNCING,
|
||||||
MS_STATE_IDLE_CAMPING,
|
MS_STATE_IDLE_CAMPING,
|
||||||
MS_STATE_DEDICATED,
|
MS_STATE_DEDICATED,
|
||||||
MS_STATE_TBF
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,15 +76,6 @@ struct l1_state_ms {
|
||||||
uint8_t tsc; // training sequence code (unused in virtual um)
|
uint8_t tsc; // training sequence code (unused in virtual um)
|
||||||
uint8_t h; // hopping enabled flag (unused in virtual um)
|
uint8_t h; // hopping enabled flag (unused in virtual um)
|
||||||
} dedicated;
|
} dedicated;
|
||||||
struct {
|
|
||||||
struct {
|
|
||||||
uint8_t usf[8];
|
|
||||||
struct llist_head tx_queue;
|
|
||||||
} ul;
|
|
||||||
struct {
|
|
||||||
uint8_t tfi[8];
|
|
||||||
} dl;
|
|
||||||
} tbf;
|
|
||||||
|
|
||||||
/* fbsb state */
|
/* fbsb state */
|
||||||
struct {
|
struct {
|
||||||
|
@ -116,6 +106,8 @@ struct l1_model_ms {
|
||||||
struct l1ctl_sock_client *lsc;
|
struct l1ctl_sock_client *lsc;
|
||||||
/* pointer to the (shared) GSMTAP/VirtUM socket to talk to BTS(s) */
|
/* pointer to the (shared) GSMTAP/VirtUM socket to talk to BTS(s) */
|
||||||
struct virt_um_inst *vui;
|
struct virt_um_inst *vui;
|
||||||
|
/* GPRS state (MAC layer) */
|
||||||
|
struct l1gprs_state *gprs;
|
||||||
/* actual per-MS state */
|
/* actual per-MS state */
|
||||||
struct l1_state_ms state;
|
struct l1_state_ms state;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@ bin_PROGRAMS = virtphy
|
||||||
|
|
||||||
virtphy_SOURCES = \
|
virtphy_SOURCES = \
|
||||||
virtphy.c \
|
virtphy.c \
|
||||||
|
l1gprs.c \
|
||||||
logging.c \
|
logging.c \
|
||||||
gsmtapl1_if.c \
|
gsmtapl1_if.c \
|
||||||
l1ctl_sock.c \
|
l1ctl_sock.c \
|
||||||
|
@ -13,6 +14,7 @@ virtphy_SOURCES = \
|
||||||
virt_prim_fbsb.c \
|
virt_prim_fbsb.c \
|
||||||
virt_prim_rach.c \
|
virt_prim_rach.c \
|
||||||
virt_prim_data.c \
|
virt_prim_data.c \
|
||||||
|
virt_prim_pdch.c \
|
||||||
virt_prim_traffic.c \
|
virt_prim_traffic.c \
|
||||||
virt_l1_sched_simple.c \
|
virt_l1_sched_simple.c \
|
||||||
virt_l1_model.c \
|
virt_l1_model.c \
|
||||||
|
|
|
@ -94,23 +94,16 @@ void gsmtapl1_tx_to_virt_um_inst(struct l1_model_ms *ms, uint32_t fn, uint8_t tn
|
||||||
uint8_t timeslot; /* tdma timeslot to send in (0-7) */
|
uint8_t timeslot; /* tdma timeslot to send in (0-7) */
|
||||||
uint8_t gsmtap_chan; /* the gsmtap channel */
|
uint8_t gsmtap_chan; /* the gsmtap channel */
|
||||||
|
|
||||||
switch (ms->state.state) {
|
if (ms->state.state == MS_STATE_DEDICATED)
|
||||||
case MS_STATE_DEDICATED:
|
|
||||||
case MS_STATE_TBF:
|
|
||||||
arfcn = ms->state.dedicated.band_arfcn;
|
arfcn = ms->state.dedicated.band_arfcn;
|
||||||
break;
|
else
|
||||||
default:
|
|
||||||
arfcn = ms->state.serving_cell.arfcn;
|
arfcn = ms->state.serving_cell.arfcn;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (l1h->msg_type) {
|
switch (l1h->msg_type) {
|
||||||
case L1CTL_DATA_TBF_REQ:
|
case L1CTL_GPRS_UL_BLOCK_REQ:
|
||||||
ul = NULL;
|
gsmtap_chan = GSMTAP_CHANNEL_PDCH;
|
||||||
rsl_chantype = RSL_CHAN_OSMO_PDCH;
|
|
||||||
timeslot = tn;
|
timeslot = tn;
|
||||||
subslot = 0;
|
subslot = 0;
|
||||||
gsmtap_chan = chantype_rsl2gsmtap2(rsl_chantype, 0, false);
|
|
||||||
break;
|
break;
|
||||||
case L1CTL_TRAFFIC_REQ:
|
case L1CTL_TRAFFIC_REQ:
|
||||||
ul = (struct l1ctl_info_ul *)l1h->data;
|
ul = (struct l1ctl_info_ul *)l1h->data;
|
||||||
|
@ -165,77 +158,6 @@ extern void prim_fbsb_sync(struct l1_model_ms *ms, struct msgb *msg);
|
||||||
*/
|
*/
|
||||||
extern uint16_t prim_pm_set_sig_strength(struct l1_model_ms *ms, uint16_t arfcn, int16_t sig_lev);
|
extern uint16_t prim_pm_set_sig_strength(struct l1_model_ms *ms, uint16_t arfcn, int16_t sig_lev);
|
||||||
|
|
||||||
/* determine if a received Downlink RLC/MAC block matches the current MS configuration */
|
|
||||||
static bool gprs_dl_block_matches_ms(struct l1_model_ms *ms, struct msgb *msg, uint8_t timeslot)
|
|
||||||
{
|
|
||||||
uint8_t payload_type;
|
|
||||||
uint8_t tfi;
|
|
||||||
|
|
||||||
if (msgb_length(msg) < 1)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* FIXME: Ensure this will also work for EGPRS! */
|
|
||||||
payload_type = msg->data[0] >> 6;
|
|
||||||
switch (payload_type) {
|
|
||||||
case 0: /* RLC Data Block */
|
|
||||||
/* forward all RLD Data Blocks destined for TFI of MS */
|
|
||||||
tfi = (msg->data[1] >> 1) & 0x1f;
|
|
||||||
if (ms->state.state == MS_STATE_TBF && ms->state.tbf.dl.tfi[timeslot] == tfi)
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
case 1: /* RLC/MAC Control without optional octets */
|
|
||||||
/* forward all RLC/MAC control blocks without optional octets, i.e. not addressed
|
|
||||||
* to a specific TFI */
|
|
||||||
return true;
|
|
||||||
case 2: /* RLC/MAC with optional control octets */
|
|
||||||
/* forward all RLD Control Blocks destined for TFI of MS */
|
|
||||||
tfi = (msg->data[2] >> 1) & 0x1f;
|
|
||||||
if (ms->state.state == MS_STATE_TBF && ms->state.tbf.dl.tfi[timeslot] == tfi)
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* determine if given USF at given timeslot is relevant to given MS or not */
|
|
||||||
static bool usf_matches_ms(struct l1_model_ms *ms, uint8_t usf, uint8_t timeslot)
|
|
||||||
{
|
|
||||||
if (ms->state.state == MS_STATE_TBF && ms->state.tbf.ul.usf[timeslot] == usf)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* extract USF from (E)GPRS RLC/MAC block */
|
|
||||||
static uint8_t get_usf_from_block(struct msgb *msg)
|
|
||||||
{
|
|
||||||
/* FIXME: Ensure this will also work for EGPRS! */
|
|
||||||
return msg->data[0] & 0x7;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* MS is authorized to transmit a block in uplink for given USF on timeslot+arfcn at FN */
|
|
||||||
static void ms_ul_tbf_may_transmit(struct l1_model_ms *ms, uint16_t arfcn, uint8_t timeslot,
|
|
||||||
uint32_t fn, uint8_t usf)
|
|
||||||
{
|
|
||||||
struct msgb *msg;
|
|
||||||
|
|
||||||
/* If USF is not for us, bail out */
|
|
||||||
if (!usf_matches_ms(ms, usf, timeslot))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* attempt to de-queue pending msgb for this UL TBF and transmit it */
|
|
||||||
msg = msgb_dequeue(&ms->state.tbf.ul.tx_queue);
|
|
||||||
if (!msg) {
|
|
||||||
printf("FN=%u, TN=%u, USF=%u: empty tx_queue, not transmitting\n", fn, timeslot, usf);
|
|
||||||
/* FIXME: send some dummy control frame? */
|
|
||||||
} else {
|
|
||||||
printf("FN=%u, TN=%u, USF=%u: transmitting queued msg\n", fn, timeslot, usf);
|
|
||||||
gsmtapl1_tx_to_virt_um_inst(ms, fn, timeslot, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg, uint32_t fn,
|
static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg, uint32_t fn,
|
||||||
uint16_t arfcn, uint8_t timeslot, uint8_t subslot,
|
uint16_t arfcn, uint8_t timeslot, uint8_t subslot,
|
||||||
uint8_t gsmtap_chantype, uint8_t chan_nr, uint8_t link_id,
|
uint8_t gsmtap_chantype, uint8_t chan_nr, uint8_t link_id,
|
||||||
|
@ -243,7 +165,6 @@ static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg,
|
||||||
{
|
{
|
||||||
struct l1_model_ms *ms = lsc->priv;
|
struct l1_model_ms *ms = lsc->priv;
|
||||||
uint8_t rxlev = dbm2rxlev(prim_pm_set_sig_strength(ms, arfcn & GSMTAP_ARFCN_MASK, MAX_SIG_LEV_DBM));
|
uint8_t rxlev = dbm2rxlev(prim_pm_set_sig_strength(ms, arfcn & GSMTAP_ARFCN_MASK, MAX_SIG_LEV_DBM));
|
||||||
uint8_t usf;
|
|
||||||
|
|
||||||
gsm_fn2gsmtime(&ms->state.downlink_time, fn);
|
gsm_fn2gsmtime(&ms->state.downlink_time, fn);
|
||||||
|
|
||||||
|
@ -310,16 +231,13 @@ static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg,
|
||||||
case GSMTAP_CHANNEL_RACH:
|
case GSMTAP_CHANNEL_RACH:
|
||||||
LOGPMS(DVIRPHY, LOGL_NOTICE, ms, "Ignoring unexpected RACH in downlink ?!?\n");
|
LOGPMS(DVIRPHY, LOGL_NOTICE, ms, "Ignoring unexpected RACH in downlink ?!?\n");
|
||||||
break;
|
break;
|
||||||
|
case GSMTAP_CHANNEL_PTCCH:
|
||||||
case GSMTAP_CHANNEL_PACCH:
|
case GSMTAP_CHANNEL_PACCH:
|
||||||
case GSMTAP_CHANNEL_PDCH:
|
case GSMTAP_CHANNEL_PDCH:
|
||||||
if (gprs_dl_block_matches_ms(ms, msg, timeslot))
|
l1ctl_tx_gprs_dl_block_ind(ms, msg, fn, timeslot, rxlev);
|
||||||
l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, snr_db, rxlev, 0, 0);
|
|
||||||
usf = get_usf_from_block(msg);
|
|
||||||
ms_ul_tbf_may_transmit(ms, arfcn, timeslot, fn, usf);
|
|
||||||
break;
|
break;
|
||||||
case GSMTAP_CHANNEL_SDCCH:
|
case GSMTAP_CHANNEL_SDCCH:
|
||||||
case GSMTAP_CHANNEL_CCCH:
|
case GSMTAP_CHANNEL_CCCH:
|
||||||
case GSMTAP_CHANNEL_PTCCH:
|
|
||||||
LOGPMS(DVIRPHY, LOGL_NOTICE, ms, "Ignoring unsupported channel type %s\n",
|
LOGPMS(DVIRPHY, LOGL_NOTICE, ms, "Ignoring unsupported channel type %s\n",
|
||||||
get_value_string(gsmtap_gsm_channel_names, gsmtap_chantype));
|
get_value_string(gsmtap_gsm_channel_names, gsmtap_chantype));
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
#include <osmocom/gsm/protocol/gsm_08_58.h>
|
#include <osmocom/gsm/protocol/gsm_08_58.h>
|
||||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||||
#include <osmocom/gsm/rsl.h>
|
#include <osmocom/gsm/rsl.h>
|
||||||
#include <osmocom/gprs/gprs_rlc.h>
|
|
||||||
|
|
||||||
#include <osmocom/bb/virtphy/virtual_um.h>
|
#include <osmocom/bb/virtphy/virtual_um.h>
|
||||||
#include <osmocom/bb/virtphy/l1ctl_sock.h>
|
#include <osmocom/bb/virtphy/l1ctl_sock.h>
|
||||||
|
@ -40,9 +39,7 @@
|
||||||
#include <osmocom/bb/virtphy/logging.h>
|
#include <osmocom/bb/virtphy/logging.h>
|
||||||
#include <osmocom/bb/virtphy/virt_l1_sched.h>
|
#include <osmocom/bb/virtphy/virt_l1_sched.h>
|
||||||
#include <osmocom/bb/l1ctl_proto.h>
|
#include <osmocom/bb/l1ctl_proto.h>
|
||||||
|
#include <osmocom/bb/l1gprs.h>
|
||||||
static void l1ctl_rx_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg);
|
|
||||||
static void l1ctl_rx_data_tbf_req(struct l1_model_ms *ms, struct msgb *msg);
|
|
||||||
|
|
||||||
static void l1_model_tch_mode_set(struct l1_model_ms *ms, uint8_t tch_mode)
|
static void l1_model_tch_mode_set(struct l1_model_ms *ms, uint8_t tch_mode)
|
||||||
{
|
{
|
||||||
|
@ -60,7 +57,6 @@ static void l1_model_tch_mode_set(struct l1_model_ms *ms, uint8_t tch_mode)
|
||||||
void l1ctl_sap_init(struct l1_model_ms *model)
|
void l1ctl_sap_init(struct l1_model_ms *model)
|
||||||
{
|
{
|
||||||
INIT_LLIST_HEAD(&model->state.sched.mframe_items);
|
INIT_LLIST_HEAD(&model->state.sched.mframe_items);
|
||||||
INIT_LLIST_HEAD(&model->state.tbf.ul.tx_queue);
|
|
||||||
|
|
||||||
prim_pm_init(model);
|
prim_pm_init(model);
|
||||||
}
|
}
|
||||||
|
@ -163,8 +159,8 @@ static bool is_l1ctl_control(uint8_t msg_type)
|
||||||
switch (msg_type) {
|
switch (msg_type) {
|
||||||
case L1CTL_DATA_REQ:
|
case L1CTL_DATA_REQ:
|
||||||
case L1CTL_DATA_CONF:
|
case L1CTL_DATA_CONF:
|
||||||
case L1CTL_DATA_TBF_REQ:
|
case L1CTL_GPRS_UL_BLOCK_REQ:
|
||||||
case L1CTL_DATA_TBF_CONF:
|
case L1CTL_GPRS_DL_BLOCK_IND:
|
||||||
case L1CTL_TRAFFIC_REQ:
|
case L1CTL_TRAFFIC_REQ:
|
||||||
case L1CTL_TRAFFIC_CONF:
|
case L1CTL_TRAFFIC_CONF:
|
||||||
case L1CTL_TRAFFIC_IND:
|
case L1CTL_TRAFFIC_IND:
|
||||||
|
@ -247,11 +243,12 @@ void l1ctl_sap_handler(struct l1_model_ms *ms, struct msgb *msg)
|
||||||
case L1CTL_SIM_REQ:
|
case L1CTL_SIM_REQ:
|
||||||
l1ctl_rx_sim_req(ms, msg);
|
l1ctl_rx_sim_req(ms, msg);
|
||||||
break;
|
break;
|
||||||
case L1CTL_TBF_CFG_REQ:
|
case L1CTL_GPRS_UL_TBF_CFG_REQ:
|
||||||
l1ctl_rx_tbf_cfg_req(ms, msg);
|
case L1CTL_GPRS_DL_TBF_CFG_REQ:
|
||||||
|
l1ctl_rx_gprs_uldl_tbf_cfg_req(ms, msg);
|
||||||
break;
|
break;
|
||||||
case L1CTL_DATA_TBF_REQ:
|
case L1CTL_GPRS_UL_BLOCK_REQ:
|
||||||
l1ctl_rx_data_tbf_req(ms, msg);
|
l1ctl_rx_gprs_ul_block_req(ms, msg);
|
||||||
goto exit_nofree;
|
goto exit_nofree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,6 +294,12 @@ void l1ctl_rx_dm_est_req(struct l1_model_ms *ms, struct msgb *msg)
|
||||||
ms->state.dedicated.subslot = subslot;
|
ms->state.dedicated.subslot = subslot;
|
||||||
ms->state.state = MS_STATE_DEDICATED;
|
ms->state.state = MS_STATE_DEDICATED;
|
||||||
|
|
||||||
|
if (rsl_chantype == RSL_CHAN_OSMO_PDCH) {
|
||||||
|
OSMO_ASSERT(ms->gprs == NULL);
|
||||||
|
ms->gprs = l1gprs_state_alloc(ms, NULL, ms);
|
||||||
|
OSMO_ASSERT(ms->gprs != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* TCH config */
|
/* TCH config */
|
||||||
if (rsl_chantype == RSL_CHAN_Bm_ACCHs || rsl_chantype == RSL_CHAN_Lm_ACCHs) {
|
if (rsl_chantype == RSL_CHAN_Bm_ACCHs || rsl_chantype == RSL_CHAN_Lm_ACCHs) {
|
||||||
ms->state.tch_mode = est_req->tch_mode;
|
ms->state.tch_mode = est_req->tch_mode;
|
||||||
|
@ -381,6 +384,9 @@ void l1ctl_rx_dm_rel_req(struct l1_model_ms *ms, struct msgb *msg)
|
||||||
ms->state.tch_mode = GSM48_CMODE_SIGN;
|
ms->state.tch_mode = GSM48_CMODE_SIGN;
|
||||||
ms->state.state = MS_STATE_IDLE_CAMPING;
|
ms->state.state = MS_STATE_IDLE_CAMPING;
|
||||||
|
|
||||||
|
l1gprs_state_free(ms->gprs);
|
||||||
|
ms->gprs = NULL;
|
||||||
|
|
||||||
/* TODO: disable ciphering */
|
/* TODO: disable ciphering */
|
||||||
/* TODO: disable audio recording / playing */
|
/* TODO: disable audio recording / playing */
|
||||||
}
|
}
|
||||||
|
@ -429,6 +435,8 @@ void l1ctl_rx_reset_req(struct l1_model_ms *ms, struct msgb *msg)
|
||||||
DEBUGPMS(DL1C, ms, "Rx L1CTL_RESET_REQ (type=FULL)\n");
|
DEBUGPMS(DL1C, ms, "Rx L1CTL_RESET_REQ (type=FULL)\n");
|
||||||
ms->state.state = MS_STATE_IDLE_SEARCHING;
|
ms->state.state = MS_STATE_IDLE_SEARCHING;
|
||||||
virt_l1_sched_stop(ms);
|
virt_l1_sched_stop(ms);
|
||||||
|
l1gprs_state_free(ms->gprs);
|
||||||
|
ms->gprs = NULL;
|
||||||
l1ctl_tx_reset(ms, L1CTL_RESET_CONF, reset_req->type);
|
l1ctl_tx_reset(ms, L1CTL_RESET_CONF, reset_req->type);
|
||||||
break;
|
break;
|
||||||
case L1CTL_RES_T_SCHED:
|
case L1CTL_RES_T_SCHED:
|
||||||
|
@ -547,145 +555,12 @@ void l1ctl_rx_sim_req(struct l1_model_ms *ms, struct msgb *msg)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void l1ctl_tx_tbf_cfg_conf(struct l1_model_ms *ms, const struct l1ctl_tbf_cfg_req *in);
|
|
||||||
|
|
||||||
static void l1ctl_rx_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg)
|
|
||||||
{
|
|
||||||
struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
|
|
||||||
struct l1ctl_tbf_cfg_req *cfg_req = (struct l1ctl_tbf_cfg_req *) l1h->data;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
LOGPMS(DL1C, LOGL_INFO, ms, "Rx L1CTL_TBF_CFG_REQ (tbf_id=%u, dir=%s, "
|
|
||||||
"usf=[%d,%d,%d,%d,%d,%d,%d,%d]\n", cfg_req->tbf_nr,
|
|
||||||
cfg_req->is_uplink ? "UL" : "DL", cfg_req->usf[0], cfg_req->usf[1],
|
|
||||||
cfg_req->usf[2], cfg_req->usf[3], cfg_req->usf[4], cfg_req->usf[5],
|
|
||||||
cfg_req->usf[6], cfg_req->usf[7]);
|
|
||||||
|
|
||||||
if (cfg_req->tbf_nr != 0) {
|
|
||||||
LOGPMS(DL1C, LOGL_ERROR, ms, "TBF_NR != 0 not supported yet!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ms->state.state == MS_STATE_DEDICATED)
|
|
||||||
LOGPMS(DL1C, LOGL_NOTICE, ms, "Hard termination of DEDICATED mode, fix L23!\n");
|
|
||||||
|
|
||||||
if (cfg_req->is_uplink) {
|
|
||||||
for (i = 0; i < 8; i++)
|
|
||||||
ms->state.tbf.ul.usf[i] = cfg_req->usf[i];
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < 8; i++)
|
|
||||||
ms->state.tbf.dl.tfi[i] = cfg_req->usf[i];
|
|
||||||
}
|
|
||||||
ms->state.state = MS_STATE_TBF;
|
|
||||||
|
|
||||||
l1ctl_tx_tbf_cfg_conf(ms, cfg_req);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
static const enum osmo_gprs_cs osmo_cs_by_l1ctl[] = {
|
|
||||||
[L1CTL_CS_NONE] = OSMO_GPRS_CS_NONE,
|
|
||||||
[L1CTL_CS1] = OSMO_GPRS_CS1,
|
|
||||||
[L1CTL_CS2] = OSMO_GPRS_CS2,
|
|
||||||
[L1CTL_CS3] = OSMO_GPRS_CS3,
|
|
||||||
[L1CTL_CS4] = OSMO_GPRS_CS4,
|
|
||||||
[L1CTL_MCS1] = OSMO_GPRS_MCS1,
|
|
||||||
[L1CTL_MCS2] = OSMO_GPRS_MCS2,
|
|
||||||
[L1CTL_MCS3] = OSMO_GPRS_MCS3,
|
|
||||||
[L1CTL_MCS4] = OSMO_GPRS_MCS4,
|
|
||||||
[L1CTL_MCS5] = OSMO_GPRS_MCS5,
|
|
||||||
[L1CTL_MCS6] = OSMO_GPRS_MCS6,
|
|
||||||
[L1CTL_MCS7] = OSMO_GPRS_MCS7,
|
|
||||||
[L1CTL_MCS8] = OSMO_GPRS_MCS8,
|
|
||||||
[L1CTL_MCS9] = OSMO_GPRS_MCS9,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int get_osmo_cs_by_l1ctl(enum l1ctl_coding_scheme l1)
|
|
||||||
{
|
|
||||||
if (l1 >= ARRAY_SIZE(osmo_cs_by_l1ctl))
|
|
||||||
return -1;
|
|
||||||
return osmo_cs_by_l1ctl[l1];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const enum l1ctl_coding_scheme l1ctl_cs_by_osmo[] = {
|
|
||||||
[OSMO_GPRS_CS_NONE] = L1CTL_CS_NONE,
|
|
||||||
[OSMO_GPRS_CS1] = L1CTL_CS1,
|
|
||||||
[OSMO_GPRS_CS2] = L1CTL_CS2,
|
|
||||||
[OSMO_GPRS_CS3] = L1CTL_CS3,
|
|
||||||
[OSMO_GPRS_CS4] = L1CTL_CS4,
|
|
||||||
[OSMO_GPRS_MCS1] = L1CTL_MCS1,
|
|
||||||
[OSMO_GPRS_MCS2] = L1CTL_MCS2,
|
|
||||||
[OSMO_GPRS_MCS3] = L1CTL_MCS3,
|
|
||||||
[OSMO_GPRS_MCS4] = L1CTL_MCS4,
|
|
||||||
[OSMO_GPRS_MCS5] = L1CTL_MCS5,
|
|
||||||
[OSMO_GPRS_MCS6] = L1CTL_MCS6,
|
|
||||||
[OSMO_GPRS_MCS7] = L1CTL_MCS7,
|
|
||||||
[OSMO_GPRS_MCS8] = L1CTL_MCS8,
|
|
||||||
[OSMO_GPRS_MCS9] = L1CTL_MCS9,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int get_l1ctl_cs_by_osmo(enum osmo_gprs_cs in)
|
|
||||||
{
|
|
||||||
if (in >= ARRAY_SIZE(l1ctl_cs_by_osmo))
|
|
||||||
return -1;
|
|
||||||
return l1ctl_cs_by_osmo[in];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void l1ctl_rx_data_tbf_req(struct l1_model_ms *ms, struct msgb *msg)
|
|
||||||
{
|
|
||||||
struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
|
|
||||||
struct l1ctl_info_ul_tbf *udt = (struct l1ctl_info_ul_tbf *) l1h->data;
|
|
||||||
enum osmo_gprs_cs osmo_cs;
|
|
||||||
int block_size;
|
|
||||||
|
|
||||||
msg->l2h = udt->payload;
|
|
||||||
|
|
||||||
LOGPMS(DL1P, LOGL_ERROR, ms, "Rx L1CTL_DATA_TBF_REQ (tbf_id=%d, data=%s)\n",
|
|
||||||
udt->tbf_nr, osmo_hexdump(msg->l2h, msgb_l2len(msg)));
|
|
||||||
if (udt->tbf_nr != 0) {
|
|
||||||
LOGPMS(DL1C, LOGL_ERROR, ms, "TBF_NR != 0 not supported yet!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ms->state.state != MS_STATE_TBF) {
|
|
||||||
LOGPMS(DL1P, LOGL_ERROR, ms, "DATA_TBF_REQ in state != TBF\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
osmo_cs = get_l1ctl_cs_by_osmo(udt->coding_scheme);
|
|
||||||
if (osmo_cs < 0) {
|
|
||||||
LOGPMS(DL1P, LOGL_ERROR, ms, "DATA_RBF_REQ with invalid CS\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
block_size = osmo_gprs_ul_block_size_bytes(osmo_cs);
|
|
||||||
|
|
||||||
if (msgb_l2len(msg) < block_size) {
|
|
||||||
int pad_len = block_size - msgb_l2len(msg);
|
|
||||||
uint8_t *pad = msgb_put(msg, pad_len);
|
|
||||||
memset(pad, GSM_MACBLOCK_PADDING, pad_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
msgb_enqueue(&ms->state.tbf.ul.tx_queue, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
/***************************************************************
|
||||||
* L1CTL TX ROUTINES *******************************************
|
* L1CTL TX ROUTINES *******************************************
|
||||||
* For more routines check the respective handler classes ******
|
* For more routines check the respective handler classes ******
|
||||||
* like virt_prim_rach.c ***************************************
|
* like virt_prim_rach.c ***************************************
|
||||||
***************************************************************/
|
***************************************************************/
|
||||||
|
|
||||||
static void l1ctl_tx_tbf_cfg_conf(struct l1_model_ms *ms, const struct l1ctl_tbf_cfg_req *in)
|
|
||||||
{
|
|
||||||
struct msgb *msg = l1ctl_msgb_alloc(L1CTL_TBF_CFG_CONF);
|
|
||||||
struct l1ctl_tbf_cfg_req *out;
|
|
||||||
|
|
||||||
/* copy over the data from the request */
|
|
||||||
out = (struct l1ctl_tbf_cfg_req *) msgb_put(msg, sizeof(*out));
|
|
||||||
*out = *in;
|
|
||||||
|
|
||||||
l1ctl_sap_tx_to_l23_inst(ms, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Transmit L1CTL_RESET_IND or L1CTL_RESET_CONF to layer 23.
|
* @brief Transmit L1CTL_RESET_IND or L1CTL_RESET_CONF to layer 23.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
../../../shared/l1gprs.c
|
|
@ -54,10 +54,10 @@ static const char* l1ctlPrimNames[] = {
|
||||||
"L1CTL_TRAFFIC_CONF",
|
"L1CTL_TRAFFIC_CONF",
|
||||||
"L1CTL_TRAFFIC_IND",
|
"L1CTL_TRAFFIC_IND",
|
||||||
"L1CTL_BURST_IND",
|
"L1CTL_BURST_IND",
|
||||||
"L1CTL_TBF_CFG_REQ",
|
"L1CTL_GPRS_UL_TBF_CFG_REQ",
|
||||||
"L1CTL_TBF_CFG_CONF",
|
"L1CTL_GPRS_DL_TBF_CFG_REQ",
|
||||||
"L1CTL_DATA_TBF_REQ",
|
"L1CTL_GPRS_UL_BLOCK_REQ",
|
||||||
"L1CTL_DATA_TBF_CONF"
|
"L1CTL_GPRS_DL_BLOCK_IND",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct log_info_cat default_categories[] = {
|
static const struct log_info_cat default_categories[] = {
|
||||||
|
@ -82,6 +82,13 @@ static const struct log_info_cat default_categories[] = {
|
||||||
.enabled = 1,
|
.enabled = 1,
|
||||||
.loglevel = LOGL_NOTICE,
|
.loglevel = LOGL_NOTICE,
|
||||||
},
|
},
|
||||||
|
[DGPRS] = {
|
||||||
|
.name = "DGPRS",
|
||||||
|
.description = "L1 GPRS (MAC leyer)",
|
||||||
|
.color = "\033[1;31m",
|
||||||
|
.enabled = 1,
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
},
|
||||||
[DMAIN] = {
|
[DMAIN] = {
|
||||||
.name = "DMAIN",
|
.name = "DMAIN",
|
||||||
.description = "Main Program / Data Structures",
|
.description = "Main Program / Data Structures",
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/msgb.h>
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
#include <osmocom/gsm/gsm_utils.h>
|
||||||
|
#include <osmocom/gsm/protocol/gsm_08_58.h>
|
||||||
|
|
||||||
|
#include <osmocom/bb/virtphy/l1ctl_sap.h>
|
||||||
|
#include <osmocom/bb/virtphy/virt_l1_sched.h>
|
||||||
|
#include <osmocom/bb/virtphy/gsmtapl1_if.h>
|
||||||
|
#include <osmocom/bb/virtphy/logging.h>
|
||||||
|
|
||||||
|
#include <osmocom/bb/l1ctl_proto.h>
|
||||||
|
#include <osmocom/bb/l1gprs.h>
|
||||||
|
|
||||||
|
void l1ctl_rx_gprs_uldl_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg)
|
||||||
|
{
|
||||||
|
const struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data;
|
||||||
|
|
||||||
|
if (OSMO_UNLIKELY(ms->gprs == NULL)) {
|
||||||
|
LOGPMS(DL1C, LOGL_ERROR, ms, "l1gprs is not initialized\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg->l1h = msgb_pull(msg, sizeof(*l1h));
|
||||||
|
|
||||||
|
if (l1h->msg_type == L1CTL_GPRS_UL_TBF_CFG_REQ)
|
||||||
|
l1gprs_handle_ul_tbf_cfg_req(ms->gprs, msg);
|
||||||
|
else
|
||||||
|
l1gprs_handle_dl_tbf_cfg_req(ms->gprs, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void l1ctl_rx_gprs_ul_block_req(struct l1_model_ms *ms, struct msgb *msg)
|
||||||
|
{
|
||||||
|
const struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data;
|
||||||
|
struct l1gprs_prim_ul_block_req req;
|
||||||
|
uint32_t fn_sched;
|
||||||
|
|
||||||
|
if (OSMO_UNLIKELY(ms->gprs == NULL)) {
|
||||||
|
LOGPMS(DL1P, LOGL_ERROR, ms, "l1gprs is not initialized\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg->l1h = (void *)l1h->data;
|
||||||
|
if (l1gprs_handle_ul_block_req(ms->gprs, &req, msg) != 0)
|
||||||
|
return;
|
||||||
|
msg->l2h = (void *)&req.data[0];
|
||||||
|
|
||||||
|
fn_sched = sched_fn_ul(ms->state.current_time,
|
||||||
|
RSL_CHAN_OSMO_PDCH | req.hdr.tn, 0x00);
|
||||||
|
if (OSMO_UNLIKELY(fn_sched != req.hdr.fn)) {
|
||||||
|
LOGPMS(DL1P, LOGL_ERROR, ms,
|
||||||
|
"GPRS UL BLOCK.req: fn_sched(%u) != fn_req(%u)\n",
|
||||||
|
fn_sched, req.hdr.fn);
|
||||||
|
/* FIXME: return; */
|
||||||
|
}
|
||||||
|
|
||||||
|
virt_l1_sched_schedule(ms, msg, fn_sched, req.hdr.tn,
|
||||||
|
&gsmtapl1_tx_to_virt_um_inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void l1ctl_tx_gprs_dl_block_ind(struct l1_model_ms *ms, const struct msgb *msg,
|
||||||
|
uint32_t fn, uint8_t tn, uint8_t rxlev)
|
||||||
|
{
|
||||||
|
struct l1gprs_prim_dl_block_ind ind;
|
||||||
|
struct msgb *nmsg;
|
||||||
|
|
||||||
|
if (ms->gprs == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ind = (struct l1gprs_prim_dl_block_ind) {
|
||||||
|
.hdr = {
|
||||||
|
.fn = fn,
|
||||||
|
.tn = tn,
|
||||||
|
},
|
||||||
|
.meas = {
|
||||||
|
.ber10k = 0, /* perfect Um, no errors */
|
||||||
|
.ci_cb = 180, /* 18 dB */
|
||||||
|
.rx_lev = rxlev,
|
||||||
|
},
|
||||||
|
.data = msgb_data(msg),
|
||||||
|
.data_len = msgb_length(msg),
|
||||||
|
};
|
||||||
|
|
||||||
|
nmsg = l1gprs_handle_dl_block_ind(ms->gprs, &ind);
|
||||||
|
if (nmsg != NULL)
|
||||||
|
l1ctl_sap_tx_to_l23_inst(ms, nmsg);
|
||||||
|
}
|
|
@ -40,8 +40,9 @@
|
||||||
#include <osmocom/bb/virtphy/gsmtapl1_if.h>
|
#include <osmocom/bb/virtphy/gsmtapl1_if.h>
|
||||||
#include <osmocom/bb/virtphy/logging.h>
|
#include <osmocom/bb/virtphy/logging.h>
|
||||||
#include <osmocom/bb/virtphy/virt_l1_sched.h>
|
#include <osmocom/bb/virtphy/virt_l1_sched.h>
|
||||||
|
#include <osmocom/bb/l1gprs.h>
|
||||||
|
|
||||||
#define DEFAULT_LOG_MASK "DL1C,2:DL1P,2:DVIRPHY,2:DMAIN,1"
|
#define DEFAULT_LOG_MASK "DL1C,2:DL1P,2:DVIRPHY,2:DGPRS,1:DMAIN,1"
|
||||||
|
|
||||||
/* this exists once in the program, and contains the state that we
|
/* this exists once in the program, and contains the state that we
|
||||||
* only keep once: L1CTL server socket, GSMTAP/VirtUM socket */
|
* only keep once: L1CTL server socket, GSMTAP/VirtUM socket */
|
||||||
|
@ -242,6 +243,7 @@ int main(int argc, char *argv[])
|
||||||
handle_options(argc, argv);
|
handle_options(argc, argv);
|
||||||
|
|
||||||
ms_log_init(log_mask);
|
ms_log_init(log_mask);
|
||||||
|
l1gprs_logging_init(DGPRS);
|
||||||
|
|
||||||
LOGP(DVIRPHY, LOGL_INFO, "Virtual physical layer starting up...\n");
|
LOGP(DVIRPHY, LOGL_INFO, "Virtual physical layer starting up...\n");
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,457 @@
|
||||||
|
/*
|
||||||
|
* l1gprs - GPRS layer 1 implementation
|
||||||
|
*
|
||||||
|
* (C) 2022-2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/logging.h>
|
||||||
|
#include <osmocom/core/talloc.h>
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
#include <osmocom/core/msgb.h>
|
||||||
|
|
||||||
|
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||||
|
#include <osmocom/gsm/protocol/gsm_44_060.h>
|
||||||
|
|
||||||
|
#include <osmocom/bb/l1ctl_proto.h>
|
||||||
|
#include <osmocom/bb/l1gprs.h>
|
||||||
|
|
||||||
|
#define LOGP_GPRS(gprs, level, fmt, args...) \
|
||||||
|
LOGP(l1gprs_log_cat, level, "%s" fmt, \
|
||||||
|
(gprs)->log_prefix, ## args)
|
||||||
|
|
||||||
|
#define LOGP_PDCH(pdch, level, fmt, args...) \
|
||||||
|
LOGP_GPRS((pdch)->gprs, level, "(PDCH-%u) " fmt, \
|
||||||
|
(pdch)->tn, ## args)
|
||||||
|
|
||||||
|
#define LOG_TBF_FMT "%cL-TBF#%03d"
|
||||||
|
#define LOG_TBF_ARGS(tbf) \
|
||||||
|
(tbf)->uplink ? 'U' : 'D', tbf->tbf_ref
|
||||||
|
|
||||||
|
static int l1gprs_log_cat = DLGLOBAL;
|
||||||
|
|
||||||
|
enum gprs_rlcmac_block_type {
|
||||||
|
GPRS_RLCMAC_DATA_BLOCK = 0x00,
|
||||||
|
GPRS_RLCMAC_CONTROL_BLOCK = 0x01,
|
||||||
|
GPRS_RLCMAC_CONTROL_BLOCK_OPT = 0x02,
|
||||||
|
GPRS_RLCMAC_RESERVED = 0x03,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct l1gprs_tbf *l1gprs_tbf_alloc(struct l1gprs_state *gprs,
|
||||||
|
bool uplink, uint8_t tbf_ref,
|
||||||
|
uint8_t slotmask)
|
||||||
|
{
|
||||||
|
struct l1gprs_tbf *tbf;
|
||||||
|
|
||||||
|
tbf = talloc(gprs, struct l1gprs_tbf);
|
||||||
|
OSMO_ASSERT(tbf != NULL);
|
||||||
|
|
||||||
|
tbf->uplink = uplink;
|
||||||
|
tbf->tbf_ref = tbf_ref;
|
||||||
|
tbf->slotmask = slotmask;
|
||||||
|
|
||||||
|
return tbf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void l1gprs_tbf_free(struct l1gprs_tbf *tbf)
|
||||||
|
{
|
||||||
|
if (tbf == NULL)
|
||||||
|
return;
|
||||||
|
llist_del(&tbf->list);
|
||||||
|
talloc_free(tbf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct l1gprs_tbf *l1gprs_find_tbf(struct l1gprs_state *gprs,
|
||||||
|
bool uplink, uint8_t tbf_ref)
|
||||||
|
{
|
||||||
|
struct l1gprs_tbf *tbf;
|
||||||
|
|
||||||
|
llist_for_each_entry(tbf, &gprs->tbf_list, list) {
|
||||||
|
if (tbf->uplink != uplink)
|
||||||
|
continue;
|
||||||
|
if (tbf->tbf_ref != tbf_ref)
|
||||||
|
continue;
|
||||||
|
return tbf;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void l1gprs_register_tbf(struct l1gprs_state *gprs,
|
||||||
|
struct l1gprs_tbf *tbf)
|
||||||
|
{
|
||||||
|
OSMO_ASSERT(tbf->slotmask != 0x00);
|
||||||
|
|
||||||
|
/* Update the PDCH states */
|
||||||
|
for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) {
|
||||||
|
struct l1gprs_pdch *pdch = &gprs->pdch[tn];
|
||||||
|
|
||||||
|
if (~tbf->slotmask & (1 << pdch->tn))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (tbf->uplink) {
|
||||||
|
pdch->ul_tbf_count++;
|
||||||
|
} else {
|
||||||
|
pdch->dl_tbf_count++;
|
||||||
|
pdch->dl_tfi_mask |= (1 << tbf->dl_tfi);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGP_PDCH(pdch, LOGL_DEBUG,
|
||||||
|
"Linked " LOG_TBF_FMT "\n",
|
||||||
|
LOG_TBF_ARGS(tbf));
|
||||||
|
}
|
||||||
|
|
||||||
|
llist_add_tail(&tbf->list, &gprs->tbf_list);
|
||||||
|
|
||||||
|
LOGP_GPRS(gprs, LOGL_INFO,
|
||||||
|
LOG_TBF_FMT " is registered\n",
|
||||||
|
LOG_TBF_ARGS(tbf));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void l1gprs_unregister_tbf(struct l1gprs_state *gprs,
|
||||||
|
bool uplink, uint8_t tbf_ref)
|
||||||
|
{
|
||||||
|
struct l1gprs_tbf *tbf;
|
||||||
|
|
||||||
|
tbf = l1gprs_find_tbf(gprs, uplink, tbf_ref);
|
||||||
|
if (tbf == NULL) {
|
||||||
|
LOGP_GPRS(gprs, LOGL_ERROR,
|
||||||
|
"%s(): " LOG_TBF_FMT " not found\n",
|
||||||
|
__func__, LOG_TBF_ARGS(tbf));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the PDCH states */
|
||||||
|
for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) {
|
||||||
|
struct l1gprs_pdch *pdch = &gprs->pdch[tn];
|
||||||
|
|
||||||
|
if (~tbf->slotmask & (1 << pdch->tn))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (tbf->uplink) {
|
||||||
|
OSMO_ASSERT(pdch->ul_tbf_count > 0);
|
||||||
|
pdch->ul_tbf_count--;
|
||||||
|
} else {
|
||||||
|
OSMO_ASSERT(pdch->dl_tbf_count > 0);
|
||||||
|
pdch->dl_tbf_count--;
|
||||||
|
pdch->dl_tfi_mask &= ~(1 << tbf->dl_tfi);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGP_PDCH(pdch, LOGL_DEBUG,
|
||||||
|
"Unlinked " LOG_TBF_FMT "\n",
|
||||||
|
LOG_TBF_ARGS(tbf));
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGP_GPRS(gprs, LOGL_INFO,
|
||||||
|
LOG_TBF_FMT " is unregistered and free()d\n",
|
||||||
|
LOG_TBF_ARGS(tbf));
|
||||||
|
|
||||||
|
l1gprs_tbf_free(tbf);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define L1GPRS_L1CTL_MSGB_SIZE 256
|
||||||
|
#define L1GPRS_L1CTL_MSGB_HEADROOM 32
|
||||||
|
|
||||||
|
static struct msgb *l1gprs_l1ctl_msgb_alloc(uint8_t msg_type)
|
||||||
|
{
|
||||||
|
struct l1ctl_hdr *l1h;
|
||||||
|
struct msgb *msg;
|
||||||
|
|
||||||
|
msg = msgb_alloc_headroom(L1GPRS_L1CTL_MSGB_SIZE,
|
||||||
|
L1GPRS_L1CTL_MSGB_HEADROOM,
|
||||||
|
"l1gprs_l1ctl_msg");
|
||||||
|
if (msg == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
msg->l1h = msgb_put(msg, sizeof(*l1h));
|
||||||
|
l1h = (struct l1ctl_hdr *)msg->l1h;
|
||||||
|
l1h->msg_type = msg_type;
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool l1gprs_pdch_filter_dl_block(const struct l1gprs_pdch *pdch,
|
||||||
|
const uint8_t *data)
|
||||||
|
{
|
||||||
|
enum gprs_rlcmac_block_type block_type = data[0] >> 6;
|
||||||
|
uint8_t dl_tfi;
|
||||||
|
|
||||||
|
switch (block_type) {
|
||||||
|
case GPRS_RLCMAC_DATA_BLOCK:
|
||||||
|
/* see 3GPP TS 44.060, section 10.2.1 */
|
||||||
|
dl_tfi = (data[1] >> 1) & 0x1f;
|
||||||
|
break;
|
||||||
|
case GPRS_RLCMAC_CONTROL_BLOCK_OPT:
|
||||||
|
/* see 3GPP TS 44.060, section 10.3.1 */
|
||||||
|
dl_tfi = (data[2] >> 1) & 0x1f;
|
||||||
|
break;
|
||||||
|
case GPRS_RLCMAC_CONTROL_BLOCK:
|
||||||
|
/* no optional octets */
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
LOGP_PDCH(pdch, LOGL_NOTICE,
|
||||||
|
"Rx Downlink block with unknown payload (0x%0x)\n",
|
||||||
|
block_type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdch->dl_tfi_mask & (1 << dl_tfi))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void l1gprs_logging_init(int logc)
|
||||||
|
{
|
||||||
|
l1gprs_log_cat = logc;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct l1gprs_state *l1gprs_state_alloc(void *ctx, const char *log_prefix, void *priv)
|
||||||
|
{
|
||||||
|
struct l1gprs_state *gprs;
|
||||||
|
|
||||||
|
gprs = talloc_zero(ctx, struct l1gprs_state);
|
||||||
|
if (gprs == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) {
|
||||||
|
struct l1gprs_pdch *pdch = &gprs->pdch[tn];
|
||||||
|
|
||||||
|
pdch->tn = tn;
|
||||||
|
pdch->gprs = gprs;
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_LLIST_HEAD(&gprs->tbf_list);
|
||||||
|
|
||||||
|
if (log_prefix == NULL)
|
||||||
|
gprs->log_prefix = talloc_asprintf(gprs, "l1gprs[0x%p]: ", gprs);
|
||||||
|
else
|
||||||
|
gprs->log_prefix = talloc_strdup(gprs, log_prefix);
|
||||||
|
gprs->priv = priv;
|
||||||
|
|
||||||
|
return gprs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void l1gprs_state_free(struct l1gprs_state *gprs)
|
||||||
|
{
|
||||||
|
if (gprs == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (!llist_empty(&gprs->tbf_list)) {
|
||||||
|
struct l1gprs_tbf *tbf;
|
||||||
|
|
||||||
|
tbf = llist_first_entry(&gprs->tbf_list, struct l1gprs_tbf, list);
|
||||||
|
LOGP_GPRS(gprs, LOGL_DEBUG,
|
||||||
|
"%s(): " LOG_TBF_FMT " is free()d\n",
|
||||||
|
__func__, LOG_TBF_ARGS(tbf));
|
||||||
|
l1gprs_tbf_free(tbf);
|
||||||
|
}
|
||||||
|
|
||||||
|
talloc_free(gprs->log_prefix);
|
||||||
|
talloc_free(gprs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int l1gprs_handle_ul_tbf_cfg_req(struct l1gprs_state *gprs, const struct msgb *msg)
|
||||||
|
{
|
||||||
|
const struct l1ctl_gprs_ul_tbf_cfg_req *req = (void *)msg->l1h;
|
||||||
|
struct l1gprs_tbf *tbf = NULL;
|
||||||
|
|
||||||
|
OSMO_ASSERT(req != NULL);
|
||||||
|
|
||||||
|
if (msgb_l1len(msg) < sizeof(*req)) {
|
||||||
|
LOGP_GPRS(gprs, LOGL_ERROR,
|
||||||
|
"Rx malformed Uplink TBF config (len=%u < %zu)\n",
|
||||||
|
msgb_l1len(msg), sizeof(*req));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGP_GPRS(gprs, LOGL_INFO,
|
||||||
|
"Rx Uplink TBF config: tbf_ref=%u, slotmask=0x%02x\n",
|
||||||
|
req->tbf_ref, req->slotmask);
|
||||||
|
|
||||||
|
if (req->slotmask != 0x00) {
|
||||||
|
tbf = l1gprs_tbf_alloc(gprs, true, req->tbf_ref, req->slotmask);
|
||||||
|
l1gprs_register_tbf(gprs, tbf);
|
||||||
|
} else {
|
||||||
|
l1gprs_unregister_tbf(gprs, true, req->tbf_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int l1gprs_handle_dl_tbf_cfg_req(struct l1gprs_state *gprs, const struct msgb *msg)
|
||||||
|
{
|
||||||
|
const struct l1ctl_gprs_dl_tbf_cfg_req *req = (void *)msg->l1h;
|
||||||
|
struct l1gprs_tbf *tbf = NULL;
|
||||||
|
|
||||||
|
OSMO_ASSERT(req != NULL);
|
||||||
|
|
||||||
|
if (msgb_l1len(msg) < sizeof(*req)) {
|
||||||
|
LOGP_GPRS(gprs, LOGL_ERROR,
|
||||||
|
"Rx malformed Downlink TBF config (len=%u < %zu)\n",
|
||||||
|
msgb_l1len(msg), sizeof(*req));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGP_GPRS(gprs, LOGL_INFO,
|
||||||
|
"Rx Downlink TBF config: tbf_ref=%u, slotmask=0x%02x, dl_tfi=%u\n",
|
||||||
|
req->tbf_ref, req->slotmask, req->dl_tfi);
|
||||||
|
|
||||||
|
if (req->dl_tfi > 31) {
|
||||||
|
LOGP_GPRS(gprs, LOGL_ERROR,
|
||||||
|
"Invalid DL TFI %u (shall be in range 0..31)\n",
|
||||||
|
req->dl_tfi);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req->slotmask != 0x00) {
|
||||||
|
tbf = l1gprs_tbf_alloc(gprs, false, req->tbf_ref, req->slotmask);
|
||||||
|
tbf->dl_tfi = req->dl_tfi;
|
||||||
|
l1gprs_register_tbf(gprs, tbf);
|
||||||
|
} else {
|
||||||
|
l1gprs_unregister_tbf(gprs, false, req->tbf_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int l1gprs_handle_ul_block_req(struct l1gprs_state *gprs,
|
||||||
|
struct l1gprs_prim_ul_block_req *req,
|
||||||
|
const struct msgb *msg)
|
||||||
|
{
|
||||||
|
const struct l1ctl_gprs_ul_block_req *l1br = (void *)msg->l1h;
|
||||||
|
const struct l1gprs_pdch *pdch = NULL;
|
||||||
|
size_t data_len;
|
||||||
|
|
||||||
|
OSMO_ASSERT(l1br != NULL);
|
||||||
|
|
||||||
|
if (OSMO_UNLIKELY(msgb_l1len(msg) < sizeof(*l1br))) {
|
||||||
|
LOGP_GPRS(gprs, LOGL_ERROR,
|
||||||
|
"Rx malformed UL BLOCK.req (len=%u < %zu)\n",
|
||||||
|
msgb_l1len(msg), sizeof(*l1br));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (OSMO_UNLIKELY(l1br->hdr.tn >= ARRAY_SIZE(gprs->pdch))) {
|
||||||
|
LOGP_GPRS(gprs, LOGL_ERROR,
|
||||||
|
"Rx malformed UL BLOCK.req (tn=%u)\n",
|
||||||
|
l1br->hdr.tn);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdch = &gprs->pdch[l1br->hdr.tn];
|
||||||
|
data_len = msgb_l1len(msg) - sizeof(*l1br);
|
||||||
|
|
||||||
|
LOGP_PDCH(pdch, LOGL_DEBUG,
|
||||||
|
"Rx UL BLOCK.req (fn=%u, len=%zu): %s\n",
|
||||||
|
ntohl(l1br->hdr.fn), data_len, osmo_hexdump(l1br->data, data_len));
|
||||||
|
|
||||||
|
if ((pdch->ul_tbf_count == 0) && (pdch->dl_tbf_count == 0)) {
|
||||||
|
LOGP_PDCH(pdch, LOGL_ERROR,
|
||||||
|
"Rx UL BLOCK.req, but this PDCH has no configured TBFs\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*req = (struct l1gprs_prim_ul_block_req) {
|
||||||
|
.hdr = {
|
||||||
|
.fn = ntohl(l1br->hdr.fn),
|
||||||
|
.tn = l1br->hdr.tn,
|
||||||
|
},
|
||||||
|
.data = &l1br->data[0],
|
||||||
|
.data_len = data_len,
|
||||||
|
};
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct msgb *l1gprs_handle_dl_block_ind(struct l1gprs_state *gprs,
|
||||||
|
const struct l1gprs_prim_dl_block_ind *ind)
|
||||||
|
{
|
||||||
|
const struct l1gprs_pdch *pdch = NULL;
|
||||||
|
struct l1ctl_gprs_dl_block_ind *l1bi;
|
||||||
|
enum osmo_gprs_cs cs;
|
||||||
|
struct msgb *msg;
|
||||||
|
|
||||||
|
if (OSMO_UNLIKELY(ind->hdr.tn >= ARRAY_SIZE(gprs->pdch))) {
|
||||||
|
LOGP_GPRS(gprs, LOGL_ERROR,
|
||||||
|
"Rx malformed DL BLOCK.ind (tn=%u)\n",
|
||||||
|
ind->hdr.tn);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdch = &gprs->pdch[ind->hdr.tn];
|
||||||
|
|
||||||
|
LOGP_PDCH(pdch, LOGL_DEBUG,
|
||||||
|
"Rx DL BLOCK.ind (fn=%u, len=%zu): %s\n",
|
||||||
|
ind->hdr.fn, ind->data_len, osmo_hexdump(ind->data, ind->data_len));
|
||||||
|
|
||||||
|
if ((pdch->ul_tbf_count == 0) && (pdch->dl_tbf_count == 0)) {
|
||||||
|
LOGP_PDCH(pdch, LOGL_ERROR,
|
||||||
|
"Rx DL BLOCK.ind, but this PDCH has no configured TBFs\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = l1gprs_l1ctl_msgb_alloc(L1CTL_GPRS_DL_BLOCK_IND);
|
||||||
|
if (OSMO_UNLIKELY(msg == NULL)) {
|
||||||
|
LOGP_GPRS(gprs, LOGL_ERROR, "l1gprs_l1ctl_msgb_alloc() failed\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
l1bi = (void *)msgb_put(msg, sizeof(*l1bi));
|
||||||
|
*l1bi = (struct l1ctl_gprs_dl_block_ind) {
|
||||||
|
.hdr = {
|
||||||
|
.fn = htonl(ind->hdr.fn),
|
||||||
|
.tn = ind->hdr.tn,
|
||||||
|
},
|
||||||
|
.meas = {
|
||||||
|
.ber10k = htons(ind->meas.ber10k),
|
||||||
|
.ci_cb = htons(ind->meas.ci_cb),
|
||||||
|
.rx_lev = ind->meas.rx_lev,
|
||||||
|
},
|
||||||
|
.usf = 0xff,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ind->data_len == 0)
|
||||||
|
return msg;
|
||||||
|
|
||||||
|
cs = osmo_gprs_dl_cs_by_block_bytes(ind->data_len);
|
||||||
|
switch (cs) {
|
||||||
|
case OSMO_GPRS_CS1:
|
||||||
|
case OSMO_GPRS_CS2:
|
||||||
|
case OSMO_GPRS_CS3:
|
||||||
|
case OSMO_GPRS_CS4:
|
||||||
|
l1bi->usf = ind->data[0] & 0x07;
|
||||||
|
/* Determine whether to include the payload or not */
|
||||||
|
if (l1gprs_pdch_filter_dl_block(pdch, ind->data))
|
||||||
|
memcpy(msgb_put(msg, ind->data_len), ind->data, ind->data_len);
|
||||||
|
break;
|
||||||
|
case OSMO_GPRS_CS_NONE:
|
||||||
|
LOGP_PDCH(pdch, LOGL_ERROR,
|
||||||
|
"Failed to determine Coding Scheme (len=%zu)\n", ind->data_len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOGP_PDCH(pdch, LOGL_NOTICE, "Coding Scheme %d is not supported\n", cs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
Loading…
Reference in New Issue