aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac8
-rw-r--r--src/Makefile.am35
-rw-r--r--src/femtobts.c288
-rw-r--r--src/femtobts.h59
-rw-r--r--src/gprs_debug.h4
-rw-r--r--src/gprs_rlcmac.h2
-rw-r--r--src/pcu_l1_if.cpp88
-rw-r--r--src/pcu_l1_if.h17
-rw-r--r--src/sysmo_l1_fwd.c145
-rw-r--r--src/sysmo_l1_hw.c215
-rw-r--r--src/sysmo_l1_if.c353
-rw-r--r--src/sysmo_l1_if.h81
12 files changed, 1285 insertions, 10 deletions
diff --git a/configure.ac b/configure.ac
index c572c880..ee5052bc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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)
diff --git a/src/Makefile.am b/src/Makefile.am
index e52a597b..b4dcfed9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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
diff --git a/src/femtobts.c b/src/femtobts.c
new file mode 100644
index 00000000..69b33be0
--- /dev/null
+++ b/src/femtobts.c
@@ -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
+};
diff --git a/src/femtobts.h b/src/femtobts.h
new file mode 100644
index 00000000..f2ac59dd
--- /dev/null
+++ b/src/femtobts.h
@@ -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 */
diff --git a/src/gprs_debug.h b/src/gprs_debug.h
index 1a5f01a0..abd5d95a 100644
--- a/src/gprs_debug.h
+++ b/src/gprs_debug.h
@@ -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,
diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h
index 24a84e52..1d3fad92 100644
--- a/src/gprs_rlcmac.h
+++ b/src/gprs_rlcmac.h
@@ -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;
diff --git a/src/pcu_l1_if.cpp b/src/pcu_l1_if.cpp
index 3ebe90ba..a4ab1d45 100644
--- a/src/pcu_l1_if.cpp
+++ b/src/pcu_l1_if.cpp
@@ -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);
diff --git a/src/pcu_l1_if.h b/src/pcu_l1_if.h
index f3ac5971..6b889e06 100644
--- a/src/pcu_l1_if.h
+++ b/src/pcu_l1_if.h
@@ -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
diff --git a/src/sysmo_l1_fwd.c b/src/sysmo_l1_fwd.c
new file mode 100644
index 00000000..535a7f00
--- /dev/null
+++ b/src/sysmo_l1_fwd.c
@@ -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;
+}
diff --git a/src/sysmo_l1_hw.c b/src/sysmo_l1_hw.c
new file mode 100644
index 00000000..87c2c55e
--- /dev/null
+++ b/src/sysmo_l1_hw.c
@@ -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;
+}
diff --git a/src/sysmo_l1_if.c b/src/sysmo_l1_if.c
new file mode 100644
index 00000000..7cb1d177
--- /dev/null
+++ b/src/sysmo_l1_if.c
@@ -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;
+}
+
diff --git a/src/sysmo_l1_if.h b/src/sysmo_l1_if.h
new file mode 100644
index 00000000..49bbb62d
--- /dev/null
+++ b/src/sysmo_l1_if.h
@@ -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 */