Change-Id: I9567d64f9d00262e36147e8d7e541e5e246bda5f Related: OS#5500changes/43/30743/12
parent
e15996378f
commit
2b462dd89d
@ -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);
|
@ -0,0 +1 @@
|
||||
../../../../../../include/l1gprs.h
|
@ -0,0 +1 @@
|
||||
../../../shared/l1gprs.c
|
@ -0,0 +1 @@
|
||||
../../../../../../include/l1gprs.h
|
@ -0,0 +1 @@
|
||||
../../../shared/l1gprs.c
|
@ -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);
|
||||
}
|
@ -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))
|
||||