Merge branch 'jolly_new'

Merge is based on jolly_new branch with two modifications.
1. Modified PCU L1 interface.
pcu_l1_if.cpp - common functions for tx and rx messages on L1 interface.
sysmo_sock.cpp - SYSMO-PCU socket functions.
openbts_sock.cpp - OpenBTS-PCU socket functions.
pcuif_proto.h - L1 interface's primitives.
2. Modified encoding of RLC/MAC Control messages, now we use structures and encode_gsm_rlcmac_downlink() function for encode control blocks (without  hand-coding).
This commit is contained in:
Ivan Kluchnikov 2012-07-12 14:49:15 +04:00
commit ef7f28cc7f
20 changed files with 3632 additions and 2415 deletions

View File

@ -24,7 +24,7 @@ dnl checks for libraries
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.3.9)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.3)
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.5.2)
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.5.1.4)
AC_MSG_CHECKING([whether to enable sysmocom-bts hardware support])
AC_ARG_ENABLE(sysmocom-bts,

6
src/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.gitignore
.libs/
RLCMACTest
libgprs.la
pcu

View File

@ -18,12 +18,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# FIXME: This has to go!!
OPENBSC_DIR = $(top_srcdir)/../openbsc/openbsc
OPENGGSN_DIR = $(top_srcdir)/../openggsn
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) -I$(OPENBSC_DIR)/include
AM_CXXFLAGS = -Wall -ldl -pthread -lgtp
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
AM_CXXFLAGS = -Wall -ldl -pthread
noinst_LTLIBRARIES = libgprs.la
@ -33,15 +29,18 @@ libgprs_la_SOURCES = \
gsm_rlcmac.cpp \
gprs_bssgp_pcu.cpp \
gprs_rlcmac.cpp \
gprs_rlcmac_data.cpp \
gprs_rlcmac_sched.cpp \
gsm_timer.cpp \
bitvector.cpp
bitvector.cpp \
pcu_l1_if.cpp
if ENABLE_SYSMOBTS
libgprs_la_SOURCES += \
sysmo_l1_if.cpp
sysmo_sock.cpp
else
libgprs_la_SOURCES += \
pcu_l1_if.cpp
openbts_sock.cpp
endif
noinst_PROGRAMS = \
@ -54,6 +53,7 @@ noinst_HEADERS = \
gsm_rlcmac.h \
gprs_bssgp_pcu.h \
gprs_rlcmac.h \
pcuif_proto.h \
pcu_l1_if.h \
gsm_timer.h \
bitvector.h

View File

@ -23,19 +23,24 @@
struct sgsn_instance *sgsn;
void *tall_bsc_ctx;
struct bssgp_bvc_ctx *bctx = btsctx_alloc(BVCI, NSEI);
struct bssgp_bvc_ctx *bctx = NULL;
struct gprs_nsvc *nsvc = NULL;
extern uint16_t spoof_mcc, spoof_mnc;
int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
{
struct bssgp_ud_hdr *budh;
int i = 0;
uint8_t trx, ts;
budh = (struct bssgp_ud_hdr *)msgb_bssgph(msg);
int tfi;
uint32_t tlli;
int i, j;
uint8_t trx, ts;
uint8_t *data;
uint16_t len;
struct gprs_rlcmac_tbf *tbf;
tbf = tbf_alloc(GPRS_RLCMAC_DL_TBF, ntohl(budh->tlli));
budh = (struct bssgp_ud_hdr *)msgb_bssgph(msg);
tlli = ntohl(budh->tlli);
if (!tbf)
{
@ -44,31 +49,88 @@ int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
/* LLC_PDU is mandatory IE */
if (!TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU))
{
LOGP(DBSSGP, LOGL_ERROR, "BSSGP TLLI=0x%08x Rx UL-UD missing mandatory IE\n", tbf->tlli);
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP TLLI=0x%08x Rx UL-UD missing mandatory IE\n", tlli);
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
}
tbf_dl_establish(tbf);
uint8_t *llc_pdu = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
uint16_t llc_pdu_len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU);
tbf_dl_data_transfer(tbf, llc_pdu, llc_pdu_len);
}
int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, struct tlv_parsed *tp)
{
uint8_t *ptmsi = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_TMSI);
uint16_t ptmsi_len = TLVP_LEN(tp, BSSGP_IE_TMSI);
LOGP(DBSSGP, LOGL_NOTICE, " P-TMSI = ");
for (int i = 0; i < ptmsi_len; i++)
data = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU);
if (len > sizeof(tbf->llc_frame))
{
LOGPC(DBSSGP, LOGL_NOTICE, "%02x", ptmsi[i]);
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP TLLI=0x%08x Rx UL-UD IE_LLC_PDU too large\n", tlli);
return bssgp_tx_status(BSSGP_CAUSE_COND_IE_ERR, NULL, msg);
}
LOGPC(DBSSGP, LOGL_NOTICE, "\n");
gprs_rlcmac_paging_request(ptmsi, ptmsi_len);
/* read IMSI. if no IMSI exists, use first paging block (any paging),
* because during attachment the IMSI might not be known, so the MS
* will listen to all paging blocks. */
char imsi[16] = "000";
if (TLVP_PRESENT(tp, BSSGP_IE_IMSI))
{
uint8_t imsi_len = TLVP_LEN(tp, BSSGP_IE_IMSI);
uint8_t *bcd_imsi = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_IMSI);
if ((bcd_imsi[0] & 0x08))
imsi_len = imsi_len * 2 - 1;
else
imsi_len = (imsi_len - 1) * 2;
for (i = 0, j = 0; j < imsi_len && j < 16; j++)
{
if (!(j & 1)) {
imsi[j] = (bcd_imsi[i] >> 4) + '0';
i++;
} else
imsi[j] = (bcd_imsi[i] & 0xf) + '0';
}
imsi[j] = '\0';
}
LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", tlli, imsi, len);
/* check for existing TBF */
if ((tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF))) {
LOGP(DRLCMAC, LOGL_INFO, "TBF: APPEND TFI: %u TLLI: 0x%08x\n", tbf->tfi, tbf->tlli);
if (tbf->state == GPRS_RLCMAC_WAIT_RELEASE) {
LOGP(DRLCMAC, LOGL_DEBUG, "TBF in WAIT RELEASE state "
"(T3193), so reuse TBF\n");
memcpy(tbf->llc_frame, data, len);
tbf->llc_length = len;
memset(&tbf->dir.dl, 0, sizeof(tbf->dir.dl)); /* reset
rlc states */
gprs_rlcmac_trigger_downlink_assignment(tbf, 1, NULL);
} else {
/* the TBF exists, so we must write it in the queue */
struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
if (!llc_msg)
return -ENOMEM;
memcpy(msgb_put(llc_msg, len), data, len);
msgb_enqueue(&tbf->llc_queue, llc_msg);
}
} else {
// Create new TBF
tfi = tfi_alloc(&trx, &ts);
if (tfi < 0) {
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
/* FIXME: send reject */
return -EBUSY;
}
tbf = tbf_alloc(tfi, trx, ts);
tbf->direction = GPRS_RLCMAC_DL_TBF;
tbf->tlli = tlli;
tbf->tlli_valid = 1;
LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] START TFI: %u TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
/* new TBF, so put first frame */
memcpy(tbf->llc_frame, data, len);
tbf->llc_length = len;
/* trigger downlink assignment and set state to ASSIGN.
* we don't use old_downlink, so the possible uplink is used
* to trigger downlink assignment. if there is no uplink,
* AGCH is used. */
gprs_rlcmac_trigger_downlink_assignment(tbf, 0, imsi);
}
return 0;
}
/* Receive a BSSGP PDU from a BSS on a PTP BVCI */
@ -90,26 +152,26 @@ int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_
switch (pdu_type) {
case BSSGP_PDUT_DL_UNITDATA:
LOGP(DBSSGP, LOGL_NOTICE, "RX: [SGSN->PCU] BSSGP_PDUT_DL_UNITDATA\n");
LOGP(DBSSGP, LOGL_DEBUG, "RX: [SGSN->PCU] BSSGP_PDUT_DL_UNITDATA\n");
gprs_bssgp_pcu_rx_dl_ud(msg, tp);
break;
case BSSGP_PDUT_PAGING_PS:
LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_PAGING_PS\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_PS\n");
break;
case BSSGP_PDUT_PAGING_CS:
LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_PAGING_CS\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_CS\n");
break;
case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_RA_CAPA_UPDATE_ACK\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RA_CAPA_UPDATE_ACK\n");
break;
case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_FLOW_CONTROL_BVC_ACK\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLOW_CONTROL_BVC_ACK\n");
break;
case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_FLOW_CONTROL_MS_ACK\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLOW_CONTROL_MS_ACK\n");
break;
default:
DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x unknown\n", bctx->bvci, pdu_type);
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u PDU type 0x%02x unknown\n", bctx->bvci, pdu_type);
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
break;
}
@ -128,41 +190,40 @@ int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp
/* FIXME: send NM_STATUS.ind to NM */
break;
case BSSGP_PDUT_SUSPEND_ACK:
LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_SUSPEND_ACK\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
break;
case BSSGP_PDUT_SUSPEND_NACK:
LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_SUSPEND_NACK\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_NACK\n");
break;
case BSSGP_PDUT_BVC_RESET_ACK:
LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_BVC_RESET_ACK\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_RESET_ACK\n");
break;
case BSSGP_PDUT_PAGING_PS:
LOGP(DBSSGP, LOGL_NOTICE, "RX: [SGSN->PCU] BSSGP_PDUT_PAGING_PS\n");
gprs_bssgp_pcu_rx_paging_ps(msg, tp);
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_PS\n");
break;
case BSSGP_PDUT_PAGING_CS:
LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_PAGING_CS\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_CS\n");
break;
case BSSGP_PDUT_RESUME_ACK:
LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_RESUME_ACK\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_ACK\n");
break;
case BSSGP_PDUT_RESUME_NACK:
LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_RESUME_NACK\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_NACK\n");
break;
case BSSGP_PDUT_FLUSH_LL:
LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_FLUSH_LL\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLUSH_LL\n");
break;
case BSSGP_PDUT_BVC_BLOCK_ACK:
LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_SUSPEND_ACK\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
break;
case BSSGP_PDUT_BVC_UNBLOCK_ACK:
LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_BVC_UNBLOCK_ACK\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_UNBLOCK_ACK\n");
break;
case BSSGP_PDUT_SGSN_INVOKE_TRACE:
LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_SGSN_INVOKE_TRACE\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SGSN_INVOKE_TRACE\n");
break;
default:
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n", bctx->bvci, bgph->pdu_type);
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n", bctx->bvci, bgph->pdu_type);
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
break;
}
@ -178,6 +239,13 @@ int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
uint16_t ns_bvci = msgb_bvci(msg);
int data_len;
int rc = 0;
struct bssgp_bvc_ctx *bctx;
if (pdu_type == BSSGP_PDUT_STATUS) {
LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u received STATUS\n",
msgb_nsei(msg), ns_bvci);
return 0;
}
/* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
@ -196,12 +264,6 @@ int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
/* look-up or create the BTS context for this BVC */
bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg));
/* Only a RESET PDU can create a new BVC context */
if (!bctx)
{
bctx = btsctx_alloc(ns_bvci, msgb_nsei(msg));
}
if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET_ACK)
{
LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
@ -219,17 +281,17 @@ int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
if (ns_bvci == BVCI_SIGNALLING)
{
LOGP(DBSSGP, LOGL_NOTICE, "rx BVCI_SIGNALLING gprs_bssgp_rx_sign\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_SIGNALLING gprs_bssgp_rx_sign\n");
rc = gprs_bssgp_pcu_rx_sign(msg, &tp, bctx);
}
else if (ns_bvci == BVCI_PTM)
{
LOGP(DBSSGP, LOGL_NOTICE, "rx BVCI_PTM bssgp_tx_status\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_PTM bssgp_tx_status\n");
rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
}
else
{
LOGP(DBSSGP, LOGL_NOTICE, "rx BVCI_PTP gprs_bssgp_rx_ptp\n");
LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_PTP gprs_bssgp_rx_ptp\n");
rc = gprs_bssgp_pcu_rx_ptp(msg, &tp, bctx);
}
return rc;
@ -239,3 +301,128 @@ int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
return 0;
}
static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, struct msgb *msg, uint16_t bvci)
{
int rc = 0;
switch (event) {
case GPRS_NS_EVT_UNIT_DATA:
/* hand the message into the BSSGP implementation */
rc = gprs_bssgp_pcu_rcvmsg(msg);
break;
default:
LOGP(DPCU, LOGL_NOTICE, "RLCMAC: Unknown event %u from NS\n", event);
if (msg)
talloc_free(msg);
rc = -EIO;
break;
}
return rc;
}
static int nsvc_unblocked = 0;
static int nsvc_signal_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
struct ns_signal_data *nssd;
if (subsys != SS_L_NS)
return -EINVAL;
nssd = (struct ns_signal_data *)signal_data;
if (nssd->nsvc != nsvc) {
LOGP(DPCU, LOGL_ERROR, "Signal received of unknown NSVC\n");
return -EINVAL;
}
switch (signal) {
case S_NS_UNBLOCK:
if (!nsvc_unblocked) {
nsvc_unblocked = 1;
LOGP(DPCU, LOGL_NOTICE, "NS-VC is unblocked.\n");
bssgp_tx_bvc_reset(bctx, bctx->bvci,
BSSGP_CAUSE_PROTO_ERR_UNSPEC);
}
break;
case S_NS_BLOCK:
if (nsvc_unblocked) {
nsvc_unblocked = 0;
LOGP(DPCU, LOGL_NOTICE, "NS-VC is blocked.\n");
}
break;
}
return 0;
}
/* create BSSGP/NS layer instances */
int gprs_bssgp_create(uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc, uint16_t lac,
uint16_t rac, uint16_t cell_id)
{
struct sockaddr_in dest;
if (bctx)
return 0; /* if already created, must return 0: no error */
bssgp_nsi = gprs_ns_instantiate(&sgsn_ns_cb, NULL);
if (!bssgp_nsi) {
LOGP(DBSSGP, LOGL_ERROR, "Failed to create NS instance\n");
return -EINVAL;
}
gprs_ns_nsip_listen(bssgp_nsi);
dest.sin_family = AF_INET;
dest.sin_port = htons(sgsn_port);
dest.sin_addr.s_addr = htonl(sgsn_ip);
nsvc = gprs_ns_nsip_connect(bssgp_nsi, &dest, nsei, nsvci);
if (!nsvc) {
LOGP(DBSSGP, LOGL_ERROR, "Failed to create NSVCt\n");
gprs_ns_destroy(bssgp_nsi);
bssgp_nsi = NULL;
return -EINVAL;
}
bctx = btsctx_alloc(bvci, nsei);
if (!bctx) {
LOGP(DBSSGP, LOGL_ERROR, "Failed to create BSSGP context\n");
nsvc = NULL;
gprs_ns_destroy(bssgp_nsi);
bssgp_nsi = NULL;
return -EINVAL;
}
bctx->ra_id.mcc = spoof_mcc ? : mcc;
bctx->ra_id.mnc = spoof_mnc ? : mnc;
bctx->ra_id.lac = lac;
bctx->ra_id.rac = rac;
bctx->cell_id = cell_id;
osmo_signal_register_handler(SS_L_NS, nsvc_signal_cb, NULL);
// bssgp_tx_bvc_reset(bctx, bctx->bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC);
return 0;
}
void gprs_bssgp_destroy(void)
{
if (!bssgp_nsi)
return;
osmo_signal_unregister_handler(SS_L_NS, nsvc_signal_cb, NULL);
nsvc = NULL;
/* FIXME: move this to libgb: btsctx_free() */
llist_del(&bctx->list);
talloc_free(bctx);
bctx = NULL;
/* FIXME: blocking... */
gprs_ns_destroy(bssgp_nsi);
bssgp_nsi = NULL;
}

View File

@ -39,23 +39,10 @@ struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei);
}
#include <gprs_debug.h>
#define BVCI 7
#define NSEI 3
#define QOS_PROFILE 0
#define BSSGP_HDR_LEN 53
#define NS_HDR_LEN 4
#define MAX_LEN_PDU 60
#define IE_LLC_PDU 14
#define BLOCK_DATA_LEN 20
#define BLOCK_LEN 23
#define CELL_ID 0
#define MNC 1
#define MCC 001
#define PCU_LAC 1
#define PCU_RAC 0
extern struct bssgp_bvc_ctx *bctx;
@ -67,4 +54,10 @@ int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp
int gprs_bssgp_pcu_rcvmsg(struct msgb *msg);
int gprs_bssgp_create(uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc, uint16_t lac,
uint16_t rac, uint16_t cell_id);
void gprs_bssgp_destroy(void);
#endif // GPRS_BSSGP_PCU_H

View File

@ -33,12 +33,15 @@
/* default categories */
static const struct log_info_cat default_categories[] = {
{"DCSN1", "\033[1;31m", "Concrete Syntax Notation One (CSN1)", LOGL_NOTICE, 1},
{"DL1IF", "\033[1;32m", "GPRS PCU L1 interface (L1IF)", LOGL_NOTICE, 1},
{"DRLCMAC", "\033[1;33m", "GPRS RLC/MAC layer (RLCMAC)", LOGL_NOTICE, 1},
{"DRLCMACDATA", "\033[1;36m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_NOTICE, 1},
{"DBSSGP", "\033[1;34m", "GPRS BSS Gateway Protocol (BSSGP)", LOGL_NOTICE , 1},
{"DPCU", "\033[1;35m", "GPRS Packet Control Unit (PCU)", LOGL_NOTICE, 1}
{"DCSN1", "\033[1;31m", "Concrete Syntax Notation One (CSN1)", LOGL_INFO, 0},
{"DL1IF", "\033[1;32m", "GPRS PCU L1 interface (L1IF)", LOGL_INFO, 1},
{"DRLCMAC", "\033[0;33m", "GPRS RLC/MAC layer (RLCMAC)", LOGL_INFO, 1},
{"DRLCMACDATA", "\033[0;33m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
{"DRLCMACDL", "\033[1;33m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
{"DRLCMACUL", "\033[1;36m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
{"DRLCMACSCHED", "\033[0;36m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
{"DBSSGP", "\033[1;34m", "GPRS BSS Gateway Protocol (BSSGP)", LOGL_INFO , 1},
{"DPCU", "\033[1;35m", "GPRS Packet Control Unit (PCU)", LOGL_NOTICE, 1},
};
enum {

View File

@ -31,6 +31,9 @@ enum {
DL1IF,
DRLCMAC,
DRLCMACDATA,
DRLCMACDL,
DRLCMACUL,
DRLCMACSCHED,
DBSSGP,
DPCU,
aDebug_LastEntry

File diff suppressed because it is too large Load Diff

View File

@ -29,24 +29,93 @@ extern "C" {
#include <osmocom/core/timer.h>
}
#define LLC_MAX_LEN 1543
#define UL_RLC_DATA_BLOCK_LEN 23
/* This special feature will delay assignment of downlink TBF by one second,
* in case there is already a TBF.
* This is usefull to debug downlink establishment during packet idle mode.
*/
//#define DEBUG_DL_ASS_IDLE
enum gprs_rlcmac_tbf_stage {
TBF_ESTABLISH,
TBF_DATA_TRANSFER,
TBF_RELEASE
/*
* PDCH instanc
*/
struct gprs_rlcmac_tbf;
struct gprs_rlcmac_pdch {
uint8_t enable; /* TS is enabled */
uint8_t tsc; /* TSC of this slot */
uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */
uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */
struct gprs_rlcmac_tbf *tbf[32]; /* array of TBF pointers, by TFI */
uint32_t last_rts_fn; /* store last frame number of RTS */
};
struct gprs_rlcmac_trx {
uint16_t arfcn;
struct gprs_rlcmac_pdch pdch[8];
};
struct gprs_rlcmac_bts {
uint8_t cs1;
uint8_t cs2;
uint8_t cs3;
uint8_t cs4;
uint8_t initial_cs;
uint8_t t3142;
uint8_t t3169;
uint8_t t3191;
uint16_t t3193_msec;
uint8_t t3195;
uint8_t n3101;
uint8_t n3103;
uint8_t n3105;
struct gprs_rlcmac_trx trx[8];
};
extern struct gprs_rlcmac_bts *gprs_rlcmac_bts;
/*
* TBF instance
*/
#define LLC_MAX_LEN 1543
#define RLC_MAX_SNS 128 /* GPRS, must be power of 2 */
#define RLC_MAX_WS 64 /* max window size */
#define RLC_MAX_LEN 54 /* CS-4 including spare bits */
#define Tassign_agch 0,500000/* wait for assignment, before transmitting DL */
#define Tassign_pacch 0,100000/* wait for assignment, before transmitting DL */
enum gprs_rlcmac_tbf_state {
WAIT_ESTABLISH,
CCCH_ESTABLISH,
PACCH_ESTABLISH,
FINISH_ESTABLISH,
WAIT_DATA_TRANSFER,
DATA_TRANSFER,
FINISH_DATA_TRANSFER,
RELEASE
GPRS_RLCMAC_NULL = 0, /* new created TBF */
GPRS_RLCMAC_ASSIGN, /* wait for downlink assignment */
GPRS_RLCMAC_FLOW, /* RLC/MAC flow, ressource needed */
GPRS_RLCMAC_FINISHED, /* flow finished, wait for release */
GPRS_RLCMAC_WAIT_RELEASE,/* wait for release or restart of DL TBF */
GPRS_RLCMAC_RELEASING, /* releasing, wait to free TBI/USF */
};
enum gprs_rlcmac_tbf_poll_state {
GPRS_RLCMAC_POLL_NONE = 0,
GPRS_RLCMAC_POLL_SCHED, /* a polling was scheduled */
};
enum gprs_rlcmac_tbf_dl_ass_state {
GPRS_RLCMAC_DL_ASS_NONE = 0,
GPRS_RLCMAC_DL_ASS_SEND_ASS, /* send downlink assignment on next RTS */
GPRS_RLCMAC_DL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
};
enum gprs_rlcmac_tbf_ul_ass_state {
GPRS_RLCMAC_UL_ASS_NONE = 0,
GPRS_RLCMAC_UL_ASS_SEND_ASS, /* send uplink assignment on next RTS */
GPRS_RLCMAC_UL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
};
enum gprs_rlcmac_tbf_ul_ack_state {
GPRS_RLCMAC_UL_ACK_NONE = 0,
GPRS_RLCMAC_UL_ACK_SEND_ACK, /* send acknowledge on next RTS */
GPRS_RLCMAC_UL_ACK_WAIT_ACK, /* wait for PACKET CONTROL ACK */
};
enum gprs_rlcmac_tbf_direction {
@ -54,30 +123,57 @@ enum gprs_rlcmac_tbf_direction {
GPRS_RLCMAC_UL_TBF
};
struct tbf_llc_pdu {
struct llist_head list;
uint8_t num;
uint8_t data[LLC_MAX_LEN];
uint16_t len;
};
struct gprs_rlcmac_tbf {
struct llist_head list;
enum gprs_rlcmac_tbf_state state;
enum gprs_rlcmac_tbf_stage stage;
enum gprs_rlcmac_tbf_direction direction;
struct gprs_rlcmac_tbf *next_tbf;
uint8_t tfi;
uint32_t tlli;
struct llist_head llc_pdus;
struct tbf_llc_pdu llc_pdu;
uint8_t llc_pdu_list_len;
uint8_t rlc_data[LLC_MAX_LEN];
uint16_t data_index;
uint8_t bsn;
uint8_t tlli_valid;
uint8_t trx, ts, tsc;
struct gprs_rlcmac_pdch *pdch;
uint16_t arfcn, ta;
uint8_t llc_frame[LLC_MAX_LEN]; /* current DL or UL frame */
uint16_t llc_index; /* current write/read position of frame */
uint16_t llc_length; /* len of current DL LLC_frame, 0 == no frame */
llist_head llc_queue; /* queued LLC DL data */
enum gprs_rlcmac_tbf_dl_ass_state dl_ass_state;
enum gprs_rlcmac_tbf_ul_ass_state ul_ass_state;
enum gprs_rlcmac_tbf_ul_ack_state ul_ack_state;
enum gprs_rlcmac_tbf_poll_state poll_state;
uint32_t poll_fn;
uint16_t ws; /* window size */
uint16_t sns; /* sequence number space */
/* Please note that all variables here will be reset when changing
* from WAIT RELEASE back to FLOW state (re-use of TBF).
* All states that need reset must be in this struct, so this is why
* variables are in both (dl and ul) structs and not outside union.
*/
union {
struct {
uint16_t bsn; /* block sequence number */
uint16_t v_s; /* send state */
uint16_t v_a; /* ack state */
char v_b[RLC_MAX_SNS/2]; /* acknowledge state array */
int32_t tx_counter; /* count all transmitted blocks */
uint8_t n3105; /* N3105 counter */
} dl;
struct {
uint16_t bsn; /* block sequence number */
uint16_t v_r; /* receive state */
uint16_t v_q; /* receive window state */
char v_n[RLC_MAX_SNS/2]; /* receive state array */
int32_t rx_counter; /* count all received blocks */
uint8_t n3103; /* N3103 counter */
uint8_t usf; /* USF */
} ul;
} dir;
uint8_t rlc_block[RLC_MAX_SNS/2][RLC_MAX_LEN]; /* block history */
uint8_t rlc_block_len[RLC_MAX_SNS/2]; /* block len of history */
struct osmo_timer_list timer;
unsigned int T; /* Txxxx number */
@ -88,6 +184,30 @@ struct gprs_rlcmac_tbf {
unsigned int num_fT_exp; /* number of consecutive fT expirations */
};
extern struct llist_head gprs_rlcmac_tbfs;
int tfi_alloc(uint8_t *_trx, uint8_t *_ts);
struct gprs_rlcmac_tbf *tbf_alloc(uint8_t tfi, uint8_t trx, uint8_t ts);
struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, int direction);
struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli, int direction);
struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn);
int find_free_usf(uint8_t trx, uint8_t ts);
void tbf_free(struct gprs_rlcmac_tbf *tbf);
void tbf_new_state(struct gprs_rlcmac_tbf *tbf,
enum gprs_rlcmac_tbf_state state);
void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
unsigned int seconds, unsigned int microseconds);
void tbf_timer_stop(struct gprs_rlcmac_tbf *tbf);
/* TS 44.060 Section 10.4.7 Table 10.4.7.1: Payload Type field */
enum gprs_rlcmac_block_type {
GPRS_RLCMAC_DATA_BLOCK = 0x0,
@ -96,64 +216,56 @@ enum gprs_rlcmac_block_type {
GPRS_RLCMAC_RESERVED = 0x3
};
extern struct llist_head gprs_rlcmac_tbfs;
int gprs_rlcmac_rcv_block(uint8_t *data, uint8_t len, uint32_t fn);
int select_pdch(uint8_t *_trx, uint8_t *_ts);
int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra,
uint32_t fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
uint8_t tfi, uint8_t usf, uint32_t tlli, uint8_t polling,
uint32_t poll_fn);
int tfi_alloc();
void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi,
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli, uint8_t new_tfi,
uint8_t usf, uint16_t arfcn, uint8_t tn, uint8_t ta, uint8_t tsc,
uint8_t poll);
static struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, gprs_rlcmac_tbf_direction dir);
void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
uint8_t old_downlink, uint8_t new_tfi, uint16_t arfcn,
uint8_t tn, uint8_t ta, uint8_t tsc, uint8_t poll);
static struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli, gprs_rlcmac_tbf_direction dir);
void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf,
uint8_t final);
static void tbf_free(struct gprs_rlcmac_tbf *tbf);
int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf);
static struct tbf_llc_pdu *tbf_llc_pdu_by_num(struct llist_head llc_pdus, uint8_t num);
void tbf_timer_cb(void *_tbf);
int tbf_add_llc_pdu(struct gprs_rlcmac_tbf *tbf, uint8_t *data, uint16_t llc_pdu_len);
struct gprs_rlcmac_tbf *tbf_alloc(gprs_rlcmac_tbf_direction dir, uint32_t tlli = 0);
int tbf_ul_establish(struct gprs_rlcmac_tbf *tbf, uint8_t ra, uint32_t Fn, uint16_t qta);
int tbf_dl_establish(struct gprs_rlcmac_tbf *tbf);
int tbf_ul_data_transfer(struct gprs_rlcmac_tbf *tbf, RlcMacUplinkDataBlock_t * ul_data_block);
int tbf_dl_data_transfer(struct gprs_rlcmac_tbf *tbf, uint8_t *llc_pdu = NULL, uint16_t llc_pdu_len = 0);
int tbf_ul_release(struct gprs_rlcmac_tbf *tbf);
int tbf_dl_release(struct gprs_rlcmac_tbf *tbf);
static void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T, unsigned int seconds);
static void tbf_gsm_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int fT, int frames);
int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra, uint32_t fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc, uint8_t tfi, uint32_t tlli = 0);
void gprs_rlcmac_tx_ul_ack(uint8_t tfi, uint32_t tlli, uint8_t ti, uint8_t bsn);
void gprs_rlcmac_data_block_parse(gprs_rlcmac_tbf* tbf, RlcMacUplinkDataBlock_t * ul_data_block);
int gprs_rlcmac_rcv_data_block(bitvec *rlc_block);
int gprs_rlcmac_rcv_control_block(bitvec *rlc_block);
void gprs_rlcmac_rcv_block(bitvec *rlc_block);
void gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr);
int gprs_rlcmac_poll_timeout(struct gprs_rlcmac_tbf *tbf);
int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
int gprs_rlcmac_tx_llc_pdus(struct gprs_rlcmac_tbf *tbf);
int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint32_t fn);
void gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf);
struct msgb *gprs_rlcmac_send_packet_uplink_assignment(
struct gprs_rlcmac_tbf *tbf, uint32_t fn);
void gprs_rlcmac_downlink_assignment(gprs_rlcmac_tbf *tbf);
struct msgb *gprs_rlcmac_send_packet_downlink_assignment(
struct gprs_rlcmac_tbf *tbf, uint32_t fn);
void gprs_rlcmac_packet_downlink_assignment(gprs_rlcmac_tbf *tbf);
void gprs_rlcmac_trigger_downlink_assignment(gprs_rlcmac_tbf *tbf,
uint8_t old_downlink, char *imsi);
int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final,
uint8_t ssn, uint8_t *rbb);
int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t *data, uint8_t len);
struct msgb *gprs_rlcmac_send_data_block_acknowledged(
struct gprs_rlcmac_tbf *tbf, uint32_t fn);
struct msgb *gprs_rlcmac_send_uplink_ack(struct gprs_rlcmac_tbf *tbf,
uint32_t fn);
int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr);
void gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len);
#endif // GPRS_RLCMAC_H

1409
src/gprs_rlcmac_data.cpp Normal file

File diff suppressed because it is too large Load Diff

184
src/gprs_rlcmac_sched.cpp Normal file
View File

@ -0,0 +1,184 @@
/* PDCH scheduler
*
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <gprs_bssgp_pcu.h>
#include <gprs_rlcmac.h>
#include <pcu_l1_if.h>
extern struct llist_head block_queue;
static uint8_t rlcmac_dl_idle[23] = {
0x47, /* control without optional header octets, no polling, USF=111 */
0x94, /* dummy downlink control message, paging mode 00 */
0x2b, /* no persistance level, 7 bits spare pattern */
0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b
};
int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_pdch *pdch;
struct gprs_rlcmac_tbf *tbf;
uint8_t usf = 0x7;
struct msgb *msg = NULL;
uint32_t poll_fn;
uint8_t i, tfi;
if (trx >= 8 || ts >= 8)
return -EINVAL;
pdch = &bts->trx[trx].pdch[ts];
if (!pdch->enable) {
LOGP(DRLCMACSCHED, LOGL_ERROR, "Received RTS on disabled PDCH: "
"TRX=%d TS=%d\n", trx, ts);
return -EIO;
}
/* store last frame number of RTS */
pdch->last_rts_fn = fn;
/* check uplink ressource for polling */
poll_fn = fn + 4;
if ((block_nr % 3) == 2)
poll_fn ++;
poll_fn = poll_fn % 2715648;
for (tfi = 0; tfi < 32; tfi++) {
tbf = pdch->tbf[tfi];
/* no TBF for this tfi, go next */
if (!tbf)
continue;
/* no polling */
if (tbf->poll_state != GPRS_RLCMAC_POLL_SCHED)
continue;
/* polling for next uplink block */
if (tbf->poll_fn == poll_fn)
break;
}
/* found uplink where a block is polled */
if (tfi < 32) {
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
"TS=%d FN=%d block_nr=%d scheduling free USF for "
"polling at FN=%d of TFI=%d\n", trx, ts, fn, block_nr,
poll_fn, tfi);
/* use free USF */
/* else, we search for uplink ressource */
} else {
/* select uplink ressource */
for (i = 0, tfi = pdch->next_ul_tfi; i < 32;
i++, tfi = (tfi + 1) & 31) {
tbf = pdch->tbf[tfi];
/* no TBF for this tfi, go next */
if (!tbf)
continue;
/* no UL TBF, go next */
if (tbf->direction != GPRS_RLCMAC_UL_TBF)
continue;
/* no UL ressources needed, go next */
/* we don't need to give ressources in FINISHED state,
* because we have received all blocks and only poll
* for packet control ack. */
if (tbf->state != GPRS_RLCMAC_FLOW)
continue;
/* use this USF */
usf = tbf->dir.ul.usf;
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: "
"TRX=%d TS=%d FN=%d block_nr=%d scheduling "
"USF=%d for required uplink ressource of "
"TBF=%d\n", trx, ts, fn, block_nr, usf, tfi);
/* next TBF to handle ressource is the next one */
pdch->next_ul_tfi = (tfi + 1) & 31;
break;
}
}
/* Prio 1: select control message */
for (tfi = 0; tfi < 32; tfi++) {
tbf = pdch->tbf[tfi];
/* no TBF for this tfi, go next */
if (!tbf)
continue;
/* schedule PACKET DOWNLINK ASSIGNMENT */
if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
msg = gprs_rlcmac_send_packet_downlink_assignment(tbf,
fn);
else
/* schedule PACKET UPLINK ASSIGNMENT */
if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
msg = gprs_rlcmac_send_packet_uplink_assignment(tbf,
fn);
else
/* schedule PACKET UPLINK ACK */
if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_SEND_ACK)
msg = gprs_rlcmac_send_uplink_ack(tbf, fn);
if (msg) {
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control "
"message at RTS for TBF=%d\n", tfi);
break;
}
}
/* Prio 2: select data message for downlink */
if (!msg) {
/* select downlink ressource */
for (i = 0, tfi = pdch->next_dl_tfi; i < 32;
i++, tfi = (tfi + 1) & 31) {
tbf = pdch->tbf[tfi];
/* no TBF for this tfi, go next */
if (!tbf)
continue;
/* no DL TBF, go next */
if (tbf->direction != GPRS_RLCMAC_DL_TBF)
continue;
/* no DL ressources needed, go next */
if (tbf->state != GPRS_RLCMAC_FLOW
&& tbf->state != GPRS_RLCMAC_FINISHED)
continue;
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data "
"message at RTS for TBF=%d\n", tfi);
/* next TBF to handle ressource is the next one */
pdch->next_dl_tfi = (tfi + 1) & 31;
/* generate DL data block */
msg = gprs_rlcmac_send_data_block_acknowledged(tbf, fn);
break;
}
}
/* Prio 3: send dummy contol message */
if (!msg) {
msg = msgb_alloc(23, "rlcmac_dl_idle");
if (!msg)
return -ENOMEM;
memcpy(msgb_put(msg, 23), rlcmac_dl_idle, 23);
}
/* msg is now available */
/* set USF */
msg->data[0] = (msg->data[0] & 0xf8) | usf;
// printf("len=%d, date=%s\n", msg->len, osmo_hexdump(msg->data, msg->len));
/* send PDTCH/PACCH to L1 */
pcu_l1if_tx_pdtch(msg, trx, ts, arfcn, fn, block_nr);
return 0;
}

View File

@ -1,415 +0,0 @@
/*
* Copyright 2012 Thomas Cooper <tacooper@vt.edu>
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GSML1PRIM_H
#define GSML1PRIM_H
#include <stdint.h>
typedef enum GsmL1_SubCh_t {
GsmL1_SubCh_NA,
} GsmL1_SubCh_t;
typedef enum GsmL1_Sapi_t {
GsmL1_Sapi_Fcch,
GsmL1_Sapi_Sch,
GsmL1_Sapi_Sacch,
GsmL1_Sapi_Sdcch,
GsmL1_Sapi_Bcch,
GsmL1_Sapi_Pch,
GsmL1_Sapi_Agch,
GsmL1_Sapi_Cbch,
GsmL1_Sapi_Rach,
GsmL1_Sapi_TchF,
GsmL1_Sapi_FacchF,
GsmL1_Sapi_TchH,
GsmL1_Sapi_FacchH,
GsmL1_Sapi_Nch,
GsmL1_Sapi_Pdtch,
GsmL1_Sapi_Pacch,
GsmL1_Sapi_Pbcch,
GsmL1_Sapi_Pagch,
GsmL1_Sapi_Ppch,
GsmL1_Sapi_Pnch,
GsmL1_Sapi_Ptcch,
GsmL1_Sapi_Prach,
GsmL1_Sapi_Idle,
GsmL1_Sapi_NUM,
} GsmL1_Sapi_t;
typedef enum GsmL1_Status_t {
GsmL1_Status_Success,
GsmL1_Status_Generic,
GsmL1_Status_NoMemory,
GsmL1_Status_Timeout,
GsmL1_Status_InvalidParam,
GsmL1_Status_Busy,
GsmL1_Status_NoRessource,
GsmL1_Status_Uninitialized,
GsmL1_Status_NullInterface,
GsmL1_Status_NullFctnPtr,
GsmL1_Status_BadCrc,
GsmL1_Status_BadUsf,
GsmL1_Status_InvalidCPS,
GsmL1_Status_UnexpectedBurst,
GsmL1_Status_UnavailCodec,
GsmL1_Status_CriticalError,
GsmL1_Status_OverheatError,
GsmL1_Status_DeviceError,
GsmL1_Status_FacchError,
GsmL1_Status_AlreadyDeactivated,
GsmL1_Status_TxBurstFifoOvrn,
GsmL1_Status_TxBurstFifoUndr,
GsmL1_Status_NotSynchronized,
GsmL1_Status_Unsupported,
GSML1_STATUS_NUM,
} GsmL1_Status_t;
typedef enum GsmL1_PrimId_t {
GsmL1_PrimId_MphInitReq,
GsmL1_PrimId_MphCloseReq,
GsmL1_PrimId_MphConnectReq,
GsmL1_PrimId_MphDisconnectReq,
GsmL1_PrimId_MphActivateReq,
GsmL1_PrimId_MphDeactivateReq,
GsmL1_PrimId_MphConfigReq,
GsmL1_PrimId_MphMeasureReq,
GsmL1_PrimId_MphInitCnf,
GsmL1_PrimId_MphCloseCnf,
GsmL1_PrimId_MphConnectCnf,
GsmL1_PrimId_MphDisconnectCnf,
GsmL1_PrimId_MphActivateCnf,
GsmL1_PrimId_MphDeactivateCnf,
GsmL1_PrimId_MphConfigCnf,
GsmL1_PrimId_MphMeasureCnf,
GsmL1_PrimId_MphTimeInd,
GsmL1_PrimId_MphSyncInd,
GsmL1_PrimId_PhEmptyFrameReq,
GsmL1_PrimId_PhDataReq,
GsmL1_PrimId_PhConnectInd,
GsmL1_PrimId_PhReadyToSendInd,
GsmL1_PrimId_PhDataInd,
GsmL1_PrimId_PhRaInd,
GsmL1_PrimId_NUM,
} GsmL1_PrimId_t;
typedef enum GsmL1_Dir_t {
GsmL1_Dir_TxDownlink,
GsmL1_Dir_RxUplink,
} GsmL1_Dir_t;
typedef enum GsmL1_DevType_t {
GsmL1_DevType_TxdRxu,
} GsmL1_DevType_t;
typedef enum GsmL1_TchPlType_t {
GsmL1_TchPlType_NA,
GsmL1_TchPlType_Efr,
GsmL1_TchPlType_Fr,
GsmL1_TchPlType_Hr,
GsmL1_TchPlType_Amr,
GsmL1_TchPlType_Amr_SidBad,
GsmL1_TchPlType_Amr_Onset,
GsmL1_TchPlType_Amr_Ratscch,
GsmL1_TchPlType_Amr_SidUpdateInH,
GsmL1_TchPlType_Amr_SidFirstP1,
GsmL1_TchPlType_Amr_SidFirstP2,
GsmL1_TchPlType_Amr_SidFirstInH,
GsmL1_TchPlType_Amr_RatscchMarker,
GsmL1_TchPlType_Amr_RatscchData,
} GsmL1_TchPlType_t;
typedef enum GsmL1_ConfigParamId_t {
GsmL1_ConfigParamId_SetNbTsc,
GsmL1_ConfigParamId_SetTxPowerLevel,
GsmL1_ConfigParamId_SetLogChParams,
GsmL1_ConfigParamId_SetCipheringParams,
} GsmL1_ConfigParamId_t;
typedef struct GsmL1_DeviceParam_t {
enum GsmL1_DevType_t devType;
int freqBand;
uint16_t u16Arfcn;
uint16_t u16BcchArfcn;
uint8_t u8NbTsc;
uint8_t u8Ncc;
float fRxPowerLevel;
float fTxPowerLevel;
} GsmL1_DeviceParam_t;
typedef struct GsmL1_MsgUnitParam_t {
uint8_t u8Buffer[256];
uint8_t u8Size;
} GsmL1_MsgUnitParam_t;
typedef struct GsmL1_MeasParam_t {
float fRssi;
float fLinkQuality;
float fBer;
int16_t i16BurstTiming;
} GsmL1_MeasParam_t;
typedef struct GsmL1_LogChParam_t {
union {
struct {
enum GsmL1_TchPlType_t tchPlType;
enum {
GsmL1_AmrCmiPhase_NA,
GsmL1_AmrCmiPhase_Odd,
} amrCmiPhase;
enum {
GsmL1_AmrCodecMode_Unset,
} amrInitCodecMode;
enum {
GsmL1_AmrCodec_Unset,
GsmL1_AmrCodec_4_75,
GsmL1_AmrCodec_5_15,
GsmL1_AmrCodec_5_9,
GsmL1_AmrCodec_6_7,
GsmL1_AmrCodec_7_4,
GsmL1_AmrCodec_7_95,
GsmL1_AmrCodec_10_2,
GsmL1_AmrCodec_12_2,
} amrActiveCodecSet[8];
} tch;
struct {
uint8_t u8Bsic;
uint8_t u8NbrOfAgch;
} rach;
struct {
uint8_t u8MsPowerLevel;
} sacch;
struct {
uint8_t u8NbrOfAgch;
} agch;
};
} GsmL1_LogChParam_t;
typedef enum GsmL1_LogChComb_t {
GsmL1_LogChComb_0,
GsmL1_LogChComb_I,
GsmL1_LogChComb_II,
GsmL1_LogChComb_IV,
GsmL1_LogChComb_V,
GsmL1_LogChComb_VII,
GsmL1_LogChComb_XIII,
} GsmL1_LogChComb_t;
enum {
GsmL1_FreqBand_850,
GsmL1_FreqBand_900,
GsmL1_FreqBand_1800,
GsmL1_FreqBand_1900,
};
typedef struct GsmL1_MphInitReq_t {
struct GsmL1_DeviceParam_t deviceParam;
} GsmL1_MphInitReq_t;
typedef struct GsmL1_MphCloseReq_t {
uint32_t hLayer1;
} GsmL1_MphCloseReq_t;
typedef struct GsmL1_MphConnectReq_t {
uint32_t hLayer1;
uint8_t u8Tn;
enum GsmL1_LogChComb_t logChComb;
} GsmL1_MphConnectReq_t;
typedef struct GsmL1_MphDisconnectReq_t {
uint32_t hLayer1;
} GsmL1_MphDisconnectReq_t;
typedef struct GsmL1_MphActivateReq_t {
uint32_t hLayer1;
struct GsmL1_LogChParam_t logChPrm;
uint8_t u8Tn;
enum GsmL1_SubCh_t subCh;
enum GsmL1_Dir_t dir;
enum GsmL1_Sapi_t sapi;
uint32_t hLayer2;
float fBFILevel;
} GsmL1_MphActivateReq_t;
typedef struct GsmL1_MphDeactivateReq_t {
uint32_t hLayer1;
uint8_t u8Tn;
enum GsmL1_SubCh_t subCh;
enum GsmL1_Dir_t dir;
enum GsmL1_Sapi_t sapi;
} GsmL1_MphDeactivateReq_t;
typedef struct GsmL1_ConfigParam_t {
struct {
enum GsmL1_Sapi_t sapi;
uint8_t u8Tn;
enum GsmL1_SubCh_t subCh;
enum GsmL1_Dir_t dir;
struct GsmL1_LogChParam_t logChParams;
} setLogChParams;
} GsmL1_ConfigParam_t;
typedef struct GsmL1_MphConfigReq_t {
uint32_t hLayer1;
enum GsmL1_ConfigParamId_t cfgParamId;
struct GsmL1_ConfigParam_t cfgParams;
} GsmL1_MphConfigReq_t;
typedef struct GsmL1_MphConfigCnf_t {
enum GsmL1_Status_t status;
enum GsmL1_ConfigParamId_t cfgParamId;
struct GsmL1_ConfigParam_t cfgParams;
} GsmL1_MphConfigCnf_t;
typedef struct GsmL1_MphMeasureReq_t {
uint32_t hLayer1;
} GsmL1_MphMeasureReq_t;
typedef struct GsmL1_MphInitCnf_t {
uint32_t hLayer1;
enum GsmL1_Status_t status;
} GsmL1_MphInitCnf_t;
typedef struct GsmL1_MphCloseCnf_t {
enum GsmL1_Status_t status;
} GsmL1_MphCloseCnf_t;
typedef struct GsmL1_MphConnectCnf_t {
enum GsmL1_Status_t status;
} GsmL1_MphConnectCnf_t;
typedef struct GsmL1_MphDisconnectCnf_t {
enum GsmL1_Status_t status;
} GsmL1_MphDisconnectCnf_t;
typedef struct GsmL1_MphActivateCnf_t {
enum GsmL1_Status_t status;
uint8_t u8Tn;
int sapi;
} GsmL1_MphActivateCnf_t;
typedef struct GsmL1_MphDeactivateCnf_t {
enum GsmL1_Status_t status;
uint8_t u8Tn;
enum GsmL1_Sapi_t sapi;
} GsmL1_MphDeactivateCnf_t;
typedef struct GsmL1_MphMeasureCnf_t {
enum GsmL1_Status_t status;
} GsmL1_MphMeasureCnf_t;
typedef struct GsmL1_MphTimeInd_t {
uint32_t u32Fn;
} GsmL1_MphTimeInd_t;
typedef struct GsmL1_MphSyncInd_t {
} GsmL1_MphSyncInd_t;
typedef struct GsmL1_PhEmptyFrameReq_t {
uint32_t hLayer1;
uint8_t u8Tn;
uint32_t u32Fn;
enum GsmL1_Sapi_t sapi;
enum GsmL1_SubCh_t subCh;
uint8_t u8BlockNbr;
} GsmL1_PhEmptyFrameReq_t;
typedef struct GsmL1_PhDataReq_t {
uint32_t hLayer1;
uint8_t u8Tn;
uint32_t u32Fn;
enum GsmL1_Sapi_t sapi;
enum GsmL1_SubCh_t subCh;
uint8_t u8BlockNbr;
struct GsmL1_MsgUnitParam_t msgUnitParam;
} GsmL1_PhDataReq_t;
typedef struct GsmL1_PhConnectInd_t {
uint8_t u8Tn;
uint8_t u8Tsc;
uint16_t u16Arfcn;
} GsmL1_PhConnectInd_t;
typedef struct GsmL1_PhReadyToSendInd_t {
uint32_t hLayer1;
uint8_t u8Tn;
uint32_t u32Fn;
enum GsmL1_Sapi_t sapi;
enum GsmL1_SubCh_t subCh;
uint8_t u8BlockNbr;
uint32_t hLayer2;
} GsmL1_PhReadyToSendInd_t;
typedef struct GsmL1_PhDataInd_t {
struct GsmL1_MeasParam_t measParam;
struct GsmL1_MsgUnitParam_t msgUnitParam;
enum GsmL1_Sapi_t sapi;
uint32_t hLayer2;
} GsmL1_PhDataInd_t;
typedef struct GsmL1_PhRaInd_t {
struct GsmL1_MeasParam_t measParam;
struct GsmL1_MsgUnitParam_t msgUnitParam;
uint32_t u32Fn;
uint32_t hLayer2;
} GsmL1_PhRaInd_t;
typedef struct GsmL1_Prim_t {
union {
struct GsmL1_MphInitReq_t mphInitReq;
struct GsmL1_MphCloseReq_t mphCloseReq;
struct GsmL1_MphConnectReq_t mphConnectReq;
struct GsmL1_MphDisconnectReq_t mphDisconnectReq;
struct GsmL1_MphActivateReq_t mphActivateReq;
struct GsmL1_MphDeactivateReq_t mphDeactivateReq;
struct GsmL1_MphConfigReq_t mphConfigReq;
struct GsmL1_MphMeasureReq_t mphMeasureReq;
struct GsmL1_MphInitCnf_t mphInitCnf;
struct GsmL1_MphCloseCnf_t mphCloseCnf;
struct GsmL1_MphConnectCnf_t mphConnectCnf;
struct GsmL1_MphDisconnectCnf_t mphDisconnectCnf;
struct GsmL1_MphActivateCnf_t mphActivateCnf;
struct GsmL1_MphDeactivateCnf_t mphDeactivateCnf;
struct GsmL1_MphConfigCnf_t mphConfigCnf;
struct GsmL1_MphMeasureCnf_t mphMeasureCnf;
struct GsmL1_MphTimeInd_t mphTimeInd;
struct GsmL1_MphSyncInd_t mphSyncInd;
struct GsmL1_PhEmptyFrameReq_t phEmptyFrameReq;
struct GsmL1_PhDataReq_t phDataReq;
struct GsmL1_PhConnectInd_t phConnectInd;
struct GsmL1_PhReadyToSendInd_t phReadyToSendInd;
struct GsmL1_PhDataInd_t phDataInd;
struct GsmL1_PhRaInd_t phRaInd;
} u;
enum GsmL1_PrimId_t id;
} GsmL1_Prim_t;
#endif

183
src/openbts_sock.cpp Normal file
View File

@ -0,0 +1,183 @@
/* openbts_sock.cpp
*
* Copyright (C) 2012 Ivan Klyuchnikov
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <errno.h>
#include <string.h>
#include <gprs_rlcmac.h>
#include <gprs_bssgp_pcu.h>
#include <pcu_l1_if.h>
#include <gprs_debug.h>
#include <bitvector.h>
#include <sys/socket.h>
#include <arpa/inet.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/gsm_utils.h>
#include <pcuif_proto.h>
}
struct femtol1_hdl {
struct gsm_time gsm_time;
uint32_t hLayer1; /* handle to the L1 instance in the DSP */
uint32_t dsp_trace_f;
uint16_t clk_cal;
struct llist_head wlc_list;
void *priv; /* user reference */
struct osmo_timer_list alive_timer;
unsigned int alive_prim_cnt;
struct osmo_fd read_ofd; /* osmo file descriptors */
struct osmo_wqueue write_q;
struct {
uint16_t arfcn;
uint8_t tn;
uint8_t tsc;
uint16_t ta;
} channel_info;
};
struct l1fwd_hdl {
struct sockaddr_storage remote_sa;
socklen_t remote_sa_len;
struct osmo_wqueue udp_wq;
struct femtol1_hdl *fl1h;
};
struct l1fwd_hdl *l1fh = talloc_zero(NULL, struct l1fwd_hdl);
// TODO: We should move this parameters to config file.
#define PCU_L1_IF_PORT 5944
/* OpenBTS socket functions */
int pcu_sock_send(struct msgb *msg)
{
osmo_wqueue_enqueue(&l1fh->udp_wq, msg);
return 0;
}
/* data has arrived on the udp socket */
static int udp_read_cb(struct osmo_fd *ofd)
{
struct msgb *msg = msgb_alloc_headroom(2048, 128, "udp_rx");
struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
struct femtol1_hdl *fl1h = l1fh->fl1h;
int rc;
if (!msg)
return -ENOMEM;
msg->l1h = msg->data;
l1fh->remote_sa_len = sizeof(l1fh->remote_sa);
rc = recvfrom(ofd->fd, msg->l1h, msgb_tailroom(msg), 0,
(struct sockaddr *) &l1fh->remote_sa, &l1fh->remote_sa_len);
if (rc < 0) {
perror("read from udp");
msgb_free(msg);
return rc;
} else if (rc == 0) {
perror("len=0 read from udp");
msgb_free(msg);
return rc;
}
msgb_put(msg, rc);
struct gsm_pcu_if *pcu_prim = (gsm_pcu_if *)(msg->l1h);
rc = pcu_rx(pcu_prim->msg_type, pcu_prim);
return rc;
}
/* callback when we can write to the UDP socket */
static int udp_write_cb(struct osmo_fd *ofd, struct msgb *msg)
{
int rc;
struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
//LOGP(DPCU, LOGL_ERROR, "UDP: Writing %u bytes for MQ_L1_WRITE queue\n", msgb_length(msg));
rc = sendto(ofd->fd, msgb_data(msg), msgb_length(msg), 0,
(const struct sockaddr *)&l1fh->remote_sa, l1fh->remote_sa_len);
if (rc < 0) {
LOGP(DPCU, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
strerror(errno));
return rc;
} else if (rc < (int)msgb_length(msg)) {
LOGP(DPCU, LOGL_ERROR, "short write to L1 msg_queue: "
"%u < %u\n", rc, msgb_length(msg));
return -EIO;
}
return 0;
}
int pcu_l1if_open()
{
struct femtol1_hdl *fl1h;
int rc;
/* allocate new femtol1_handle */
fl1h = talloc_zero(NULL, struct femtol1_hdl);
INIT_LLIST_HEAD(&fl1h->wlc_list);
l1fh->fl1h = fl1h;
fl1h->priv = l1fh;
struct osmo_wqueue * queue = &((l1fh->fl1h)->write_q);
osmo_wqueue_init(queue, 10);
queue->bfd.when |= BSC_FD_READ;
queue->bfd.data = l1fh;
queue->bfd.priv_nr = 0;
/* Open UDP */
struct osmo_wqueue *wq = &l1fh->udp_wq;
osmo_wqueue_init(wq, 10);
wq->write_cb = udp_write_cb;
wq->read_cb = udp_read_cb;
wq->bfd.when |= BSC_FD_READ;
wq->bfd.data = l1fh;
wq->bfd.priv_nr = 0;
rc = osmo_sock_init_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM,
IPPROTO_UDP, NULL, PCU_L1_IF_PORT,
OSMO_SOCK_F_BIND);
if (rc < 0) {
perror("sock_init");
exit(1);
}
return 0;
}
void pcu_l1if_close(void)
{
gprs_bssgp_destroy();
/* FIXME: cleanup l1if */
talloc_free(l1fh->fl1h);
}

View File

@ -1,6 +1,6 @@
/* pcu_l1_if.cpp
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -17,63 +17,25 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/un.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/msgb.h>
}
#include <gprs_rlcmac.h>
#include <pcu_l1_if.h>
#include <gprs_debug.h>
#include <bitvector.h>
#include <gsmL1prim.h>
#include <sys/socket.h>
#include <linux/in.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/gsm_utils.h>
}
#define MAX_UDP_LENGTH 1500
#define msgb_l1prim(msg) ((GsmL1_Prim_t *)(msg)->l1h)
struct femtol1_hdl {
struct gsm_time gsm_time;
uint32_t hLayer1; /* handle to the L1 instance in the DSP */
uint32_t dsp_trace_f;
uint16_t clk_cal;
struct llist_head wlc_list;
void *priv; /* user reference */
struct osmo_timer_list alive_timer;
unsigned int alive_prim_cnt;
struct osmo_fd read_ofd; /* osmo file descriptors */
struct osmo_wqueue write_q;
struct {
uint16_t arfcn;
uint8_t tn;
uint8_t tsc;
uint16_t ta;
} channel_info;
};
struct l1fwd_hdl {
struct sockaddr_storage remote_sa;
socklen_t remote_sa_len;
struct osmo_wqueue udp_wq;
struct femtol1_hdl *fl1h;
};
struct l1fwd_hdl *l1fh = talloc_zero(NULL, struct l1fwd_hdl);
struct pcu_l1if_bts pcu_l1if_bts;
#include <gprs_bssgp_pcu.h>
#include <pcuif_proto.h>
// Variable for storage current FN.
int frame_number;
@ -88,244 +50,398 @@ void set_current_fn(int fn)
frame_number = fn;
}
struct msgb *l1p_msgb_alloc(void)
{
struct msgb *msg = msgb_alloc(sizeof(GsmL1_Prim_t), "l1_prim");
/*
* PCU messages
*/
if (msg)
msg->l1h = msgb_put(msg, sizeof(GsmL1_Prim_t));
struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
msg = msgb_alloc(sizeof(struct gsm_pcu_if), "pcu_sock_tx");
if (!msg)
return NULL;
msgb_put(msg, sizeof(struct gsm_pcu_if));
pcu_prim = (struct gsm_pcu_if *) msg->data;
pcu_prim->msg_type = msg_type;
pcu_prim->bts_nr = bts_nr;
return msg;
}
// Send RLC/MAC block to OpenBTS.
void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
static int pcu_tx_act_req(uint8_t trx, uint8_t ts, uint8_t activate)
{
struct msgb *nmsg = l1p_msgb_alloc();
GsmL1_Prim_t *prim = msgb_l1prim(nmsg);
prim->id = GsmL1_PrimId_PhDataReq;
prim->u.phDataReq.sapi = GsmL1_Sapi_Pdtch;
memcpy(prim->u.phDataReq.msgUnitParam.u8Buffer, msg->data, msg->len);
prim->u.phDataReq.msgUnitParam.u8Size = msg->len;
osmo_wqueue_enqueue(&l1fh->udp_wq, nmsg);
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_act_req *act_req;
LOGP(DL1IF, LOGL_INFO, "Sending %s request: trx=%d ts=%d\n",
(activate) ? "activate" : "deactivate", trx, ts);
msg = pcu_msgb_alloc(PCU_IF_MSG_ACT_REQ, 0);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
act_req = &pcu_prim->u.act_req;
act_req->activate = activate;
act_req->trx_nr = trx;
act_req->ts_nr = ts;
return pcu_sock_send(msg);
}
static int pcu_tx_data_req(uint8_t trx, uint8_t ts, uint8_t sapi,
uint16_t arfcn, uint32_t fn, uint8_t block_nr, uint8_t *data,
uint8_t len)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_data *data_req;
LOGP(DL1IF, LOGL_DEBUG, "Sending data request: trx=%d ts=%d sapi=%d "
"arfcn=%d fn=%d block=%d data=%s\n", trx, ts, sapi, arfcn, fn,
block_nr, osmo_hexdump(data, len));
msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_REQ, 0);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
data_req = &pcu_prim->u.data_req;
data_req->sapi = sapi;
data_req->fn = fn;
data_req->arfcn = arfcn;
data_req->trx_nr = trx;
data_req->ts_nr = ts;
data_req->block_nr = block_nr;
memcpy(data_req->data, data, len);
data_req->len = len;
return pcu_sock_send(msg);
}
void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PDTCH, arfcn, fn, block_nr,
msg->data, msg->len);
msgb_free(msg);
}
void pcu_l1if_tx_agch(bitvec * block, int len)
void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
struct msgb *msg = l1p_msgb_alloc();
GsmL1_Prim_t *prim = msgb_l1prim(msg);
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr,
msg->data, msg->len);
msgb_free(msg);
}
void pcu_l1if_tx_agch(bitvec * block, int plen)
{
uint8_t data[23]; /* prefix PLEN */
prim->id = GsmL1_PrimId_PhDataReq;
prim->u.phDataReq.sapi = GsmL1_Sapi_Agch;
bitvec_pack(block, prim->u.phDataReq.msgUnitParam.u8Buffer);
prim->u.phDataReq.msgUnitParam.u8Size = len;
osmo_wqueue_enqueue(&l1fh->udp_wq, msg);
/* FIXME: why does OpenBTS has no PLEN and no fill in message? */
bitvec_pack(block, data + 1);
data[0] = (plen << 2) | 0x01;
pcu_tx_data_req(0, 0, PCU_IF_SAPI_AGCH, 0, 0, 0, data, 23);
}
void pcu_l1if_tx_pch(bitvec * block, int len)
void pcu_l1if_tx_pch(bitvec * block, int plen, char *imsi)
{
struct msgb *msg = l1p_msgb_alloc();
GsmL1_Prim_t *prim = msgb_l1prim(msg);
prim->id = GsmL1_PrimId_PhDataReq;
prim->u.phDataReq.sapi = GsmL1_Sapi_Pch;
bitvec_pack(block, prim->u.phDataReq.msgUnitParam.u8Buffer);
prim->u.phDataReq.msgUnitParam.u8Size = len;
osmo_wqueue_enqueue(&l1fh->udp_wq, msg);
uint8_t data[23+3]; /* prefix PLEN */
/* paging group */
if (!imsi || strlen(imsi) < 3)
return;
imsi += strlen(imsi) - 3;
data[0] = imsi[0];
data[1] = imsi[1];
data[2] = imsi[2];
bitvec_pack(block, data + 3+1);
data[3] = (plen << 2) | 0x01;
pcu_tx_data_req(0, 0, PCU_IF_SAPI_PCH, 0, 0, 0, data, 23+3);
}
int pcu_l1if_rx_pdch(GsmL1_PhDataInd_t *data_ind)
static void pcu_l1if_tx_bcch(uint8_t *data, int len)
{
bitvec *block = bitvec_alloc(data_ind->msgUnitParam.u8Size);
bitvec_unpack(block, data_ind->msgUnitParam.u8Buffer);
gprs_rlcmac_rcv_block(block);
bitvec_free(block);
return 0;
pcu_tx_data_req(0, 0, PCU_IF_SAPI_BCCH, 0, 0, 0, data, len);
}
static int handle_ph_connect_ind(struct femtol1_hdl *fl1, GsmL1_PhConnectInd_t *connect_ind)
{
pcu_l1if_bts.trx[0].arfcn = connect_ind->u16Arfcn;
pcu_l1if_bts.trx[0].ts[connect_ind->u8Tn].enable = 1;
pcu_l1if_bts.trx[0].ts[connect_ind->u8Tn].tsc = connect_ind->u8Tsc;
(l1fh->fl1h)->channel_info.arfcn = connect_ind->u16Arfcn;
(l1fh->fl1h)->channel_info.tn = connect_ind->u8Tn;
(l1fh->fl1h)->channel_info.tsc = connect_ind->u8Tsc;
LOGP(DL1IF, LOGL_NOTICE, "RX: [ PCU <- BTS ] PhConnectInd: ARFCN: %u TN: %u TSC: %u \n",
connect_ind->u16Arfcn, (unsigned)connect_ind->u8Tn, (unsigned)connect_ind->u8Tsc);
return 0;
}
static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1, GsmL1_PhReadyToSendInd_t *readytosend_ind)
{
gprs_rlcmac_rcv_rts_block(0,0, (l1fh->fl1h)->channel_info.arfcn, readytosend_ind->u32Fn, 0);
return 1;
}
static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_ind)
static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
{
int rc = 0;
LOGP(DL1IF, LOGL_DEBUG, "Data indication received: sapi=%d arfcn=%d "
"block=%d data=%s\n", data_ind->sapi,
data_ind->arfcn, data_ind->block_nr,
osmo_hexdump(data_ind->data, data_ind->len));
switch (data_ind->sapi) {
case GsmL1_Sapi_Rach:
break;
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Pacch:
pcu_l1if_rx_pdch(data_ind);
break;
case GsmL1_Sapi_Pbcch:
case GsmL1_Sapi_Pagch:
case GsmL1_Sapi_Ppch:
case GsmL1_Sapi_Pnch:
case GsmL1_Sapi_Ptcch:
case GsmL1_Sapi_Prach:
case PCU_IF_SAPI_PDTCH:
rc = gprs_rlcmac_rcv_block(data_ind->data, data_ind->len,
data_ind->fn);
break;
default:
LOGP(DL1IF, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %u \n", data_ind->sapi);
break;
LOGP(DL1IF, LOGL_ERROR, "Received PCU data indication with "
"unsupported sapi %d\n", data_ind->sapi);
rc = -EINVAL;
}
return rc;
}
static int handle_ph_ra_ind(struct femtol1_hdl *fl1, GsmL1_PhRaInd_t *ra_ind)
static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
{
int rc = 0;
(l1fh->fl1h)->channel_info.ta = ra_ind->measParam.i16BurstTiming;
rc = gprs_rlcmac_rcv_rach(ra_ind->msgUnitParam.u8Buffer[0], ra_ind->u32Fn, ra_ind->measParam.i16BurstTiming);
return rc;
}
/* handle any random indication from the L1 */
int pcu_l1if_handle_l1prim(struct femtol1_hdl *fl1, struct msgb *msg)
{
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
int rc = 0;
LOGP(DL1IF, LOGL_DEBUG, "RTS request received: trx=%d ts=%d sapi=%d "
"arfcn=%d fn=%d block=%d\n", rts_req->trx_nr, rts_req->ts_nr,
rts_req->sapi, rts_req->arfcn, rts_req->fn, rts_req->block_nr);
switch (l1p->id) {
case GsmL1_PrimId_PhConnectInd:
rc = handle_ph_connect_ind(fl1, &l1p->u.phConnectInd);
switch (rts_req->sapi) {
case PCU_IF_SAPI_PDTCH:
gprs_rlcmac_rcv_rts_block(rts_req->trx_nr, rts_req->ts_nr,
rts_req->arfcn, rts_req->fn, rts_req->block_nr);
break;
case GsmL1_PrimId_PhReadyToSendInd:
rc = handle_ph_readytosend_ind(fl1, &l1p->u.phReadyToSendInd);
break;
case GsmL1_PrimId_PhDataInd:
rc = handle_ph_data_ind(fl1, &l1p->u.phDataInd);
break;
case GsmL1_PrimId_PhRaInd:
rc = handle_ph_ra_ind(fl1, &l1p->u.phRaInd);
case PCU_IF_SAPI_PTCCH:
/* FIXME */
{
struct msgb *msg = msgb_alloc(23, "l1_prim");
memset(msgb_put(msg, 23), 0x2b, 23);
pcu_l1if_tx_ptcch(msg, rts_req->trx_nr, rts_req->ts_nr,
rts_req->arfcn, rts_req->fn, rts_req->block_nr);
}
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received PCU RTS request with "
"unsupported sapi %d\n", rts_req->sapi);
rc = -EINVAL;
}
return rc;
}
static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
{
int rc = 0;
LOGP(DL1IF, LOGL_INFO, "RACH request received: sapi=%d "
"qta=%d, ra=%d, fn=%d\n", rach_ind->sapi, rach_ind->qta,
rach_ind->ra, rach_ind->fn);
switch (rach_ind->sapi) {
case PCU_IF_SAPI_RACH:
rc = gprs_rlcmac_rcv_rach(rach_ind->ra, rach_ind->fn,
rach_ind->qta);
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received PCU rach request with "
"unsupported sapi %d\n", rach_ind->sapi);
rc = -EINVAL;
}
/* Special return value '1' means: do not free */
if (rc != 1)
msgb_free(msg);
return rc;
}
/* OpenBTS socket functions */
// TODO: We should move this parameters to config file.
#define PCU_L1_IF_PORT 5944
/* data has arrived on the udp socket */
static int udp_read_cb(struct osmo_fd *ofd)
static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
{
struct msgb *msg = msgb_alloc_headroom(2048, 128, "udp_rx");
struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
struct femtol1_hdl *fl1h = l1fh->fl1h;
int rc;
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
int rc = 0;
int trx, ts, tfi;
struct gprs_rlcmac_tbf *tbf;
int i;
if (!msg)
return -ENOMEM;
LOGP(DL1IF, LOGL_DEBUG, "Info indication received:\n");
msg->l1h = msg->data;
l1fh->remote_sa_len = sizeof(l1fh->remote_sa);
rc = recvfrom(ofd->fd, msg->l1h, msgb_tailroom(msg), 0,
(struct sockaddr *) &l1fh->remote_sa, &l1fh->remote_sa_len);
if (rc < 0) {
perror("read from udp");
msgb_free(msg);
return rc;
} else if (rc == 0) {
perror("len=0 read from udp");
msgb_free(msg);
return rc;
if (!(info_ind->flags & PCU_IF_FLAG_ACTIVE)) {
LOGP(DL1IF, LOGL_NOTICE, "BTS not available\n");
bssgp_failed:
/* free all TBF */
for (trx = 0; trx < 8; trx++) {
bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
for (ts = 0; ts < 8; ts++) {
for (tfi = 0; tfi < 32; tfi++) {
tbf = bts->trx[trx].pdch[ts].tbf[tfi];
if (tbf)
tbf_free(tbf);
}
}
}
gprs_bssgp_destroy();
return 0;
}
LOGP(DL1IF, LOGL_INFO, "BTS available\n");
LOGP(DL1IF, LOGL_DEBUG, " mcc=%d\n", info_ind->mcc);
LOGP(DL1IF, LOGL_DEBUG, " mnc=%d\n", info_ind->mnc);
LOGP(DL1IF, LOGL_DEBUG, " lac=%d\n", info_ind->lac);
LOGP(DL1IF, LOGL_DEBUG, " rac=%d\n", info_ind->rac);
LOGP(DL1IF, LOGL_DEBUG, " cell_id=%d\n", info_ind->cell_id);
LOGP(DL1IF, LOGL_DEBUG, " nsei=%d\n", info_ind->nsei);
LOGP(DL1IF, LOGL_DEBUG, " nse_timer=%d %d %d %d %d %d %d\n",
info_ind->nse_timer[0], info_ind->nse_timer[1],
info_ind->nse_timer[2], info_ind->nse_timer[3],
info_ind->nse_timer[4], info_ind->nse_timer[5],
info_ind->nse_timer[6]);
LOGP(DL1IF, LOGL_DEBUG, " cell_timer=%d %d %d %d %d %d %d %d %d %d "
"%d\n",
info_ind->cell_timer[0], info_ind->cell_timer[1],
info_ind->cell_timer[2], info_ind->cell_timer[3],
info_ind->cell_timer[4], info_ind->cell_timer[5],
info_ind->cell_timer[6], info_ind->cell_timer[7],
info_ind->cell_timer[8], info_ind->cell_timer[9],
info_ind->cell_timer[10]);
LOGP(DL1IF, LOGL_DEBUG, " repeat_time=%d\n", info_ind->repeat_time);
LOGP(DL1IF, LOGL_DEBUG, " repeat_count=%d\n", info_ind->repeat_count);
LOGP(DL1IF, LOGL_DEBUG, " bvci=%d\n", info_ind->bvci);
LOGP(DL1IF, LOGL_DEBUG, " t3142=%d\n", info_ind->t3142);
LOGP(DL1IF, LOGL_DEBUG, " t3169=%d\n", info_ind->t3169);
LOGP(DL1IF, LOGL_DEBUG, " t3191=%d\n", info_ind->t3191);
LOGP(DL1IF, LOGL_DEBUG, " t3193=%d (ms)\n", info_ind->t3193_10ms * 10);
LOGP(DL1IF, LOGL_DEBUG, " t3195=%d\n", info_ind->t3195);
LOGP(DL1IF, LOGL_DEBUG, " n3101=%d\n", info_ind->n3101);
LOGP(DL1IF, LOGL_DEBUG, " n3103=%d\n", info_ind->n3103);
LOGP(DL1IF, LOGL_DEBUG, " n3105=%d\n", info_ind->n3105);
LOGP(DL1IF, LOGL_DEBUG, " cv_countdown=%d\n", info_ind->cv_countdown);
LOGP(DL1IF, LOGL_DEBUG, " dl_tbf_ext=%d\n", info_ind->dl_tbf_ext);
LOGP(DL1IF, LOGL_DEBUG, " ul_tbf_ext=%d\n", info_ind->ul_tbf_ext);
for (i = 0; i < 4; i++) {
if ((info_ind->flags & (PCU_IF_FLAG_CS1 << i)))
LOGP(DL1IF, LOGL_DEBUG, " Use CS%d\n", i+1);
}
for (i = 0; i < 9; i++) {
if ((info_ind->flags & (PCU_IF_FLAG_MCS1 << i)))
LOGP(DL1IF, LOGL_DEBUG, " Use MCS%d\n", i+1);
}
LOGP(DL1IF, LOGL_DEBUG, " initial_cs=%d\n", info_ind->initial_cs);
LOGP(DL1IF, LOGL_DEBUG, " initial_mcs=%d\n", info_ind->initial_mcs);
LOGP(DL1IF, LOGL_DEBUG, " nsvci=%d\n", info_ind->nsvci[0]);
LOGP(DL1IF, LOGL_DEBUG, " local_port=%d\n", info_ind->local_port[0]);
LOGP(DL1IF, LOGL_DEBUG, " remote_port=%d\n", info_ind->remote_port[0]);
LOGP(DL1IF, LOGL_DEBUG, " remote_ip=%d\n", info_ind->remote_ip[0]);
rc = gprs_bssgp_create(info_ind->remote_ip[0], info_ind->remote_port[0],
info_ind->nsei, info_ind->nsvci[0], info_ind->bvci,
info_ind->mcc, info_ind->mnc, info_ind->lac, info_ind->rac,
info_ind->cell_id);
if (rc < 0) {
LOGP(DL1IF, LOGL_NOTICE, "SGSN not available\n");
goto bssgp_failed;
}
bts->cs1 = !!(info_ind->flags & PCU_IF_FLAG_CS1);
bts->cs2 = !!(info_ind->flags & PCU_IF_FLAG_CS2);
bts->cs3 = !!(info_ind->flags & PCU_IF_FLAG_CS3);
bts->cs4 = !!(info_ind->flags & PCU_IF_FLAG_CS4);
if (!bts->cs1 && !bts->cs2 && !bts->cs3 && !bts->cs4)
bts->cs1 = 1;
if (info_ind->t3142) { /* if timer values are set */
bts->t3142 = info_ind->t3142;
bts->t3169 = info_ind->t3169;
bts->t3191 = info_ind->t3191;
bts->t3193_msec = info_ind->t3193_10ms * 10;
bts->t3195 = info_ind->t3195;
bts->n3101 = info_ind->n3101;
bts->n3103 = info_ind->n3103;
bts->n3105 = info_ind->n3105;
}
if (info_ind->initial_cs < 1 || info_ind->initial_cs > 4)
bts->initial_cs = 1;
else
bts->initial_cs = info_ind->initial_cs;
for (trx = 0; trx < 8; trx++) {
bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
for (ts = 0; ts < 8; ts++) {
if ((info_ind->trx[trx].pdch_mask & (1 << ts))) {
/* FIXME: activate dynamically at RLCMAC */
if (!bts->trx[trx].pdch[ts].enable)
pcu_tx_act_req(trx, ts, 1);
bts->trx[trx].pdch[ts].enable = 1;
bts->trx[trx].pdch[ts].tsc =
info_ind->trx[trx].tsc[ts];
LOGP(DL1IF, LOGL_INFO, "PDCH: trx=%d ts=%d\n",
trx, ts);
} else {
if (bts->trx[trx].pdch[ts].enable)
pcu_tx_act_req(trx, ts, 0);
bts->trx[trx].pdch[ts].enable = 0;
/* kick all tbf FIXME: multislot */
for (tfi = 0; tfi < 32; tfi++) {
tbf = bts->trx[trx].pdch[ts].tbf[tfi];
if (tbf)
tbf_free(tbf);
}
}
}
}
msgb_put(msg, rc);
rc = pcu_l1if_handle_l1prim(fl1h, msg);
return rc;
}
/* callback when we can write to the UDP socket */
static int udp_write_cb(struct osmo_fd *ofd, struct msgb *msg)
static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
{
int rc;
struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
int trx, ts, tfi;
struct gprs_rlcmac_tbf *tbf;
uint32_t elapsed;
uint8_t fn13 = time_ind->fn % 13;
//DEBUGP(DGPRS, "UDP: Writing %u bytes for MQ_L1_WRITE queue\n", msgb_l1len(msg));
/* omit frame numbers not starting at a MAC block */
if (fn13 != 0 && fn13 != 4 && fn13 != 8)
return 0;
rc = sendto(ofd->fd, msg->l1h, msgb_l1len(msg), 0,
(const struct sockaddr *)&l1fh->remote_sa, l1fh->remote_sa_len);
if (rc < 0) {
LOGP(DPCU, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
strerror(errno));
return rc;
} else if (rc < (int)msgb_l1len(msg)) {
LOGP(DPCU, LOGL_ERROR, "short write to L1 msg_queue: "
"%u < %u\n", rc, msgb_l1len(msg));
return -EIO;
LOGP(DL1IF, LOGL_DEBUG, "Time indication received: %d\n",
time_ind->fn % 52);
set_current_fn(time_ind->fn);
/* check for poll timeout */
for (trx = 0; trx < 8; trx++) {
for (ts = 0; ts < 8; ts++) {
for (tfi = 0; tfi < 32; tfi++) {
tbf = bts->trx[trx].pdch[ts].tbf[tfi];
if (!tbf)
continue;
if (tbf->poll_state != GPRS_RLCMAC_POLL_SCHED)
continue;
elapsed = (frame_number - tbf->poll_fn)
% 2715648;
if (elapsed >= 20 && elapsed < 200)
gprs_rlcmac_poll_timeout(tbf);
}
}
}
return 0;
}
int pcu_l1if_open()
int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim)
{
//struct l1fwd_hdl *l1fh;
struct femtol1_hdl *fl1h;
int rc;
int rc = 0;
memset(&pcu_l1if_bts, 0, sizeof(pcu_l1if_bts));
/* allocate new femtol1_handle */
fl1h = talloc_zero(NULL, struct femtol1_hdl);
INIT_LLIST_HEAD(&fl1h->wlc_list);
l1fh->fl1h = fl1h;
fl1h->priv = l1fh;
struct osmo_wqueue * queue = &((l1fh->fl1h)->write_q);
osmo_wqueue_init(queue, 10);
queue->bfd.when |= BSC_FD_READ;
queue->bfd.data = l1fh;
queue->bfd.priv_nr = 0;
/* Open UDP */
struct osmo_wqueue *wq = &l1fh->udp_wq;
osmo_wqueue_init(wq, 10);
wq->write_cb = udp_write_cb;
wq->read_cb = udp_read_cb;
wq->bfd.when |= BSC_FD_READ;
wq->bfd.data = l1fh;
wq->bfd.priv_nr = 0;
rc = osmo_sock_init_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM,
IPPROTO_UDP, NULL, PCU_L1_IF_PORT,
OSMO_SOCK_F_BIND);
if (rc < 0) {
perror("sock_init");
exit(1);
switch (msg_type) {
case PCU_IF_MSG_DATA_IND:
rc = pcu_rx_data_ind(&pcu_prim->u.data_ind);
break;
case PCU_IF_MSG_RTS_REQ:
rc = pcu_rx_rts_req(&pcu_prim->u.rts_req);
break;
case PCU_IF_MSG_RACH_IND:
rc = pcu_rx_rach_ind(&pcu_prim->u.rach_ind);
break;
case PCU_IF_MSG_INFO_IND:
rc = pcu_rx_info_ind(&pcu_prim->u.info_ind);
break;
case PCU_IF_MSG_TIME_IND:
rc = pcu_rx_time_ind(&pcu_prim->u.time_ind);
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received unknwon PCU msg type %d\n",
msg_type);
rc = -EINVAL;
}
return 0;
return rc;
}

View File

@ -29,33 +29,19 @@ extern "C" {
#include <osmocom/gsm/gsm_utils.h>
}
struct pcu_l1if_ts {
uint8_t enable;
uint8_t tsc;
};
struct pcu_l1if_trx {
uint16_t arfcn;
struct pcu_l1if_ts ts[8];
};
struct pcu_l1if_bts {
struct pcu_l1if_trx trx[8];
};
extern struct pcu_l1if_bts pcu_l1if_bts;
int get_current_fn();
void set_current_fn(int fn);
void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr);
void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr);
void pcu_l1if_tx_agch(bitvec * block, int len);
void pcu_l1if_tx_pch(bitvec * block, int len);
void pcu_l1if_tx_pch(bitvec * block, int plen, char *imsi);
int pcu_l1if_open(void);
void pcu_l1if_close(void);
int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim);
int pcu_sock_send(struct msgb *msg);
#endif // PCU_L1_IF_H

View File

@ -20,66 +20,96 @@
#include <gprs_bssgp_pcu.h>
#include <arpa/inet.h>
#include <pcu_l1_if.h>
#include <gprs_rlcmac.h>
#include <gsm_timer.h>
#include <gprs_debug.h>
#include <unistd.h>
#include <getopt.h>
// TODO: We should move this parameters to config file.
#define SGSN_IP "127.0.0.1"
#define SGSN_PORT 23000
#define NSVCI 4
struct gprs_rlcmac_bts *gprs_rlcmac_bts;
extern struct gprs_nsvc *nsvc;
uint16_t spoof_mcc = 0, spoof_mnc = 0;
int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, struct msgb *msg, uint16_t bvci)
static void print_help()
{
int rc = 0;
switch (event) {
case GPRS_NS_EVT_UNIT_DATA:
/* hand the message into the BSSGP implementation */
rc = gprs_bssgp_pcu_rcvmsg(msg);
break;
default:
LOGP(DPCU, LOGL_ERROR, "RLCMAC: Unknown event %u from NS\n", event);
if (msg)
talloc_free(msg);
rc = -EIO;
break;
printf( "Some useful options:\n"
" -h --help this text\n"
" -m --mcc MCC use given MCC instead of value "
"provided by BTS\n"
" -n --mnc MNC use given MNC instead of value "
"provided by BTS\n"
);
}
/* FIXME: finally get some option parsing code into libosmocore */
static void handle_options(int argc, char **argv)
{
while (1) {
int option_idx = 0, c;
static const struct option long_options[] = {
{ "help", 0, 0, 'h' },
{ "mcc", 1, 0, 'm' },
{ "mnc", 1, 0, 'n' },
{ 0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "hm:n:",
long_options, &option_idx);
if (c == -1)
break;
switch (c) {
case 'h':
print_help();
exit(0);
break;
case 'm':
spoof_mcc = atoi(optarg);
break;
case 'n':
spoof_mnc = atoi(optarg);
break;
default:
fprintf(stderr, "Unknown option '%c'\n", c);
exit(0);
break;
}
}
return rc;
}
int main(int argc, char *argv[])
{
uint16_t nsvci = NSVCI;
struct gprs_ns_inst *sgsn_nsi;
struct gprs_nsvc *nsvc;
struct gprs_rlcmac_bts *bts;
int rc;
bts = gprs_rlcmac_bts = talloc_zero(NULL, struct gprs_rlcmac_bts);
if (!gprs_rlcmac_bts)
return -ENOMEM;
gprs_rlcmac_bts->initial_cs = 1;
bts->initial_cs = 1;
bts->cs1 = 1;
bts->t3142 = 20;
bts->t3169 = 5;
bts->t3191 = 5;
bts->t3193_msec = 100;
bts->t3195 = 5;
bts->n3101 = 10;
bts->n3103 = 4;
bts->n3105 = 8;
osmo_init_logging(&gprs_log_info);
pcu_l1if_open();
sgsn_nsi = gprs_ns_instantiate(&sgsn_ns_cb, NULL);
bssgp_nsi = sgsn_nsi;
if (!bssgp_nsi)
{
LOGP(DPCU, LOGL_ERROR, "Unable to instantiate NS\n");
exit(1);
handle_options(argc, argv);
if ((!!spoof_mcc) + (!!spoof_mnc) == 1) {
fprintf(stderr, "--mcc and --mnc must be specified "
"together.\n");
exit(0);
}
bctx = btsctx_alloc(BVCI, NSEI);
bctx->cell_id = CELL_ID;
bctx->nsei = NSEI;
bctx->ra_id.mnc = MNC;
bctx->ra_id.mcc = MCC;
bctx->ra_id.lac = PCU_LAC;
bctx->ra_id.rac = PCU_RAC;
bctx->bvci = BVCI;
uint8_t cause = 39;
gprs_ns_nsip_listen(sgsn_nsi);
rc = pcu_l1if_open();
struct sockaddr_in dest;
dest.sin_family = AF_INET;
dest.sin_port = htons(SGSN_PORT);
inet_aton(SGSN_IP, &dest.sin_addr);
if (rc < 0)
return rc;
nsvc = gprs_ns_nsip_connect(sgsn_nsi, &dest, NSEI, nsvci);
unsigned i = 0;
while (1)
{
osmo_gsm_timers_check();
@ -87,11 +117,10 @@ int main(int argc, char *argv[])
osmo_gsm_timers_update();
osmo_select_main(0);
if (i == 7)
{
bssgp_tx_bvc_reset(bctx, BVCI, cause);
}
i++;
}
}
pcu_l1if_close();
talloc_free(gprs_rlcmac_bts);
return 0;
}

137
src/pcuif_proto.h Normal file
View File

@ -0,0 +1,137 @@
#ifndef _PCUIF_PROTO_H
#define _PCUIF_PROTO_H
/* msg_type */
#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */
#define PCU_IF_MSG_DATA_IND 0x02 /* receive data from given channel */
#define PCU_IF_MSG_RTS_REQ 0x10 /* ready to send data to given chan. */
#define PCU_IF_MSG_RACH_IND 0x22 /* receive rach */
#define PCU_IF_MSG_INFO_IND 0x32 /* retrieve BTS info */
#define PCU_IF_MSG_ACT_REQ 0x40 /* activate/deactivate PDCH */
#define PCU_IF_MSG_TIME_IND 0x52 /* gsm time indication */
/* sapi */
#define PCU_IF_SAPI_RACH 0x01 /* channel request on CCCH */
#define PCU_IF_SAPI_AGCH 0x02 /* assignment on CCCH */
#define PCU_IF_SAPI_PCH 0x03 /* paging request on CCCH */
#define PCU_IF_SAPI_BCCH 0x04 /* SI on BCCH */
#define PCU_IF_SAPI_PDTCH 0x05 /* packet data/control/ccch block */
#define PCU_IF_SAPI_PRACH 0x06 /* packet random access channel */
#define PCU_IF_SAPI_PTCCH 0x07 /* packet TA control channel */
/* flags */
#define PCU_IF_FLAG_ACTIVE (1 << 0)/* BTS is active */
#define PCU_IF_FLAG_SYSMO (1 << 1)/* access PDCH of sysmoBTS directly */
#define PCU_IF_FLAG_CS1 (1 << 16)
#define PCU_IF_FLAG_CS2 (1 << 17)
#define PCU_IF_FLAG_CS3 (1 << 18)
#define PCU_IF_FLAG_CS4 (1 << 19)
#define PCU_IF_FLAG_MCS1 (1 << 20)
#define PCU_IF_FLAG_MCS2 (1 << 21)
#define PCU_IF_FLAG_MCS3 (1 << 22)
#define PCU_IF_FLAG_MCS4 (1 << 23)
#define PCU_IF_FLAG_MCS5 (1 << 24)
#define PCU_IF_FLAG_MCS6 (1 << 25)
#define PCU_IF_FLAG_MCS7 (1 << 26)
#define PCU_IF_FLAG_MCS8 (1 << 27)
#define PCU_IF_FLAG_MCS9 (1 << 28)
struct gsm_pcu_if_data {
uint8_t sapi;
uint8_t len;
uint8_t data[162];
uint32_t fn;
uint16_t arfcn;
uint8_t trx_nr;
uint8_t ts_nr;
uint8_t block_nr;
} __attribute__ ((packed));
struct gsm_pcu_if_rts_req {
uint8_t sapi;
uint8_t spare[3];
uint32_t fn;
uint16_t arfcn;
uint8_t trx_nr;
uint8_t ts_nr;
uint8_t block_nr;
} __attribute__ ((packed));
struct gsm_pcu_if_rach_ind {
uint8_t sapi;
uint8_t ra;
int16_t qta;
uint32_t fn;
uint16_t arfcn;
} __attribute__ ((packed));
struct gsm_pcu_if_info_trx {
uint16_t arfcn;
uint8_t pdch_mask; /* PDCH channels per TS */
uint8_t spare;
uint8_t tsc[8]; /* TSC per channel */
} __attribute__ ((packed));
struct gsm_pcu_if_info_ind {
uint32_t flags;
struct gsm_pcu_if_info_trx trx[8]; /* TRX infos per BTS */
/* RAI */
uint16_t mcc, mnc, lac, rac;
/* NSE */
uint16_t nsei;
uint8_t nse_timer[7];
uint8_t cell_timer[11];
/* cell */
uint16_t cell_id;
uint16_t repeat_time;
uint8_t repeat_count;
uint16_t bvci;
uint8_t t3142;
uint8_t t3169;
uint8_t t3191;
uint8_t t3193_10ms;
uint8_t t3195;
uint8_t n3101;
uint8_t n3103;
uint8_t n3105;
uint8_t cv_countdown;
uint16_t dl_tbf_ext;
uint16_t ul_tbf_ext;
uint8_t initial_cs;
uint8_t initial_mcs;
/* NSVC */
uint16_t nsvci[2];
uint16_t local_port[2];
uint16_t remote_port[2];
uint32_t remote_ip[2];
} __attribute__ ((packed));
struct gsm_pcu_if_act_req {
uint8_t activate;
uint8_t trx_nr;
uint8_t ts_nr;
uint8_t spare;
} __attribute__ ((packed));
struct gsm_pcu_if_time_ind {
uint32_t fn;
} __attribute__ ((packed));
struct gsm_pcu_if {
/* context based information */
uint8_t msg_type; /* message type */
uint8_t bts_nr; /* bts number */
uint8_t spare[2];
union {
struct gsm_pcu_if_data data_req;
struct gsm_pcu_if_data data_ind;
struct gsm_pcu_if_rts_req rts_req;
struct gsm_pcu_if_rach_ind rach_ind;
struct gsm_pcu_if_info_ind info_ind;
struct gsm_pcu_if_act_req act_req;
struct gsm_pcu_if_time_ind time_ind;
} u;
} __attribute__ ((packed));
#endif /* _PCUIF_PROTO_H */

View File

@ -1,562 +0,0 @@
/* sysmo_l1_if.cpp
*
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/un.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/msgb.h>
}
#include <gprs_rlcmac.h>
#include <pcu_l1_if.h>
#include <gprs_debug.h>
#include "../../osmo-bts/include/osmo-bts/pcuif_proto.h"
static int pcu_sock_send(struct msgb *msg);
static void pcu_sock_timeout(void *_priv);
struct pcu_l1if_bts pcu_l1if_bts;
// Variable for storage current FN.
int frame_number;
int get_current_fn()
{
return frame_number;
}
void set_current_fn(int fn)
{
frame_number = fn;
}
/*
* PCU messages
*/
struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
msg = msgb_alloc(sizeof(struct gsm_pcu_if), "pcu_sock_tx");
if (!msg)
return NULL;
msgb_put(msg, sizeof(struct gsm_pcu_if));
pcu_prim = (struct gsm_pcu_if *) msg->data;
pcu_prim->msg_type = msg_type;
pcu_prim->bts_nr = bts_nr;
return msg;
}
static int pcu_tx_act_req(uint8_t trx, uint8_t ts, uint8_t activate)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_act_req *act_req;
LOGP(DL1IF, LOGL_INFO, "Sending %s request: trx=%d ts=%d\n",
(activate) ? "activate" : "deactivate", trx, ts);
msg = pcu_msgb_alloc(PCU_IF_MSG_ACT_REQ, 0);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
act_req = &pcu_prim->u.act_req;
act_req->activate = activate;
act_req->trx_nr = trx;
act_req->ts_nr = ts;
return pcu_sock_send(msg);
}
static int pcu_tx_data_req(uint8_t trx, uint8_t ts, uint8_t sapi,
uint16_t arfcn, uint32_t fn, uint8_t block_nr, uint8_t *data,
uint8_t len)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_data *data_req;
LOGP(DL1IF, LOGL_DEBUG, "Sending data request: trx=%d ts=%d sapi=%d "
"arfcn=%d fn=%d block=%d data=%s\n", trx, ts, sapi, arfcn, fn,
block_nr, osmo_hexdump(data, len));
msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_REQ, 0);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
data_req = &pcu_prim->u.data_req;
data_req->sapi = sapi;
data_req->fn = fn;
data_req->arfcn = arfcn;
data_req->trx_nr = trx;
data_req->ts_nr = ts;
data_req->block_nr = block_nr;
memcpy(data_req->data, data, len);
data_req->len = len;
return pcu_sock_send(msg);
}
void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PDTCH, arfcn, fn, block_nr,
msg->data, msg->len);
msgb_free(msg);
}
void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr,
msg->data, msg->len);
msgb_free(msg);
}
void pcu_l1if_tx_agch(bitvec * block, int len)
{
uint8_t data[24]; /* prefix PLEN */
/* FIXME: why does OpenBTS has no PLEN and no fill in message? */
bitvec_pack(block, data + 1);
data[0] = (len << 2) | 0x01;
pcu_tx_data_req(0, 0, PCU_IF_SAPI_AGCH, 0, 0, 0, data, 23);
}
static void pcu_l1if_tx_bcch(uint8_t *data, int len)
{
pcu_tx_data_req(0, 0, PCU_IF_SAPI_BCCH, 0, 0, 0, data, len);
}
static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
{
int rc = 0;
bitvec *block;
LOGP(DL1IF, LOGL_DEBUG, "Data indication received: sapi=%d arfcn=%d "
"block=%d data=%s\n", data_ind->sapi,
data_ind->arfcn, data_ind->block_nr,
osmo_hexdump(data_ind->data, data_ind->len));
switch (data_ind->sapi) {
case PCU_IF_SAPI_PDTCH:
block = bitvec_alloc(data_ind->len);
if (!block) {
rc = -ENOMEM;
break;
}
bitvec_unpack(block, data_ind->data);
gprs_rlcmac_rcv_block(block);
bitvec_free(block);
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received PCU data indication with "
"unsupported sapi %d\n", data_ind->sapi);
rc = -EINVAL;
}
return rc;
}
static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
{
int rc = 0;
LOGP(DL1IF, LOGL_DEBUG, "RTS request received: trx=%d ts=%d sapi=%d "
"arfcn=%d fn=%d block=%d\n", rts_req->trx_nr, rts_req->ts_nr,
rts_req->sapi, rts_req->arfcn, rts_req->fn, rts_req->block_nr);
switch (rts_req->sapi) {
case PCU_IF_SAPI_PDTCH:
gprs_rlcmac_rcv_rts_block(rts_req->trx_nr, rts_req->ts_nr,
rts_req->arfcn, rts_req->fn, rts_req->block_nr);
break;
case PCU_IF_SAPI_PTCCH:
/* FIXME */
{
struct msgb *msg = msgb_alloc(23, "l1_prim");
memset(msgb_put(msg, 23), 0x2b, 23);
pcu_l1if_tx_ptcch(msg, rts_req->trx_nr, rts_req->ts_nr,
rts_req->arfcn, rts_req->fn, rts_req->block_nr);
}
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received PCU RTS request with "
"unsupported sapi %d\n", rts_req->sapi);
rc = -EINVAL;
}
return rc;
}
static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
{
int rc = 0;
LOGP(DL1IF, LOGL_INFO, "RACH request received: sapi=%d "
"qta=%d, ra=%d, fn=%d\n", rach_ind->sapi, rach_ind->qta,
rach_ind->ra, rach_ind->fn);
switch (rach_ind->sapi) {
case PCU_IF_SAPI_RACH:
rc = gprs_rlcmac_rcv_rach(rach_ind->ra, rach_ind->fn,
rach_ind->qta);
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received PCU rach request with "
"unsupported sapi %d\n", rach_ind->sapi);
rc = -EINVAL;
}
return rc;
}
static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
{
int rc = 0;
int trx, ts;
// uint8_t si13[23];
LOGP(DL1IF, LOGL_INFO, "Info indication received:\n");
if (!(info_ind->flags & PCU_IF_FLAG_ACTIVE)) {
LOGP(DL1IF, LOGL_NOTICE, "BTS not available\n");
return 0;
}
LOGP(DL1IF, LOGL_INFO, "BTS available\n");
for (trx = 0; trx < 8; trx++) {
pcu_l1if_bts.trx[trx].arfcn = info_ind->trx[trx].arfcn;
for (ts = 0; ts < 8; ts++) {
if ((info_ind->trx[trx].pdch_mask & (1 << ts))) {
/* FIXME: activate dynamically at RLCMAC */
if (!pcu_l1if_bts.trx[trx].ts[ts].enable)
pcu_tx_act_req(trx, ts, 1);
pcu_l1if_bts.trx[trx].ts[ts].enable = 1;
pcu_l1if_bts.trx[trx].ts[ts].tsc =
info_ind->trx[trx].tsc[ts];
LOGP(DL1IF, LOGL_INFO, "PDCH: trx=%d ts=%d\n",
trx, ts);
} else {
if (pcu_l1if_bts.trx[trx].ts[ts].enable)
pcu_tx_act_req(trx, ts, 0);
pcu_l1if_bts.trx[trx].ts[ts].enable = 0;
}
}
}
#warning FIXME: RAC
// rc = generate_si13(si13, 0 /* rac */);
// printf("rc=%d\n", rc);
// pcu_l1if_tx_bcch(si13, 23);
return rc;
}
static int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim)
{
int rc = 0;
switch (msg_type) {
case PCU_IF_MSG_DATA_IND:
rc = pcu_rx_data_ind(&pcu_prim->u.data_ind);
break;
case PCU_IF_MSG_RTS_REQ:
rc = pcu_rx_rts_req(&pcu_prim->u.rts_req);
break;
case PCU_IF_MSG_RACH_IND:
rc = pcu_rx_rach_ind(&pcu_prim->u.rach_ind);
break;
case PCU_IF_MSG_INFO_IND:
rc = pcu_rx_info_ind(&pcu_prim->u.info_ind);
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received unknwon PCU msg type %d\n",
msg_type);
rc = -EINVAL;
}
return rc;
}
/*
* SYSMO-PCU socket functions
*/
struct pcu_sock_state {
struct osmo_fd conn_bfd; /* fd for connection to lcr */
struct osmo_timer_list timer; /* socket connect retry timer */
struct llist_head upqueue; /* queue for sending messages */
} *pcu_sock_state = NULL;
static int pcu_sock_send(struct msgb *msg)
{
struct pcu_sock_state *state = pcu_sock_state;
struct osmo_fd *conn_bfd;
if (!state) {
LOGP(DL1IF, LOGL_NOTICE, "PCU socket not created, dropping "
"message\n");
return -EINVAL;
}
conn_bfd = &state->conn_bfd;
if (conn_bfd->fd <= 0) {
LOGP(DL1IF, LOGL_NOTICE, "PCU socket not connected, dropping "
"message\n");
return -EIO;
}
msgb_enqueue(&state->upqueue, msg);
conn_bfd->when |= BSC_FD_WRITE;
return 0;
}
static void pcu_sock_close(struct pcu_sock_state *state)
{
struct osmo_fd *bfd = &state->conn_bfd;
LOGP(DL1IF, LOGL_NOTICE, "PCU socket has LOST connection\n");
close(bfd->fd);
bfd->fd = -1;
osmo_fd_unregister(bfd);
/* flush the queue */
while (!llist_empty(&state->upqueue)) {
struct msgb *msg = msgb_dequeue(&state->upqueue);
msgb_free(msg);
}
/* disable all slots */
memset(&pcu_l1if_bts, 0, sizeof(pcu_l1if_bts));
state->timer.cb = pcu_sock_timeout;
osmo_timer_schedule(&state->timer, 5, 0);
}
static int pcu_sock_read(struct osmo_fd *bfd)
{
struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
struct gsm_pcu_if *pcu_prim;
struct msgb *msg;
int rc;
msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx");
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->tail;
rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
if (rc == 0)
goto close;
if (rc < 0) {
if (errno == EAGAIN)
return 0;
goto close;
}
rc = pcu_rx(pcu_prim->msg_type, pcu_prim);
/* as we always synchronously process the message in pcu_rx() and
* its callbacks, we can free the message here. */
msgb_free(msg);
return rc;
close:
msgb_free(msg);
pcu_sock_close(state);
return -1;
}
static int pcu_sock_write(struct osmo_fd *bfd)
{
struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
int rc;
while (!llist_empty(&state->upqueue)) {
struct msgb *msg, *msg2;
struct gsm_pcu_if *pcu_prim;
/* peek at the beginning of the queue */
msg = llist_entry(state->upqueue.next, struct msgb, list);
pcu_prim = (struct gsm_pcu_if *)msg->data;
bfd->when &= ~BSC_FD_WRITE;
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
LOGP(DL1IF, LOGL_ERROR, "message type (%d) with ZERO "
"bytes!\n", pcu_prim->msg_type);
goto dontsend;
}
/* try to send it over the socket */
rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
if (rc == 0)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
bfd->when |= BSC_FD_WRITE;
break;
}
goto close;
}
dontsend:
/* _after_ we send it, we can deueue */
msg2 = msgb_dequeue(&state->upqueue);
assert(msg == msg2);
msgb_free(msg);
}
return 0;
close:
pcu_sock_close(state);
return -1;
}
static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
{
int rc = 0;
if (flags & BSC_FD_READ)
rc = pcu_sock_read(bfd);
if (rc < 0)
return rc;
if (flags & BSC_FD_WRITE)
rc = pcu_sock_write(bfd);
return rc;
}
int pcu_l1if_open(void)
{
struct pcu_sock_state *state;
struct osmo_fd *bfd;
struct sockaddr_un local;
unsigned int namelen;
int rc;
memset(&pcu_l1if_bts, 0, sizeof(pcu_l1if_bts));
state = pcu_sock_state;
if (!state) {
state = talloc_zero(NULL, struct pcu_sock_state);
if (!state)
return -ENOMEM;
INIT_LLIST_HEAD(&state->upqueue);
}
bfd = &state->conn_bfd;
bfd->fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (bfd->fd < 0) {
LOGP(DL1IF, LOGL_ERROR, "Failed to create PCU-SYSMO socket.\n");
talloc_free(state);
return -1;
}
local.sun_family = AF_UNIX;
strncpy(local.sun_path, "/tmp/pcu_bts", sizeof(local.sun_path));
local.sun_path[sizeof(local.sun_path) - 1] = '\0';
/* we use the same magic that X11 uses in Xtranssock.c for
* calculating the proper length of the sockaddr */
#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
local.sun_len = strlen(local.sun_path);
#endif
#if defined(BSD44SOCKETS) || defined(SUN_LEN)
namelen = SUN_LEN(&local);
#else
namelen = strlen(local.sun_path) +
offsetof(struct sockaddr_un, sun_path);
#endif
rc = connect(bfd->fd, (struct sockaddr *) &local, namelen);
if (rc != 0) {
LOGP(DL1IF, LOGL_ERROR, "Failed to Connect the PCU-SYSMO "
"socket, delaying... '%s'\n", local.sun_path);
close(bfd->fd);
bfd->fd = -1;
state->timer.cb = pcu_sock_timeout;
osmo_timer_schedule(&state->timer, 5, 0);
return -1;
}
bfd->when = BSC_FD_READ;
bfd->cb = pcu_sock_cb;
bfd->data = state;
rc = osmo_fd_register(bfd);
if (rc < 0) {
LOGP(DL1IF, LOGL_ERROR, "Could not register PCU fd: %d\n", rc);
close(bfd->fd);
talloc_free(state);
return rc;
}
LOGP(DL1IF, LOGL_NOTICE, "PCU-SYSMO socket has been connected\n");
pcu_sock_state = state;
return 0;
}
void pcu_l1if_close(void)
{
struct pcu_sock_state *state = pcu_sock_state;
struct osmo_fd *bfd;
if (!state)
return;
if (osmo_timer_pending(&state->timer))
osmo_timer_del(&state->timer);
bfd = &state->conn_bfd;
if (bfd->fd > 0)
pcu_sock_close(state);
talloc_free(state);
pcu_sock_state = NULL;
}
static void pcu_sock_timeout(void *_priv)
{
pcu_l1if_open();
}

303
src/sysmo_sock.cpp Normal file
View File

@ -0,0 +1,303 @@
/* sysmo_sock.cpp
*
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/un.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/msgb.h>
}
#include <gprs_rlcmac.h>
#include <pcu_l1_if.h>
#include <gprs_debug.h>
#include <gprs_bssgp_pcu.h>
#include <pcuif_proto.h>
/*
* SYSMO-PCU socket functions
*/
struct pcu_sock_state {
struct osmo_fd conn_bfd; /* fd for connection to lcr */
struct osmo_timer_list timer; /* socket connect retry timer */
struct llist_head upqueue; /* queue for sending messages */
} *pcu_sock_state = NULL;
static void pcu_sock_timeout(void *_priv);
int pcu_sock_send(struct msgb *msg)
{
struct pcu_sock_state *state = pcu_sock_state;
struct osmo_fd *conn_bfd;
if (!state) {
LOGP(DL1IF, LOGL_NOTICE, "PCU socket not created, dropping "
"message\n");
return -EINVAL;
}
conn_bfd = &state->conn_bfd;
if (conn_bfd->fd <= 0) {
LOGP(DL1IF, LOGL_NOTICE, "PCU socket not connected, dropping "
"message\n");
return -EIO;
}
msgb_enqueue(&state->upqueue, msg);
conn_bfd->when |= BSC_FD_WRITE;
return 0;
}
static void pcu_sock_close(struct pcu_sock_state *state)
{
struct osmo_fd *bfd = &state->conn_bfd;
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_tbf *tbf;
uint8_t trx, ts, tfi;
LOGP(DL1IF, LOGL_NOTICE, "PCU socket has LOST connection\n");
close(bfd->fd);
bfd->fd = -1;
osmo_fd_unregister(bfd);
/* flush the queue */
while (!llist_empty(&state->upqueue)) {
struct msgb *msg = msgb_dequeue(&state->upqueue);
msgb_free(msg);
}
/* disable all slots, kick all TBFs */
for (trx = 0; trx < 8; trx++) {
for (ts = 0; ts < 8; ts++) {
bts->trx[trx].pdch[ts].enable = 0;
for (tfi = 0; tfi < 32; tfi++) {
tbf = bts->trx[trx].pdch[ts].tbf[tfi];
if (tbf)
tbf_free(tbf);
}
}
}
gprs_bssgp_destroy();
state->timer.cb = pcu_sock_timeout;
osmo_timer_schedule(&state->timer, 5, 0);
}
static int pcu_sock_read(struct osmo_fd *bfd)
{
struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
struct gsm_pcu_if *pcu_prim;
struct msgb *msg;
int rc;
msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx");
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->tail;
rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
if (rc == 0)
goto close;
if (rc < 0) {
if (errno == EAGAIN)
return 0;
goto close;
}
rc = pcu_rx(pcu_prim->msg_type, pcu_prim);
/* as we always synchronously process the message in pcu_rx() and
* its callbacks, we can free the message here. */
msgb_free(msg);
return rc;
close:
msgb_free(msg);
pcu_sock_close(state);
return -1;
}
static int pcu_sock_write(struct osmo_fd *bfd)
{
struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
int rc;
while (!llist_empty(&state->upqueue)) {
struct msgb *msg, *msg2;
struct gsm_pcu_if *pcu_prim;
/* peek at the beginning of the queue */
msg = llist_entry(state->upqueue.next, struct msgb, list);
pcu_prim = (struct gsm_pcu_if *)msg->data;
bfd->when &= ~BSC_FD_WRITE;
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
LOGP(DL1IF, LOGL_ERROR, "message type (%d) with ZERO "
"bytes!\n", pcu_prim->msg_type);
goto dontsend;
}
/* try to send it over the socket */
rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
if (rc == 0)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
bfd->when |= BSC_FD_WRITE;
break;
}
goto close;
}
dontsend:
/* _after_ we send it, we can deueue */
msg2 = msgb_dequeue(&state->upqueue);
assert(msg == msg2);
msgb_free(msg);
}
return 0;
close:
pcu_sock_close(state);
return -1;
}
static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
{
int rc = 0;
if (flags & BSC_FD_READ)
rc = pcu_sock_read(bfd);
if (rc < 0)
return rc;
if (flags & BSC_FD_WRITE)
rc = pcu_sock_write(bfd);
return rc;
}
int pcu_l1if_open(void)
{
struct pcu_sock_state *state;
struct osmo_fd *bfd;
struct sockaddr_un local;
unsigned int namelen;
int rc;
state = pcu_sock_state;
if (!state) {
state = talloc_zero(NULL, struct pcu_sock_state);
if (!state)
return -ENOMEM;
INIT_LLIST_HEAD(&state->upqueue);
}
bfd = &state->conn_bfd;
bfd->fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (bfd->fd < 0) {
LOGP(DL1IF, LOGL_ERROR, "Failed to create PCU-SYSMO socket.\n");
talloc_free(state);
return -1;
}
local.sun_family = AF_UNIX;
strncpy(local.sun_path, "/tmp/pcu_bts", sizeof(local.sun_path));
local.sun_path[sizeof(local.sun_path) - 1] = '\0';
/* we use the same magic that X11 uses in Xtranssock.c for
* calculating the proper length of the sockaddr */
#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
local.sun_len = strlen(local.sun_path);
#endif
#if defined(BSD44SOCKETS) || defined(SUN_LEN)
namelen = SUN_LEN(&local);
#else
namelen = strlen(local.sun_path) +
offsetof(struct sockaddr_un, sun_path);
#endif
rc = connect(bfd->fd, (struct sockaddr *) &local, namelen);
if (rc != 0) {
LOGP(DL1IF, LOGL_ERROR, "Failed to Connect the PCU-SYSMO "
"socket, delaying... '%s'\n", local.sun_path);
close(bfd->fd);
bfd->fd = -1;
state->timer.cb = pcu_sock_timeout;
osmo_timer_schedule(&state->timer, 5, 0);
return 0;
}
bfd->when = BSC_FD_READ;
bfd->cb = pcu_sock_cb;
bfd->data = state;
rc = osmo_fd_register(bfd);
if (rc < 0) {
LOGP(DL1IF, LOGL_ERROR, "Could not register PCU fd: %d\n", rc);
close(bfd->fd);
talloc_free(state);
return rc;
}
LOGP(DL1IF, LOGL_NOTICE, "PCU-SYSMO socket has been connected\n");
pcu_sock_state = state;
return 0;
}
void pcu_l1if_close(void)
{
struct pcu_sock_state *state = pcu_sock_state;
struct osmo_fd *bfd;
if (!state)
return;
if (osmo_timer_pending(&state->timer))
osmo_timer_del(&state->timer);
bfd = &state->conn_bfd;
if (bfd->fd > 0)
pcu_sock_close(state);
talloc_free(state);
pcu_sock_state = NULL;
}
static void pcu_sock_timeout(void *_priv)
{
pcu_l1if_open();
}

111
src/tbf.txt Normal file
View File

@ -0,0 +1,111 @@
This document describes the handling of TBFs
--------------------------------------------
Notes:
TBF
Instance for one temporary block flow.
LLC Frame
Current frame to be transmitted or received. If tbf->llc_length is not 0,
the frame exists, if 0, the frame does not exist. If tbf->llc_index is not
0, parts of the frame have been transmitted or received so far.
LLC Queue
Queue of next frames to be transmitted.
States:
GPRS_RLCMAC_ASSIGN
After a downlink TBF is created, it resides in this state until the
block flow can start. This is required to give the mobile time to listen
to connect to downlink PDCH.
GPRS_RLCMAC_FLOW,
During packet flow, this state indicates downlink and uplink TBF block
flow.
GPRS_RLCMAC_FINISHED,
Uplink TBF:
After final block is received AND all other blocks are completely
received, the state is entered. The PACKET CONTROL ACK is still not
received. (Counter N3103 is counted on each poll request.)
Downlink TBF:
All blocks including the final block has been transmitted. Not all
downlink blocks are acknowledged yet. (Counter N3015 is counted on each
poll request.)
GPRS_RLCMAC_WAIT_RELEASE,
The all blocks on downlink TBF have been acked by mobile. The penalty
timer T3192 is running on mobile.
GPRS_RLCMAC_RELEASING,
Wait for TFI/USF to be re-used. This state is entered when a counter
reaches it's maximum and T3169 is running.
When downlink LLC PDU is received:
If downlink TBF exists for given TLLI, but not in RELEASING state:
If downlink TBF is in FLOW or FINISHED state:
Enqueue PDU to LLC Queue of TBF.
Done.
If downlink TBF is in WAIT RELEASE state:
Attach PDU to LLC Frame of TBF.
Put TBF back into FLOW state.
Done.
If dowlink TBF does not exists for given TLLI, or in RELEASING state:
Create new downlink TBF.
Attach PDU to LLC Frame of TBF.
If uplink TBF exists for given TLLI, but not in RELEASING state:
Assign packet channel PACCH.
Done.
If uplink TBF does not exists for given TLLI, or in RELEASING state:
Assign packet channel AGCH.
Done.
When channel request for uplink TBF is received:
Create new uplink TBF.
If channel request was received on RACH
Assign packet channel AGCH.
Done
If channel request was received on PACCH
Assign packet channel PACCH.
Done
Handling of LLC Frame of downlink TBF and LLC Queue of downlink TBF:
If a downlink TBF is created, the LLC PDU is attached to LLC Frame of TBF
(downlink flow is assigned to MS).
If a downlink TBF exists, the LLC PDU is enqueued to LLC Queue of TBF.
During transfer of downlink blocks, the LLC Queue is dequeued whenever all
segments of LLC Frame were used for generation of downlink blocks. (If a
block is partly filled with LLC Frame segment, its remaining space is filled
with the next LLC Frame from the Queue.)
If the transfer is finished, no more LLC Frames are dequeued from the LLC
Queue, in case a new frame has arrived during lifetime of TBF.
If the all downlink blocks have been acknowledged, the LLC Queue is
dequeued and an existing frame is attached to LLC Frame. If so, the new
state is FLOW (downlink flow is assigned to MS), otherwise WAIT RELEASE.
If a new LLC PDU is attached to LLC Frame during WAIT RELEASE state, the
state is changed to FLOW (downlink flow is assigned to MS).
Handling of LLC Frame on uplink TBF:
Received uplink blocks are appended to LLC Frame of TBF. If the PDU is
complete, it is forwarded to the upper layer.
Polling:
In order to poll uplink control block from MS, a special poll state and
frame number is stored at TBF. The scheduler reads that value and will not
assign uplink ressource for other TBFs at that frame number.
When there is no uplink transmission received on the block, a timeout is
indicated by layer 1 interface. There are two ways of checking timeout:
- The received frame is bad (BFI).
- The GSM indicates that the block should have been already received.