aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric <ewild@sysmocom.de>2022-10-31 15:01:26 +0100
committerEric <ewild@sysmocom.de>2022-10-31 15:01:26 +0100
commit2ecd9f698f90b9d00612384008bec08c14ed2b1d (patch)
treeb0e88e55d8c9d2cf25ce395407418d488197d744
parent70bd9415a254b41ea5c70c01169b16398e599e65 (diff)
trcon<->l1 data if without socketsmstx_oldtrxcon
direct call of UL rx receive data handler + sched check if ts is active tx dir call stub for for tx thread queue. Change-Id: I5911004db58742cf39b968fcf87bc1243f7a374a
-rw-r--r--Transceiver52M/Makefile.am1
-rw-r--r--Transceiver52M/ms/ms_commandhandler.cpp5
-rw-r--r--Transceiver52M/ms/ms_rx_upper.cpp166
-rw-r--r--Transceiver52M/ms/ms_rx_upper.h14
-rw-r--r--Transceiver52M/ms/ms_state.h175
-rw-r--r--trxcon/trx_if.c43
-rw-r--r--trxcon/trx_if.h1
-rw-r--r--trxcon/trxcon.c19
8 files changed, 65 insertions, 359 deletions
diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am
index c4434fe..6b76d25 100644
--- a/Transceiver52M/Makefile.am
+++ b/Transceiver52M/Makefile.am
@@ -191,7 +191,6 @@ noinst_HEADERS += \
ms/bladerf_specific.h \
ms/uhd_specific.h \
ms/ms_rx_upper.h \
- ms/ms_state.h \
itrq.h
# -fsanitize=address,undefined -shared-libsan -O0
#
diff --git a/Transceiver52M/ms/ms_commandhandler.cpp b/Transceiver52M/ms/ms_commandhandler.cpp
index be1eec4..940b298 100644
--- a/Transceiver52M/ms/ms_commandhandler.cpp
+++ b/Transceiver52M/ms/ms_commandhandler.cpp
@@ -20,10 +20,8 @@
*/
#include <radioInterface.h>
-#include "l1if.h"
#include "ms_rx_upper.h"
#include "syncthing.h"
-#include "ms_state.h"
void upper_trx::driveControl()
{
@@ -199,8 +197,6 @@ void upper_trx::commandhandler(char *buffer, char *response)
sprintf(response, "RSP SETSLOT 1 %d %d", timeslot, corrCode);
return;
}
- mStates.chanType[timeslot] = (ChannelCombination)corrCode;
- mStates.setModulus(timeslot);
sprintf(response, "RSP SETSLOT 0 %d %d", timeslot, corrCode);
} else if (!strcmp(command, "SETRXMASK")) {
int slot;
@@ -214,7 +210,6 @@ void upper_trx::commandhandler(char *buffer, char *response)
}
} else if (!strcmp(command, "SYNC")) {
// msleep(10);
- mStates.mode = trx_mode::TRX_MODE_MS_TRACK;
sprintf(response, "RSP SYNC 0");
mMaxExpectedDelay = 48;
// setRxGain(30);
diff --git a/Transceiver52M/ms/ms_rx_upper.cpp b/Transceiver52M/ms/ms_rx_upper.cpp
index 7f4d4af..5d53221 100644
--- a/Transceiver52M/ms/ms_rx_upper.cpp
+++ b/Transceiver52M/ms/ms_rx_upper.cpp
@@ -21,12 +21,10 @@
#include "sigProcLib.h"
#include "syncthing.h"
-#include "l1if.h"
#include <signalVector.h>
#include <radioVector.h>
#include <radioInterface.h>
#include "grgsm_vitac/grgsm_vitac.h"
-#include "ms_state.h"
#include "ms_rx_upper.h"
extern "C" {
@@ -39,6 +37,15 @@ extern "C" {
void __lsan_do_recoverable_leak_check();
}
+namespace trxcon
+{
+extern "C" {
+#include <trxcon/trx_if.h>
+}
+trx_instance *trxcon_instance; // local handle
+static tx_queue_t txq;
+} // namespace trxcon
+
#ifdef LOG
#undef LOG
#define LOG(...) upper_trx::dummy_log()
@@ -89,35 +96,6 @@ void upper_trx::start_ms()
ms_trx::start();
}
-/* Detect SCH synchronization sequence within a burst */
-bool upper_trx::detectSCH(ms_TransceiverState *state, signalVector &burst, struct estim_burst_params *ebp)
-{
- int shift;
- sch_detect_type full;
- float mag, threshold = 4.0;
-
- full = (state->mode == trx_mode::TRX_MODE_MS_TRACK) ? sch_detect_type::SCH_DETECT_NARROW :
- sch_detect_type::SCH_DETECT_FULL;
-
- if (!detectSCHBurst(burst, threshold, rx_sps, full, ebp))
- return false;
-
- std::clog << "SCH : Timing offset " << ebp->toa << " symbols" << std::endl;
-
- mag = fabsf(ebp->toa);
- if (mag < 1.0f)
- return true;
-
- shift = (int)(mag / 2.0f);
- if (!shift)
- shift++;
-
- shift = ebp->toa > 0 ? shift : -shift;
- std::clog << "SCH : shift -> " << shift << " symbols" << std::endl;
- // mRadioInterface->applyOffset(shift);
- return false;
-}
-
SoftVector *upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset) __attribute__((optnone))
{
float pow, avg = 1.0;
@@ -161,50 +139,20 @@ SoftVector *upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingO
return &bits;
}
- CorrType type = TSC;
-
- // tickle UL by returning null bursts if demod is skipped due to unused TS
- switch (mStates.mode) {
- case trx_mode::TRX_MODE_MS_TRACK:
- if (mStates.chanType[burst_time.TN()] == ChannelCombination::NONE_INACTIVE) {
- type = OFF;
- goto release;
- } else if (is_sch)
- type = SCH;
- else if (!is_fcch) // all ts0, but not fcch or sch..
- type = TSC;
- break;
-
- case trx_mode::TRX_MODE_OFF:
- default:
- goto release;
- }
+ auto ts = trxcon::trxcon_instance->ts_list[burst_time.TN()];
+ if (ts == NULL || ts->mf_layout == NULL)
+ return 0;
convert_and_scale<float, int16_t>(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
pow = energyDetect(sv, 20 * rx_sps);
if (pow < -1) {
LOG(ALERT) << "Received empty burst";
- goto release;
+ return NULL;
}
avg = sqrt(pow);
-
- if (type == SCH) {
- std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
- int d_c0_burst_start = get_sch_chan_imp_resp(ss, &chan_imp_resp[0]);
- detect_burst(ss, &chan_imp_resp[0], d_c0_burst_start, outbin);
-
- for (int i = 0; i < 148; i++)
- (bits)[i] = (!outbin[i]) < 1 ? -1 : 1;
-
- // auto rv = decode_sch(bits->begin(), false);
- // dbgout << "U SCH@"
- // << " " << e.gsmts.FN() << ":" << e.gsmts.TN() << " " << d_c0_burst_start
- // << " DECODE:" << (rv ? "yes" : "---") << std::endl;
-
- // std::cerr << dbgout.str();
- } else {
+ {
float ncmax, dcmax;
std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
std::complex<float> chan_imp_resp2[CHAN_IMP_RESP_LENGTH * d_OSR];
@@ -227,14 +175,10 @@ SoftVector *upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingO
for (int i = 0; i < 148; i++)
(bits)[i] = (outbin[i]) < 1 ? -1 : 1;
}
-
RSSI = (int)floor(20.0 * log10(rxFullScale / avg));
timingOffset = (int)round(0);
return &bits;
-
-release:
- return NULL;
}
void upper_trx::driveReceiveFIFO()
@@ -248,12 +192,13 @@ void upper_trx::driveReceiveFIFO()
SoftVector *rxBurst = pullRadioVector(burstTime, RSSI, TOA);
- trxd_from_trx response;
- response.ts = burstTime.TN();
- response.fn = htonl(burstTime.FN());
- response.rssi = RSSI;
- response.toa = htons(TOA);
if (rxBurst) {
+ trxd_from_trx response;
+ response.ts = burstTime.TN();
+ response.fn = htonl(burstTime.FN());
+ response.rssi = RSSI;
+ response.toa = htons(TOA);
+
SoftVector::const_iterator burstItr = rxBurst->begin();
if (burstTime.TN() == 0 && gsm_sch_check_fn(burstTime.FN())) {
clamp_array(rxBurst->begin(), 148, 1.5f);
@@ -267,45 +212,19 @@ void upper_trx::driveReceiveFIFO()
for (int i = 0; i < 148; i++)
((int8_t *)response.symbols)[i] = *burstItr++ > 0.0f ? -127 : 127;
}
+ trxcon::trx_data_rx_handler(trxcon::trxcon_instance, (uint8_t *)&response);
}
-
-#ifdef IPCIF
- push_d(response);
-#else
- int rv = sendto(mDataSockets, &response, sizeof(trxd_from_trx), 0, (struct sockaddr *)&datadest,
- sizeof(struct sockaddr_in));
- if (rv < 0) {
- std::cerr << "fuck, send?" << std::endl;
- exit(0);
- }
-
-#endif
}
void upper_trx::driveTx()
{
-#ifdef IPCIF
- auto burst = pop_d();
- if (!burst) {
- // std::cerr << "wtf no tx burst?" << std::endl;
- // exit(0);
- continue;
- }
-#else
- trxd_to_trx buffer;
-
- socklen_t addr_len = sizeof(datasrc);
- int rdln = recvfrom(mDataSockets, (void *)&buffer, sizeof(trxd_to_trx), 0, &datasrc, &addr_len);
- if (rdln < 0 && errno == EAGAIN) {
- std::cerr << "fuck, rcv?" << std::endl;
- exit(0);
+ trxd_to_trx e;
+ while (!trxcon::txq.spsc_pop(&e)) {
+ trxcon::txq.spsc_prep_pop();
}
- if(rdln < sizeof(buffer)) // nope ind has len 6 or something like that
- return;
+ trxd_to_trx *burst = &e;
- trxd_to_trx *burst = &buffer;
-#endif
auto proper_fn = ntohl(burst->fn);
// std::cerr << "got burst!" << proper_fn << ":" << burst->ts
// << " current: " << timekeeper.gsmtime().FN()
@@ -341,30 +260,8 @@ void upper_trx::driveTx()
submit_burst(burst_buf, txburst->size(), currTime);
delete txburst;
-
-#ifdef IPCIF
- free(burst);
-#endif
}
-// __attribute__((xray_always_instrument)) static void *rx_stream_callback(struct bladerf *dev,
-// struct bladerf_stream *stream,
-// struct bladerf_metadata *meta, void *samples,
-// size_t num_samples, void *user_data)
-// {
-// struct ms_trx *trx = (struct ms_trx *)user_data;
-// return trx->rx_cb(dev, stream, meta, samples, num_samples, user_data);
-// }
-
-// __attribute__((xray_always_instrument)) static void *tx_stream_callback(struct bladerf *dev,
-// struct bladerf_stream *stream,
-// struct bladerf_metadata *meta, void *samples,
-// size_t num_samples, void *user_data)
-// {
-// struct ms_trx *trx = (struct ms_trx *)user_data;
-// return BLADERF_STREAM_NO_DATA;
-// }
-
int trxc_main(int argc, char *argv[])
{
pthread_setname_np(pthread_self(), "main_trxc");
@@ -384,14 +281,21 @@ int trxc_main(int argc, char *argv[])
return status;
}
-extern "C" volatile bool gshutdown = false;
-extern "C" void init_external_transceiver(int argc, char **argv)
+extern "C" {
+void init_external_transceiver(struct trx_instance *trx, int argc, char **argv)
{
+ trxcon::trxcon_instance = (trxcon::trx_instance *)trx;
std::cout << "init?" << std::endl;
trxc_main(argc, argv);
}
-extern "C" void stop_trx()
+void close_external_transceiver(int argc, char **argv)
{
std::cout << "Shutting down transceiver..." << std::endl;
}
+
+void tx_external_transceiver(uint8_t *burst)
+{
+ trxcon::txq.spsc_push((trxd_to_trx *)burst);
+}
+} \ No newline at end of file
diff --git a/Transceiver52M/ms/ms_rx_upper.h b/Transceiver52M/ms/ms_rx_upper.h
index 09154bf..5e25a48 100644
--- a/Transceiver52M/ms/ms_rx_upper.h
+++ b/Transceiver52M/ms/ms_rx_upper.h
@@ -28,13 +28,12 @@
#include "GSMCommon.h"
#include "radioClock.h"
#include "syncthing.h"
-#include "ms_state.h"
+#include "l1if.h"
+using tx_queue_t = spsc_cond<8 * 1, trxd_to_trx, true, false>;
class upper_trx : public ms_trx {
int rx_sps, tx_sps;
- ms_TransceiverState mStates;
-
bool mOn; ///< flag to indicate that transceiver is powered on
double mTxFreq; ///< the transmit frequency
double mRxFreq; ///< the receive frequency
@@ -42,9 +41,6 @@ class upper_trx : public ms_trx {
unsigned mMaxExpectedDelay; ///< maximum TOA offset in GSM symbols
unsigned long long mRxSlotMask[8]; ///< MS - enabled multiframe slot mask
- int mDataSockets;
- sockaddr_in datadest;
- sockaddr datasrc;
int mCtrlSockets;
sockaddr_in ctrldest;
sockaddr ctrlsrc;
@@ -98,8 +94,6 @@ class upper_trx : public ms_trx {
SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
- bool detectSCH(ms_TransceiverState *state, signalVector &burst, struct estim_burst_params *ebp);
-
std::thread thr_control, thr_rx, thr_tx;
public:
@@ -110,12 +104,8 @@ class upper_trx : public ms_trx {
{
auto c_srcport = 6700 + 2 * 0 + 1;
auto c_dstport = 6700 + 2 * 0 + 101;
- auto d_srcport = 6700 + 2 * 0 + 2;
- auto d_dstport = 6700 + 2 * 0 + 102;
openudp(&mCtrlSockets, c_srcport, "127.0.0.1");
- openudp(&mDataSockets, d_srcport, "127.0.0.1");
resolveAddress(&ctrldest, "127.0.0.1", c_dstport);
- resolveAddress(&datadest, "127.0.0.1", d_dstport);
};
};
diff --git a/Transceiver52M/ms/ms_state.h b/Transceiver52M/ms/ms_state.h
deleted file mode 100644
index c00d072..0000000
--- a/Transceiver52M/ms/ms_state.h
+++ /dev/null
@@ -1,175 +0,0 @@
-#pragma once
-
-#include <radioVector.h>
-#include <signalVector.h>
-
-enum class trx_mode {
- TRX_MODE_OFF,
- TRX_MODE_BTS,
- TRX_MODE_MS_ACQUIRE,
- TRX_MODE_MS_TRACK,
-};
-
-enum class ChannelCombination {
- FILL, ///< Channel is transmitted, but unused
- I, ///< TCH/FS
- II, ///< TCH/HS, idle every other slot
- III, ///< TCH/HS
- IV, ///< FCCH+SCH+CCCH+BCCH, uplink RACH
- V, ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
- VI, ///< CCCH+BCCH, uplink RACH
- VII, ///< SDCCH/8 + SACCH/8
- VIII, ///< TCH/F + FACCH/F + SACCH/M
- IX, ///< TCH/F + SACCH/M
- X, ///< TCH/FD + SACCH/MD
- XI, ///< PBCCH+PCCCH+PDTCH+PACCH+PTCCH
- XII, ///< PCCCH+PDTCH+PACCH+PTCCH
- XIII, ///< PDTCH+PACCH+PTCCH
- NONE_INACTIVE, ///< Channel is inactive, default
- LOOPBACK ///< similar go VII, used in loopback testing
-};
-
-struct ms_TransceiverState {
- ms_TransceiverState() : mFreqOffsets(10), mode(trx_mode::TRX_MODE_OFF)
- {
- for (int i = 0; i < 8; i++) {
- chanType[i] = ChannelCombination::NONE_INACTIVE;
- fillerModulus[i] = 26;
-
- for (int n = 0; n < 102; n++)
- fillerTable[n][i] = nullptr;
- }
- }
-
- ~ms_TransceiverState()
- {
- for (int i = 0; i < 8; i++) {
- for (int n = 0; n < 102; n++)
- delete fillerTable[n][i];
- }
- }
-
- void setModulus(size_t timeslot)
- {
- switch (chanType[timeslot]) {
- case ChannelCombination::NONE_INACTIVE:
- case ChannelCombination::I:
- case ChannelCombination::II:
- case ChannelCombination::III:
- case ChannelCombination::FILL:
- fillerModulus[timeslot] = 26;
- break;
- case ChannelCombination::IV:
- case ChannelCombination::VI:
- case ChannelCombination::V:
- fillerModulus[timeslot] = 51;
- break;
- //case V:
- case ChannelCombination::VII:
- fillerModulus[timeslot] = 102;
- break;
- case ChannelCombination::XIII:
- fillerModulus[timeslot] = 52;
- break;
- default:
- break;
- }
- }
-
- CorrType expectedCorrType(GSM::Time currTime, unsigned long long *mRxSlotMask)
- {
- unsigned burstTN = currTime.TN();
- unsigned burstFN = currTime.FN();
-
- if (mode == trx_mode::TRX_MODE_MS_TRACK) {
- /* 102 modulus case currently unhandled */
- if (fillerModulus[burstTN] > 52)
- return OFF;
-
- int modFN = burstFN % fillerModulus[burstTN];
- unsigned long long reg = (unsigned long long)1 << modFN;
- if (reg & mRxSlotMask[burstTN])
- return TSC;
- else
- return OFF;
- }
-
- switch (chanType[burstTN]) {
- case ChannelCombination::NONE_INACTIVE:
- return OFF;
- break;
- case ChannelCombination::FILL:
- return IDLE;
- break;
- case ChannelCombination::I:
- return TSC;
- /*if (burstFN % 26 == 25)
- return IDLE;
- else
- return TSC;*/
- break;
- case ChannelCombination::II:
- return TSC;
- break;
- case ChannelCombination::III:
- return TSC;
- break;
- case ChannelCombination::IV:
- case ChannelCombination::VI:
- return RACH;
- break;
- case ChannelCombination::V: {
- int mod51 = burstFN % 51;
- if ((mod51 <= 36) && (mod51 >= 14))
- return RACH;
- else if ((mod51 == 4) || (mod51 == 5))
- return RACH;
- else if ((mod51 == 45) || (mod51 == 46))
- return RACH;
- else
- return TSC;
- break;
- }
- case ChannelCombination::VII:
- if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
- return IDLE;
- else
- return TSC;
- break;
- case ChannelCombination::XIII: {
- int mod52 = burstFN % 52;
- if ((mod52 == 12) || (mod52 == 38))
- return RACH;
- else if ((mod52 == 25) || (mod52 == 51))
- return IDLE;
- else
- return TSC;
- break;
- }
- case ChannelCombination::LOOPBACK:
- if ((burstFN % 51 <= 50) && (burstFN % 51 >= 48))
- return IDLE;
- else
- return TSC;
- break;
- default:
- return OFF;
- break;
- }
- }
-
- /* Initialize a multiframe slot in the filler table */
- void init(size_t slot, signalVector *burst, bool fill);
-
- ChannelCombination chanType[8];
-
- /* The filler table */
- signalVector *fillerTable[102][8];
- int fillerModulus[8];
-
- /* Received noise energy levels */
- avgVector mFreqOffsets;
-
- /* Transceiver mode */
- trx_mode mode;
-}; \ No newline at end of file
diff --git a/trxcon/trx_if.c b/trxcon/trx_if.c
index ffad6c0..ba2ef53 100644
--- a/trxcon/trx_if.c
+++ b/trxcon/trx_if.c
@@ -602,32 +602,9 @@ rsp_error:
static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
{
struct trx_instance *trx = ofd->data;
- struct trx_meas_set meas;
uint8_t buf[TRXD_BUF_SIZE];
- sbit_t bits[148];
- int8_t rssi, tn;
- int16_t toa256;
- uint32_t fn;
ssize_t read_len;
-#ifdef IPCIF
- struct trxd_from_trx* rcvd = trxif_from_trx_d();
- if (!rcvd) {
- LOGP(DTRX, LOGL_ERROR, "read() failed with rc=%zd\n", rcvd);
- return rcvd;
- }
-
- tn = rcvd->ts;
- fn = rcvd->fn;
- rssi = -(int8_t) rcvd->rssi;
- toa256 = (int16_t) rcvd->toa;
-
- /* Copy and convert bits {254..0} to sbits {-127..127} */
- //osmo_ubit2sbit(bits, rcvd->symbols, 148);
- memcpy(bits, rcvd->symbols, 148);
-
- free(rcvd);
-#else
read_len = read(ofd->fd, buf, sizeof(buf));
if (read_len <= 0) {
LOGP(DTRXD, LOGL_ERROR, "read() failed with rc=%zd\n", read_len);
@@ -641,7 +618,18 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
read_len);
return -EINVAL;
}
-#endif
+
+ return trx_data_rx_handler(trx, buf);
+}
+
+int trx_data_rx_handler(struct trx_instance *trx, uint8_t *buf)
+{
+ struct trx_meas_set meas;
+ sbit_t bits[148];
+ int8_t rssi, tn;
+ int16_t toa256;
+ uint32_t fn;
+
tn = buf[0];
fn = osmo_load32be(buf + 1);
rssi = -(int8_t)buf[5];
@@ -681,6 +669,8 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
return 0;
}
+extern void tx_external_transceiver(uint8_t *burst) __attribute__((weak));
+
int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
uint8_t pwr, const ubit_t *bits)
{
@@ -719,7 +709,10 @@ int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
memcpy(buf + 6, bits, 148);
/* Send data to transceiver */
- send(trx->trx_ofd_data.fd, buf, 154, 0);
+ if (tx_external_transceiver)
+ tx_external_transceiver(buf);
+ else
+ send(trx->trx_ofd_data.fd, buf, 154, 0);
#endif
return 0;
diff --git a/trxcon/trx_if.h b/trxcon/trx_if.h
index abbde97..8cf95d8 100644
--- a/trxcon/trx_if.h
+++ b/trxcon/trx_if.h
@@ -84,3 +84,4 @@ int trx_if_cmd_measure(struct trx_instance *trx,
int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
uint8_t pwr, const ubit_t *bits);
+int trx_data_rx_handler(struct trx_instance *trx, uint8_t *buf); \ No newline at end of file
diff --git a/trxcon/trxcon.c b/trxcon/trxcon.c
index 4e3ac27..67fb1d7 100644
--- a/trxcon/trxcon.c
+++ b/trxcon/trxcon.c
@@ -271,9 +271,8 @@ static void signal_handler(int signum)
}
}
-extern void init_external_transceiver(int argc, char **argv);
-extern void stop_trx();
-extern volatile bool gshutdown;
+extern void init_external_transceiver(struct trx_instance *trx, int argc, char **argv) __attribute__((weak));
+extern void close_external_transceiver(int argc, char **argv) __attribute__((weak));
int main(int argc, char **argv)
{
@@ -372,14 +371,14 @@ int main(int argc, char **argv)
/* Initialize pseudo-random generator */
srand(time(NULL));
- init_external_transceiver(argc, argv);
-
- // while (!app_data.quit)
- // osmo_select_main(0);
-
- gshutdown = true;
- stop_trx();
+ if (init_external_transceiver)
+ init_external_transceiver(app_data.trx, argc, argv);
+ else
+ while (!app_data.quit)
+ osmo_select_main(0);
+ if (close_external_transceiver)
+ close_external_transceiver(argc, argv);
exit:
/* Close active connections */