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:
commit
ef7f28cc7f
|
@ -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,
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
.gitignore
|
||||
.libs/
|
||||
RLCMACTest
|
||||
libgprs.la
|
||||
pcu
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -31,6 +31,9 @@ enum {
|
|||
DL1IF,
|
||||
DRLCMAC,
|
||||
DRLCMACDATA,
|
||||
DRLCMACDL,
|
||||
DRLCMACUL,
|
||||
DRLCMACSCHED,
|
||||
DBSSGP,
|
||||
DPCU,
|
||||
aDebug_LastEntry
|
||||
|
|
1388
src/gprs_rlcmac.cpp
1388
src/gprs_rlcmac.cpp
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
415
src/gsmL1prim.h
415
src/gsmL1prim.h
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
131
src/pcu_main.cpp
131
src/pcu_main.cpp
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
|
@ -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.
|
||||
|
Loading…
Reference in New Issue