/* * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace GSM; using namespace CommandLine; // Load configuration from a file. ConfigurationTable gConfig("OpenBTS.config"); // Initialize Logger form the config. LogInitializer gLogInitializer; // All of the other globals that rely on the global configuration file need to // be declared here. // The global SIPInterface object. SIP::SIPInterface gSIPInterface; // Configure the BTS object based on the config file. // So don't create this until AFTER loading the config file. GSMConfig gBTS; // 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. ConnectionServerSocket *gCLIServerSock = NULL; pid_t gTransceiverPid = 0; void restartTransceiver() { // This is harmless - if someone is running OpenBTS they WANT no transceiver // instance at the start anyway. if (gTransceiverPid > 0) { LOG(INFO) << "RESTARTING TRANSCEIVER"; kill(gTransceiverPid,SIGKILL); // TODO - call on ctrl-c (put in signal?) } // Start the transceiver binary, if the path is defined. // If the path is not defined, the transceiver must be started by some other process. const char *TRXPath = NULL; if (gConfig.defines("TRX.Path")) TRXPath=gConfig.getStr("TRX.Path"); if (TRXPath) { const char *TRXLogLevel = gConfig.getStr("TRX.LogLevel"); const char *TRXLogFileName = NULL; if (gConfig.defines("TRX.LogFileName")) TRXLogFileName=gConfig.getStr("TRX.LogFileName"); gTransceiverPid = vfork(); LOG_ASSERT(gTransceiverPid>=0); if (gTransceiverPid==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); } } } void startBTS() { COUT("\nStarting the system..."); if (gConfig.defines("Control.TMSITable.SavePath")) { gTMSITable.load(gConfig.getStr("Control.TMSITable.SavePath")); } LOG(ALARM) << "OpenBTS starting, ver " << VERSION << " build date " << __DATE__; restartTransceiver(); // Start the SIP interface. gSIPInterface.start(); // 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(gBTS.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")); // 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; iclose(); gCLIServerSock = NULL; } // Closing server standard input to shutdown local CLI cin.setstate(ios::eofbit); // cin.putback('\n'); fclose(stdin); } void signalHandler(int sig) { COUT("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."; } // Catch signal to shutdown gracefully if (signal(SIGTERM, signalHandler) == SIG_ERR) { cerr << "Error while setting handler for SIGTERM."; } // Catch Ctrl-C signal if (signal(SIGINT, signalHandler) == SIG_ERR) { cerr << "Error while setting handler for SIGINT."; } cout << endl << endl << gOpenBTSWelcome << endl; startBTS(); if (strcasecmp(gConfig.getStr("CLI.Type"),"TCP") == 0) { ConnectionServerSocketTCP serverSock(gConfig.getNum("CLI.TCP.Port"), gConfig.getStr("CLI.TCP.IP")); gCLIServerSock = &serverSock; runCLIServer(&serverSock); gCLIServerSock = NULL; } else if (strcasecmp(gConfig.getStr("CLI.Type"),"Unix") == 0) { ConnectionServerSocketUnix serverSock(gConfig.getStr("CLI.Unix.Path")); gCLIServerSock = &serverSock; runCLIServer(&serverSock); gCLIServerSock = NULL; } else { runCLI(&gParser); } stopBTS(); } // vim: ts=4 sw=4