Adding direct access to DSP of sysmoBTS for PDTCH traffice

In order to use this feature, sysmoBTS requires option "-M", otherwise
the traffic is forwarded through socket interface.

This is essential, if PCU runs on processor of sysmoBTS. The reaction
time and delay of PDTCH frames could heavily degrade proper packet flow.
This commit is contained in:
Andreas Eversberg 2012-12-18 10:47:28 +01:00
parent 106ea79337
commit a23c7eee15
12 changed files with 1285 additions and 10 deletions

View File

@ -34,6 +34,14 @@ AC_ARG_ENABLE(sysmocom-bts,
AC_MSG_RESULT([$enable_sysmocom_bts])
AM_CONDITIONAL(ENABLE_SYSMOBTS, test "x$enable_sysmocom_bts" = "xyes")
AC_MSG_CHECKING([whether to enable direct DSP access for PDCH of sysmocom-bts])
AC_ARG_ENABLE(sysmocom-dsp,
AC_HELP_STRING([--enable-sysmocom-dsp],
[enable code for sysmocom DSP [default=no]]),
[enable_sysmocom_dsp="yes"],[enable_sysmocom_dsp="no"])
AC_MSG_RESULT([$enable_sysmocom_dsp])
AM_CONDITIONAL(ENABLE_SYSMODSP, test "x$enable_sysmocom_dsp" = "xyes")
AC_OUTPUT(
src/Makefile
Makefile)

View File

@ -19,6 +19,11 @@
#
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS)
if ENABLE_SYSMODSP
AM_CPPFLAGS += -DENABLE_SYSMODSP
endif
AM_CXXFLAGS = -Wall -ldl -pthread
noinst_LTLIBRARIES = libgprs.la
@ -50,6 +55,11 @@ noinst_PROGRAMS = \
bin_PROGRAMS = \
osmo-pcu
if ENABLE_SYSMODSP
noinst_PROGRAMS += \
osmo-pcu-remote
endif
noinst_HEADERS = \
gprs_debug.h \
csn1.h \
@ -60,7 +70,9 @@ noinst_HEADERS = \
pcu_l1_if.h \
gsm_timer.h \
bitvector.h \
pcu_vty.h
pcu_vty.h \
sysmo_l1_if.h \
femtobts.h
RLCMACTest_SOURCES = RLCMACTest.cpp
RLCMACTest_LDADD = \
@ -69,6 +81,18 @@ RLCMACTest_LDADD = \
$(COMMON_LA)
osmo_pcu_SOURCES = pcu_main.cpp
if ENABLE_SYSMODSP
osmo_pcu_SOURCES += sysmo_l1_if.c \
sysmo_l1_hw.c \
femtobts.c
osmo_pcu_remote_SOURCES = pcu_main.cpp \
sysmo_l1_if.c \
sysmo_l1_fwd.c \
femtobts.c
endif
osmo_pcu_LDADD = \
libgprs.la \
$(LIBOSMOGB_LIBS) \
@ -76,4 +100,13 @@ osmo_pcu_LDADD = \
$(LIBOSMOGSM_LIBS) \
$(COMMON_LA)
if ENABLE_SYSMODSP
osmo_pcu_remote_LDADD = \
libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(COMMON_LA)
endif
#MOSTLYCLEANFILES += testSource testDestination

288
src/femtobts.c Normal file
View File

@ -0,0 +1,288 @@
/* sysmocom femtobts L1 API related definitions */
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* 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 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/>.
*
*/
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1const.h>
#include <sysmocom/femtobts/gsml1dbg.h>
#include "femtobts.h"
const enum l1prim_type femtobts_l1prim_type[GsmL1_PrimId_NUM] = {
[GsmL1_PrimId_MphInitReq] = L1P_T_REQ,
[GsmL1_PrimId_MphCloseReq] = L1P_T_REQ,
[GsmL1_PrimId_MphConnectReq] = L1P_T_REQ,
[GsmL1_PrimId_MphDisconnectReq] = L1P_T_REQ,
[GsmL1_PrimId_MphActivateReq] = L1P_T_REQ,
[GsmL1_PrimId_MphDeactivateReq] = L1P_T_REQ,
[GsmL1_PrimId_MphConfigReq] = L1P_T_REQ,
[GsmL1_PrimId_MphMeasureReq] = L1P_T_REQ,
[GsmL1_PrimId_MphInitCnf] = L1P_T_CONF,
[GsmL1_PrimId_MphCloseCnf] = L1P_T_CONF,
[GsmL1_PrimId_MphConnectCnf] = L1P_T_CONF,
[GsmL1_PrimId_MphDisconnectCnf] = L1P_T_CONF,
[GsmL1_PrimId_MphActivateCnf] = L1P_T_CONF,
[GsmL1_PrimId_MphDeactivateCnf] = L1P_T_CONF,
[GsmL1_PrimId_MphConfigCnf] = L1P_T_CONF,
[GsmL1_PrimId_MphMeasureCnf] = L1P_T_CONF,
[GsmL1_PrimId_MphTimeInd] = L1P_T_IND,
[GsmL1_PrimId_MphSyncInd] = L1P_T_IND,
[GsmL1_PrimId_PhEmptyFrameReq] = L1P_T_REQ,
[GsmL1_PrimId_PhDataReq] = L1P_T_REQ,
[GsmL1_PrimId_PhConnectInd] = L1P_T_IND,
[GsmL1_PrimId_PhReadyToSendInd] = L1P_T_IND,
[GsmL1_PrimId_PhDataInd] = L1P_T_IND,
[GsmL1_PrimId_PhRaInd] = L1P_T_IND,
};
const struct value_string femtobts_l1prim_names[GsmL1_PrimId_NUM+1] = {
{ GsmL1_PrimId_MphInitReq, "MPH-INIT.req" },
{ GsmL1_PrimId_MphCloseReq, "MPH-CLOSE.req" },
{ GsmL1_PrimId_MphConnectReq, "MPH-CONNECT.req" },
{ GsmL1_PrimId_MphDisconnectReq,"MPH-DISCONNECT.req" },
{ GsmL1_PrimId_MphActivateReq, "MPH-ACTIVATE.req" },
{ GsmL1_PrimId_MphDeactivateReq,"MPH-DEACTIVATE.req" },
{ GsmL1_PrimId_MphConfigReq, "MPH-CONFIG.req" },
{ GsmL1_PrimId_MphMeasureReq, "MPH-MEASURE.req" },
{ GsmL1_PrimId_MphInitCnf, "MPH-INIT.conf" },
{ GsmL1_PrimId_MphCloseCnf, "MPH-CLOSE.conf" },
{ GsmL1_PrimId_MphConnectCnf, "MPH-CONNECT.conf" },
{ GsmL1_PrimId_MphDisconnectCnf,"MPH-DISCONNECT.conf" },
{ GsmL1_PrimId_MphActivateCnf, "MPH-ACTIVATE.conf" },
{ GsmL1_PrimId_MphDeactivateCnf,"MPH-DEACTIVATE.conf" },
{ GsmL1_PrimId_MphConfigCnf, "MPH-CONFIG.conf" },
{ GsmL1_PrimId_MphMeasureCnf, "MPH-MEASURE.conf" },
{ GsmL1_PrimId_MphTimeInd, "MPH-TIME.ind" },
{ GsmL1_PrimId_MphSyncInd, "MPH-SYNC.ind" },
{ GsmL1_PrimId_PhEmptyFrameReq, "PH-EMPTY_FRAME.req" },
{ GsmL1_PrimId_PhDataReq, "PH-DATA.req" },
{ GsmL1_PrimId_PhConnectInd, "PH-CONNECT.ind" },
{ GsmL1_PrimId_PhReadyToSendInd,"PH-READY_TO_SEND.ind" },
{ GsmL1_PrimId_PhDataInd, "PH-DATA.ind" },
{ GsmL1_PrimId_PhRaInd, "PH-RA.ind" },
{ 0, NULL }
};
const GsmL1_PrimId_t femtobts_l1prim_req2conf[GsmL1_PrimId_NUM] = {
[GsmL1_PrimId_MphInitReq] = GsmL1_PrimId_MphInitCnf,
[GsmL1_PrimId_MphCloseReq] = GsmL1_PrimId_MphCloseCnf,
[GsmL1_PrimId_MphConnectReq] = GsmL1_PrimId_MphConnectCnf,
[GsmL1_PrimId_MphDisconnectReq] = GsmL1_PrimId_MphDisconnectCnf,
[GsmL1_PrimId_MphActivateReq] = GsmL1_PrimId_MphActivateCnf,
[GsmL1_PrimId_MphDeactivateReq] = GsmL1_PrimId_MphDeactivateCnf,
[GsmL1_PrimId_MphConfigReq] = GsmL1_PrimId_MphConfigCnf,
[GsmL1_PrimId_MphMeasureReq] = GsmL1_PrimId_MphMeasureCnf,
};
const enum l1prim_type femtobts_sysprim_type[SuperFemto_PrimId_NUM] = {
[SuperFemto_PrimId_SystemInfoReq] = L1P_T_REQ,
[SuperFemto_PrimId_SystemInfoCnf] = L1P_T_CONF,
[SuperFemto_PrimId_SystemFailureInd] = L1P_T_IND,
[SuperFemto_PrimId_ActivateRfReq] = L1P_T_REQ,
[SuperFemto_PrimId_ActivateRfCnf] = L1P_T_CONF,
[SuperFemto_PrimId_DeactivateRfReq] = L1P_T_REQ,
[SuperFemto_PrimId_DeactivateRfCnf] = L1P_T_CONF,
[SuperFemto_PrimId_SetTraceFlagsReq] = L1P_T_REQ,
[SuperFemto_PrimId_RfClockInfoReq] = L1P_T_REQ,
[SuperFemto_PrimId_RfClockInfoCnf] = L1P_T_CONF,
[SuperFemto_PrimId_RfClockSetupReq] = L1P_T_REQ,
[SuperFemto_PrimId_RfClockSetupCnf] = L1P_T_CONF,
[SuperFemto_PrimId_Layer1ResetReq] = L1P_T_REQ,
[SuperFemto_PrimId_Layer1ResetCnf] = L1P_T_CONF,
};
const struct value_string femtobts_sysprim_names[SuperFemto_PrimId_NUM+1] = {
{ SuperFemto_PrimId_SystemInfoReq, "SYSTEM-INFO.req" },
{ SuperFemto_PrimId_SystemInfoCnf, "SYSTEM-INFO.conf" },
{ SuperFemto_PrimId_SystemFailureInd, "SYSTEM-FAILURE.ind" },
{ SuperFemto_PrimId_ActivateRfReq, "ACTIVATE-RF.req" },
{ SuperFemto_PrimId_ActivateRfCnf, "ACTIVATE-RF.conf" },
{ SuperFemto_PrimId_DeactivateRfReq, "DEACTIVATE-RF.req" },
{ SuperFemto_PrimId_DeactivateRfCnf, "DEACTIVATE-RF.conf" },
{ SuperFemto_PrimId_SetTraceFlagsReq, "SET-TRACE-FLAGS.req" },
{ SuperFemto_PrimId_RfClockInfoReq, "RF-CLOCK-INFO.req" },
{ SuperFemto_PrimId_RfClockInfoCnf, "RF-CLOCK-INFO.conf" },
{ SuperFemto_PrimId_RfClockSetupReq, "RF-CLOCK-SETUP.req" },
{ SuperFemto_PrimId_RfClockSetupCnf, "RF-CLOCK-SETUP.conf" },
{ SuperFemto_PrimId_Layer1ResetReq, "LAYER1-RESET.req" },
{ SuperFemto_PrimId_Layer1ResetCnf, "LAYER1-RESET.conf" },
{ 0, NULL }
};
const SuperFemto_PrimId_t femtobts_sysprim_req2conf[SuperFemto_PrimId_NUM] = {
[SuperFemto_PrimId_SystemInfoReq] = SuperFemto_PrimId_SystemInfoCnf,
[SuperFemto_PrimId_ActivateRfReq] = SuperFemto_PrimId_ActivateRfCnf,
[SuperFemto_PrimId_DeactivateRfReq] = SuperFemto_PrimId_DeactivateRfCnf,
[SuperFemto_PrimId_RfClockInfoReq] = SuperFemto_PrimId_RfClockInfoCnf,
[SuperFemto_PrimId_RfClockSetupReq] = SuperFemto_PrimId_RfClockSetupCnf,
[SuperFemto_PrimId_Layer1ResetReq] = SuperFemto_PrimId_Layer1ResetCnf,
};
const struct value_string femtobts_l1sapi_names[GsmL1_Sapi_NUM+1] = {
{ GsmL1_Sapi_Fcch, "FCCH" },
{ GsmL1_Sapi_Sch, "SCH" },
{ GsmL1_Sapi_Sacch, "SACCH" },
{ GsmL1_Sapi_Sdcch, "SDCCH" },
{ GsmL1_Sapi_Bcch, "BCCH" },
{ GsmL1_Sapi_Pch, "PCH" },
{ GsmL1_Sapi_Agch, "AGCH" },
{ GsmL1_Sapi_Cbch, "CBCH" },
{ GsmL1_Sapi_Rach, "RACH" },
{ GsmL1_Sapi_TchF, "TCH/F" },
{ GsmL1_Sapi_FacchF, "FACCH/F" },
{ GsmL1_Sapi_TchH, "TCH/H" },
{ GsmL1_Sapi_FacchH, "FACCH/H" },
{ GsmL1_Sapi_Nch, "NCH" },
{ GsmL1_Sapi_Pdtch, "PDTCH" },
{ GsmL1_Sapi_Pacch, "PACCH" },
{ GsmL1_Sapi_Pbcch, "PBCCH" },
{ GsmL1_Sapi_Pagch, "PAGCH" },
{ GsmL1_Sapi_Ppch, "PPCH" },
{ GsmL1_Sapi_Pnch, "PNCH" },
{ GsmL1_Sapi_Ptcch, "PTCCH" },
{ GsmL1_Sapi_Prach, "PRACH" },
{ 0, NULL }
};
const struct value_string femtobts_l1status_names[GSML1_STATUS_NUM+1] = {
{ GsmL1_Status_Success, "Success" },
{ GsmL1_Status_Generic, "Generic error" },
{ GsmL1_Status_NoMemory, "Not enough memory" },
{ GsmL1_Status_Timeout, "Timeout" },
{ GsmL1_Status_InvalidParam, "Invalid parameter" },
{ GsmL1_Status_Busy, "Resource busy" },
{ GsmL1_Status_NoRessource, "No more resources" },
{ GsmL1_Status_Uninitialized, "Trying to use uninitialized resource" },
{ GsmL1_Status_NullInterface, "Trying to call a NULL interface" },
{ GsmL1_Status_NullFctnPtr, "Trying to call a NULL function ptr" },
{ GsmL1_Status_BadCrc, "Bad CRC" },
{ GsmL1_Status_BadUsf, "Bad USF" },
{ GsmL1_Status_InvalidCPS, "Invalid CPS field" },
{ GsmL1_Status_UnexpectedBurst, "Unexpected burst" },
{ GsmL1_Status_UnavailCodec, "AMR codec is unavailable" },
{ GsmL1_Status_CriticalError, "Critical error" },
{ GsmL1_Status_OverheatError, "Overheat error" },
{ GsmL1_Status_DeviceError, "Device error" },
{ GsmL1_Status_FacchError, "FACCH / TCH order error" },
{ GsmL1_Status_AlreadyDeactivated, "Lchan already deactivated" },
{ GsmL1_Status_TxBurstFifoOvrn, "FIFO overrun" },
{ GsmL1_Status_TxBurstFifoUndr, "FIFO underrun" },
{ GsmL1_Status_NotSynchronized, "Not synchronized" },
{ GsmL1_Status_Unsupported, "Unsupported feature" },
{ 0, NULL }
};
const struct value_string femtobts_tracef_names[29] = {
{ DBG_DEBUG, "DEBUG" },
{ DBG_L1WARNING, "L1_WARNING" },
{ DBG_ERROR, "ERROR" },
{ DBG_L1RXMSG, "L1_RX_MSG" },
{ DBG_L1RXMSGBYTE, "L1_RX_MSG_BYTE" },
{ DBG_L1TXMSG, "L1_TX_MSG" },
{ DBG_L1TXMSGBYTE, "L1_TX_MSG_BYTE" },
{ DBG_MPHCNF, "MPH_CNF" },
{ DBG_MPHIND, "MPH_IND" },
{ DBG_MPHREQ, "MPH_REQ" },
{ DBG_PHIND, "PH_IND" },
{ DBG_PHREQ, "PH_REQ" },
{ DBG_PHYRF, "PHY_RF" },
{ DBG_PHYRFMSGBYTE, "PHY_MSG_BYTE" },
{ DBG_MODE, "MODE" },
{ DBG_TDMAINFO, "TDMA_INFO" },
{ DBG_BADCRC, "BAD_CRC" },
{ DBG_PHINDBYTE, "PH_IND_BYTE" },
{ DBG_PHREQBYTE, "PH_REQ_BYTE" },
{ DBG_DEVICEMSG, "DEVICE_MSG" },
{ DBG_RACHINFO, "RACH_INFO" },
{ DBG_LOGCHINFO, "LOG_CH_INFO" },
{ DBG_MEMORY, "MEMORY" },
{ DBG_PROFILING, "PROFILING" },
{ DBG_TESTCOMMENT, "TEST_COMMENT" },
{ DBG_TEST, "TEST" },
{ DBG_STATUS, "STATUS" },
{ 0, NULL }
};
const struct value_string femtobts_tch_pl_names[] = {
{ GsmL1_TchPlType_NA, "N/A" },
{ GsmL1_TchPlType_Fr, "FR" },
{ GsmL1_TchPlType_Hr, "HR" },
{ GsmL1_TchPlType_Efr, "EFR" },
{ GsmL1_TchPlType_Amr, "AMR(IF2)" },
{ GsmL1_TchPlType_Amr_SidBad, "AMR(SID BAD)" },
{ GsmL1_TchPlType_Amr_Onset, "AMR(ONSET)" },
{ GsmL1_TchPlType_Amr_Ratscch, "AMR(RATSCCH)" },
{ GsmL1_TchPlType_Amr_SidUpdateInH, "AMR(SID_UPDATE INH)" },
{ GsmL1_TchPlType_Amr_SidFirstP1, "AMR(SID_FIRST P1)" },
{ GsmL1_TchPlType_Amr_SidFirstP2, "AMR(SID_FIRST P2)" },
{ GsmL1_TchPlType_Amr_SidFirstInH, "AMR(SID_FIRST INH)" },
{ GsmL1_TchPlType_Amr_RatscchMarker, "AMR(RATSCCH MARK)" },
{ GsmL1_TchPlType_Amr_RatscchData, "AMR(RATSCCH DATA)" },
{ 0, NULL }
};
const struct value_string femtobts_clksrc_names[] = {
{ SuperFemto_ClkSrcId_None, "None" },
{ SuperFemto_ClkSrcId_Ocxo, "ocxo" },
{ SuperFemto_ClkSrcId_Tcxo, "tcxo" },
{ SuperFemto_ClkSrcId_External, "ext" },
{ SuperFemto_ClkSrcId_GpsPps, "gps" },
{ SuperFemto_ClkSrcId_Trx, "trx" },
{ SuperFemto_ClkSrcId_Rx, "rx" },
{ SuperFemto_ClkSrcId_Edge, "edge" },
{ SuperFemto_ClkSrcId_NetList, "nwl" },
{ 0, NULL }
};
const struct value_string femtobts_dir_names[] = {
{ GsmL1_Dir_TxDownlink, "TxDL" },
{ GsmL1_Dir_TxUplink, "TxUL" },
{ GsmL1_Dir_RxUplink, "RxUL" },
{ GsmL1_Dir_RxDownlink, "RxDL" },
{ GsmL1_Dir_TxDownlink|GsmL1_Dir_RxUplink, "BOTH" },
{ 0, NULL }
};
const struct value_string femtobts_chcomb_names[] = {
{ GsmL1_LogChComb_0, "dummy" },
{ GsmL1_LogChComb_I, "tch_f" },
{ GsmL1_LogChComb_II, "tch_h" },
{ GsmL1_LogChComb_IV, "ccch" },
{ GsmL1_LogChComb_V, "ccch_sdcch4" },
{ GsmL1_LogChComb_VII, "sdcch8" },
{ GsmL1_LogChComb_XIII, "pdtch" },
{ 0, NULL }
};
const uint8_t pdch_msu_size[_NUM_PDCH_CS] = {
[PDCH_CS_1] = 23,
[PDCH_CS_2] = 34,
[PDCH_CS_3] = 40,
[PDCH_CS_4] = 54,
[PDCH_MCS_1] = 27,
[PDCH_MCS_2] = 33,
[PDCH_MCS_3] = 42,
[PDCH_MCS_4] = 49,
[PDCH_MCS_5] = 60,
[PDCH_MCS_6] = 78,
[PDCH_MCS_7] = 118,
[PDCH_MCS_8] = 142,
[PDCH_MCS_9] = 154
};

59
src/femtobts.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef FEMTOBTS_H
#define FEMTOBTS_H
#include <stdlib.h>
#include <osmocom/core/utils.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1const.h>
#ifdef L1_HAS_RTP_MODE
/* This is temporarily disabled, as AMR has some bugs in RTP mode */
//#define USE_L1_RTP_MODE /* Tell L1 to use RTP mode */
#endif
enum l1prim_type {
L1P_T_REQ,
L1P_T_CONF,
L1P_T_IND,
};
const enum l1prim_type femtobts_l1prim_type[GsmL1_PrimId_NUM];
const struct value_string femtobts_l1prim_names[GsmL1_PrimId_NUM+1];
const GsmL1_PrimId_t femtobts_l1prim_req2conf[GsmL1_PrimId_NUM];
const enum l1prim_type femtobts_sysprim_type[SuperFemto_PrimId_NUM];
const struct value_string femtobts_sysprim_names[SuperFemto_PrimId_NUM+1];
const SuperFemto_PrimId_t femtobts_sysprim_req2conf[SuperFemto_PrimId_NUM];
const struct value_string femtobts_l1sapi_names[GsmL1_Sapi_NUM+1];
const struct value_string femtobts_l1status_names[GSML1_STATUS_NUM+1];
const struct value_string femtobts_tracef_names[29];
const struct value_string femtobts_tch_pl_names[15];
const struct value_string femtobts_clksrc_names[8];
const struct value_string femtobts_dir_names[6];
enum pdch_cs {
PDCH_CS_1,
PDCH_CS_2,
PDCH_CS_3,
PDCH_CS_4,
PDCH_MCS_1,
PDCH_MCS_2,
PDCH_MCS_3,
PDCH_MCS_4,
PDCH_MCS_5,
PDCH_MCS_6,
PDCH_MCS_7,
PDCH_MCS_8,
PDCH_MCS_9,
_NUM_PDCH_CS
};
const uint8_t pdch_msu_size[_NUM_PDCH_CS];
#endif /* FEMTOBTS_H */

View File

@ -21,10 +21,14 @@
#define GPRS_DEBUG_H
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h>
#ifdef __cplusplus
};
#endif
/* Debug Areas of the code */
enum {
DCSN1,

View File

@ -58,6 +58,7 @@ struct gprs_rlcmac_pdch {
};
struct gprs_rlcmac_trx {
void *fl1h;
uint16_t arfcn;
struct gprs_rlcmac_pdch pdch[8];
struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */
@ -65,6 +66,7 @@ struct gprs_rlcmac_trx {
};
struct gprs_rlcmac_bts {
uint8_t bsic;
uint8_t fc_interval;
uint8_t cs1;
uint8_t cs2;

View File

@ -37,6 +37,15 @@ extern "C" {
#include <gprs_bssgp_pcu.h>
#include <pcuif_proto.h>
// FIXME: move this, when changed from c++ to c.
extern "C" {
void *l1if_open_pdch(void *priv, uint32_t hlayer1);
int l1if_close_pdch(void *obj);
int l1if_connect_pdch(void *obj, uint8_t ts);
int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len);
}
extern void *tall_pcu_ctx;
// Variable for storage current FN.
@ -126,16 +135,32 @@ static int pcu_tx_data_req(uint8_t trx, uint8_t ts, uint8_t sapi,
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);
#ifdef ENABLE_SYSMODSP
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
if (bts->trx[trx].fl1h)
l1if_pdch_req(bts->trx[trx].fl1h, ts, 0, fn, arfcn, block_nr,
msg->data, msg->len);
else
#endif
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);
#ifdef ENABLE_SYSMODSP
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
if (bts->trx[trx].fl1h)
l1if_pdch_req(bts->trx[trx].fl1h, ts, 1, fn, arfcn, block_nr,
msg->data, msg->len);
else
#endif
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr,
msg->data, msg->len);
msgb_free(msg);
}
@ -166,11 +191,18 @@ void pcu_l1if_tx_pch(bitvec * block, int plen, char *imsi)
pcu_tx_data_req(0, 0, PCU_IF_SAPI_PCH, 0, 0, 0, data, 23+3);
}
// FIXME: remove this, when changed from c++ to c.
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);
}
extern "C" int pcu_rx_data_ind_pdtch(uint8_t trx, uint8_t ts, uint8_t *data,
uint8_t len, uint32_t fn)
{
return gprs_rlcmac_rcv_block(trx, ts, data, len, fn);
}
static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
{
int rc = 0;
@ -182,7 +214,7 @@ static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
switch (data_ind->sapi) {
case PCU_IF_SAPI_PDTCH:
rc = gprs_rlcmac_rcv_block(data_ind->trx_nr, data_ind->ts_nr,
rc = pcu_rx_data_ind_pdtch(data_ind->trx_nr, data_ind->ts_nr,
data_ind->data, data_ind->len, data_ind->fn);
break;
default:
@ -216,6 +248,13 @@ static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf)
return rc;
}
// FIXME: remove this, when changed from c++ to c.
extern "C" int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
return gprs_rlcmac_rcv_rts_block(trx, ts, arfcn, fn, block_nr);
}
static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
{
int rc = 0;
@ -226,7 +265,7 @@ static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
switch (rts_req->sapi) {
case PCU_IF_SAPI_PDTCH:
gprs_rlcmac_rcv_rts_block(rts_req->trx_nr, rts_req->ts_nr,
pcu_rx_rts_req_pdtch(rts_req->trx_nr, rts_req->ts_nr,
rts_req->arfcn, rts_req->fn, rts_req->block_nr);
break;
case PCU_IF_SAPI_PTCCH:
@ -337,6 +376,7 @@ bssgp_failed:
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", ntohs(info_ind->cell_id));
LOGP(DL1IF, LOGL_DEBUG, " bsic=%d\n", info_ind->bsic);
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],
@ -365,6 +405,7 @@ bssgp_failed:
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);
bts->bsic = info_ind->bsic;
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);
@ -415,11 +456,40 @@ bssgp_failed:
for (trx = 0; trx < 8; trx++) {
bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
if ((info_ind->flags & PCU_IF_FLAG_SYSMO)
&& info_ind->trx[trx].hlayer1) {
#ifdef ENABLE_SYSMODSP
LOGP(DL1IF, LOGL_DEBUG, " TRX %d hlayer1=%x\n", trx,
info_ind->trx[trx].hlayer1);
if (!bts->trx[trx].fl1h)
bts->trx[trx].fl1h = l1if_open_pdch(
(void *)trx,
info_ind->trx[trx].hlayer1);
if (!bts->trx[trx].fl1h) {
LOGP(DL1IF, LOGL_FATAL, "Failed to open direct "
"DSP access for PDCH.\n");
exit(0);
}
#else
LOGP(DL1IF, LOGL_FATAL, "Compiled without direct DSP "
"access for PDCH, but enabled at "
"BTS. Please deactivate it!\n");
exit(0);
#endif
#warning close TBD
}
for (ts = 0; ts < 8; ts++) {
pdch = &bts->trx[trx].pdch[ts];
if ((info_ind->trx[trx].pdch_mask & (1 << ts))) {
/* FIXME: activate dynamically at RLCMAC */
if (!pdch->enable) {
#ifdef ENABLE_SYSMODSP
if ((info_ind->flags &
PCU_IF_FLAG_SYSMO))
l1if_connect_pdch(
bts->trx[trx].fl1h, ts);
#endif
pcu_tx_act_req(trx, ts, 1);
INIT_LLIST_HEAD(&pdch->paging_list);
pdch->enable = 1;
@ -449,9 +519,9 @@ static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
/* omit frame numbers not starting at a MAC block */
if (fn13 != 0 && fn13 != 4 && fn13 != 8)
return 0;
LOGP(DL1IF, LOGL_DEBUG, "Time indication received: %d\n",
time_ind->fn % 52);
#warning uncomment
// LOGP(DL1IF, LOGL_DEBUG, "Time indication received: %d\n",
// time_ind->fn % 52);
set_current_fn(time_ind->fn);

View File

@ -21,12 +21,15 @@
#define PCU_L1_IF_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <osmocom/core/write_queue.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/bitvec.h>
#include <osmocom/gsm/gsm_utils.h>
#ifdef __cplusplus
}
int get_current_fn();
@ -44,4 +47,18 @@ 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
#ifdef __cplusplus
extern "C"
#endif
int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr);
#ifdef __cplusplus
extern "C"
#endif
int pcu_rx_data_ind_pdtch(uint8_t trx, uint8_t ts, uint8_t *data,
uint8_t len, uint32_t fn);
#endif // PCU_L1_IF_H

145
src/sysmo_l1_fwd.c Normal file
View File

@ -0,0 +1,145 @@
/* Interface handler for Sysmocom L1 (forwarding) */
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* 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 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/>.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/select.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/gsm_utils.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h>
#include <sysmocom/femtobts/gsml1const.h>
#include <sysmocom/femtobts/gsml1types.h>
#include "gprs_debug.h"
#include "sysmo_l1_if.h"
#define L1FWD_L1_PORT 9999
#define L1FWD_SYS_PORT 9998
#define L1FWD_TCH_PORT 9997
#define L1FWD_PDTCH_PORT 9996
static const uint16_t fwd_udp_ports[] = {
[MQ_SYS_WRITE] = L1FWD_SYS_PORT,
[MQ_L1_WRITE] = L1FWD_L1_PORT,
[MQ_TCH_WRITE] = L1FWD_TCH_PORT,
[MQ_PDTCH_WRITE]= L1FWD_PDTCH_PORT,
};
static int fwd_read_cb(struct osmo_fd *ofd)
{
struct msgb *msg = msgb_alloc_headroom(sizeof(SuperFemto_Prim_t) + 128,
128, "udp_rx");
struct femtol1_hdl *fl1h = ofd->data;
int rc;
if (!msg)
return -ENOMEM;
msg->l1h = msg->data;
rc = read(ofd->fd, msg->l1h, msgb_tailroom(msg));
if (rc < 0) {
LOGP(DL1IF, LOGL_ERROR, "Short read from UDP\n");
msgb_free(msg);
return rc;
} else if (rc == 0) {
LOGP(DL1IF, LOGL_ERROR, "Len=0 from UDP\n");
msgb_free(msg);
return rc;
}
msgb_put(msg, rc);
if (ofd->priv_nr == MQ_SYS_WRITE)
rc = l1if_handle_sysprim(fl1h, msg);
else
rc = l1if_handle_l1prim(ofd->priv_nr, fl1h, msg);
return rc;
}
static int prim_write_cb(struct osmo_fd *ofd, struct msgb *msg)
{
/* write to the fd */
return write(ofd->fd, msg->head, msg->len);
}
int l1if_transport_open(int q, struct femtol1_hdl *fl1h)
{
int rc;
char *bts_host = getenv("L1FWD_BTS_HOST");
printf("sizeof(GsmL1_Prim_t) = %zu\n", sizeof(GsmL1_Prim_t));
printf("sizeof(SuperFemto_Prim_t) = %zu\n", sizeof(SuperFemto_Prim_t));
if (!bts_host) {
fprintf(stderr, "You have to set the L1FWD_BTS_HOST environment variable\n");
exit(2);
}
struct osmo_wqueue *wq = &fl1h->write_q[q];
struct osmo_fd *ofd = &wq->bfd;
osmo_wqueue_init(wq, 10);
wq->write_cb = prim_write_cb;
wq->read_cb = fwd_read_cb;
ofd->data = fl1h;
ofd->priv_nr = q;
ofd->when |= BSC_FD_READ;
rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
bts_host, fwd_udp_ports[q],
OSMO_SOCK_F_CONNECT);
if (rc < 0)
return rc;
return 0;
}
int l1if_transport_close(int q, struct femtol1_hdl *fl1h)
{
struct osmo_wqueue *wq = &fl1h->write_q[q];
struct osmo_fd *ofd = &wq->bfd;
osmo_wqueue_clear(wq);
osmo_fd_unregister(ofd);
close(ofd->fd);
return 0;
}

215
src/sysmo_l1_hw.c Normal file
View File

@ -0,0 +1,215 @@
/* Interface handler for Sysmocom L1 (real hardware) */
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* 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 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/>.
*
*/
#include <assert.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/select.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/gsm_utils.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h>
#include <sysmocom/femtobts/gsml1const.h>
#include <sysmocom/femtobts/gsml1types.h>
#include "gprs_debug.h"
#include "femtobts.h"
#include "sysmo_l1_if.h"
#ifdef HW_SYSMOBTS_V1
#define DEV_SYS_DSP2ARM_NAME "/dev/msgq/femtobts_dsp2arm"
#define DEV_SYS_ARM2DSP_NAME "/dev/msgq/femtobts_arm2dsp"
#define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_dsp2arm"
#define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_arm2dsp"
#else
#define DEV_SYS_DSP2ARM_NAME "/dev/msgq/superfemto_dsp2arm"
#define DEV_SYS_ARM2DSP_NAME "/dev/msgq/superfemto_arm2dsp"
#define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_sig_dsp2arm"
#define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_sig_arm2dsp"
#define DEV_TCH_DSP2ARM_NAME "/dev/msgq/gsml1_tch_dsp2arm"
#define DEV_TCH_ARM2DSP_NAME "/dev/msgq/gsml1_tch_arm2dsp"
#define DEV_PDTCH_DSP2ARM_NAME "/dev/msgq/gsml1_pdtch_dsp2arm"
#define DEV_PDTCH_ARM2DSP_NAME "/dev/msgq/gsml1_pdtch_arm2dsp"
#endif
static const char *rd_devnames[] = {
[MQ_SYS_READ] = DEV_SYS_DSP2ARM_NAME,
[MQ_L1_READ] = DEV_L1_DSP2ARM_NAME,
#ifndef HW_SYSMOBTS_V1
[MQ_TCH_READ] = DEV_TCH_DSP2ARM_NAME,
[MQ_PDTCH_READ] = DEV_PDTCH_DSP2ARM_NAME,
#endif
};
static const char *wr_devnames[] = {
[MQ_SYS_WRITE] = DEV_SYS_ARM2DSP_NAME,
[MQ_L1_WRITE] = DEV_L1_ARM2DSP_NAME,
#ifndef HW_SYSMOBTS_V1
[MQ_TCH_WRITE] = DEV_TCH_ARM2DSP_NAME,
[MQ_PDTCH_WRITE]= DEV_PDTCH_ARM2DSP_NAME,
#endif
};
/* callback when there's something to read from the l1 msg_queue */
static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what)
{
//struct msgb *msg = l1p_msgb_alloc();
struct msgb *msg = msgb_alloc_headroom(sizeof(SuperFemto_Prim_t) + 128,
128, "1l_fd");
struct femtol1_hdl *fl1h = ofd->data;
int rc;
msg->l1h = msg->data;
rc = read(ofd->fd, msg->l1h, msgb_tailroom(msg));
if (rc < 0) {
if (rc != -1)
LOGP(DL1IF, LOGL_ERROR, "error reading from L1 msg_queue: %s\n",
strerror(errno));
msgb_free(msg);
return rc;
}
msgb_put(msg, rc);
switch (ofd->priv_nr) {
case MQ_SYS_WRITE:
if (rc != sizeof(SuperFemto_Prim_t))
LOGP(DL1IF, LOGL_NOTICE, "%u != "
"sizeof(SuperFemto_Prim_t)\n", rc);
return l1if_handle_sysprim(fl1h, msg);
case MQ_L1_WRITE:
#ifndef HW_SYSMOBTS_V1
case MQ_TCH_WRITE:
case MQ_PDTCH_WRITE:
#endif
if (rc != sizeof(GsmL1_Prim_t))
LOGP(DL1IF, LOGL_NOTICE, "%u != "
"sizeof(GsmL1_Prim_t)\n", rc);
return l1if_handle_l1prim(ofd->priv_nr, fl1h, msg);
default:
/* The compiler can't know that priv_nr is an enum. Assist. */
LOGP(DL1IF, LOGL_FATAL, "writing on a wrong queue: %d\n",
ofd->priv_nr);
exit(0);
break;
}
};
/* callback when we can write to one of the l1 msg_queue devices */
static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg)
{
int rc;
rc = write(ofd->fd, msg->l1h, msgb_l1len(msg));
if (rc < 0) {
LOGP(DL1IF, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
strerror(errno));
return rc;
} else if (rc < msg->len) {
LOGP(DL1IF, LOGL_ERROR, "short write to L1 msg_queue: "
"%u < %u\n", rc, msg->len);
return -EIO;
}
return 0;
}
int l1if_transport_open(int q, struct femtol1_hdl *hdl)
{
int rc;
/* Step 1: Open all msg_queue file descriptors */
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
struct osmo_wqueue *wq = &hdl->write_q[q];
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
rc = open(rd_devnames[q], O_RDONLY);
if (rc < 0) {
LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue: %s\n",
strerror(errno));
return rc;
}
read_ofd->fd = rc;
read_ofd->priv_nr = q;
read_ofd->data = hdl;
read_ofd->cb = l1if_fd_cb;
read_ofd->when = BSC_FD_READ;
rc = osmo_fd_register(read_ofd);
if (rc < 0) {
close(read_ofd->fd);
read_ofd->fd = -1;
return rc;
}
rc = open(wr_devnames[q], O_WRONLY);
if (rc < 0) {
LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue: %s\n",
strerror(errno));
goto out_read;
}
osmo_wqueue_init(wq, 10);
wq->write_cb = l1fd_write_cb;
write_ofd->fd = rc;
write_ofd->priv_nr = q;
write_ofd->data = hdl;
write_ofd->when = BSC_FD_WRITE;
rc = osmo_fd_register(write_ofd);
if (rc < 0) {
close(write_ofd->fd);
write_ofd->fd = -1;
goto out_read;
}
return 0;
out_read:
close(hdl->read_ofd[q].fd);
osmo_fd_unregister(&hdl->read_ofd[q]);
return rc;
}
int l1if_transport_close(int q, struct femtol1_hdl *hdl)
{
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
osmo_fd_unregister(read_ofd);
close(read_ofd->fd);
read_ofd->fd = -1;
osmo_fd_unregister(write_ofd);
close(write_ofd->fd);
write_ofd->fd = -1;
return 0;
}

353
src/sysmo_l1_if.c Normal file
View File

@ -0,0 +1,353 @@
#include <string.h>
#include <errno.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h>
#include <sysmocom/femtobts/gsml1const.h>
#include <sysmocom/femtobts/gsml1types.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/timer.h>
#include <sysmo_l1_if.h>
#include <gprs_debug.h>
#include <pcu_l1_if.h>
uint32_t l1if_ts_to_hLayer2(uint8_t trx, uint8_t ts)
{
return (ts << 16) | (trx << 24);
}
/* allocate a msgb containing a GsmL1_Prim_t */
struct msgb *l1p_msgb_alloc(void)
{
struct msgb *msg = msgb_alloc(sizeof(GsmL1_Prim_t), "l1_prim");
if (msg)
msg->l1h = msgb_put(msg, sizeof(GsmL1_Prim_t));
return msg;
}
static int l1if_req_pdch(struct femtol1_hdl *fl1h, struct msgb *msg)
{
struct osmo_wqueue *wqueue = &fl1h->write_q[MQ_PDTCH_WRITE];
osmo_wqueue_enqueue(wqueue, msg);
return 0;
}
static void *prim_init(GsmL1_Prim_t *prim, GsmL1_PrimId_t id, struct femtol1_hdl *gl1)
{
prim->id = id;
/* for some reason the hLayer1 field is not always at the same position
* in the GsmL1_Prim_t, so we have to have this ugly case statement here... */
switch (id) {
case GsmL1_PrimId_MphInitReq:
//prim->u.mphInitReq.hLayer1 = gl1->hLayer1;
break;
case GsmL1_PrimId_MphCloseReq:
prim->u.mphCloseReq.hLayer1 = gl1->hLayer1;
break;
case GsmL1_PrimId_MphConnectReq:
prim->u.mphConnectReq.hLayer1 = gl1->hLayer1;
break;
case GsmL1_PrimId_MphDisconnectReq:
prim->u.mphDisconnectReq.hLayer1 = gl1->hLayer1;
break;
case GsmL1_PrimId_MphActivateReq:
prim->u.mphActivateReq.hLayer1 = gl1->hLayer1;
break;
case GsmL1_PrimId_MphDeactivateReq:
prim->u.mphDeactivateReq.hLayer1 = gl1->hLayer1;
break;
case GsmL1_PrimId_MphConfigReq:
prim->u.mphConfigReq.hLayer1 = gl1->hLayer1;
break;
case GsmL1_PrimId_MphMeasureReq:
prim->u.mphMeasureReq.hLayer1 = gl1->hLayer1;
break;
case GsmL1_PrimId_MphInitCnf:
case GsmL1_PrimId_MphCloseCnf:
case GsmL1_PrimId_MphConnectCnf:
case GsmL1_PrimId_MphDisconnectCnf:
case GsmL1_PrimId_MphActivateCnf:
case GsmL1_PrimId_MphDeactivateCnf:
case GsmL1_PrimId_MphConfigCnf:
case GsmL1_PrimId_MphMeasureCnf:
break;
case GsmL1_PrimId_MphTimeInd:
break;
case GsmL1_PrimId_MphSyncInd:
break;
case GsmL1_PrimId_PhEmptyFrameReq:
prim->u.phEmptyFrameReq.hLayer1 = gl1->hLayer1;
break;
case GsmL1_PrimId_PhDataReq:
prim->u.phDataReq.hLayer1 = gl1->hLayer1;
break;
case GsmL1_PrimId_PhConnectInd:
break;
case GsmL1_PrimId_PhReadyToSendInd:
break;
case GsmL1_PrimId_PhDataInd:
break;
case GsmL1_PrimId_PhRaInd:
break;
default:
LOGP(DL1IF, LOGL_ERROR, "unknown L1 primitive %u\n", id);
break;
}
return &prim->u;
}
struct sapi_dir {
GsmL1_Sapi_t sapi;
GsmL1_Dir_t dir;
};
static const struct sapi_dir pdtch_sapis[] = {
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_RxUplink },
{ GsmL1_Sapi_Ptcch, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_Prach, GsmL1_Dir_RxUplink },
#if 0
{ GsmL1_Sapi_Ptcch, GsmL1_Dir_RxUplink },
{ GsmL1_Sapi_Pacch, GsmL1_Dir_TxDownlink },
#endif
};
/* connect PDTCH */
int l1if_connect_pdch(void *obj, uint8_t ts)
{
struct femtol1_hdl *fl1h = obj;
struct msgb *msg = l1p_msgb_alloc();
GsmL1_MphConnectReq_t *cr;
cr = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphConnectReq, fl1h);
cr->u8Tn = ts;
cr->logChComb = GsmL1_LogChComb_XIII;
return l1if_req_pdch(fl1h, msg);
}
static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1h,
GsmL1_PhReadyToSendInd_t *rts_ind)
{
struct gsm_time g_time;
int rc = 0;
gsm_fn2gsmtime(&g_time, rts_ind->u32Fn);
DEBUGP(DL1IF, "Rx PH-RTS.ind %02u/%02u/%02u SAPI=%s\n",
g_time.t1, g_time.t2, g_time.t3,
get_value_string(femtobts_l1sapi_names, rts_ind->sapi));
switch (rts_ind->sapi) {
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Pacch:
rc = pcu_rx_rts_req_pdtch((long)fl1h->priv, rts_ind->u8Tn,
rts_ind->u16Arfcn, rts_ind->u32Fn, rts_ind->u8BlockNbr);
case GsmL1_Sapi_Ptcch:
// FIXME
default:
break;
}
return rc;
}
static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
GsmL1_PhDataInd_t *data_ind, struct msgb *l1p_msg)
{
int rc = 0;
gsmtap_send(fl1h->gsmtap, data_ind->u16Arfcn | GSMTAP_ARFCN_F_UPLINK,
data_ind->u8Tn, GSMTAP_CHANNEL_PACCH, 0,
data_ind->u32Fn, 0, 0, data_ind->msgUnitParam.u8Buffer,
data_ind->msgUnitParam.u8Size);
DEBUGP(DL1IF, "Rx PH-DATA.ind %s (hL2 %08x): %s",
get_value_string(femtobts_l1sapi_names, data_ind->sapi),
data_ind->hLayer2,
osmo_hexdump(data_ind->msgUnitParam.u8Buffer,
data_ind->msgUnitParam.u8Size));
switch (data_ind->sapi) {
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Pacch:
/* drop incomplete UL block */
if (data_ind->msgUnitParam.u8Buffer[0]
!= GsmL1_PdtchPlType_Full)
break;
/* PDTCH / PACCH frame handling */
pcu_rx_data_ind_pdtch((long)fl1h->priv, data_ind->u8Tn,
data_ind->msgUnitParam.u8Buffer + 1,
data_ind->msgUnitParam.u8Size - 1,
data_ind->u32Fn);
break;
case GsmL1_Sapi_Ptcch:
// FIXME
break;
default:
LOGP(DL1IF, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %s\n",
get_value_string(femtobts_l1sapi_names, data_ind->sapi));
break;
}
return rc;
}
#define MIN_QUAL_RACH 5.0f
static int handle_ph_ra_ind(struct femtol1_hdl *fl1h, GsmL1_PhRaInd_t *ra_ind)
{
uint8_t acc_delay;
if (ra_ind->measParam.fLinkQuality < MIN_QUAL_RACH)
return 0;
DEBUGP(DL1IF, "Rx PH-RA.ind");
/* check for under/overflow / sign */
if (ra_ind->measParam.i16BurstTiming < 0)
acc_delay = 0;
else
acc_delay = ra_ind->measParam.i16BurstTiming >> 2;
#if 0
if (acc_delay > bts->max_ta) {
LOGP(DL1C, LOGL_INFO, "ignoring RACH request %u > max_ta(%u)\n",
acc_delay, btsb->max_ta);
return 0;
}
#endif
return 0;
}
/* handle any random indication from the L1 */
int l1if_handle_l1prim(int wq, struct femtol1_hdl *fl1h, struct msgb *msg)
{
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
int rc = 0;
LOGP(DL1IF, LOGL_DEBUG, "Rx L1 prim %s on queue %d\n",
get_value_string(femtobts_l1prim_names, l1p->id), wq);
switch (l1p->id) {
#if 0
case GsmL1_PrimId_MphTimeInd:
rc = handle_mph_time_ind(fl1h, &l1p->u.mphTimeInd);
break;
case GsmL1_PrimId_MphSyncInd:
break;
case GsmL1_PrimId_PhConnectInd:
break;
#endif
case GsmL1_PrimId_PhReadyToSendInd:
rc = handle_ph_readytosend_ind(fl1h, &l1p->u.phReadyToSendInd);
break;
case GsmL1_PrimId_PhDataInd:
rc = handle_ph_data_ind(fl1h, &l1p->u.phDataInd, msg);
break;
case GsmL1_PrimId_PhRaInd:
rc = handle_ph_ra_ind(fl1h, &l1p->u.phRaInd);
break;
default:
break;
}
msgb_free(msg);
return rc;
}
int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg)
{
return -ENOTSUP;
}
/* send packet data request to L1 */
int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len)
{
struct femtol1_hdl *fl1h = obj;
struct msgb *msg;
GsmL1_Prim_t *l1p;
GsmL1_PhDataReq_t *data_req;
GsmL1_MsgUnitParam_t *msu_param;
struct gsm_time g_time;
gsm_fn2gsmtime(&g_time, fn);
DEBUGP(DL1IF, "TX packet data %02u/%02u/%02u is_ptcch=%d ts=%d "
"block_nr=%d, arfcn=%d, len=%d\n", g_time.t1, g_time.t2,
g_time.t3, is_ptcch, ts, block_nr, arfcn, len);
msg = l1p_msgb_alloc();
l1p = msgb_l1prim(msg);
l1p->id = GsmL1_PrimId_PhDataReq;
data_req = &l1p->u.phDataReq;
data_req->hLayer1 = fl1h->hLayer1;
data_req->sapi = (is_ptcch) ? GsmL1_Sapi_Ptcch : GsmL1_Sapi_Pdtch;
data_req->subCh = GsmL1_SubCh_NA;
data_req->u8BlockNbr = block_nr;
data_req->u8Tn = ts;
data_req->u32Fn = fn;
msu_param = &data_req->msgUnitParam;
msu_param->u8Size = len;
memcpy(msu_param->u8Buffer, data, len);
gsmtap_send(fl1h->gsmtap, arfcn, data_req->u8Tn, GSMTAP_CHANNEL_PACCH,
0, data_req->u32Fn, 0, 0,
data_req->msgUnitParam.u8Buffer,
data_req->msgUnitParam.u8Size);
/* transmit */
osmo_wqueue_enqueue(&fl1h->write_q[MQ_PDTCH_WRITE], msg);
return 0;
}
void *l1if_open_pdch(void *priv, uint32_t hlayer1)
{
struct femtol1_hdl *fl1h;
int rc;
fl1h = talloc_zero(priv, struct femtol1_hdl);
if (!fl1h)
return NULL;
fl1h->hLayer1 = hlayer1;
fl1h->priv = priv;
fl1h->clk_cal = 0;
/* default clock source: OCXO */
fl1h->clk_src = SuperFemto_ClkSrcId_Ocxo;
rc = l1if_transport_open(MQ_PDTCH_WRITE, fl1h);
if (rc < 0) {
talloc_free(fl1h);
return NULL;
}
fl1h->gsmtap = gsmtap_source_init("localhost", GSMTAP_UDP_PORT, 1);
if (fl1h->gsmtap)
gsmtap_source_add_sink(fl1h->gsmtap);
return fl1h;
}
static int l1if_close_pdch(void *obj)
{
struct femtol1_hdl *fl1h = obj;
if (fl1h)
l1if_transport_close(MQ_PDTCH_WRITE, fl1h);
return 0;
}

81
src/sysmo_l1_if.h Normal file
View File

@ -0,0 +1,81 @@
#ifndef _SYSMO_L1_IF_H
#define _SYSMO_L1_IF_H
#include <osmocom/core/select.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/gsm/gsm_utils.h>
#include "femtobts.h"
enum {
MQ_SYS_READ,
MQ_L1_READ,
#ifndef HW_SYSMOBTS_V1
MQ_TCH_READ,
MQ_PDTCH_READ,
#endif
_NUM_MQ_READ
};
enum {
MQ_SYS_WRITE,
MQ_L1_WRITE,
#ifndef HW_SYSMOBTS_V1
MQ_TCH_WRITE,
MQ_PDTCH_WRITE,
#endif
_NUM_MQ_WRITE
};
struct femtol1_hdl {
struct gsm_time gsm_time;
uint32_t hLayer1; /* handle to the L1 instance in the DSP */
uint32_t dsp_trace_f;
int clk_cal;
uint8_t clk_src;
struct llist_head wlc_list;
struct gsmtap_inst *gsmtap;
uint32_t gsmtap_sapi_mask;
void *priv; /* user reference */
struct osmo_timer_list alive_timer;
unsigned int alive_prim_cnt;
struct osmo_fd read_ofd[_NUM_MQ_READ]; /* osmo file descriptors */
struct osmo_wqueue write_q[_NUM_MQ_WRITE];
struct {
uint8_t dsp_version[3];
uint8_t fpga_version[3];
uint32_t band_support; /* bitmask of GSM_BAND_* */
} hw_info;
};
#define msgb_l1prim(msg) ((GsmL1_Prim_t *)(msg)->l1h)
#define msgb_sysprim(msg) ((SuperFemto_Prim_t *)(msg)->l1h)
typedef int l1if_compl_cb(struct msgb *l1_msg, void *data);
/* send a request primitive to the L1 and schedule completion call-back */
int l1if_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
int is_system_prim, l1if_compl_cb *cb, void *data);
int l1if_reset(struct femtol1_hdl *hdl);
int l1if_activate_rf(struct femtol1_hdl *hdl, int on);
int l1if_set_trace_flags(struct femtol1_hdl *hdl, uint32_t flags);
int l1if_set_txpower(struct femtol1_hdl *fl1h, float tx_power);
struct msgb *l1p_msgb_alloc(void);
struct msgb *sysp_msgb_alloc(void);
uint32_t l1if_lchan_to_hLayer2(struct gsm_lchan *lchan);
struct gsm_lchan *l1if_hLayer2_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer2);
/* tch.c */
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan);
#endif /* _SYSMO_L1_IF_H */