blob: 5b213f7561d70b88faa592e492b3951621fd0adf [file] [log] [blame]
Ericb7253c62022-11-28 19:21:08 +01001/*
2 * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
3 * All Rights Reserved
4 *
5 * Author: Eric Wild <ewild@sysmocom.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#include "sigProcLib.h"
23#include "ms.h"
24#include <signalVector.h>
25#include <radioVector.h>
26#include <radioInterface.h>
27#include <grgsm_vitac/grgsm_vitac.h>
28
29extern "C" {
30#include <stdio.h>
31#include <stdlib.h>
32#include <stdint.h>
33#include <string.h>
34#include <getopt.h>
35#include <unistd.h>
36#include <signal.h>
37#include <errno.h>
38#include <time.h>
39#include <fenv.h>
40
41#include "sch.h"
42#include "convolve.h"
43#include "convert.h"
44
45#ifdef LSANDEBUG
46void __lsan_do_recoverable_leak_check();
47#endif
48}
49
50#include "ms_upper.h"
51
52namespace trxcon
53{
54extern "C" {
55#include <osmocom/core/fsm.h>
56#include <osmocom/core/msgb.h>
57#include <osmocom/core/talloc.h>
58#include <osmocom/core/signal.h>
59#include <osmocom/core/select.h>
60#include <osmocom/gsm/gsm_utils.h>
61
62#include <osmocom/core/logging.h>
63#include <osmocom/bb/trxcon/logging.h>
64
65#include <osmocom/bb/trxcon/trxcon.h>
66#include <osmocom/bb/trxcon/trxcon_fsm.h>
67#include <osmocom/bb/trxcon/phyif.h>
68#include <osmocom/bb/trxcon/l1ctl_server.h>
69}
70struct trxcon_inst *g_trxcon;
71// trx_instance *trxcon_instance; // local handle
72struct internal_q_tx_buf {
73 trxcon_phyif_burst_req r;
74 uint8_t buf[148];
75};
76using tx_queue_t = spsc_cond<8 * 1, internal_q_tx_buf, true, false>;
77using cmd_queue_t = spsc_cond<8 * 1, trxcon_phyif_cmd, true, false>;
78using cmdr_queue_t = spsc_cond<8 * 1, trxcon_phyif_rsp, false, false>;
79static tx_queue_t txq;
80static cmd_queue_t cmdq_to_phy;
81static cmdr_queue_t cmdq_from_phy;
82
83extern bool trxc_l1ctl_init(void *tallctx);
84
85} // namespace trxcon
86extern "C" void trxc_log_init(void *tallctx);
87
88#ifdef LOG
89#undef LOG
90#define LOG(...) upper_trx::dummy_log()
91#endif
92
93#define DBGLG(...) upper_trx::dummy_log()
94
95std::atomic<bool> g_exit_flag;
96
Ericb3157b92023-05-23 11:32:34 +020097void upper_trx::stop_upper_threads()
98{
99 g_exit_flag = true;
100
101 if (thr_control.joinable())
102 thr_control.join();
103 if (thr_rx.joinable())
104 thr_rx.join();
105 if (thr_tx.joinable())
106 thr_tx.join();
107}
Ericb7253c62022-11-28 19:21:08 +0100108void upper_trx::start_threads()
109{
110 thr_control = std::thread([this] {
Eric805e0d92023-05-23 11:29:18 +0200111 set_name_aff_sched(sched_params::thread_names::U_CTL);
Ericb7253c62022-11-28 19:21:08 +0100112 while (!g_exit_flag) {
113 driveControl();
114 }
Ericb3157b92023-05-23 11:32:34 +0200115 std::cerr << "exit control!" << std::endl;
Ericb7253c62022-11-28 19:21:08 +0100116 });
117 msleep(1);
118 thr_tx = std::thread([this] {
Eric805e0d92023-05-23 11:29:18 +0200119 set_name_aff_sched(sched_params::thread_names::U_TX);
Ericb7253c62022-11-28 19:21:08 +0100120 while (!g_exit_flag) {
121 driveTx();
122 }
Ericb3157b92023-05-23 11:32:34 +0200123 std::cerr << "exit tx U!" << std::endl;
Ericb7253c62022-11-28 19:21:08 +0100124 });
125
126 // atomic ensures data is not written to q until loop reads
127 start_lower_ms();
128
Eric805e0d92023-05-23 11:29:18 +0200129 set_name_aff_sched(sched_params::thread_names::U_RX);
Ericb7253c62022-11-28 19:21:08 +0100130 while (!g_exit_flag) {
Ericb3157b92023-05-23 11:32:34 +0200131 // set_upper_ready(true) needs to happen during cmd handling:
132 // the main loop is driven by rx, so unless rx is on AND transceiver is on we get stuck..
Ericb7253c62022-11-28 19:21:08 +0100133 driveReceiveFIFO();
134 trxcon::osmo_select_main(1);
135
136 trxcon::trxcon_phyif_rsp r;
137 if (trxcon::cmdq_from_phy.spsc_pop(&r)) {
138 DBGLG() << "HAVE RESP:" << r.type << std::endl;
139 trxcon_phyif_handle_rsp(trxcon::g_trxcon, &r);
140 }
141 }
Ericb3157b92023-05-23 11:32:34 +0200142 set_upper_ready(false);
143 std::cerr << "exit rx U!" << std::endl;
144 mOn = false;
Ericb7253c62022-11-28 19:21:08 +0100145
146#ifdef LSANDEBUG
147 std::thread([this] {
Eric805e0d92023-05-23 11:29:18 +0200148 set_name_aff_sched(sched_params::thread_names::LEAKCHECK);
Ericb7253c62022-11-28 19:21:08 +0100149
150 while (1) {
151 std::this_thread::sleep_for(std::chrono::seconds{ 5 });
152 __lsan_do_recoverable_leak_check();
153 }
154 }).detach();
155#endif
156}
157
158void upper_trx::start_lower_ms()
159{
160 ms_trx::start();
161}
162
163// signalvector is owning despite claiming not to, but we can pretend, too..
164static void static_free(void *wData){};
165static void *static_alloc(size_t newSize)
166{
167 return 0;
168};
169
170bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
171{
172 float pow, avg = 1.0;
173 const auto zero_pad_len = 40; // give the VA some runway for misaligned bursts
174 const auto workbuf_size = zero_pad_len + ONE_TS_BURST_LEN + zero_pad_len;
175 static complex workbuf[workbuf_size];
176
177 static signalVector sv(workbuf, zero_pad_len, ONE_TS_BURST_LEN, static_alloc, static_free);
178 one_burst e;
179 auto ss = reinterpret_cast<std::complex<float> *>(&workbuf[zero_pad_len]);
180 std::fill(workbuf, workbuf + workbuf_size, 0);
181 // assert(sv.begin() == &workbuf[40]);
182
183 while (!rxqueue.spsc_pop(&e)) {
184 rxqueue.spsc_prep_pop();
185 }
186
187 wTime = e.gsmts;
188
189 const auto is_sch = gsm_sch_check_ts(wTime.TN(), wTime.FN());
190 const auto is_fcch = gsm_fcch_check_ts(wTime.TN(), wTime.FN());
191
192 trxcon::trxcon_phyif_rtr_ind i = { static_cast<uint32_t>(wTime.FN()), static_cast<uint8_t>(wTime.TN()) };
193 trxcon::trxcon_phyif_rtr_rsp r = {};
194 trxcon_phyif_handle_rtr_ind(trxcon::g_trxcon, &i, &r);
195 if (!(r.flags & TRXCON_PHYIF_RTR_F_ACTIVE))
196 return false;
197
198 if (is_fcch) {
199 // return trash
200 return true;
201 }
202
203 if (is_sch) {
204 for (int i = 0; i < 148; i++)
205 (demodded_softbits)[i] = (e.sch_bits[i]);
206 RSSI = 10;
207 timingOffset = 0;
208 return true;
209 }
210
Eric Wild621a49e2023-01-12 16:21:58 +0100211 convert_and_scale(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
Ericb7253c62022-11-28 19:21:08 +0100212
213 pow = energyDetect(sv, 20 * 4 /*sps*/);
214 if (pow < -1) {
215 LOG(ALERT) << "Received empty burst";
216 return false;
217 }
218
219 avg = sqrt(pow);
220 {
221 float ncmax;
222 std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
223 auto normal_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp[0], &ncmax, mTSC);
224#ifdef DBGXX
225 float dcmax;
226 std::complex<float> chan_imp_resp2[CHAN_IMP_RESP_LENGTH * d_OSR];
227 auto dummy_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp2[0], &dcmax, TS_DUMMY);
228 auto is_nb = ncmax > dcmax;
229 // DBGLG() << " U " << (is_nb ? "NB" : "DB") << "@ o nb: " << normal_burst_start
230 // << " o db: " << dummy_burst_start << std::endl;
231#endif
232 normal_burst_start = normal_burst_start < 39 ? normal_burst_start : 39;
233 normal_burst_start = normal_burst_start > -39 ? normal_burst_start : -39;
234#ifdef DBGXX
235 // fprintf(stderr, "%s %d\n", (is_nb ? "N":"D"), burst_time.FN());
236 // if (is_nb)
237#endif
238 detect_burst(ss, &chan_imp_resp[0], normal_burst_start, demodded_softbits);
239#ifdef DBGXX
240 // else
241 // detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin);
242#endif
243 }
244 RSSI = (int)floor(20.0 * log10(rxFullScale / avg));
245 // FIXME: properly handle offset, sch/nb alignment diff? handled by lower anyway...
246 timingOffset = (int)round(0);
247
248 return true;
249}
250
251void upper_trx::driveReceiveFIFO()
252{
253 int RSSI;
254 int TOA; // in 1/256 of a symbol
255 GSM::Time burstTime;
256
257 if (!mOn)
258 return;
259
260 if (pullRadioVector(burstTime, RSSI, TOA)) {
261 trxcon::trxcon_phyif_burst_ind bi;
262 bi.fn = burstTime.FN();
263 bi.tn = burstTime.TN();
264 bi.rssi = RSSI;
265 bi.toa256 = TOA;
266 bi.burst = (sbit_t *)demodded_softbits;
267 bi.burst_len = sizeof(demodded_softbits);
268 trxcon_phyif_handle_burst_ind(trxcon::g_trxcon, &bi);
269 }
270
271 struct trxcon::trxcon_phyif_rts_ind rts {
272 static_cast<uint32_t>(burstTime.FN()), static_cast<uint8_t>(burstTime.TN())
273 };
274 trxcon_phyif_handle_rts_ind(trxcon::g_trxcon, &rts);
275}
276
277void upper_trx::driveTx()
278{
279 trxcon::internal_q_tx_buf e;
280 static BitVector newBurst(sizeof(e.buf));
281 while (!trxcon::txq.spsc_pop(&e)) {
282 trxcon::txq.spsc_prep_pop();
283 }
284
285 // ensure our tx cb is tickled and can exit
286 if (g_exit_flag) {
Ericb3157b92023-05-23 11:32:34 +0200287 submit_burst_ts(0, 1337, 1);
Ericb7253c62022-11-28 19:21:08 +0100288 return;
289 }
290
291 trxcon::internal_q_tx_buf *burst = &e;
292
293#ifdef TXDEBUG
294 DBGLG() << "got burst!" << burst->r.fn << ":" << burst->ts << " current: " << timekeeper.gsmtime().FN()
295 << " dff: " << (int64_t)((int64_t)timekeeper.gsmtime().FN() - (int64_t)burst->r.fn) << std::endl;
296#endif
297
298 auto currTime = GSM::Time(burst->r.fn, burst->r.tn);
299 int RSSI = (int)burst->r.pwr;
300
301 BitVector::iterator itr = newBurst.begin();
302 auto *bufferItr = burst->buf;
303 while (itr < newBurst.end())
304 *itr++ = *bufferItr++;
305
306 auto txburst = modulateBurst(newBurst, 8 + (currTime.TN() % 4 == 0), 4);
307 scaleVector(*txburst, txFullScale * pow(10, -RSSI / 10));
308
309 // float -> int16
310 blade_sample_type burst_buf[txburst->size()];
Eric Wild621a49e2023-01-12 16:21:58 +0100311 convert_and_scale(burst_buf, txburst->begin(), txburst->size() * 2, 1);
Ericb7253c62022-11-28 19:21:08 +0100312#ifdef TXDEBUG
313 auto check = signalVector(txburst->size(), 40);
Eric Wild621a49e2023-01-12 16:21:58 +0100314 convert_and_scale(check.begin(), burst_buf, txburst->size() * 2, 1);
Ericb7253c62022-11-28 19:21:08 +0100315 estim_burst_params ebp;
316 auto d = detectAnyBurst(check, 2, 4, 4, CorrType::RACH, 40, &ebp);
317 if (d)
318 DBGLG() << "RACH D! " << ebp.toa << std::endl;
319 else
320 DBGLG() << "RACH NOOOOOOOOOO D! " << ebp.toa << std::endl;
321
322 // memory read --binary --outfile /tmp/mem.bin &burst_buf[0] --count 2500 --force
323#endif
324 submit_burst(burst_buf, txburst->size(), currTime);
325 delete txburst;
326}
327
328#ifdef TXDEBUG
329static const char *cmd2str(trxcon::trxcon_phyif_cmd_type c)
330{
331 switch (c) {
332 case trxcon::TRXCON_PHYIF_CMDT_RESET:
333 return "TRXCON_PHYIF_CMDT_RESET";
334 case trxcon::TRXCON_PHYIF_CMDT_POWERON:
335 return "TRXCON_PHYIF_CMDT_POWERON";
336 case trxcon::TRXCON_PHYIF_CMDT_POWEROFF:
337 return "TRXCON_PHYIF_CMDT_POWEROFF";
338 case trxcon::TRXCON_PHYIF_CMDT_MEASURE:
339 return "TRXCON_PHYIF_CMDT_MEASURE";
340 case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H0:
341 return "TRXCON_PHYIF_CMDT_SETFREQ_H0";
342 case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H1:
343 return "TRXCON_PHYIF_CMDT_SETFREQ_H1";
344 case trxcon::TRXCON_PHYIF_CMDT_SETSLOT:
345 return "TRXCON_PHYIF_CMDT_SETSLOT";
346 case trxcon::TRXCON_PHYIF_CMDT_SETTA:
347 return "TRXCON_PHYIF_CMDT_SETTA";
348 default:
349 return "UNKNOWN COMMAND!";
350 }
351}
352
353static void print_cmd(trxcon::trxcon_phyif_cmd_type c)
354{
355 DBGLG() << cmd2str(c) << std::endl;
356}
357#endif
358
359bool upper_trx::driveControl()
360{
361 trxcon::trxcon_phyif_rsp r;
362 trxcon::trxcon_phyif_cmd cmd;
363 while (!trxcon::cmdq_to_phy.spsc_pop(&cmd)) {
364 trxcon::cmdq_to_phy.spsc_prep_pop();
365 }
366
367 if (g_exit_flag)
368 return false;
369
370#ifdef TXDEBUG
371 print_cmd(cmd.type);
372#endif
373
374 switch (cmd.type) {
375 case trxcon::TRXCON_PHYIF_CMDT_RESET:
376 set_ta(0);
377 break;
378 case trxcon::TRXCON_PHYIF_CMDT_POWERON:
Ericb7253c62022-11-28 19:21:08 +0100379 if (!mOn) {
Ericb7253c62022-11-28 19:21:08 +0100380 mOn = true;
Ericb3157b92023-05-23 11:32:34 +0200381 set_upper_ready(true);
Ericb7253c62022-11-28 19:21:08 +0100382 }
383 break;
384 case trxcon::TRXCON_PHYIF_CMDT_POWEROFF:
385 break;
386 case trxcon::TRXCON_PHYIF_CMDT_MEASURE:
387 r.type = trxcon::trxcon_phyif_cmd_type::TRXCON_PHYIF_CMDT_MEASURE;
388 r.param.measure.band_arfcn = cmd.param.measure.band_arfcn;
389 // FIXME: do we want to measure anything, considering the transceiver just syncs by.. syncing?
390 r.param.measure.dbm = -80;
391 tuneRx(trxcon::gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 0) * 1000 * 100);
392 tuneTx(trxcon::gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 1) * 1000 * 100);
393 trxcon::cmdq_from_phy.spsc_push(&r);
394 break;
395 case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H0:
396 tuneRx(trxcon::gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 0) * 1000 * 100);
397 tuneTx(trxcon::gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 1) * 1000 * 100);
398 break;
399 case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H1:
400 break;
401 case trxcon::TRXCON_PHYIF_CMDT_SETSLOT:
402 break;
403 case trxcon::TRXCON_PHYIF_CMDT_SETTA:
404 set_ta(cmd.param.setta.ta);
405 break;
406 }
407 return false;
408}
409
410// trxcon C call(back) if
411extern "C" {
412int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon::trxcon_phyif_burst_req *br)
413{
414 if (br->burst_len == 0) // dummy/nope
415 return 0;
416 OSMO_ASSERT(br->burst != 0);
417
418 trxcon::internal_q_tx_buf b;
419 b.r = *br;
420 memcpy(b.buf, (void *)br->burst, br->burst_len);
421
422 if (!g_exit_flag)
423 trxcon::txq.spsc_push(&b);
424 return 0;
425}
426
427int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon::trxcon_phyif_cmd *cmd)
428{
429#ifdef TXDEBUG
430 DBGLG() << "TOP C: " << cmd2str(cmd->type) << std::endl;
431#endif
432 if (!g_exit_flag)
433 trxcon::cmdq_to_phy.spsc_push(cmd);
434 // q for resp polling happens in main loop
435 return 0;
436}
437
438void trxcon_phyif_close(void *phyif)
439{
440}
441
442void trxcon_l1ctl_close(struct trxcon::trxcon_inst *trxcon)
443{
444 /* Avoid use-after-free: both *fi and *trxcon are children of
445 * the L2IF (L1CTL connection), so we need to re-parent *fi
446 * to NULL before calling l1ctl_client_conn_close(). */
447 talloc_steal(NULL, trxcon->fi);
448 trxcon::l1ctl_client_conn_close((struct trxcon::l1ctl_client *)trxcon->l2if);
449}
450
451int trxcon_l1ctl_send(struct trxcon::trxcon_inst *trxcon, struct trxcon::msgb *msg)
452{
453 struct trxcon::l1ctl_client *l1c = (struct trxcon::l1ctl_client *)trxcon->l2if;
454
455 return trxcon::l1ctl_client_send(l1c, msg);
456}
457}
458
459void sighandler(int sigset)
460{
461 // we might get a sigpipe in case the l1ctl ud socket disconnects because mobile quits
Ericb3157b92023-05-23 11:32:34 +0200462 if (sigset == SIGPIPE || sigset == SIGINT) {
Ericb7253c62022-11-28 19:21:08 +0100463 g_exit_flag = true;
464
465 // we know the flag is atomic and it prevents the trxcon cb handlers from writing
466 // to the queues, so submit some trash to unblock the threads & exit
Ericd0c10552022-12-28 16:52:54 +0100467 trxcon::trxcon_phyif_cmd cmd = {};
468 trxcon::internal_q_tx_buf b = {};
Ericb7253c62022-11-28 19:21:08 +0100469 trxcon::txq.spsc_push(&b);
470 trxcon::cmdq_to_phy.spsc_push(&cmd);
Ericb3157b92023-05-23 11:32:34 +0200471 msleep(200);
Ericb7253c62022-11-28 19:21:08 +0100472
473 return;
474 }
475}
476
477int main(int argc, char *argv[])
478{
479 auto tall_trxcon_ctx = talloc_init("trxcon context");
480 signal(SIGPIPE, sighandler);
Ericb3157b92023-05-23 11:32:34 +0200481 signal(SIGINT, sighandler);
Ericb7253c62022-11-28 19:21:08 +0100482
483 trxcon::msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
484 trxc_log_init(tall_trxcon_ctx);
485
486 trxcon::g_trxcon = trxcon::trxcon_inst_alloc(tall_trxcon_ctx, 0, 0);
487 trxcon::g_trxcon->gsmtap = nullptr;
488 trxcon::g_trxcon->phyif = nullptr;
489 trxcon::g_trxcon->phy_quirks.fbsb_extend_fns = 866; // 4 seconds, known to work.
490
491 convolve_init();
492 convert_init();
493 sigProcLibSetup();
494 initvita();
495
496 int status = 0;
497 auto trx = new upper_trx();
498 trx->do_auto_gain = true;
499
500 status = trx->init_dev_and_streams();
Eric Wild10b4e312023-01-11 18:53:48 +0100501 if (status < 0) {
502 std::cerr << "Error initializing hardware, quitting.." << std::endl;
503 return -1;
504 }
Eric805e0d92023-05-23 11:29:18 +0200505 trx->set_name_aff_sched(sched_params::thread_names::MAIN);
Ericb7253c62022-11-28 19:21:08 +0100506
507 if (!trxcon::trxc_l1ctl_init(tall_trxcon_ctx)) {
508 std::cerr << "Error initializing l1ctl, quitting.." << std::endl;
509 return -1;
510 }
511
Ericb3157b92023-05-23 11:32:34 +0200512 // blocking, will return when global exit is requested
Ericb7253c62022-11-28 19:21:08 +0100513 trx->start_threads();
Ericb3157b92023-05-23 11:32:34 +0200514
Ericb7253c62022-11-28 19:21:08 +0100515 trx->stop_threads();
Ericb3157b92023-05-23 11:32:34 +0200516 trx->stop_upper_threads();
Ericb7253c62022-11-28 19:21:08 +0100517
518 return status;
519}