diff options
-rw-r--r-- | configure.ac | 8 | ||||
-rw-r--r-- | src/Makefile.am | 35 | ||||
-rw-r--r-- | src/femtobts.c | 288 | ||||
-rw-r--r-- | src/femtobts.h | 59 | ||||
-rw-r--r-- | src/gprs_debug.h | 4 | ||||
-rw-r--r-- | src/gprs_rlcmac.h | 2 | ||||
-rw-r--r-- | src/pcu_l1_if.cpp | 88 | ||||
-rw-r--r-- | src/pcu_l1_if.h | 17 | ||||
-rw-r--r-- | src/sysmo_l1_fwd.c | 145 | ||||
-rw-r--r-- | src/sysmo_l1_hw.c | 215 | ||||
-rw-r--r-- | src/sysmo_l1_if.c | 353 | ||||
-rw-r--r-- | src/sysmo_l1_if.h | 81 |
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 */ |