blob: f5ddd9b2d742fe1d183c3beeefef4e70d03f9710 [file] [log] [blame]
dburgessb3a0ca42011-10-12 07:44:40 +00001/*
2* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
3*
Pau Espin Pedrol21d03d32019-07-22 12:05:52 +02004* SPDX-License-Identifier: GPL-3.0+
5*
dburgessb3a0ca42011-10-12 07:44:40 +00006* This software is distributed under the terms of the GNU Public License.
7* See the COPYING file in the main directory for details.
8*
9* This use of this software may be subject to additional restrictions.
10* See the LEGAL file in the main directory for details.
11
12 This program is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
24*/
25
dburgessb3a0ca42011-10-12 07:44:40 +000026#include <stdio.h>
Pau Espin Pedrol27424a32019-08-07 12:45:53 +020027#include <netinet/in.h>
Alexander Chemerise8905a02015-06-03 23:47:56 -040028#include <iomanip> // std::setprecision
Alexander Chemerise692ce92015-06-12 00:15:31 -040029#include <fstream>
dburgessb3a0ca42011-10-12 07:44:40 +000030#include "Transceiver.h"
31#include <Logger.h>
Ericcca5d932023-07-25 18:44:03 +020032#include <grgsm_vitac/grgsm_vitac.h>
dburgessb3a0ca42011-10-12 07:44:40 +000033
Pau Espin Pedroldb936b92018-09-03 16:50:49 +020034extern "C" {
35#include "osmo_signal.h"
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +020036#include "proto_trxd.h"
37
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +020038#include <osmocom/core/utils.h>
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +020039#include <osmocom/core/socket.h>
Vadim Yanitskiyb3123252019-07-15 23:53:08 +070040#include <osmocom/core/bits.h>
Pau Espin Pedrol553a2502020-07-29 18:05:25 +020041#include <osmocom/vty/cpu_sched_vty.h>
Pau Espin Pedroldb936b92018-09-03 16:50:49 +020042}
43
ttsou2173abf2012-08-08 00:51:31 +000044#ifdef HAVE_CONFIG_H
45#include "config.h"
46#endif
dburgessb3a0ca42011-10-12 07:44:40 +000047
Alexander Chemerisd734e2d2013-06-16 14:30:58 +040048using namespace GSM;
49
Erica7143bf2020-04-08 01:39:17 +020050Transceiver *transceiver;
51
kurtis.heimerlec842de2012-11-23 08:37:32 +000052#define USB_LATENCY_INTRVL 10,0
ttsou2173abf2012-08-08 00:51:31 +000053
Thomas Tsoufa3a7872013-10-17 21:23:34 -040054/* Number of running values use in noise average */
55#define NOISE_CNT 20
ttsoue8dde022012-12-06 15:43:55 +000056
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +020057
58static void dispatch_trx_rate_ctr_change(TransceiverState *state, unsigned int chan) {
59 thread_enable_cancel(false);
60 state->ctrs.chan = chan;
61 osmo_signal_dispatch(SS_DEVICE, S_TRX_COUNTER_CHANGE, &state->ctrs);
62 thread_enable_cancel(true);
63}
64
Thomas Tsouf0782732013-10-29 15:55:47 -040065TransceiverState::TransceiverState()
Pau Espin Pedrol7d8676a2020-08-26 14:30:46 +020066 : mFiller(FILLER_ZERO), mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT),
Eric1f37e4d2020-09-04 20:36:34 +020067 mPower(0.0), mMuted(false), first_dl_fn_rcv()
Thomas Tsouf0782732013-10-29 15:55:47 -040068{
69 for (int i = 0; i < 8; i++) {
70 chanType[i] = Transceiver::NONE;
71 fillerModulus[i] = 26;
72 chanResponse[i] = NULL;
73 DFEForward[i] = NULL;
74 DFEFeedback[i] = NULL;
75
76 for (int n = 0; n < 102; n++)
77 fillerTable[n][i] = NULL;
78 }
Pau Espin Pedrola71c5d02020-07-01 11:50:01 +020079 memset(&ctrs, 0, sizeof(struct trx_counters));
Thomas Tsouf0782732013-10-29 15:55:47 -040080}
81
82TransceiverState::~TransceiverState()
83{
84 for (int i = 0; i < 8; i++) {
85 delete chanResponse[i];
86 delete DFEForward[i];
87 delete DFEFeedback[i];
88
89 for (int n = 0; n < 102; n++)
90 delete fillerTable[n][i];
91 }
92}
93
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010094bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
Tom Tsou64ad7122015-05-19 18:26:31 -070095{
Tom Tsou64ad7122015-05-19 18:26:31 -070096 signalVector *burst;
97
98 if ((sps != 1) && (sps != 4))
99 return false;
100
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +0200101 mFiller = filler;
102
Tom Tsou64ad7122015-05-19 18:26:31 -0700103 for (size_t n = 0; n < 8; n++) {
Tom Tsou64ad7122015-05-19 18:26:31 -0700104 for (size_t i = 0; i < 102; i++) {
105 switch (filler) {
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100106 case FILLER_DUMMY:
Tom Tsou8ee2f382016-03-06 20:57:34 -0800107 burst = generateDummyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -0700108 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100109 case FILLER_NORM_RAND:
Tom Tsou8ee2f382016-03-06 20:57:34 -0800110 burst = genRandNormalBurst(rtsc, sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -0700111 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100112 case FILLER_EDGE_RAND:
Tom Tsouaf717b22016-03-06 22:19:15 -0800113 burst = generateEdgeBurst(rtsc);
114 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100115 case FILLER_ACCESS_RAND:
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300116 burst = genRandAccessBurst(rach_delay, sps, n);
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300117 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100118 case FILLER_ZERO:
Tom Tsou64ad7122015-05-19 18:26:31 -0700119 default:
Tom Tsou8ee2f382016-03-06 20:57:34 -0800120 burst = generateEmptyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -0700121 }
122
123 scaleVector(*burst, scale);
124 fillerTable[i][n] = burst;
125 }
126
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100127 if ((filler == FILLER_NORM_RAND) ||
128 (filler == FILLER_EDGE_RAND)) {
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700129 chanType[n] = TSC;
Tom Tsouaf717b22016-03-06 22:19:15 -0800130 }
Thomas Tsou15d743e2014-01-25 02:34:03 -0500131 }
Tom Tsou64ad7122015-05-19 18:26:31 -0700132
133 return false;
Thomas Tsouf0782732013-10-29 15:55:47 -0400134}
135
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200136Transceiver::Transceiver(const struct trx_cfg *cfg,
Alexander Chemerise8905a02015-06-03 23:47:56 -0400137 GSM::Time wTransmitLatency,
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200138 RadioInterface *wRadioInterface)
Eric5561f112022-07-19 21:18:21 +0200139 : mChans(cfg->num_chans), cfg(cfg),
140 mCtrlSockets(mChans), mClockSocket(-1),
141 mTxPriorityQueues(mChans), mReceiveFIFO(mChans),
142 mRxServiceLoopThreads(mChans), mRxLowerLoopThread(nullptr), mTxLowerLoopThread(nullptr),
143 mTxPriorityQueueServiceLoopThreads(mChans), mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
144 mOn(false),mForceClockInterface(false), mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0),
145 mMaxExpectedDelayNB(0), mWriteBurstToDiskMask(0), mVersionTRXD(mChans), mStates(mChans)
dburgessb3a0ca42011-10-12 07:44:40 +0000146{
dburgessb3a0ca42011-10-12 07:44:40 +0000147 txFullScale = mRadioInterface->fullScaleInputValue();
148 rxFullScale = mRadioInterface->fullScaleOutputValue();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300149
Vadim Yanitskiy0ff9c9f2020-10-24 16:48:33 +0700150 for (size_t i = 0; i < ARRAY_SIZE(mHandover); i++) {
151 for (size_t j = 0; j < ARRAY_SIZE(mHandover[i]); j++)
Alexander Chemeris5a068062015-06-20 01:38:47 +0300152 mHandover[i][j] = false;
153 }
dburgessb3a0ca42011-10-12 07:44:40 +0000154}
155
156Transceiver::~Transceiver()
157{
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800158 stop();
159
dburgessb3a0ca42011-10-12 07:44:40 +0000160 sigProcLibDestroy();
Thomas Tsoud647ec52013-10-29 15:17:34 -0400161
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200162 if (mClockSocket >= 0)
163 close(mClockSocket);
164
Thomas Tsou204a9f12013-10-29 18:34:16 -0400165 for (size_t i = 0; i < mChans; i++) {
166 mTxPriorityQueues[i].clear();
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200167 if (mDataSockets[i] >= 0)
168 close(mDataSockets[i]);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400169 }
dburgessb3a0ca42011-10-12 07:44:40 +0000170}
Thomas Tsou83e06892013-08-20 16:10:01 -0400171
Erica7143bf2020-04-08 01:39:17 +0200172int Transceiver::ctrl_sock_cb(struct osmo_fd *bfd, unsigned int flags)
173{
174 int rc = 0;
175 int chan = static_cast<int>(reinterpret_cast<uintptr_t>(bfd->data));
176
177 if (flags & OSMO_FD_READ)
178 rc = transceiver->ctrl_sock_handle_rx(chan);
179 if (rc < 0)
180 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
181
182 if (flags & OSMO_FD_WRITE)
183 rc = transceiver->ctrl_sock_write(chan);
184 if (rc < 0)
185 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
186
187 return rc;
188}
189
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800190/*
191 * Initialize transceiver
192 *
193 * Start or restart the control loop. Any further control is handled through the
194 * socket API. Randomize the central radio clock set the downlink burst
195 * counters. Note that the clock will not update until the radio starts, but we
196 * are still expected to report clock indications through control channel
197 * activity.
198 */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200199bool Transceiver::init()
Thomas Tsou83e06892013-08-20 16:10:01 -0400200{
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500201 int d_srcport, d_dstport, c_srcport, c_dstport;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400202 if (!mChans) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100203 LOG(FATAL) << "No channels assigned";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400204 return false;
205 }
206
Tom Tsou2079a3c2016-03-06 00:58:56 -0800207 if (!sigProcLibSetup()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100208 LOG(FATAL) << "Failed to initialize signal processing library";
Thomas Tsou83e06892013-08-20 16:10:01 -0400209 return false;
210 }
211
Ericcca5d932023-07-25 18:44:03 +0200212 initvita();
213
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200214 mDataSockets.resize(mChans, -1);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400215
Thomas Tsou204a9f12013-10-29 18:34:16 -0400216
Thomas Tsouccb73e12014-04-15 17:41:28 -0400217 /* Filler table retransmissions - support only on channel 0 */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200218 if (cfg->filler == FILLER_DUMMY)
Thomas Tsouccb73e12014-04-15 17:41:28 -0400219 mStates[0].mRetrans = true;
220
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800221 /* Setup sockets */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200222 mClockSocket = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200223 cfg->bind_addr, cfg->base_port,
224 cfg->remote_addr, cfg->base_port + 100,
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200225 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
Pau Espin Pedrole7195ac2019-08-26 10:55:04 +0200226 if (mClockSocket < 0)
227 return false;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200228
Thomas Tsou204a9f12013-10-29 18:34:16 -0400229 for (size_t i = 0; i < mChans; i++) {
Erica7143bf2020-04-08 01:39:17 +0200230 int rv;
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200231 FillerType filler = cfg->filler;
232 c_srcport = cfg->base_port + 2 * i + 1;
233 c_dstport = cfg->base_port + 2 * i + 101;
234 d_srcport = cfg->base_port + 2 * i + 2;
235 d_dstport = cfg->base_port + 2 * i + 102;
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500236
Erica7143bf2020-04-08 01:39:17 +0200237 rv = osmo_sock_init2_ofd(&mCtrlSockets[i].conn_bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200238 cfg->bind_addr, c_srcport,
239 cfg->remote_addr, c_dstport,
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200240 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
Erica7143bf2020-04-08 01:39:17 +0200241 if (rv < 0)
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200242 return false;
243
Erica7143bf2020-04-08 01:39:17 +0200244 mCtrlSockets[i].conn_bfd.cb = ctrl_sock_cb;
245 mCtrlSockets[i].conn_bfd.data = reinterpret_cast<void*>(i);
246
247
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200248 mDataSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200249 cfg->bind_addr, d_srcport,
250 cfg->remote_addr, d_dstport,
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200251 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
Eric1a26b4f2020-04-13 00:03:01 +0200252 if (mDataSockets[i] < 0)
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200253 return false;
Erica7143bf2020-04-08 01:39:17 +0200254
255 if (i && filler == FILLER_DUMMY)
256 filler = FILLER_ZERO;
257
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200258 mStates[i].init(filler, cfg->tx_sps, txFullScale, cfg->rtsc, cfg->rach_delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400259 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400260
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800261 /* Randomize the central clock */
262 GSM::Time startTime(random() % gHyperframe, 0);
263 mRadioInterface->getClock()->set(startTime);
264 mTransmitDeadlineClock = startTime;
265 mLastClockUpdateTime = startTime;
266 mLatencyUpdateTime = startTime;
267
Thomas Tsou83e06892013-08-20 16:10:01 -0400268 return true;
269}
dburgessb3a0ca42011-10-12 07:44:40 +0000270
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800271/*
272 * Start the transceiver
273 *
274 * Submit command(s) to the radio device to commence streaming samples and
275 * launch threads to handle sample I/O. Re-synchronize the transmit burst
276 * counters to the central radio clock here as well.
277 */
278bool Transceiver::start()
279{
280 ScopedLock lock(mLock);
281
282 if (mOn) {
283 LOG(ERR) << "Transceiver already running";
Ivan Kluchnikov194a9b12015-04-23 17:08:27 +0300284 return true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800285 }
286
287 LOG(NOTICE) << "Starting the transceiver";
288
289 GSM::Time time = mRadioInterface->getClock()->get();
290 mTransmitDeadlineClock = time;
291 mLastClockUpdateTime = time;
292 mLatencyUpdateTime = time;
293
294 if (!mRadioInterface->start()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100295 LOG(FATAL) << "Device failed to start";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800296 return false;
297 }
298
299 /* Device is running - launch I/O threads */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200300 mRxLowerLoopThread = new Thread(cfg->stack_size);
301 mTxLowerLoopThread = new Thread(cfg->stack_size);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800302 mTxLowerLoopThread->start((void * (*)(void*))
303 TxLowerLoopAdapter,(void*) this);
304 mRxLowerLoopThread->start((void * (*)(void*))
305 RxLowerLoopAdapter,(void*) this);
306
307 /* Launch uplink and downlink burst processing threads */
308 for (size_t i = 0; i < mChans; i++) {
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200309 TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
310 params->trx = this;
311 params->num = i;
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200312 mRxServiceLoopThreads[i] = new Thread(cfg->stack_size);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800313 mRxServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200314 RxUpperLoopAdapter, (void*) params);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800315
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200316 params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
317 params->trx = this;
318 params->num = i;
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200319 mTxPriorityQueueServiceLoopThreads[i] = new Thread(cfg->stack_size);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800320 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200321 TxUpperLoopAdapter, (void*) params);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800322 }
323
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200324 mForceClockInterface = true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800325 mOn = true;
326 return true;
327}
328
329/*
330 * Stop the transceiver
331 *
332 * Perform stopping by disabling receive streaming and issuing cancellation
333 * requests to running threads. Most threads will timeout and terminate once
334 * device is disabled, but the transmit loop may block waiting on the central
335 * UMTS clock. Explicitly signal the clock to make sure that the transmit loop
336 * makes it to the thread cancellation point.
337 */
338void Transceiver::stop()
339{
340 ScopedLock lock(mLock);
341
342 if (!mOn)
343 return;
344
345 LOG(NOTICE) << "Stopping the transceiver";
346 mTxLowerLoopThread->cancel();
347 mRxLowerLoopThread->cancel();
Tom Tsoud67bd602017-06-15 15:35:02 -0700348 mTxLowerLoopThread->join();
349 mRxLowerLoopThread->join();
350 delete mTxLowerLoopThread;
351 delete mRxLowerLoopThread;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800352
353 for (size_t i = 0; i < mChans; i++) {
354 mRxServiceLoopThreads[i]->cancel();
355 mTxPriorityQueueServiceLoopThreads[i]->cancel();
356 }
357
358 LOG(INFO) << "Stopping the device";
359 mRadioInterface->stop();
360
361 for (size_t i = 0; i < mChans; i++) {
362 mRxServiceLoopThreads[i]->join();
363 mTxPriorityQueueServiceLoopThreads[i]->join();
364 delete mRxServiceLoopThreads[i];
365 delete mTxPriorityQueueServiceLoopThreads[i];
366
367 mTxPriorityQueues[i].clear();
368 }
369
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800370 mOn = false;
371 LOG(NOTICE) << "Transceiver stopped";
372}
373
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500374void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400375 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000376{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500377 signalVector *burst;
378 radioVector *radio_burst;
379
Thomas Tsou204a9f12013-10-29 18:34:16 -0400380 if (chan >= mTxPriorityQueues.size()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100381 LOGCHAN(chan, DTRXDDL, FATAL) << "Invalid channel";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400382 return;
383 }
384
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500385 if (wTime.TN() > 7) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100386 LOGCHAN(chan, DTRXDDL, FATAL) << "Received burst with invalid slot " << wTime.TN();
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500387 return;
388 }
389
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800390 /* Use the number of bits as the EDGE burst indicator */
391 if (bits.size() == EDGE_BURST_NBITS)
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200392 burst = modulateEdgeBurst(bits, cfg->tx_sps);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800393 else
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200394 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), cfg->tx_sps);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800395
Vadim Yanitskiy2a637a52021-01-26 23:55:22 +0100396 scaleVector(*burst, txFullScale * pow(10, (double) -RSSI / 20));
dburgessb3a0ca42011-10-12 07:44:40 +0000397
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500398 radio_burst = new radioVector(wTime, burst);
399
400 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000401}
402
Thomas Tsou15d743e2014-01-25 02:34:03 -0500403void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
404{
405 int TN, modFN;
406 TransceiverState *state = &mStates[chan];
407
408 TN = burst->getTime().TN();
409 modFN = burst->getTime().FN() % state->fillerModulus[TN];
410
411 delete state->fillerTable[modFN][TN];
412 state->fillerTable[modFN][TN] = burst->getVector();
413 burst->setVector(NULL);
414}
415
dburgessb3a0ca42011-10-12 07:44:40 +0000416void Transceiver::pushRadioVector(GSM::Time &nowTime)
417{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400418 int TN, modFN;
419 radioVector *burst;
420 TransceiverState *state;
421 std::vector<signalVector *> bursts(mChans);
422 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500423 std::vector<bool> filler(mChans, true);
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200424 bool ratectr_changed;
dburgessb3a0ca42011-10-12 07:44:40 +0000425
Pau Espin Pedrol8b0c5362020-07-09 17:39:20 +0200426 TN = nowTime.TN();
427
Thomas Tsou204a9f12013-10-29 18:34:16 -0400428 for (size_t i = 0; i < mChans; i ++) {
429 state = &mStates[i];
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200430 ratectr_changed = false;
431
Pau Espin Pedrol7d8676a2020-08-26 14:30:46 +0200432 zeros[i] = state->chanType[TN] == NONE || state->mMuted;
Pau Espin Pedrol8b0c5362020-07-09 17:39:20 +0200433
434 Mutex *mtx = mTxPriorityQueues[i].getMutex();
435 mtx->lock();
dburgessb3a0ca42011-10-12 07:44:40 +0000436
Thomas Tsou204a9f12013-10-29 18:34:16 -0400437 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
Pau Espin Pedrolc249ce22020-07-17 18:33:10 +0200438 LOGCHAN(i, DTRXDDL, INFO) << "dumping STALE burst in TRX->SDR interface ("
Pau Espin Pedrolf37b0ad2018-04-25 18:01:27 +0200439 << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200440 state->ctrs.tx_stale_bursts++;
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200441 ratectr_changed = true;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500442 if (state->mRetrans)
443 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500444 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400445 }
446
Thomas Tsou204a9f12013-10-29 18:34:16 -0400447 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500448 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500449
450 if (state->mRetrans) {
451 updateFillerTable(i, burst);
452 } else {
453 burst->setVector(NULL);
454 filler[i] = false;
455 }
456
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500457 delete burst;
Pau Espin Pedrol8b0c5362020-07-09 17:39:20 +0200458 } else {
459 modFN = nowTime.FN() % state->fillerModulus[TN];
460 bursts[i] = state->fillerTable[modFN][TN];
Pau Espin Pedroldf675782020-07-17 14:09:28 +0200461 if (i == 0 && state->mFiller == FILLER_ZERO) {
Pau Espin Pedrolc249ce22020-07-17 18:33:10 +0200462 LOGCHAN(i, DTRXDDL, INFO) << "No Tx burst available for " << nowTime
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200463 << ", retrans=" << state->mRetrans;
464 state->ctrs.tx_unavailable_bursts++;
465 ratectr_changed = true;
466 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400467 }
Pau Espin Pedrol8b0c5362020-07-09 17:39:20 +0200468
469 mtx->unlock();
470
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200471 if (ratectr_changed)
Pau Espin Pedrol8b0c5362020-07-09 17:39:20 +0200472 dispatch_trx_rate_ctr_change(state, i);
dburgessb3a0ca42011-10-12 07:44:40 +0000473 }
474
Thomas Tsou204a9f12013-10-29 18:34:16 -0400475 mRadioInterface->driveTransmitRadio(bursts, zeros);
476
Thomas Tsou15d743e2014-01-25 02:34:03 -0500477 for (size_t i = 0; i < mChans; i++) {
478 if (!filler[i])
479 delete bursts[i];
480 }
dburgessb3a0ca42011-10-12 07:44:40 +0000481}
482
Thomas Tsou204a9f12013-10-29 18:34:16 -0400483void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000484{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400485 TransceiverState *state = &mStates[chan];
486
487 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000488 case NONE:
489 case I:
490 case II:
491 case III:
492 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400493 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000494 break;
495 case IV:
496 case VI:
497 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400498 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000499 break;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200500 //case V:
dburgessb3a0ca42011-10-12 07:44:40 +0000501 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400502 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000503 break;
ttsoufc40a842013-06-09 22:38:18 +0000504 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400505 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000506 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000507 default:
508 break;
509 }
510}
511
512
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700513CorrType Transceiver::expectedCorrType(GSM::Time currTime,
514 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000515{
Alexander Chemeris5a068062015-06-20 01:38:47 +0300516 static int tchh_subslot[26] = { 0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,1 };
517 static int sdcch4_subslot[102] = { 3,3,3,3,0,0,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2,
518 3,3,3,3,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2 };
519 static int sdcch8_subslot[102] = { 5,5,5,5,6,6,6,6,7,7,7,7,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,0,0,0,0,
520 1,1,1,1,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,4,4,4,4 };
Thomas Tsou204a9f12013-10-29 18:34:16 -0400521 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000522 unsigned burstTN = currTime.TN();
523 unsigned burstFN = currTime.FN();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300524 int subch;
dburgessb3a0ca42011-10-12 07:44:40 +0000525
Thomas Tsou204a9f12013-10-29 18:34:16 -0400526 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000527 case NONE:
528 return OFF;
529 break;
530 case FILL:
531 return IDLE;
532 break;
533 case I:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300534 // TODO: Are we expecting RACH on an IDLE frame?
535/* if (burstFN % 26 == 25)
536 return IDLE;*/
537 if (mHandover[burstTN][0])
538 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000539 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000540 break;
541 case II:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300542 subch = tchh_subslot[burstFN % 26];
543 if (subch == 1)
544 return IDLE;
545 if (mHandover[burstTN][0])
546 return RACH;
ttsou20642972013-03-27 22:00:25 +0000547 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000548 break;
549 case III:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300550 subch = tchh_subslot[burstFN % 26];
551 if (mHandover[burstTN][subch])
552 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000553 return TSC;
554 break;
555 case IV:
556 case VI:
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200557 return cfg->ext_rach ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000558 break;
559 case V: {
560 int mod51 = burstFN % 51;
561 if ((mod51 <= 36) && (mod51 >= 14))
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200562 return cfg->ext_rach ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000563 else if ((mod51 == 4) || (mod51 == 5))
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200564 return cfg->ext_rach ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000565 else if ((mod51 == 45) || (mod51 == 46))
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200566 return cfg->ext_rach ? EXT_RACH : RACH;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300567 else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
568 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000569 else
570 return TSC;
571 break;
572 }
573 case VII:
574 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
575 return IDLE;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300576 else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
577 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000578 else
579 return TSC;
580 break;
ttsoufc40a842013-06-09 22:38:18 +0000581 case XIII: {
582 int mod52 = burstFN % 52;
583 if ((mod52 == 12) || (mod52 == 38))
Vadim Yanitskiy00ddcfa2022-10-26 04:11:28 +0700584 return RACH; /* RACH is always 8-bit on PTCCH/U */
ttsoufc40a842013-06-09 22:38:18 +0000585 else if ((mod52 == 25) || (mod52 == 51))
586 return IDLE;
Pau Espin Pedrolbfc1d0b2019-08-26 16:34:45 +0200587 else /* Enable 8-PSK burst detection if EDGE is enabled */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200588 return cfg->egprs ? EDGE : TSC;
ttsoufc40a842013-06-09 22:38:18 +0000589 break;
590 }
dburgessb3a0ca42011-10-12 07:44:40 +0000591 case LOOPBACK:
592 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
593 return IDLE;
594 else
595 return TSC;
596 break;
597 default:
598 return OFF;
599 break;
600 }
dburgessb3a0ca42011-10-12 07:44:40 +0000601}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400602
Alexander Chemerise692ce92015-06-12 00:15:31 -0400603void writeToFile(radioVector *radio_burst, size_t chan)
604{
605 GSM::Time time = radio_burst->getTime();
606 std::ostringstream fname;
607 fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
608 std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
609 outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
610 outfile.close();
611}
612
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200613double Transceiver::rssiOffset(size_t chan)
614{
615 if (cfg->force_rssi_offset)
616 return cfg->rssi_offset;
617 return mRadioInterface->rssiOffset(chan) + cfg->rssi_offset;
618}
619
Ericcca5d932023-07-25 18:44:03 +0200620static SoftVector *demodAnyBurst_va(const signalVector &burst, CorrType type, int sps, int rach_max_toa, int tsc)
621{
622 auto conved_beg = reinterpret_cast<const std::complex<float> *>(&burst.begin()[0]);
623 std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
624 float ncmax;
625 const unsigned burst_len_bits = 148 + 8;
626 char demodded_softbits[burst_len_bits];
627 SoftVector *bits = new SoftVector(burst_len_bits);
628
629 if (type == CorrType::TSC) {
630 auto rach_burst_start = get_norm_chan_imp_resp(conved_beg, chan_imp_resp, &ncmax, tsc);
631 rach_burst_start = std::max(rach_burst_start, 0);
632 detect_burst_nb(conved_beg, chan_imp_resp, rach_burst_start, demodded_softbits);
633 } else {
634 auto normal_burst_start = get_access_imp_resp(conved_beg, chan_imp_resp, &ncmax, 0);
635 normal_burst_start = std::max(normal_burst_start, 0);
636 detect_burst_ab(conved_beg, chan_imp_resp, normal_burst_start, demodded_softbits, rach_max_toa);
637 }
638
639 float *s = &bits->begin()[0];
640 for (unsigned int i = 0; i < 148; i++)
641 s[i] = demodded_softbits[i] * -1;
642 for (unsigned int i = 148; i < burst_len_bits; i++)
643 s[i] = 0;
644 return bits;
645}
646
647#define USE_VA
648
649#ifdef USE_VA
650// signalvector is owning despite claiming not to, but we can pretend, too..
651static void dummy_free(void *wData){};
652static void *dummy_alloc(size_t newSize)
653{
654 return 0;
655};
656#endif
657
Thomas Tsou30421a72013-11-13 23:14:48 -0500658/*
659 * Pull bursts from the FIFO and handle according to the slot
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200660 * and burst correlation type. Equalzation is currently disabled.
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200661 * returns 0 on success (bi filled), negative on error (bi content undefined):
662 * -ENOENT: timeslot is off (fn and tn in bi are filled),
663 * -EIO: read error
Thomas Tsou30421a72013-11-13 23:14:48 -0500664 */
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200665int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
dburgessb3a0ca42011-10-12 07:44:40 +0000666{
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800667 int rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200668 struct estim_burst_params ebp;
669 float max = -1.0, avg = 0.0;
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200670 unsigned max_toa;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500671 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500672 signalVector *burst;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200673 GSM::Time burstTime;
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200674 SoftVector *rxBurst;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500675 TransceiverState *state = &mStates[chan];
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200676 bool ctr_changed = false;
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200677 double rssi_offset;
Ericcca5d932023-07-25 18:44:03 +0200678 static complex burst_shift_buffer[625];
679 static signalVector shift_vec(burst_shift_buffer, 0, 625, dummy_alloc, dummy_free);
680 signalVector *shvec_ptr = &shift_vec;
dburgessb3a0ca42011-10-12 07:44:40 +0000681
Thomas Tsou30421a72013-11-13 23:14:48 -0500682 /* Blocking FIFO read */
683 radioVector *radio_burst = mReceiveFIFO[chan]->read();
Pau Espin Pedrol94c52412019-09-06 13:48:35 +0200684 if (!radio_burst) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100685 LOGCHAN(chan, DTRXDUL, ERROR) << "ReceiveFIFO->read() returned no burst";
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200686 return -EIO;
Pau Espin Pedrol94c52412019-09-06 13:48:35 +0200687 }
dburgessb3a0ca42011-10-12 07:44:40 +0000688
Thomas Tsou30421a72013-11-13 23:14:48 -0500689 /* Set time and determine correlation type */
Ericc27fe602021-05-05 17:33:18 +0200690 burstTime = radio_burst->getTime() + cfg->ul_fn_offset;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200691 CorrType type = expectedCorrType(burstTime, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000692
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200693 /* Initialize struct bi */
694 bi->nbits = 0;
695 bi->fn = burstTime.FN();
696 bi->tn = burstTime.TN();
697 bi->rssi = 0.0;
698 bi->toa = 0.0;
699 bi->noise = 0.0;
700 bi->idle = false;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200701 bi->modulation = MODULATION_GMSK;
702 bi->tss = 0; /* TODO: we only support tss 0 right now */
703 bi->tsc = 0;
704 bi->ci = 0.0;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200705
Pau Espin Pedrol0d56d752019-09-09 10:20:58 +0200706 /* Debug: dump bursts to disk */
707 /* bits 0-7 - chan 0 timeslots
708 * bits 8-15 - chan 1 timeslots */
709 if (mWriteBurstToDiskMask & ((1<<bi->tn) << (8*chan)))
710 writeToFile(radio_burst, chan);
711
712 /* No processing if the timeslot is off.
713 * Not even power level or noise calculation. */
714 if (type == OFF) {
715 delete radio_burst;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200716 return -ENOENT;
Pau Espin Pedrol0d56d752019-09-09 10:20:58 +0200717 }
718
Pau Espin Pedrol7d8676a2020-08-26 14:30:46 +0200719 /* If TRX RF is locked/muted by BTS, send idle burst indications */
720 if (state->mMuted)
721 goto ret_idle;
722
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500723 /* Select the diversity channel with highest energy */
724 for (size_t i = 0; i < radio_burst->chans(); i++) {
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200725 float pow = energyDetect(*radio_burst->getVector(i), 20 * cfg->rx_sps);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500726 if (pow > max) {
727 max = pow;
728 max_i = i;
729 }
730 avg += pow;
731 }
732
733 if (max_i < 0) {
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200734 LOGCHAN(chan, DTRXDUL, INFO) << "Received empty burst";
735 state->ctrs.rx_empty_burst++;
736 ctr_changed = true;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200737 goto ret_idle;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500738 }
739
Thomas Tsou30421a72013-11-13 23:14:48 -0500740 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500741 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500742 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400743
Alexander Chemeris2b542102015-06-08 22:46:38 -0400744 if (type == IDLE) {
745 /* Update noise levels */
746 state->mNoises.insert(avg);
747 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200748 }
Alexander Chemeris2b542102015-06-08 22:46:38 -0400749
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200750 rssi_offset = rssiOffset(chan);
751 bi->rssi = 20.0 * log10(rxFullScale / avg) + rssi_offset;
752 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssi_offset;
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200753
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200754 if (type == IDLE)
755 goto ret_idle;
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400756
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200757 max_toa = (type == RACH || type == EXT_RACH) ?
758 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200759
Ericcca5d932023-07-25 18:44:03 +0200760 if (cfg->use_va) {
761 // shifted burst copy to make the old demod and detection happy
762 std::copy(burst->begin() + 20, burst->end() - 20, shift_vec.begin());
763 } else {
764 shvec_ptr = burst;
765 }
766
Thomas Tsou30421a72013-11-13 23:14:48 -0500767 /* Detect normal or RACH bursts */
Ericcca5d932023-07-25 18:44:03 +0200768 rc = detectAnyBurst(*shvec_ptr, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200769 if (rc <= 0) {
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200770 if (rc == -SIGERR_CLIP) {
771 LOGCHAN(chan, DTRXDUL, INFO) << "Clipping detected on received RACH or Normal Burst";
772 state->ctrs.rx_clipping++;
773 ctr_changed = true;
774 } else if (rc != SIGERR_NONE) {
775 LOGCHAN(chan, DTRXDUL, INFO) << "Unhandled RACH or Normal Burst detection error";
776 state->ctrs.rx_no_burst_detected++;
777 ctr_changed = true;
778 }
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200779 goto ret_idle;
dburgessb3a0ca42011-10-12 07:44:40 +0000780 }
dburgessb3a0ca42011-10-12 07:44:40 +0000781
Ericcca5d932023-07-25 18:44:03 +0200782 if (cfg->use_va) {
783 scaleVector(*burst, { (1. / (float)((1 << 14) - 1)), 0 });
784 rxBurst = demodAnyBurst_va(*burst, (CorrType)rc, cfg->rx_sps, max_toa, mTSC);
785 } else {
786 rxBurst = demodAnyBurst(*shvec_ptr, (CorrType)rc, cfg->rx_sps, &ebp);
787 }
788
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200789 bi->toa = ebp.toa;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200790 bi->tsc = ebp.tsc;
791 bi->ci = ebp.ci;
Thomas Tsouef25dba2013-11-14 15:31:24 -0500792
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200793 /* EDGE demodulator returns 444 (gSlotLen * 3) bits */
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200794 if (rxBurst->size() == EDGE_BURST_NBITS) {
795 bi->modulation = MODULATION_8PSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200796 bi->nbits = EDGE_BURST_NBITS;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200797 } else { /* size() here is actually gSlotLen + 8, due to guard periods */
798 bi->modulation = MODULATION_GMSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200799 bi->nbits = gSlotLen;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200800 }
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200801
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200802 // Convert -1..+1 soft bits to 0..1 soft bits
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200803 vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits);
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200804
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200805 delete rxBurst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500806 delete radio_burst;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200807 return 0;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200808
809ret_idle:
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200810 if (ctr_changed)
811 dispatch_trx_rate_ctr_change(state, chan);
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200812 bi->idle = true;
813 delete radio_burst;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200814 return 0;
dburgessb3a0ca42011-10-12 07:44:40 +0000815}
816
dburgessb3a0ca42011-10-12 07:44:40 +0000817void Transceiver::reset()
818{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400819 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
820 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000821}
822
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200823
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700824/**
825 * Matches a buffer with a command.
826 * @param buf a buffer to look command in
827 * @param cmd a command to look in buffer
828 * @param params pointer to arguments, or NULL
829 * @return true if command matches, otherwise false
830 */
831static bool match_cmd(char *buf,
832 const char *cmd, char **params)
833{
834 size_t cmd_len = strlen(cmd);
835
836 /* Check a command itself */
837 if (strncmp(buf, cmd, cmd_len))
838 return false;
839
840 /* A command has arguments */
841 if (params != NULL) {
842 /* Make sure there is a space */
843 if (buf[cmd_len] != ' ')
844 return false;
845
846 /* Update external pointer */
847 *params = buf + cmd_len + 1;
848 }
849
850 return true;
851}
852
Erica7143bf2020-04-08 01:39:17 +0200853void Transceiver::ctrl_sock_send(ctrl_msg& m, int chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000854{
Erica7143bf2020-04-08 01:39:17 +0200855 ctrl_sock_state& s = mCtrlSockets[chan];
856 struct osmo_fd *conn_bfd = &s.conn_bfd;
857
858 s.txmsgqueue.push_back(m);
Harald Welte94def472020-10-19 12:28:26 +0200859 osmo_fd_write_enable(conn_bfd);
Erica7143bf2020-04-08 01:39:17 +0200860}
861
862int Transceiver::ctrl_sock_write(int chan)
863{
864 int rc;
865 ctrl_sock_state& s = mCtrlSockets[chan];
866
867 if (s.conn_bfd.fd < 0) {
868 return -EIO;
869 }
870
871 while (s.txmsgqueue.size()) {
872 const ctrl_msg m = s.txmsgqueue.front();
873
Harald Welte94def472020-10-19 12:28:26 +0200874 osmo_fd_write_disable(&s.conn_bfd);
Erica7143bf2020-04-08 01:39:17 +0200875
876 /* try to send it over the socket */
877 rc = write(s.conn_bfd.fd, m.data, strlen(m.data) + 1);
878 if (rc == 0)
879 goto close;
880 if (rc < 0) {
881 if (errno == EAGAIN) {
Harald Welte94def472020-10-19 12:28:26 +0200882 osmo_fd_write_enable(&s.conn_bfd);
Erica7143bf2020-04-08 01:39:17 +0200883 break;
884 }
885 goto close;
886 }
887
888 s.txmsgqueue.pop_front();
889 }
890 return 0;
891
892close:
893 LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets write(" << s.conn_bfd.fd << ") failed: " << rc;
894 return -1;
895}
896
897int Transceiver::ctrl_sock_handle_rx(int chan)
898{
899 ctrl_msg cmd_received;
900 ctrl_msg cmd_to_send;
901 char *buffer = cmd_received.data;
902 char *response = cmd_to_send.data;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700903 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700904 int msgLen;
Erica7143bf2020-04-08 01:39:17 +0200905 ctrl_sock_state& s = mCtrlSockets[chan];
Thomas Tsoud647ec52013-10-29 15:17:34 -0400906
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700907 /* Attempt to read from control socket */
Erica7143bf2020-04-08 01:39:17 +0200908 msgLen = read(s.conn_bfd.fd, buffer, sizeof(cmd_received.data)-1);
909 if (msgLen < 0 && errno == EAGAIN)
910 return 0; /* Try again later */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200911 if (msgLen <= 0) {
Erica7143bf2020-04-08 01:39:17 +0200912 LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets read(" << s.conn_bfd.fd << ") failed: " << msgLen;
913 return -EIO;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200914 }
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700915
Erica7143bf2020-04-08 01:39:17 +0200916
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700917 /* Zero-terminate received string */
918 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000919
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700920 /* Verify a command signature */
921 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100922 LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
Erica7143bf2020-04-08 01:39:17 +0200923 return -EIO;
dburgessb3a0ca42011-10-12 07:44:40 +0000924 }
dburgessb3a0ca42011-10-12 07:44:40 +0000925
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700926 /* Set command pointer */
927 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200928 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700929
930 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800931 stop();
932 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700933 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700934 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000935 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700936 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000937 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300938 for (int i = 0; i < 8; i++) {
939 for (int j = 0; j < 8; j++)
940 mHandover[i][j] = false;
941 }
Tom Tsou365bc382016-10-19 15:26:04 -0700942 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700943 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700944 unsigned ts = 0, ss = 0;
945 sscanf(params, "%u %u", &ts, &ss);
946 if (ts > 7 || ss > 7) {
Pau Espin Pedrol0fafe032019-11-27 13:17:59 +0100947 sprintf(response, "RSP HANDOVER 1 %u %u", ts, ss);
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700948 } else {
949 mHandover[ts][ss] = true;
950 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
951 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700952 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700953 unsigned ts = 0, ss = 0;
954 sscanf(params, "%u %u", &ts, &ss);
955 if (ts > 7 || ss > 7) {
956 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
957 } else {
958 mHandover[ts][ss] = false;
959 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
960 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700961 } else if (match_cmd(command, "SETMAXDLY", &params)) {
Vadim Yanitskiyc7fc94d2024-01-23 04:12:20 +0700962 //set expected maximum time-of-arrival for Access Bursts
dburgessb3a0ca42011-10-12 07:44:40 +0000963 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700964 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300965 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000966 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700967 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Vadim Yanitskiyc7fc94d2024-01-23 04:12:20 +0700968 //set expected maximum time-of-arrival for Normal Bursts
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300969 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700970 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300971 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
972 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700973 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000974 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700975 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400976 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000977 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700978 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000979 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500980 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000981 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500982 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000983 }
984 else {
Pau Espin Pedrol1b3a8882020-05-29 16:15:18 +0200985 sprintf(response,"RSP NOISELEV 1 0");
dburgessb3a0ca42011-10-12 07:44:40 +0000986 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700987 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800988 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700989 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800990 power = mRadioInterface->setPowerAttenuation(power, chan);
991 mStates[chan].mPower = power;
992 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700993 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800994 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700995 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800996 power = mStates[chan].mPower + step;
997 power = mRadioInterface->setPowerAttenuation(power, chan);
998 mStates[chan].mPower = power;
999 sprintf(response, "RSP ADJPOWER 0 %d", power);
Pau Espin Pedrol0e09e7c2020-05-29 16:39:07 +02001000} else if (match_cmd(command, "NOMTXPOWER", NULL)) {
1001 int power = mRadioInterface->getNominalTxPower(chan);
Pau Espin Pedrol405f17a2020-06-19 14:48:35 +02001002 sprintf(response, "RSP NOMTXPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +07001003 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +00001004 // tune receiver
1005 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +07001006 sscanf(params, "%d", &freqKhz);
Vadim Yanitskiy3b8f7c42018-08-23 13:41:06 +07001007 mRxFreq = (freqKhz + cfg->freq_offset_khz) * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001008 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001009 LOGCHAN(chan, DTRXCTRL, FATAL) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +00001010 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
1011 }
1012 else
1013 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +07001014 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +00001015 // tune txmtr
1016 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +07001017 sscanf(params, "%d", &freqKhz);
Vadim Yanitskiy3b8f7c42018-08-23 13:41:06 +07001018 mTxFreq = (freqKhz + cfg->freq_offset_khz) * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001019 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001020 LOGCHAN(chan, DTRXCTRL, FATAL) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +00001021 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
1022 }
1023 else
1024 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +07001025 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +00001026 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -05001027 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +07001028 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -07001029 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -05001030 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -07001031 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +01001032 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +00001033 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -04001034 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +00001035 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +07001036 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -07001037 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +00001038 int corrCode;
1039 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +07001040 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +00001041 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001042 LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +00001043 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
Erica7143bf2020-04-08 01:39:17 +02001044 return 0;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001045 }
Thomas Tsou204a9f12013-10-29 18:34:16 -04001046 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
1047 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +00001048 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Pau Espin Pedrol13c81092019-07-03 16:17:06 +02001049 } else if (match_cmd(command, "SETFORMAT", &params)) {
1050 // set TRXD protocol version
1051 unsigned version_recv;
1052 sscanf(params, "%u", &version_recv);
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +02001053 LOGCHAN(chan, DTRXCTRL, INFO) << "BTS requests TRXD version switch: " << version_recv;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +02001054 if (version_recv > TRX_DATA_FORMAT_VER) {
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +02001055 LOGCHAN(chan, DTRXCTRL, INFO) << "rejecting TRXD version " << version_recv
Vadim Yanitskiyb6f238c2021-05-27 23:35:21 +02001056 << " in favor of " << TRX_DATA_FORMAT_VER;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +02001057 sprintf(response, "RSP SETFORMAT %u %u", TRX_DATA_FORMAT_VER, version_recv);
1058 } else {
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +02001059 LOGCHAN(chan, DTRXCTRL, NOTICE) << "switching to TRXD version " << version_recv;
1060 mVersionTRXD[chan] = version_recv;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +02001061 sprintf(response, "RSP SETFORMAT %u %u", version_recv, version_recv);
1062 }
Pau Espin Pedrol7d8676a2020-08-26 14:30:46 +02001063 } else if (match_cmd(command, "RFMUTE", &params)) {
1064 // (Un)mute RF TX and RX
1065 unsigned mute;
1066 sscanf(params, "%u", &mute);
1067 mStates[chan].mMuted = mute ? true : false;
1068 sprintf(response, "RSP RFMUTE 0 %u", mute);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +07001069 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Martin Hauke066fd042019-10-13 19:08:00 +02001070 // debug command! may change or disappear without notice
Alexander Chemerise692ce92015-06-12 00:15:31 -04001071 // set a mask which bursts to dump to disk
1072 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +07001073 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -04001074 mWriteBurstToDiskMask = mask;
1075 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +07001076 } else {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001077 LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +02001078 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +00001079 }
1080
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +02001081 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Erica7143bf2020-04-08 01:39:17 +02001082 transceiver->ctrl_sock_send(cmd_to_send, chan);
1083 return 0;
dburgessb3a0ca42011-10-12 07:44:40 +00001084}
1085
Thomas Tsou204a9f12013-10-29 18:34:16 -04001086bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001087{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001088 int msgLen;
Tom Tsoue8871082016-07-01 02:46:04 -07001089 int burstLen;
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001090 struct trxd_hdr_v01_dl *dl;
1091 char buffer[sizeof(*dl) + EDGE_BURST_NBITS];
Vadim Yanitskiyb3123252019-07-15 23:53:08 +07001092 uint32_t fn;
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001093 uint8_t tn;
dburgessb3a0ca42011-10-12 07:44:40 +00001094
1095 // check data socket
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001096 msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
1097 if (msgLen <= 0) {
Eric1a26b4f2020-04-13 00:03:01 +02001098 LOGCHAN(chan, DTRXDDL, NOTICE) << "mDataSockets read(" << mDataSockets[chan] << ") failed: " << msgLen;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001099 return false;
1100 }
dburgessb3a0ca42011-10-12 07:44:40 +00001101
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001102 switch (msgLen) {
1103 case sizeof(*dl) + gSlotLen: /* GSM burst */
1104 burstLen = gSlotLen;
1105 break;
1106 case sizeof(*dl) + EDGE_BURST_NBITS: /* EDGE burst */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +02001107 if (cfg->tx_sps != 4) {
1108 LOGCHAN(chan, DTRXDDL, ERROR) << "EDGE burst received but SPS is set to " << cfg->tx_sps;
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001109 return false;
1110 }
1111 burstLen = EDGE_BURST_NBITS;
1112 break;
1113 default:
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001114 LOGCHAN(chan, DTRXDDL, ERROR) << "badly formatted packet on GSM->TRX interface (len="<< msgLen << ")";
Tom Tsoue8871082016-07-01 02:46:04 -07001115 return false;
dburgessb3a0ca42011-10-12 07:44:40 +00001116 }
1117
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001118 dl = (struct trxd_hdr_v01_dl *) buffer;
dburgessb3a0ca42011-10-12 07:44:40 +00001119
Vadim Yanitskiyb3123252019-07-15 23:53:08 +07001120 /* Convert TDMA FN to the host endianness */
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001121 fn = osmo_load32be(&dl->common.fn);
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001122 tn = dl->common.tn;
Vadim Yanitskiyb3123252019-07-15 23:53:08 +07001123
Vadim Yanitskiydd571c62019-07-15 23:56:56 +07001124 /* Make sure we support the received header format */
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001125 switch (dl->common.version) {
Vadim Yanitskiydd571c62019-07-15 23:56:56 +07001126 case 0:
1127 /* Version 1 has the same format */
1128 case 1:
1129 break;
Vadim Yanitskiydd571c62019-07-15 23:56:56 +07001130 default:
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001131 LOGCHAN(chan, DTRXDDL, ERROR) << "Rx TRXD message with unknown header version " << unsigned(dl->common.version);
Vadim Yanitskiydd571c62019-07-15 23:56:56 +07001132 return false;
1133 }
1134
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001135 LOGCHAN(chan, DTRXDDL, DEBUG) << "Rx TRXD message (hdr_ver=" << unsigned(dl->common.version)
Pau Espin Pedrol99330742020-07-15 14:05:31 +02001136 << "): fn=" << fn << ", tn=" << unsigned(tn) << ", burst_len=" << burstLen;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001137
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001138 TransceiverState *state = &mStates[chan];
1139 GSM::Time currTime = GSM::Time(fn, tn);
1140
1141 /* Verify proper FN order in DL stream */
1142 if (state->first_dl_fn_rcv[tn]) {
1143 int32_t delta = GSM::FNDelta(currTime.FN(), state->last_dl_time_rcv[tn].FN());
1144 if (delta == 1) {
1145 /* usual expected scenario, continue code flow */
1146 } else if (delta == 0) {
Pau Espin Pedrolc249ce22020-07-17 18:33:10 +02001147 LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with repeated FN " << currTime;
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001148 state->ctrs.tx_trxd_fn_repeated++;
1149 dispatch_trx_rate_ctr_change(state, chan);
1150 return true;
1151 } else if (delta < 0) {
Pau Espin Pedrolc249ce22020-07-17 18:33:10 +02001152 LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with previous FN " << currTime
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001153 << " vs last " << state->last_dl_time_rcv[tn];
1154 state->ctrs.tx_trxd_fn_outoforder++;
1155 dispatch_trx_rate_ctr_change(state, chan);
1156 /* Allow adding radio vector below, since it gets sorted in the queue */
1157 } else if (chan == 0 && state->mFiller == FILLER_ZERO) {
1158 /* delta > 1. Some FN was lost in the middle. We can only easily rely
1159 * on consecutive FNs in TRX0 since it must transmit continuously in all
1160 * setups. Also, osmo-trx supports optionally filling empty bursts on
1161 * its own. In that case bts-trx is not obliged to submit all bursts. */
Pau Espin Pedrolc249ce22020-07-17 18:33:10 +02001162 LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with future FN " << currTime
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001163 << " vs last " << state->last_dl_time_rcv[tn]
1164 << ", " << delta - 1 << " FN lost";
1165 state->ctrs.tx_trxd_fn_skipped += delta - 1;
1166 dispatch_trx_rate_ctr_change(state, chan);
1167 }
1168 if (delta > 0)
1169 state->last_dl_time_rcv[tn] = currTime;
1170 } else { /* Initial check, simply store state */
1171 state->first_dl_fn_rcv[tn] = true;
1172 state->last_dl_time_rcv[tn] = currTime;
1173 }
1174
Tom Tsou7c741ec2016-07-19 11:20:59 -07001175 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +00001176 BitVector::iterator itr = newBurst.begin();
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001177 uint8_t *bufferItr = dl->soft_bits;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001178 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +00001179 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001180
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001181 addRadioVector(chan, newBurst, dl->tx_att, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +00001182
1183 return true;
dburgessb3a0ca42011-10-12 07:44:40 +00001184}
dburgessb3a0ca42011-10-12 07:44:40 +00001185
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001186bool Transceiver::driveReceiveRadio()
Thomas Tsou204a9f12013-10-29 18:34:16 -04001187{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +02001188 int rc = mRadioInterface->driveReceiveRadio();
1189 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001190 usleep(100000);
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001191 return true;
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -04001192 }
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001193 if (rc < 0)
1194 return false;
1195
1196 if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
Philipp Maierfdefbfa2020-05-22 12:17:13 +02001197 if (mForceClockInterface)
1198 LOGC(DTRXCLK, NOTICE) << "Sending CLOCK indications";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001199 mForceClockInterface = false;
1200 return writeClockInterface();
1201 }
1202 return true;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001203}
1204
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001205void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001206{
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001207 std::ostringstream os;
1208 for (size_t i=0; i < bi->nbits; i++) {
1209 if (bi->rx_burst[i] > 0.5) os << "1";
1210 else if (bi->rx_burst[i] > 0.25) os << "|";
1211 else if (bi->rx_burst[i] > 0.0) os << "'";
1212 else os << "-";
1213 }
1214
Pau Espin Pedrole91544d2020-10-13 17:03:37 +02001215 double rssi_offset = rssiOffset(chan);
1216
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001217 LOGCHAN(chan, DTRXDUL, DEBUG) << std::fixed << std::right
Pau Espin Pedrol1fba1042019-09-06 14:22:31 +02001218 << " time: " << unsigned(bi->tn) << ":" << bi->fn
Pau Espin Pedrole91544d2020-10-13 17:03:37 +02001219 << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssi_offset)
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001220 << "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
Pau Espin Pedrole91544d2020-10-13 17:03:37 +02001221 << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssi_offset)
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001222 << "dBFS/" << std::setw(6) << -bi->noise << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001223 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001224 << " C/I: " << std::setw(5) << std::setprecision(2) << bi->ci << "dB"
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001225 << " bits: " << os;
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001226}
1227
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001228bool Transceiver::driveReceiveFIFO(size_t chan)
Thomas Tsou204a9f12013-10-29 18:34:16 -04001229{
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001230 struct trx_ul_burst_ind bi;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +02001231 int rc;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001232
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +02001233 if ((rc = pullRadioVector(chan, &bi)) < 0) {
1234 if (rc == -ENOENT) { /* timeslot off, continue processing */
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001235 LOGCHAN(chan, DTRXDUL, DEBUG) << unsigned(bi.tn) << ":" << bi.fn << " timeslot is off";
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +02001236 return true;
1237 }
1238 return false; /* other errors: we want to stop the process */
1239 }
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001240
Pau Espin Pedrol199a3062020-07-27 10:58:51 +02001241 if (!bi.idle && log_check_level(DTRXDUL, LOGL_DEBUG))
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001242 logRxBurst(chan, &bi);
dburgessb3a0ca42011-10-12 07:44:40 +00001243
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +02001244 switch (mVersionTRXD[chan]) {
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001245 case 0:
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001246 return trxd_send_burst_ind_v0(chan, mDataSockets[chan], &bi);
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001247 case 1:
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001248 return trxd_send_burst_ind_v1(chan, mDataSockets[chan], &bi);
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001249 default:
1250 OSMO_ASSERT(false);
1251 }
dburgessb3a0ca42011-10-12 07:44:40 +00001252}
1253
Thomas Tsou204a9f12013-10-29 18:34:16 -04001254void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +00001255{
1256
1257 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001258 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +00001259 assure that transmit packets arrive at the radio/USRP
1260 before they need to be transmitted.
1261
1262 Deadline clock indicates the burst that needs to be
1263 pushed into the FIFO right NOW. If transmit queue does
1264 not have a burst, stick in filler data.
1265 */
1266
1267
1268 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001269
dburgessb3a0ca42011-10-12 07:44:40 +00001270 if (mOn) {
1271 //radioClock->wait(); // wait until clock updates
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001272 LOGC(DTRXCLK, DEBUG) << "radio clock " << radioClock->get();
dburgessb3a0ca42011-10-12 07:44:40 +00001273 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
1274 // if underrun, then we're not providing bursts to radio/USRP fast
1275 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -04001276 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001277 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +00001278 // only update latency at the defined frame interval
1279 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001280 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001281 LOGC(DTRXCLK, INFO) << "new latency: " << mTransmitLatency << " (underrun "
1282 << radioClock->get() << " vs "
1283 << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +00001284 mLatencyUpdateTime = radioClock->get();
1285 }
1286 }
1287 else {
1288 // if underrun hasn't occurred in the last sec (216 frames) drop
1289 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001290 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001291 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1292 mTransmitLatency.decTN();
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001293 LOGC(DTRXCLK, INFO) << "reduced latency: " << mTransmitLatency;
kurtis.heimerle380af32011-11-26 03:18:55 +00001294 mLatencyUpdateTime = radioClock->get();
1295 }
1296 }
1297 }
dburgessb3a0ca42011-10-12 07:44:40 +00001298 }
dburgessb3a0ca42011-10-12 07:44:40 +00001299 // time to push burst to transmit FIFO
1300 pushRadioVector(mTransmitDeadlineClock);
1301 mTransmitDeadlineClock.incTN();
1302 }
dburgessb3a0ca42011-10-12 07:44:40 +00001303 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001304
1305 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001306}
1307
1308
1309
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001310bool Transceiver::writeClockInterface()
dburgessb3a0ca42011-10-12 07:44:40 +00001311{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001312 int msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001313 char command[50];
1314 // FIXME -- This should be adaptive.
1315 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1316
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001317 LOGC(DTRXCLK, INFO) << "sending " << command;
dburgessb3a0ca42011-10-12 07:44:40 +00001318
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001319 msgLen = write(mClockSocket, command, strlen(command) + 1);
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001320 if (msgLen <= 0) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001321 LOGC(DTRXCLK, ERROR) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001322 return false;
1323 }
dburgessb3a0ca42011-10-12 07:44:40 +00001324
1325 mLastClockUpdateTime = mTransmitDeadlineClock;
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001326 return true;
Thomas Tsou92c16df2013-09-28 18:04:19 -04001327}
dburgessb3a0ca42011-10-12 07:44:40 +00001328
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001329void *RxUpperLoopAdapter(TrxChanThParams *params)
Thomas Tsou204a9f12013-10-29 18:34:16 -04001330{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001331 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001332 Transceiver *trx = params->trx;
1333 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001334
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001335 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001336
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001337 snprintf(thread_name, 16, "RxUpper%zu", num);
1338 set_selfthread_name(thread_name);
Pau Espin Pedrol553a2502020-07-29 18:05:25 +02001339 OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001340
Thomas Tsou204a9f12013-10-29 18:34:16 -04001341 while (1) {
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001342 if (!trx->driveReceiveFIFO(num)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001343 LOGCHAN(num, DTRXDUL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001344 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
1345 break;
1346 }
Thomas Tsou204a9f12013-10-29 18:34:16 -04001347 pthread_testcancel();
1348 }
1349 return NULL;
1350}
1351
1352void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001353{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001354 set_selfthread_name("RxLower");
Pau Espin Pedrol553a2502020-07-29 18:05:25 +02001355 OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001356
dburgessb3a0ca42011-10-12 07:44:40 +00001357 while (1) {
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001358 if (!transceiver->driveReceiveRadio()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001359 LOGC(DTRXDUL, FATAL) << "Something went wrong in thread RxLower, requesting stop";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001360 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
1361 break;
1362 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001363 pthread_testcancel();
1364 }
1365 return NULL;
1366}
1367
Thomas Tsou204a9f12013-10-29 18:34:16 -04001368void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001369{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001370 set_selfthread_name("TxLower");
Pau Espin Pedrol553a2502020-07-29 18:05:25 +02001371 OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001372
Thomas Tsou92c16df2013-09-28 18:04:19 -04001373 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001374 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001375 pthread_testcancel();
1376 }
1377 return NULL;
1378}
1379
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001380void *TxUpperLoopAdapter(TrxChanThParams *params)
dburgessb3a0ca42011-10-12 07:44:40 +00001381{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001382 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001383 Transceiver *trx = params->trx;
1384 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001385
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001386 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001387
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001388 snprintf(thread_name, 16, "TxUpper%zu", num);
1389 set_selfthread_name(thread_name);
Pau Espin Pedrol553a2502020-07-29 18:05:25 +02001390 OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001391
dburgessb3a0ca42011-10-12 07:44:40 +00001392 while (1) {
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001393 if (!trx->driveTxPriorityQueue(num)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001394 LOGCHAN(num, DTRXDDL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001395 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
1396 break;
1397 }
dburgessb3a0ca42011-10-12 07:44:40 +00001398 pthread_testcancel();
1399 }
1400 return NULL;
1401}