TrueBTS: first compiling L1-only BTS code (TrueBTS)
This is far from being complete, but it should be a nice Layer1-only BTS code. There is no interface to higher layers yet...
This commit is contained in:
parent
1c41c4e0ae
commit
dd9ba70f53
|
@ -66,7 +66,7 @@ class SAPMux {
|
||||||
virtual void writeHighSide(const L2Frame& frame);
|
virtual void writeHighSide(const L2Frame& frame);
|
||||||
virtual void writeLowSide(const L2Frame& frame);
|
virtual void writeLowSide(const L2Frame& frame);
|
||||||
|
|
||||||
void upstream( L2DL * wUpstream, unsigned wSAPI=0 )
|
virtual void upstream( L2DL * wUpstream, unsigned wSAPI=0 )
|
||||||
{ assert(mUpstream[wSAPI]==NULL); mUpstream[wSAPI]=wUpstream; }
|
{ assert(mUpstream[wSAPI]==NULL); mUpstream[wSAPI]=wUpstream; }
|
||||||
void downstream( L1FEC * wDownstream )
|
void downstream( L1FEC * wDownstream )
|
||||||
{ assert(mDownstream==NULL); mDownstream=wDownstream; }
|
{ assert(mDownstream==NULL); mDownstream=wDownstream; }
|
||||||
|
|
|
@ -93,6 +93,11 @@ MAKE_TDMA_MAPPING(CCCH_3,TDMA_BEACON_CCCH,true,false,0x55,true,51);
|
||||||
|
|
||||||
// TODO -- Other CCCH subchannels 4-8 for support of C-IV.
|
// TODO -- Other CCCH subchannels 4-8 for support of C-IV.
|
||||||
|
|
||||||
|
const TDMAMapping GSM::gCCCH[4] = {
|
||||||
|
GSM::gCCCH_0Mapping, GSM::gCCCH_1Mapping,
|
||||||
|
GSM::gCCCH_2Mapping, GSM::gCCCH_3Mapping
|
||||||
|
};
|
||||||
|
|
||||||
const unsigned SDCCH_4_0DFrames[] = {22,23,24,25};
|
const unsigned SDCCH_4_0DFrames[] = {22,23,24,25};
|
||||||
MAKE_TDMA_MAPPING(SDCCH_4_0D,SDCCH_4_0,true,false,0x01,true,51);
|
MAKE_TDMA_MAPPING(SDCCH_4_0D,SDCCH_4_0,true,false,0x01,true,51);
|
||||||
|
|
||||||
|
|
|
@ -134,6 +134,9 @@ extern const TDMAMapping gCCCH_5Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5
|
||||||
extern const TDMAMapping gCCCH_6Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B6
|
extern const TDMAMapping gCCCH_6Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B6
|
||||||
extern const TDMAMapping gCCCH_7Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B7
|
extern const TDMAMapping gCCCH_7Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B7
|
||||||
extern const TDMAMapping gCCCH_8Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B8
|
extern const TDMAMapping gCCCH_8Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B8
|
||||||
|
|
||||||
|
extern const TDMAMapping gCCCH[4];
|
||||||
|
|
||||||
//@}
|
//@}
|
||||||
/**@name SDCCH */
|
/**@name SDCCH */
|
||||||
//@{
|
//@{
|
||||||
|
|
|
@ -32,7 +32,9 @@ libGSML1_la_SOURCES = \
|
||||||
GSML1FEC.cpp \
|
GSML1FEC.cpp \
|
||||||
GSMTDMA.cpp \
|
GSMTDMA.cpp \
|
||||||
GSMTransfer.cpp \
|
GSMTransfer.cpp \
|
||||||
|
GSMSAPMux.cpp \
|
||||||
OsmoSAPMux.cpp \
|
OsmoSAPMux.cpp \
|
||||||
|
OsmoLogicalChannel.cpp \
|
||||||
GSMTAPDump.cpp
|
GSMTAPDump.cpp
|
||||||
|
|
||||||
libGSM_la_SOURCES = \
|
libGSM_la_SOURCES = \
|
||||||
|
@ -51,7 +53,6 @@ libGSM_la_SOURCES = \
|
||||||
GSML3RRElements.cpp \
|
GSML3RRElements.cpp \
|
||||||
GSML3RRMessages.cpp \
|
GSML3RRMessages.cpp \
|
||||||
GSMLogicalChannel.cpp \
|
GSMLogicalChannel.cpp \
|
||||||
GSMSAPMux.cpp \
|
|
||||||
PowerManager.cpp
|
PowerManager.cpp
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
/**@file Osmocom Logical Channel. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||||
|
* Copyright 2011 Harald Welte <laforge@gnumonks.org>
|
||||||
|
*
|
||||||
|
* This software is distributed under the terms of the GNU Affero Public License.
|
||||||
|
* See the COPYING file in the main directory for details.
|
||||||
|
*
|
||||||
|
* This use of this software may be subject to additional restrictions.
|
||||||
|
* See the LEGAL file in the main directory for details.
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "OsmoLogicalChannel.h"
|
||||||
|
#include "OsmoThreadMuxer.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace GSM;
|
||||||
|
|
||||||
|
|
||||||
|
ARFCNManager *OsmoTS::getARFCNmgr()
|
||||||
|
{
|
||||||
|
TransceiverManager *trxmgr = getTRX()->getTRXmgr();
|
||||||
|
return trxmgr->ARFCN(getTSnr());
|
||||||
|
}
|
||||||
|
|
||||||
|
OsmoTS::OsmoTS(OsmoTRX &trx, unsigned int ts_nr, unsigned comb)
|
||||||
|
{
|
||||||
|
assert(ts_nr < 8);
|
||||||
|
mComb = comb;
|
||||||
|
mTSnr = ts_nr;
|
||||||
|
mTRX = &trx;
|
||||||
|
mNLchan = 0;
|
||||||
|
|
||||||
|
TransceiverManager *trxmgr = trx.getTRXmgr();
|
||||||
|
ARFCNManager *radio = trxmgr->ARFCN(ts_nr);
|
||||||
|
radio->setSlot(ts_nr, comb);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OsmoLogicalChannel::open()
|
||||||
|
{
|
||||||
|
LOG(INFO);
|
||||||
|
if (mSACCHL1) mSACCHL1->open();
|
||||||
|
if (mL1) mL1->open();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OsmoLogicalChannel::connect()
|
||||||
|
{
|
||||||
|
mMux.downstream(mL1);
|
||||||
|
if (mL1) mL1->upstream(&mMux);
|
||||||
|
//mMux.upstream(mL2[s],s);
|
||||||
|
//if (mL2[s]) mL2[s]->downstream(&mMux);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OsmoLogicalChannel::writeLowSide(const L2Frame& frame)
|
||||||
|
{
|
||||||
|
mTM->writeLowSide(frame, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ostream& GSM::operator<<(ostream& os, const OsmoLogicalChannel& lchan)
|
||||||
|
{
|
||||||
|
unsigned int trx_nr, ts_nr, ss_nr;
|
||||||
|
trx_nr = lchan.TS()->getTRX()->getTN();
|
||||||
|
ts_nr = lchan.TS()->getTSnr();
|
||||||
|
ss_nr = lchan.SSnr();
|
||||||
|
|
||||||
|
os << "(" << trx_nr << "," << ts_nr << "," << ss_nr << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OsmoLogicalChannel::downstream(ARFCNManager* radio)
|
||||||
|
{
|
||||||
|
assert(mL1);
|
||||||
|
mL1->downstream(radio);
|
||||||
|
if (mSACCHL1) mSACCHL1->downstream(radio);
|
||||||
|
}
|
||||||
|
|
||||||
|
OsmoCCCHLchan::OsmoCCCHLchan(OsmoTS *osmo_ts, unsigned int ss_nr)
|
||||||
|
:OsmoNDCCHLogicalChannel(osmo_ts, ss_nr)
|
||||||
|
{
|
||||||
|
mL1 = new CCCHL1FEC(gCCCH[ss_nr]);
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
OsmoSDCCHLchan::OsmoSDCCHLchan(OsmoTS *osmo_ts, unsigned int ss_nr)
|
||||||
|
:OsmoLogicalChannel(osmo_ts, ss_nr)
|
||||||
|
{
|
||||||
|
unsigned int ts_nr = osmo_ts->getTSnr();
|
||||||
|
const CompleteMapping *wMapping = NULL;
|
||||||
|
|
||||||
|
switch (osmo_ts->getComb()) {
|
||||||
|
case 5:
|
||||||
|
wMapping = &gSDCCH4[ss_nr];
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
wMapping = &gSDCCH8[ss_nr];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
mL1 = new SDCCHL1FEC(ts_nr, wMapping->LCH());
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
OsmoTCHFACCHLchan::OsmoTCHFACCHLchan(OsmoTS *osmo_ts, unsigned int ss_nr)
|
||||||
|
:OsmoLogicalChannel(osmo_ts, ss_nr)
|
||||||
|
{
|
||||||
|
unsigned int ts_nr = osmo_ts->getTSnr();
|
||||||
|
mL1 = new TCHFACCHL1FEC(ts_nr, gTCHF_T[ts_nr].LCH());
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// vim: ts=4 sw=4
|
||||||
|
|
|
@ -0,0 +1,369 @@
|
||||||
|
/**@file Logical Channel. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||||
|
*
|
||||||
|
* This software is distributed under the terms of the GNU Affero Public License.
|
||||||
|
* See the COPYING file in the main directory for details.
|
||||||
|
*
|
||||||
|
* This use of this software may be subject to additional restrictions.
|
||||||
|
* See the LEGAL file in the main directory for details.
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef OSMOLOGICALCHANNEL_H
|
||||||
|
#define OSMOLOGICALCHANNEL_H
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
#include "GSML1FEC.h"
|
||||||
|
#include "GSMSAPMux.h"
|
||||||
|
#include "GSMTDMA.h"
|
||||||
|
#include "OsmoSAPMux.h"
|
||||||
|
|
||||||
|
#include <TRXManager.h>
|
||||||
|
#include <Logger.h>
|
||||||
|
|
||||||
|
class ARFCNManager;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace GSM {
|
||||||
|
|
||||||
|
class OsmoTRX;
|
||||||
|
class OsmoThreadMuxer;
|
||||||
|
class OsmoLogicalChannel;
|
||||||
|
|
||||||
|
/* virtual class from which we derive timeslots */
|
||||||
|
class OsmoTS {
|
||||||
|
protected:
|
||||||
|
OsmoTRX *mTRX;
|
||||||
|
unsigned int mTSnr;
|
||||||
|
OsmoLogicalChannel *mLchan[8];
|
||||||
|
unsigned int mNLchan;
|
||||||
|
unsigned int mComb;
|
||||||
|
public:
|
||||||
|
OsmoTS(OsmoTRX &trx, unsigned int ts_nr, unsigned comb);
|
||||||
|
unsigned int getTSnr() const { return mTSnr; }
|
||||||
|
unsigned int getComb() { return mComb; }
|
||||||
|
const OsmoTRX *getTRX() const { return mTRX; }
|
||||||
|
ARFCNManager *getARFCNmgr();
|
||||||
|
OsmoLogicalChannel *getLchan(unsigned int nr) {
|
||||||
|
assert(nr < 8);
|
||||||
|
if (nr < mNLchan)
|
||||||
|
return mLchan[nr];
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* One TRX (8TS) */
|
||||||
|
class OsmoTRX {
|
||||||
|
protected:
|
||||||
|
TransceiverManager *mTRXmgr;
|
||||||
|
OsmoThreadMuxer *mThreadMux;
|
||||||
|
unsigned int mTN;
|
||||||
|
OsmoTS *mTS[8];
|
||||||
|
public:
|
||||||
|
OsmoTRX(TransceiverManager &TRXmgr, unsigned int trx_nr) {
|
||||||
|
mTRXmgr = &TRXmgr;
|
||||||
|
mTN = trx_nr;
|
||||||
|
}
|
||||||
|
|
||||||
|
TransceiverManager *getTRXmgr() const { return mTRXmgr; }
|
||||||
|
OsmoThreadMuxer *getThreadMux() const { return mThreadMux; }
|
||||||
|
unsigned int getTN() const { return mTN; }
|
||||||
|
|
||||||
|
OsmoTS *getTS(unsigned int nr) {
|
||||||
|
assert(nr < 8);
|
||||||
|
return mTS[nr];
|
||||||
|
}
|
||||||
|
|
||||||
|
OsmoLogicalChannel *getLchan(unsigned int ts_nr, unsigned int lchan_nr) {
|
||||||
|
OsmoTS *ts = getTS(ts_nr);
|
||||||
|
return ts->getLchan(lchan_nr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
A complete logical channel.
|
||||||
|
Includes processors for L1, L2, L3, as needed.
|
||||||
|
The layered structure of GSM is defined in GSM 04.01 7, as well as many other places.
|
||||||
|
The concept of the logical channel and the channel types are defined in GSM 04.03.
|
||||||
|
This is virtual class; specific channel types are subclasses.
|
||||||
|
*/
|
||||||
|
//class OsmoLogicalChannel : public virtual LogicalChannelCommon {
|
||||||
|
class OsmoLogicalChannel {
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
L1FEC *mL1; ///< L1 forward error correction
|
||||||
|
OsmoSAPMux mMux; ///< service access point multiplex
|
||||||
|
OsmoThreadMuxer *mTM;
|
||||||
|
unsigned int mSS; // sub-slot (logical channel within TS)
|
||||||
|
OsmoTS *mTS;
|
||||||
|
SACCHL1FEC *mSACCHL1; ///< The associated SACCH, if any.
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
Blank initializer just nulls the pointers.
|
||||||
|
Specific sub-class initializers allocate new components as needed.
|
||||||
|
*/
|
||||||
|
OsmoLogicalChannel(OsmoTS *osmo_ts, unsigned int ss_nr)
|
||||||
|
:mSACCHL1(NULL)
|
||||||
|
{
|
||||||
|
mTS = osmo_ts;
|
||||||
|
mSS = ss_nr;
|
||||||
|
/* resolve the thread muxer */
|
||||||
|
const OsmoTRX *otrx = osmo_ts->getTRX();
|
||||||
|
mTM = otrx->getThreadMux();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Connect an ARFCN manager to link L1FEC to the radio. */
|
||||||
|
void downstream(ARFCNManager* radio);
|
||||||
|
|
||||||
|
/**@name Accessors. */
|
||||||
|
//@{
|
||||||
|
SACCHL1FEC* SACCH() { return mSACCHL1; }
|
||||||
|
const SACCHL1FEC* SACCH() const { return mSACCHL1; }
|
||||||
|
const OsmoTS* TS() const { return mTS; }
|
||||||
|
unsigned int SSnr() const { return mSS; }
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/**@name Pass-throughs. */
|
||||||
|
//@{
|
||||||
|
|
||||||
|
/** Set L1 physical parameters from a RACH or pre-exsting channel. */
|
||||||
|
virtual void setPhy(float wRSSI, float wTimingError) {
|
||||||
|
assert(mSACCHL1);
|
||||||
|
mSACCHL1->setPhy(wRSSI, wTimingError);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set L1 physical parameters from an existing logical channel. */
|
||||||
|
virtual void setPhy(const OsmoLogicalChannel& other) {
|
||||||
|
assert(mSACCHL1);
|
||||||
|
mSACCHL1->setPhy(*other.SACCH());
|
||||||
|
}
|
||||||
|
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/**@name L3 interfaces */
|
||||||
|
//@{
|
||||||
|
//
|
||||||
|
virtual void writeLowSide(const L2Frame& frame);
|
||||||
|
|
||||||
|
//@} // passthrough
|
||||||
|
|
||||||
|
/**@name Channel stats from the physical layer */
|
||||||
|
//@{
|
||||||
|
/** RSSI wrt full scale. */
|
||||||
|
virtual float RSSI() const { return mSACCHL1->RSSI(); }
|
||||||
|
/** Uplink timing error. */
|
||||||
|
virtual float timingError() const { return mSACCHL1->timingError(); }
|
||||||
|
/** Actual MS uplink power. */
|
||||||
|
virtual int actualMSPower() const { return mSACCHL1->actualMSPower(); }
|
||||||
|
/** Actual MS uplink timing advance. */
|
||||||
|
virtual int actualMSTiming() const { return mSACCHL1->actualMSTiming(); }
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/** Return the channel type. */
|
||||||
|
virtual ChannelType type() const =0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Make the channel ready for a new transaction.
|
||||||
|
The channel is closed with primitives from L3.
|
||||||
|
*/
|
||||||
|
virtual void open();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/**
|
||||||
|
Make the normal inter-layer connections.
|
||||||
|
Should be called from inside the constructor after
|
||||||
|
the channel components are created.
|
||||||
|
*/
|
||||||
|
virtual void connect();
|
||||||
|
|
||||||
|
friend std::ostream& operator<<(std::ostream& os, const OsmoLogicalChannel& lchan);
|
||||||
|
};
|
||||||
|
|
||||||
|
//std::ostream& GSM::operator<<(std::ostream& os, const OsmoLogicalChannel& lchan);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Standalone dedicated control channel.
|
||||||
|
GSM 04.06 4.1.3: "A dedicated control channel (DCCH) is a point-to-point
|
||||||
|
bi-directional or uni-directional control channel. ... A SDCCH (Stand-alone
|
||||||
|
DCCH) is a bi-directional DCCH whose allocation is not linked to the
|
||||||
|
allocation of a TCH. The bit rate of a SDCCH is 598/765 kbit/s.
|
||||||
|
"
|
||||||
|
*/
|
||||||
|
class OsmoSDCCHLchan : public OsmoLogicalChannel {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
OsmoSDCCHLchan(OsmoTS *osmo_ts, unsigned int ss_nr);
|
||||||
|
|
||||||
|
ChannelType type() const { return SDCCHType; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Logical channel for NDCCHs that use Bbis format and a pseudolength.
|
||||||
|
This is a virtual base class this is extended for CCCH & BCCH.
|
||||||
|
See GSM 04.06 4.1.1, 4.1.3.
|
||||||
|
*/
|
||||||
|
class OsmoNDCCHLogicalChannel : public OsmoLogicalChannel {
|
||||||
|
|
||||||
|
public:
|
||||||
|
OsmoNDCCHLogicalChannel(OsmoTS *osmo_ts, unsigned int ss_nr) :
|
||||||
|
OsmoLogicalChannel(osmo_ts, ss_nr) { };
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Common control channel.
|
||||||
|
The "uplink" component of the CCCH is the RACH.
|
||||||
|
See GSM 04.03 4.1.2: "A common control channel is a point-to-multipoint
|
||||||
|
bi-directional control channel. Common control channels are physically
|
||||||
|
sub-divided into the common control channel (CCCH), the packet common control
|
||||||
|
channel (PCCCH), and the Compact packet common control channel (CPCCCH)."
|
||||||
|
*/
|
||||||
|
class OsmoCCCHLchan : public OsmoNDCCHLogicalChannel {
|
||||||
|
public:
|
||||||
|
OsmoCCCHLchan(OsmoTS *osmo_ts, unsigned int ss_nr);
|
||||||
|
|
||||||
|
ChannelType type() const { return CCCHType; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class OsmoTCHFACCHLchan : public OsmoLogicalChannel {
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
TCHFACCHL1FEC * mTCHL1;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
OsmoTCHFACCHLchan(OsmoTS *osmo_ts, unsigned int ss_nr);
|
||||||
|
|
||||||
|
ChannelType type() const { return FACCHType; }
|
||||||
|
|
||||||
|
void sendTCH(const unsigned char* frame)
|
||||||
|
{ assert(mTCHL1); mTCHL1->sendTCH(frame); }
|
||||||
|
|
||||||
|
unsigned char* recvTCH()
|
||||||
|
{ assert(mTCHL1); return mTCHL1->recvTCH(); }
|
||||||
|
|
||||||
|
unsigned queueSize() const
|
||||||
|
{ assert(mTCHL1); return mTCHL1->queueSize(); }
|
||||||
|
|
||||||
|
bool radioFailure() const
|
||||||
|
{ assert(mTCHL1); return mTCHL1->radioFailure(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/* timeslot in Combination I (TCH/F) */
|
||||||
|
class OsmoComb1TS : public OsmoTS {
|
||||||
|
protected:
|
||||||
|
public:
|
||||||
|
OsmoComb1TS(OsmoTRX &trx, unsigned int ts_nr) :OsmoTS(trx, ts_nr, 1) {
|
||||||
|
ARFCNManager* radio = getARFCNmgr();
|
||||||
|
/* create logical channel */
|
||||||
|
OsmoTCHFACCHLchan * chan = new OsmoTCHFACCHLchan(this, 0);
|
||||||
|
chan->downstream(radio);
|
||||||
|
chan->open();
|
||||||
|
mLchan[0] = chan;
|
||||||
|
mNLchan = 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* timeslot in Combination 5 (FCCH, SCH, CCCH, BCCH and 4*SDCCH/4) */
|
||||||
|
class OsmoComb5TS : public OsmoTS {
|
||||||
|
public:
|
||||||
|
OsmoComb5TS(OsmoTRX &trx, unsigned int tn) :OsmoTS(trx, tn, 5) {
|
||||||
|
ARFCNManager* radio = getARFCNmgr();
|
||||||
|
|
||||||
|
SCHL1FEC SCH;
|
||||||
|
SCH.downstream(radio);
|
||||||
|
SCH.open();
|
||||||
|
|
||||||
|
FCCHL1FEC FCCH;
|
||||||
|
FCCH.downstream(radio);
|
||||||
|
SCH.open();
|
||||||
|
|
||||||
|
RACHL1FEC RACH(gRACHC5Mapping);
|
||||||
|
RACH.downstream(radio);
|
||||||
|
RACH.open();
|
||||||
|
|
||||||
|
OsmoCCCHLchan CCCH0(this, 0);
|
||||||
|
CCCH0.downstream(radio);
|
||||||
|
CCCH0.open();
|
||||||
|
|
||||||
|
OsmoCCCHLchan CCCH1(this, 1);
|
||||||
|
CCCH1.downstream(radio);
|
||||||
|
CCCH1.open();
|
||||||
|
|
||||||
|
OsmoCCCHLchan CCCH2(this, 2);
|
||||||
|
CCCH2.downstream(radio);
|
||||||
|
CCCH2.open();
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
/* create logical channel */
|
||||||
|
OsmoSDCCHLchan * chan = new OsmoSDCCHLchan(this, i);
|
||||||
|
chan->downstream(radio);
|
||||||
|
chan->open();
|
||||||
|
mLchan[i] = chan;
|
||||||
|
mNLchan++;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* timeslot in Combination 7 (SDCCH/8) */
|
||||||
|
class OsmoComb7TS : public OsmoTS {
|
||||||
|
protected:
|
||||||
|
public:
|
||||||
|
OsmoComb7TS(OsmoTRX &trx, unsigned int tn) :OsmoTS(trx, tn, 7) {
|
||||||
|
for (unsigned int i = 0; i < 8; i++) {
|
||||||
|
ARFCNManager* radio = getARFCNmgr();
|
||||||
|
/* create logical channel */
|
||||||
|
OsmoSDCCHLchan *chan = new OsmoSDCCHLchan(this, i);
|
||||||
|
chan->downstream(radio);
|
||||||
|
chan->open();
|
||||||
|
mLchan[i] = chan;
|
||||||
|
mNLchan++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}; // GSM
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// vim: ts=4 sw=4
|
|
@ -27,6 +27,7 @@
|
||||||
#include "OsmoSAPMux.h"
|
#include "OsmoSAPMux.h"
|
||||||
#include "GSMTransfer.h"
|
#include "GSMTransfer.h"
|
||||||
#include "GSML1FEC.h"
|
#include "GSML1FEC.h"
|
||||||
|
#include "OsmoLogicalChannel.h"
|
||||||
|
|
||||||
#include <Logger.h>
|
#include <Logger.h>
|
||||||
|
|
||||||
|
@ -36,15 +37,45 @@ using namespace GSM;
|
||||||
|
|
||||||
void OsmoSAPMux::writeHighSide(const L2Frame& frame)
|
void OsmoSAPMux::writeHighSide(const L2Frame& frame)
|
||||||
{
|
{
|
||||||
// The SAP may or may not be present, depending on the channel type.
|
|
||||||
OBJLOG(DEEPDEBUG) << "OsmoSAPMux::writeHighSide " << frame;
|
OBJLOG(DEEPDEBUG) << "OsmoSAPMux::writeHighSide " << frame;
|
||||||
|
/* put it into the top side of the L2 FIFO */
|
||||||
|
mL2Q.write(new L2Frame(frame));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void OsmoSAPMux::writeLowSide(const L2Frame& frame)
|
void OsmoSAPMux::writeLowSide(const L2Frame& frame)
|
||||||
{
|
{
|
||||||
OBJLOG(DEEPDEBUG) << "OsmoSAPMux::writeLowSide SAP" << frame.SAPI() << " " << frame;
|
OBJLOG(DEEPDEBUG) << "OsmoSAPMux::writeLowSide SAP" << frame.SAPI() << " " << frame;
|
||||||
|
assert(mLchan);
|
||||||
|
/* simply pass it right through to the OsmoThreadMux */
|
||||||
|
mLchan->writeLowSide(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OsmoSAPMux::dispatch()
|
||||||
|
{
|
||||||
|
L2Frame *frame;
|
||||||
|
|
||||||
|
assert(mDownstream);
|
||||||
|
|
||||||
|
/* blocking read from FIFO */
|
||||||
|
frame = mL2Q.read();
|
||||||
|
|
||||||
|
/* blocking write to L1Encoder */
|
||||||
|
mLock.lock();
|
||||||
|
mDownstream->writeHighSide(*frame);
|
||||||
|
mLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSM::OsmoSAPRoutine( OsmoSAPMux *osm )
|
||||||
|
{
|
||||||
|
while (1) {
|
||||||
|
osm->dispatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OsmoSAPMux::start()
|
||||||
|
{
|
||||||
|
OBJLOG(DEBUG) << "OsmoSAPMux";
|
||||||
|
mQueueThread.start((void*(*)(void*))OsmoSAPRoutine,(void *)this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// vim: ts=4 sw=4
|
// vim: ts=4 sw=4
|
||||||
|
|
|
@ -35,6 +35,8 @@
|
||||||
|
|
||||||
namespace GSM {
|
namespace GSM {
|
||||||
|
|
||||||
|
class OsmoLogicalChannel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A SAPMux is a multipexer the connects a single L1 to multiple L2s.
|
A SAPMux is a multipexer the connects a single L1 to multiple L2s.
|
||||||
A "service access point" in GSM/ISDN is analogous to port number in IP.
|
A "service access point" in GSM/ISDN is analogous to port number in IP.
|
||||||
|
@ -44,10 +46,13 @@ namespace GSM {
|
||||||
class OsmoSAPMux : public SAPMux {
|
class OsmoSAPMux : public SAPMux {
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
OsmoLogicalChannel *mLchan;
|
||||||
|
L2FrameFIFO mL2Q;
|
||||||
|
Thread mQueueThread;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
OsmoSAPMux(){
|
OsmoSAPMux() {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~OsmoSAPMux() {}
|
virtual ~OsmoSAPMux() {}
|
||||||
|
@ -55,13 +60,23 @@ class OsmoSAPMux : public SAPMux {
|
||||||
virtual void writeHighSide(const L2Frame& frame);
|
virtual void writeHighSide(const L2Frame& frame);
|
||||||
virtual void writeLowSide(const L2Frame& frame);
|
virtual void writeLowSide(const L2Frame& frame);
|
||||||
|
|
||||||
|
void upstream(OsmoLogicalChannel *lchan) {
|
||||||
|
assert(mLchan == NULL);
|
||||||
|
mLchan = lchan;
|
||||||
|
}
|
||||||
|
|
||||||
void upstream( L2DL * wUpstream, unsigned wSAPI=0 )
|
void upstream( L2DL * wUpstream, unsigned wSAPI=0 )
|
||||||
{ }
|
{ }
|
||||||
void downstream( L1FEC * wDownstream )
|
/* use downstream() of SAPMux */
|
||||||
{ }
|
|
||||||
|
|
||||||
|
/* actual dispatch routine, called in endless loop */
|
||||||
|
void dispatch();
|
||||||
|
|
||||||
|
/* start the dispatch thread */
|
||||||
|
void start();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void OsmoSAPRoutine( OsmoSAPMux *osm );
|
||||||
|
|
||||||
} // namespace GSM
|
} // namespace GSM
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Harald Welte <laforge@gnumonks.org>
|
||||||
|
*
|
||||||
|
* This software is distributed under the terms of the GNU Affero Public License.
|
||||||
|
* See the COPYING file in the main directory for details.
|
||||||
|
*
|
||||||
|
* This use of this software may be subject to additional restrictions.
|
||||||
|
* See the LEGAL file in the main directory for details.
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "GSMTransfer.h"
|
||||||
|
#include "OsmoLogicalChannel.h"
|
||||||
|
#include "OsmoThreadMuxer.h"
|
||||||
|
#include <Logger.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace GSM;
|
||||||
|
|
||||||
|
|
||||||
|
OsmoThreadMuxer::writeLowSide(const L2Frame& frame,
|
||||||
|
struct OsmoLogicalChannel *lchan)
|
||||||
|
{
|
||||||
|
OBJLOG(DEEPDEBUG) << "OsmoThreadMuxer::writeLowSide" << lchan << " " << frame;
|
||||||
|
/* resolve SAPI, SS, TS, TRX numbers */
|
||||||
|
/* build primitive that we can put into the up-queue */
|
||||||
|
}
|
||||||
|
|
||||||
|
// vim: ts=4 sw=4
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Harald Welte <laforge@gnumonks.org>
|
||||||
|
*
|
||||||
|
* This software is distributed under the terms of the GNU Affero Public License.
|
||||||
|
* See the COPYING file in the main directory for details.
|
||||||
|
*
|
||||||
|
* This use of this software may be subject to additional restrictions.
|
||||||
|
* See the LEGAL file in the main directory for details.
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OsmoThreadMuxer_H
|
||||||
|
#define OsmoThreadMuxer_H
|
||||||
|
|
||||||
|
#include "OsmoLogicalChannel.h"
|
||||||
|
#include <TRXManager.h>
|
||||||
|
|
||||||
|
namespace GSM {
|
||||||
|
|
||||||
|
|
||||||
|
/* The idea of this monster is to provide an interface between the
|
||||||
|
* heavily multi-threaded OpenBTS architecture and the single-threaded
|
||||||
|
* osmo-bts architecture.
|
||||||
|
*
|
||||||
|
* In the Uplink Rx path, L1Decoder calls OsmoSAPMux:writeLowSide,
|
||||||
|
* which directly passes the call through to * OsmoThreadMuxer:writeLowSide
|
||||||
|
*
|
||||||
|
* At this point, the L2Frame needs to be converted from unpacked bits
|
||||||
|
* to packed bits, and wrapped with some layer1 primitive header. Next,
|
||||||
|
* it is enqueued into a FIFO leading towards osmo-bts. Whenever that
|
||||||
|
* FIFO has data to be written, we signal this via the sock_fd.
|
||||||
|
* (socketpair). The other fd ends up in the select() loop of osmo-bts.
|
||||||
|
*
|
||||||
|
* In the Downling Tx path, the OsmoThreadMuxer thread FIXME
|
||||||
|
*/
|
||||||
|
class OsmoThreadMuxer {
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int mSockFd[2];
|
||||||
|
OsmoTRX *mTRX[1];
|
||||||
|
unsigned int mNumTRX;
|
||||||
|
|
||||||
|
public:
|
||||||
|
OsmoThreadMuxer() {
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = socketpair(AF_UNIX, SOCK_DGRAM, 0, mSockFd);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getUserFd() {
|
||||||
|
return mSockFd[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
int addTRX(TransceiverManager &trx_mgr, unsigned int trx_nr) {
|
||||||
|
/* for now we only support a single TRX */
|
||||||
|
assert(mNumTRX == 0);
|
||||||
|
OsmoTRX *otrx = new OsmoTRX(trx_mgr, trx_nr);
|
||||||
|
mTRX[mNumTRX++] = otrx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* receive frame synchronously from L1Decoder->OsmoSAPMux and
|
||||||
|
* euqneue it towards osmo-bts */
|
||||||
|
virtual void writeLowSide(const L2Frame& frame,
|
||||||
|
OsmoLogicalChannel *lchan);
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // GSM
|
||||||
|
|
||||||
|
#endif /* OsmoThreadMuxer_H */
|
|
@ -25,7 +25,8 @@ AM_CXXFLAGS = -Wall -g -pthread
|
||||||
|
|
||||||
noinst_PROGRAMS = \
|
noinst_PROGRAMS = \
|
||||||
OpenBTS \
|
OpenBTS \
|
||||||
OpenBTScli
|
OpenBTScli \
|
||||||
|
TrueBTS
|
||||||
|
|
||||||
OpenBTS_SOURCES = OpenBTS.cpp
|
OpenBTS_SOURCES = OpenBTS.cpp
|
||||||
OpenBTS_LDADD = \
|
OpenBTS_LDADD = \
|
||||||
|
@ -49,6 +50,15 @@ OpenBTScli_LDADD = \
|
||||||
$(COMMON_LA) \
|
$(COMMON_LA) \
|
||||||
$(CLI_LA)
|
$(CLI_LA)
|
||||||
|
|
||||||
|
TrueBTS_SOURCES = TrueBTS.cpp
|
||||||
|
TrueBTS_LDADD = \
|
||||||
|
$(GLOBALS_LA) \
|
||||||
|
$(COMMON_LA) \
|
||||||
|
$(GSML1_LA) \
|
||||||
|
$(TRX_LA) \
|
||||||
|
$(COMMON_LA) \
|
||||||
|
$(CLI_LA)
|
||||||
|
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
OpenBTS.config.example \
|
OpenBTS.config.example \
|
||||||
|
|
|
@ -0,0 +1,694 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||||
|
*
|
||||||
|
* This software is distributed under the terms of the GNU Affero Public License.
|
||||||
|
* See the COPYING file in the main directory for details.
|
||||||
|
*
|
||||||
|
* This use of this software may be subject to additional restrictions.
|
||||||
|
* See the LEGAL file in the main directory for details.
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include <TRXManager.h>
|
||||||
|
#include <GSML1FEC.h>
|
||||||
|
#include <GSMConfig.h>
|
||||||
|
#include <GSMSAPMux.h>
|
||||||
|
#include <GSML3RRMessages.h>
|
||||||
|
#include <OsmoLogicalChannel.h>
|
||||||
|
|
||||||
|
#include <Globals.h>
|
||||||
|
|
||||||
|
#include <Logger.h>
|
||||||
|
#include <CLI.h>
|
||||||
|
#include <CLIServer.h>
|
||||||
|
#include <CLIParser.h>
|
||||||
|
#include <PowerManager.h>
|
||||||
|
#include <RRLPQueryController.h>
|
||||||
|
#include <Configuration.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace GSM;
|
||||||
|
using namespace CommandLine;
|
||||||
|
|
||||||
|
static int daemonize(std::string &lockfile, int &lfp);
|
||||||
|
static int forkLoop();
|
||||||
|
|
||||||
|
class DaemonInitializer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DaemonInitializer(bool doDaemonize)
|
||||||
|
: mLockFileFD(-1)
|
||||||
|
{
|
||||||
|
// Start in daemon mode?
|
||||||
|
if (doDaemonize)
|
||||||
|
if (daemonize(mLockFileName, mLockFileFD) != EXIT_SUCCESS)
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
~DaemonInitializer()
|
||||||
|
{
|
||||||
|
if (mLockFileFD >= 0) close(mLockFileFD);
|
||||||
|
if (mLockFileName.size() > 0) {
|
||||||
|
if (unlink(mLockFileName.data()) == 0) {
|
||||||
|
LOG(INFO) << "Deleted lock file " << mLockFileName;
|
||||||
|
} else {
|
||||||
|
LOG(INFO) << "Error while deleting lock file " << mLockFileName
|
||||||
|
<< " code=" << errno << ": " << strerror(errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string mLockFileName;
|
||||||
|
int mLockFileFD;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Restarter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Restarter(bool restartOnCrash)
|
||||||
|
{
|
||||||
|
if (restartOnCrash)
|
||||||
|
if (forkLoop() != EXIT_SUCCESS)
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Load configuration from a file.
|
||||||
|
ConfigurationTable gConfig("TrueBTS.config");
|
||||||
|
/// Initialize Logger form the config.
|
||||||
|
static LogInitializer sgLogInitializer;
|
||||||
|
/// Fork daemon if needed.
|
||||||
|
static DaemonInitializer sgDaemonInitializer(gConfig.defines("Server.Daemonize"));
|
||||||
|
/// Fork a child and restart it if it crash. Kind of failsafe.
|
||||||
|
static Restarter sgRestarter(gConfig.defines("Server.RestartOnCrash"));
|
||||||
|
|
||||||
|
|
||||||
|
// All of the other globals that rely on the global configuration file need to
|
||||||
|
// be declared here.
|
||||||
|
|
||||||
|
/// Configure the BTS object based on the config file.
|
||||||
|
/// So don't create this until AFTER loading the config file.
|
||||||
|
GSMConfigL1 &gBTSL1;
|
||||||
|
|
||||||
|
/// Our interface to the software-defined radio.
|
||||||
|
TransceiverManager gTRX(1, gConfig.getStr("TRX.IP"), gConfig.getNum("TRX.Port"));
|
||||||
|
|
||||||
|
/// Pointer to the server socket if we run remote CLI.
|
||||||
|
static ConnectionServerSocket *sgCLIServerSock = NULL;
|
||||||
|
|
||||||
|
/// We store Transceiver PID if we start it.
|
||||||
|
static pid_t sgTransceiverPid = 0;
|
||||||
|
static int sgTransceiverPidFileFd = -1;
|
||||||
|
static std::string sgTransceiverPidFile;
|
||||||
|
|
||||||
|
void Control::AccessGrantResponder(unsigned requestReference, const GSM::Time& when,
|
||||||
|
float RSSI, float timingError)
|
||||||
|
{
|
||||||
|
/* Do nothing */
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Function to shutdown the process when something wrong happens. */
|
||||||
|
void shutdownOpenbts()
|
||||||
|
{
|
||||||
|
kill(SIGTERM, getpid());
|
||||||
|
}
|
||||||
|
|
||||||
|
static int openPidFile(const std::string &lockfile)
|
||||||
|
{
|
||||||
|
int lfp = open(lockfile.data(), O_RDWR|O_CREAT, 0640);
|
||||||
|
if (lfp < 0) {
|
||||||
|
LOG(ERROR) << "Unable to create PID file " << lockfile << ", code="
|
||||||
|
<< errno << " (" << strerror(errno) << ")";
|
||||||
|
} else {
|
||||||
|
LOG(INFO) << "Created PID file " << lockfile;
|
||||||
|
}
|
||||||
|
return lfp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lockPidFile(const std::string &lockfile, int lfp, bool block=false)
|
||||||
|
{
|
||||||
|
if (lockf(lfp, block?F_LOCK:F_TLOCK,0) < 0) {
|
||||||
|
LOG(ERROR) << "Unable to lock PID file " << lockfile << ", code="
|
||||||
|
<< errno << " (" << strerror(errno) << ")";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int writePidFile(const std::string &lockfile, int lfp, int pid)
|
||||||
|
{
|
||||||
|
// Clear old file content first
|
||||||
|
if (ftruncate(lfp, 0) < 0) {
|
||||||
|
LOG(ERROR) << "Unable to clear PID file " << lockfile << ", code="
|
||||||
|
<< errno << " (" << strerror(errno) << ")";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write PID
|
||||||
|
char tempBuf[64];
|
||||||
|
snprintf(tempBuf, sizeof(tempBuf), "%d\n", pid);
|
||||||
|
ssize_t tempDataLen = strlen(tempBuf);
|
||||||
|
lseek(lfp, 0, SEEK_SET);
|
||||||
|
if (write(lfp, tempBuf, tempDataLen) != tempDataLen) {
|
||||||
|
LOG(ERROR) << "Unable to write PID to file " << lockfile << ", code="
|
||||||
|
<< errno << " (" << strerror(errno) << ")";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int readPidFile(const std::string &lockfile, int lfp, int &pid)
|
||||||
|
{
|
||||||
|
char tempBuf[64];
|
||||||
|
lseek(lfp, 0, SEEK_SET);
|
||||||
|
int bytesRead = read(lfp, tempBuf, sizeof(tempBuf));
|
||||||
|
if (bytesRead <= 0) {
|
||||||
|
LOG(ERROR) << "Unable to read PID from file " << lockfile << ", code="
|
||||||
|
<< errno << " (" << strerror(errno) << ")";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
tempBuf[bytesRead<sizeof(tempBuf)?bytesRead:sizeof(tempBuf)-1] = '\0';
|
||||||
|
int res = sscanf(tempBuf, " %d", &pid);
|
||||||
|
if (res < 1) {
|
||||||
|
LOG(ERROR) << "Unable to parse PID from file " << lockfile << ", code="
|
||||||
|
<< errno << " (" << strerror(errno) << ")";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int startTransceiver()
|
||||||
|
{
|
||||||
|
// Start the transceiver binary, if the path is defined.
|
||||||
|
// If the path is not defined, the transceiver must be started by some other process.
|
||||||
|
if (gConfig.defines("TRX.Path")) {
|
||||||
|
|
||||||
|
// Open and lock PID file, taking care of old transceiver instance.
|
||||||
|
sgTransceiverPidFile = gConfig.getStr("TRX.WritePID");
|
||||||
|
sgTransceiverPidFileFd = openPidFile(sgTransceiverPidFile);
|
||||||
|
if (sgTransceiverPidFileFd < 0) return EXIT_SUCCESS;
|
||||||
|
int pid;
|
||||||
|
if (lockPidFile(sgTransceiverPidFile, sgTransceiverPidFileFd, false) != EXIT_SUCCESS) {
|
||||||
|
// Another OpenBTS instance is running and blocking PID file.
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (readPidFile(sgTransceiverPidFile, sgTransceiverPidFileFd, pid) == EXIT_SUCCESS) {
|
||||||
|
// There is no harm in this. Transceiver's owner is not
|
||||||
|
// running and could safely kill it.
|
||||||
|
kill(pid, SIGTERM);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start transceiver
|
||||||
|
const char *TRXPath = gConfig.getStr("TRX.Path");
|
||||||
|
const char *TRXLogLevel = gConfig.getStr("TRX.LogLevel");
|
||||||
|
const char *TRXLogFileName = NULL;
|
||||||
|
if (gConfig.defines("TRX.LogFileName")) TRXLogFileName=gConfig.getStr("TRX.LogFileName");
|
||||||
|
sgTransceiverPid = vfork();
|
||||||
|
LOG_ASSERT(sgTransceiverPid>=0);
|
||||||
|
if (sgTransceiverPid==0) {
|
||||||
|
// Pid==0 means this is the process that starts the transceiver.
|
||||||
|
execl(TRXPath,"transceiver",TRXLogLevel,TRXLogFileName,NULL);
|
||||||
|
LOG(ERROR) << "cannot start transceiver";
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
// Now we can finally write transceiver PID to the file.
|
||||||
|
if (writePidFile(sgTransceiverPidFile, sgTransceiverPidFileFd, sgTransceiverPid) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serverCleanup()
|
||||||
|
{
|
||||||
|
if (sgTransceiverPid) {
|
||||||
|
kill(sgTransceiverPid, SIGTERM);
|
||||||
|
if (sgTransceiverPidFileFd >= 0) {
|
||||||
|
close(sgTransceiverPidFileFd);
|
||||||
|
}
|
||||||
|
if (sgTransceiverPidFile.size() > 0) {
|
||||||
|
if (unlink(sgTransceiverPidFile.data()) == 0) {
|
||||||
|
LOG(INFO) << "Deleted lock file " << sgTransceiverPidFile;
|
||||||
|
} else {
|
||||||
|
LOG(INFO) << "Error while deleting lock file " << sgTransceiverPidFile
|
||||||
|
<< " code=" << errno << ": " << strerror(errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void exitCLI()
|
||||||
|
{
|
||||||
|
if (sgCLIServerSock == NULL) {
|
||||||
|
serverCleanup();
|
||||||
|
_exit(EXIT_SUCCESS);
|
||||||
|
} else {
|
||||||
|
// Closing server sock
|
||||||
|
sgCLIServerSock->close();
|
||||||
|
sgCLIServerSock = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closing server standard input to shutdown local CLI
|
||||||
|
// Following functions are not async-signal-safe, but I don't have
|
||||||
|
// better idea how to do this.
|
||||||
|
cin.setstate(ios::eofbit);
|
||||||
|
// cin.putback('\n');
|
||||||
|
fclose(stdin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void daemonChildHandler(int signum)
|
||||||
|
{
|
||||||
|
LOG(INFO) << "Handling signal " << signum;
|
||||||
|
switch(signum) {
|
||||||
|
case SIGALRM:
|
||||||
|
// alarm() fired.
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
break;
|
||||||
|
case SIGUSR1:
|
||||||
|
//Child sent us a signal. Good sign!
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
break;
|
||||||
|
case SIGCHLD:
|
||||||
|
// Child has died
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int daemonize(std::string &lockfile, int &lfp)
|
||||||
|
{
|
||||||
|
// Already a daemon
|
||||||
|
if ( getppid() == 1 ) return EXIT_SUCCESS;
|
||||||
|
|
||||||
|
// Sanity checks
|
||||||
|
if (strcasecmp(gConfig.getStr("CLI.Type"),"Local") == 0) {
|
||||||
|
LOG(ERROR) << "OpenBTS runs in daemon mode, but CLI is set to Local!";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (!gConfig.defines("Server.WritePID")) {
|
||||||
|
LOG(ERROR) << "OpenBTS runs in daemon mode, but Server.WritePID is not set in config!";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// According to the Filesystem Hierarchy Standard 5.13.2:
|
||||||
|
// "The naming convention for PID files is <program-name>.pid."
|
||||||
|
// The same standard specifies that PID files should be placed
|
||||||
|
// in /var/run, but we make this configurable.
|
||||||
|
lockfile = gConfig.getStr("Server.WritePID");
|
||||||
|
|
||||||
|
// Create the PID file as the current user
|
||||||
|
if ((lfp=openPidFile(lockfile)) < 0) return EXIT_FAILURE;
|
||||||
|
|
||||||
|
// Drop user if there is one, and we were run as root
|
||||||
|
/* if ( getuid() == 0 || geteuid() == 0 ) {
|
||||||
|
struct passwd *pw = getpwnam(RUN_AS_USER);
|
||||||
|
if ( pw ) {
|
||||||
|
syslog( LOG_NOTICE, "setting user to " RUN_AS_USER );
|
||||||
|
setuid( pw->pw_uid );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Trap signals that we expect to receive
|
||||||
|
signal(SIGCHLD, daemonChildHandler);
|
||||||
|
signal(SIGUSR1, daemonChildHandler);
|
||||||
|
signal(SIGALRM, daemonChildHandler);
|
||||||
|
|
||||||
|
// Fork off the parent process
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
LOG(ERROR) << "Unable to fork daemon, code=" << errno
|
||||||
|
<< " (" << strerror(errno) << ")";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
// If we got a good PID, then we can exit the parent process.
|
||||||
|
if (pid > 0) {
|
||||||
|
// Wait for confirmation from the child via SIGUSR1 or SIGCHLD.
|
||||||
|
LOG(INFO) << "Forked child process with PID " << pid;
|
||||||
|
// Some recommend to add timeout here too (it will signal SIGALRM),
|
||||||
|
// but I don't think it's a good idea if we start on a slow system.
|
||||||
|
// Or may be we should make timeout value configurable and set it
|
||||||
|
// a big enough value.
|
||||||
|
// alarm(2);
|
||||||
|
// pause() should not return.
|
||||||
|
pause();
|
||||||
|
LOG(ERROR) << "Executing code after pause()!";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now lock our PID file and write our PID to it
|
||||||
|
if (lockPidFile(lockfile, lfp) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||||
|
if (writePidFile(lockfile, lfp, getpid()) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||||
|
|
||||||
|
// At this point we are executing as the child process
|
||||||
|
pid_t parent = getppid();
|
||||||
|
|
||||||
|
// Return signals to default handlers
|
||||||
|
signal(SIGCHLD, SIG_DFL);
|
||||||
|
signal(SIGUSR1, SIG_DFL);
|
||||||
|
signal(SIGALRM, SIG_DFL);
|
||||||
|
|
||||||
|
// Change the file mode mask
|
||||||
|
// This will restrict file creation mode to 750 (complement of 027).
|
||||||
|
umask(gConfig.getNum("Server.umask"));
|
||||||
|
|
||||||
|
// Create a new SID for the child process
|
||||||
|
pid_t sid = setsid();
|
||||||
|
if (sid < 0) {
|
||||||
|
LOG(ERROR) << "Unable to create a new session, code=" << errno
|
||||||
|
<< " (" << strerror(errno) << ")";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change the current working directory. This prevents the current
|
||||||
|
// directory from being locked; hence not being able to remove it.
|
||||||
|
if (gConfig.defines("Server.ChdirToRoot")) {
|
||||||
|
if (chdir("/") < 0) {
|
||||||
|
LOG(ERROR) << "Unable to change directory to %s, code" << errno
|
||||||
|
<< " (" << strerror(errno) << ")";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else {
|
||||||
|
LOG(INFO) << "Changed current directory to \"/\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect standard files to /dev/null
|
||||||
|
if (freopen( "/dev/null", "r", stdin) == NULL)
|
||||||
|
LOG(WARN) << "Error redirecting stdin to /dev/null";
|
||||||
|
if (freopen( "/dev/null", "w", stdout) == NULL)
|
||||||
|
LOG(WARN) << "Error redirecting stdout to /dev/null";
|
||||||
|
if (freopen( "/dev/null", "w", stderr) == NULL)
|
||||||
|
LOG(WARN) << "Error redirecting stderr to /dev/null";
|
||||||
|
|
||||||
|
// Tell the parent process that we are okay
|
||||||
|
kill(parent, SIGUSR1);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int forkLoop()
|
||||||
|
{
|
||||||
|
bool shouldExit = false;
|
||||||
|
sigset_t chldSignalSet;
|
||||||
|
sigemptyset(&chldSignalSet);
|
||||||
|
sigaddset(&chldSignalSet, SIGCHLD);
|
||||||
|
sigaddset(&chldSignalSet, SIGTERM);
|
||||||
|
sigaddset(&chldSignalSet, SIGINT);
|
||||||
|
sigaddset(&chldSignalSet, SIGKILL);
|
||||||
|
|
||||||
|
// Block signals to avoid race condition.
|
||||||
|
// It will be delivered to us in sigwait() when we are ready to handle it.
|
||||||
|
sigprocmask(SIG_BLOCK, &chldSignalSet, NULL);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
// Fork off the parent process
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
// fork() failed.
|
||||||
|
LOG(ERROR) << "Unable to fork child, code=" << errno
|
||||||
|
<< " (" << strerror(errno) << ")";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else if (pid > 0) {
|
||||||
|
// Parent process
|
||||||
|
// Wait for child process to exit (SIGCHLD).
|
||||||
|
LOG(INFO) << "Forked child process with PID " << pid;
|
||||||
|
int signum = -1;
|
||||||
|
while (signum != SIGCHLD) {
|
||||||
|
sigwait(&chldSignalSet, &signum);
|
||||||
|
switch(signum) {
|
||||||
|
case SIGCHLD:
|
||||||
|
LOG(ERROR) << "Child with PID " << pid << " died.";
|
||||||
|
if (shouldExit) exit(EXIT_SUCCESS);
|
||||||
|
break;
|
||||||
|
case SIGTERM:
|
||||||
|
case SIGINT:
|
||||||
|
case SIGKILL:
|
||||||
|
// Forward signal to the child.
|
||||||
|
kill(pid, signum);
|
||||||
|
// We will exit child exits and send us SIGCHLD.
|
||||||
|
shouldExit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Child process
|
||||||
|
// Unblock signals we blocked.
|
||||||
|
sigprocmask(SIG_UNBLOCK, &chldSignalSet, NULL);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void signalHandler(int sig)
|
||||||
|
{
|
||||||
|
COUT("Handling signal " << sig);
|
||||||
|
LOG(INFO) << "Handling signal " << sig;
|
||||||
|
switch(sig){
|
||||||
|
case SIGHUP:
|
||||||
|
// re-read the config
|
||||||
|
// TODO::
|
||||||
|
break;
|
||||||
|
case SIGTERM:
|
||||||
|
case SIGINT:
|
||||||
|
// finalize the server
|
||||||
|
exitCLI();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
srandom(time(NULL));
|
||||||
|
|
||||||
|
// Catch signal to re-read config
|
||||||
|
if (signal(SIGHUP, signalHandler) == SIG_ERR) {
|
||||||
|
cerr << "Error while setting handler for SIGHUP.";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
// Catch signal to shutdown gracefully
|
||||||
|
if (signal(SIGTERM, signalHandler) == SIG_ERR) {
|
||||||
|
cerr << "Error while setting handler for SIGTERM.";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
// Catch Ctrl-C signal
|
||||||
|
if (signal(SIGINT, signalHandler) == SIG_ERR) {
|
||||||
|
cerr << "Error while setting handler for SIGINT.";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
// Various TTY signals
|
||||||
|
// We don't really care about return values of these.
|
||||||
|
signal(SIGTSTP,SIG_IGN);
|
||||||
|
signal(SIGTTOU,SIG_IGN);
|
||||||
|
signal(SIGTTIN,SIG_IGN);
|
||||||
|
|
||||||
|
cout << endl << endl << gOpenBTSWelcome << endl;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
cout << endl << "Starting the system..." << endl;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (gConfig.defines("Control.TMSITable.SavePath")) {
|
||||||
|
gTMSITable.load(gConfig.getStr("Control.TMSITable.SavePath"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LOG(ALARM) << "TrueBTS starting, ver " << VERSION << " build date " << __DATE__;
|
||||||
|
|
||||||
|
startTransceiver();
|
||||||
|
|
||||||
|
// Start the transceiver interface.
|
||||||
|
// Sleep long enough for the USRP to bootload.
|
||||||
|
sleep(5);
|
||||||
|
gTRX.start();
|
||||||
|
|
||||||
|
// Set up the interface to the radio.
|
||||||
|
// Get a handle to the C0 transceiver interface.
|
||||||
|
ARFCNManager* radio = gTRX.ARFCN(0);
|
||||||
|
|
||||||
|
// Tuning.
|
||||||
|
// Make sure its off for tuning.
|
||||||
|
radio->powerOff();
|
||||||
|
// Set TSC same as BCC everywhere.
|
||||||
|
radio->setTSC(gBTSL1.BCC());
|
||||||
|
// Tune.
|
||||||
|
radio->tune(gConfig.getNum("GSM.ARFCN"));
|
||||||
|
|
||||||
|
// Turn on and power up.
|
||||||
|
radio->powerOn();
|
||||||
|
radio->setPower(gConfig.getNum("GSM.PowerManager.MinAttenDB"));
|
||||||
|
|
||||||
|
// Set maximum expected delay spread.
|
||||||
|
radio->setMaxDelay(gConfig.getNum("GSM.MaxExpectedDelaySpread"));
|
||||||
|
|
||||||
|
// Set Receiver Gain
|
||||||
|
radio->setRxGain(gConfig.getNum("GSM.RxGain"));
|
||||||
|
|
||||||
|
OsmoTRX TRX0(gTRX, 0);
|
||||||
|
|
||||||
|
OsmoComb5TS TS0(TRX0, 0);
|
||||||
|
OsmoComb1TS TS1(TRX0, 1);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// C-V on C0T0
|
||||||
|
radio->setSlot(0,5);
|
||||||
|
// SCH
|
||||||
|
SCHL1FEC SCH;
|
||||||
|
SCH.downstream(radio);
|
||||||
|
SCH.open();
|
||||||
|
// FCCH
|
||||||
|
FCCHL1FEC FCCH;
|
||||||
|
FCCH.downstream(radio);
|
||||||
|
FCCH.open();
|
||||||
|
|
||||||
|
// BCCH
|
||||||
|
BCCHL1FEC BCCH;
|
||||||
|
BCCH.downstream(radio);
|
||||||
|
BCCH.open();
|
||||||
|
// RACH
|
||||||
|
RACHL1FEC RACH(gRACHC5Mapping);
|
||||||
|
RACH.downstream(radio);
|
||||||
|
RACH.open();
|
||||||
|
// CCCHs
|
||||||
|
CCCHLogicalChannel CCCH0(gCCCH_0Mapping);
|
||||||
|
CCCH0.downstream(radio);
|
||||||
|
CCCH0.open();
|
||||||
|
CCCHLogicalChannel CCCH1(gCCCH_1Mapping);
|
||||||
|
CCCH1.downstream(radio);
|
||||||
|
CCCH1.open();
|
||||||
|
CCCHLogicalChannel CCCH2(gCCCH_2Mapping);
|
||||||
|
CCCH2.downstream(radio);
|
||||||
|
CCCH2.open();
|
||||||
|
// use CCCHs as AGCHs
|
||||||
|
gBTS.addAGCH(&CCCH0);
|
||||||
|
gBTS.addAGCH(&CCCH1);
|
||||||
|
gBTS.addAGCH(&CCCH2);
|
||||||
|
|
||||||
|
// C-V C0T0 SDCCHs
|
||||||
|
SDCCHLogicalChannel C0T0SDCCH[4] = {
|
||||||
|
SDCCHLogicalChannel(0,gSDCCH_4_0),
|
||||||
|
SDCCHLogicalChannel(0,gSDCCH_4_1),
|
||||||
|
SDCCHLogicalChannel(0,gSDCCH_4_2),
|
||||||
|
SDCCHLogicalChannel(0,gSDCCH_4_3),
|
||||||
|
};
|
||||||
|
Thread C0T0SDCCHControlThread[4];
|
||||||
|
for (int i=0; i<4; i++) {
|
||||||
|
C0T0SDCCH[i].downstream(radio);
|
||||||
|
C0T0SDCCHControlThread[i].start((void*(*)(void*))Control::DCCHDispatcher,&C0T0SDCCH[i]);
|
||||||
|
C0T0SDCCH[i].open();
|
||||||
|
gBTS.addSDCCH(&C0T0SDCCH[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count configured slots.
|
||||||
|
unsigned sCount = 1;
|
||||||
|
|
||||||
|
bool halfDuplex = gConfig.defines("GSM.HalfDuplex");
|
||||||
|
if (halfDuplex) { LOG(NOTICE) << "Configuring for half-duplex operation." ; }
|
||||||
|
else { LOG(NOTICE) << "Configuring for full-duplex operation."; }
|
||||||
|
|
||||||
|
if (halfDuplex) sCount++;
|
||||||
|
|
||||||
|
// Create C-VII slots.
|
||||||
|
for (int i=0; i<gConfig.getNum("GSM.NumC7s"); i++) {
|
||||||
|
gBTS.createCombinationVII(gTRX,sCount/8,sCount);
|
||||||
|
if (halfDuplex) sCount++;
|
||||||
|
sCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create C-I slots.
|
||||||
|
for (int i=0; i<gConfig.getNum("GSM.NumC1s"); i++) {
|
||||||
|
gBTS.createCombinationI(gTRX,sCount/8,sCount);
|
||||||
|
if (halfDuplex) sCount++;
|
||||||
|
sCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Set up idle filling on C0 as needed.
|
||||||
|
while (sCount<8) {
|
||||||
|
gBTS.createCombination0(gTRX,sCount/8,sCount);
|
||||||
|
if (halfDuplex) sCount++;
|
||||||
|
sCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Note: The number of different paging subchannels on
|
||||||
|
the CCCH is:
|
||||||
|
|
||||||
|
MAX(1,(3 - BS-AG-BLKS-RES)) * BS-PA-MFRMS
|
||||||
|
if CCCH-CONF = "001"
|
||||||
|
(9 - BS-AG-BLKS-RES) * BS-PA-MFRMS
|
||||||
|
for other values of CCCH-CONF
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Set up the pager.
|
||||||
|
// Set up paging channels.
|
||||||
|
// HACK -- For now, use a single paging channel, since paging groups are broken.
|
||||||
|
gBTS.addPCH(&CCCH2);
|
||||||
|
|
||||||
|
// Be sure we are not over-reserving.
|
||||||
|
LOG_ASSERT(gConfig.getNum("GSM.PagingReservations")<gBTS.numAGCHs());
|
||||||
|
|
||||||
|
// OK, now it is safe to start the BTS.
|
||||||
|
gBTS.start();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
LOG(INFO) << "system ready";
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (strcasecmp(gConfig.getStr("CLI.Type"),"TCP") == 0) {
|
||||||
|
ConnectionServerSocketTCP serverSock(gConfig.getNum("CLI.TCP.Port"),
|
||||||
|
gConfig.getStr("CLI.TCP.IP"));
|
||||||
|
sgCLIServerSock = &serverSock;
|
||||||
|
runCLIServer(&serverSock);
|
||||||
|
sgCLIServerSock = NULL;
|
||||||
|
} else if (strcasecmp(gConfig.getStr("CLI.Type"),"Unix") == 0) {
|
||||||
|
ConnectionServerSocketUnix serverSock(gConfig.getStr("CLI.Unix.Path"));
|
||||||
|
sgCLIServerSock = &serverSock;
|
||||||
|
runCLIServer(&serverSock);
|
||||||
|
sgCLIServerSock = NULL;
|
||||||
|
} else {
|
||||||
|
runCLI(&gParser);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} catch(SocketError) {
|
||||||
|
// Shutdown without core dump.
|
||||||
|
// SocketError is a usual case, e.g. it's fired when transceiver fails.
|
||||||
|
LOG(ALARM) << "Uncaught exception. Shutting down.";
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (!gBTS.hold()) {
|
||||||
|
exitBTS(0, cout);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
serverCleanup();
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// vim: ts=4 sw=4
|
Reference in New Issue