blob: d4e057cfda23e049aea0f42b5ddda5f052c174da [file] [log] [blame]
Eric Wild901f6892022-05-07 15:36:47 +02001/*
2 * OsmocomBB <-> SDR connection bridge
3 * Transceiver interface handlers
4 *
5 * Copyright (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
6 * Copyright (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
7 *
8 * All Rights Reserved
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Affero General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Affero General Public License for more details.
19 *
20 * You should have received a copy of the GNU Affero General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24#include <stdio.h>
25#include <errno.h>
26#include <stdint.h>
27#include <unistd.h>
28#include <stdlib.h>
29#include <string.h>
30
31#include <netinet/in.h>
32
Eric Wild901f6892022-05-07 15:36:47 +020033#include <osmocom/core/logging.h>
34#include <osmocom/core/select.h>
35#include <osmocom/core/socket.h>
36#include <osmocom/core/talloc.h>
37#include <osmocom/core/bits.h>
38#include <osmocom/core/fsm.h>
39
40#include <osmocom/gsm/gsm_utils.h>
41
Erice29b3c82022-10-27 16:54:09 +020042#include <osmocom/bb/l1sched/l1sched.h>
43#include <osmocom/bb/trxcon/trxcon.h>
44#include <osmocom/bb/trxcon/trx_if.h>
45#include <osmocom/bb/trxcon/logging.h>
Eric Wild901f6892022-05-07 15:36:47 +020046
Erice29b3c82022-10-27 16:54:09 +020047#define S(x) (1 << (x))
48
49static void trx_fsm_cleanup_cb(struct osmo_fsm_inst *fi,
50 enum osmo_fsm_term_cause cause);
Eric Wild901f6892022-05-07 15:36:47 +020051
52static struct value_string trx_evt_names[] = {
53 { 0, NULL } /* no events? */
54};
55
56static struct osmo_fsm_state trx_fsm_states[] = {
57 [TRX_STATE_OFFLINE] = {
58 .out_state_mask = (
Erice29b3c82022-10-27 16:54:09 +020059 S(TRX_STATE_IDLE) |
60 S(TRX_STATE_RSP_WAIT)),
Eric Wild901f6892022-05-07 15:36:47 +020061 .name = "OFFLINE",
62 },
63 [TRX_STATE_IDLE] = {
64 .out_state_mask = UINT32_MAX,
65 .name = "IDLE",
66 },
67 [TRX_STATE_ACTIVE] = {
68 .out_state_mask = (
Erice29b3c82022-10-27 16:54:09 +020069 S(TRX_STATE_IDLE) |
70 S(TRX_STATE_RSP_WAIT)),
Eric Wild901f6892022-05-07 15:36:47 +020071 .name = "ACTIVE",
72 },
73 [TRX_STATE_RSP_WAIT] = {
74 .out_state_mask = (
Erice29b3c82022-10-27 16:54:09 +020075 S(TRX_STATE_IDLE) |
76 S(TRX_STATE_ACTIVE) |
77 S(TRX_STATE_OFFLINE)),
Eric Wild901f6892022-05-07 15:36:47 +020078 .name = "RSP_WAIT",
79 },
80};
81
82static struct osmo_fsm trx_fsm = {
Erice29b3c82022-10-27 16:54:09 +020083 .name = "trx_interface",
Eric Wild901f6892022-05-07 15:36:47 +020084 .states = trx_fsm_states,
85 .num_states = ARRAY_SIZE(trx_fsm_states),
Erice29b3c82022-10-27 16:54:09 +020086 .log_subsys = DTRXC,
Eric Wild901f6892022-05-07 15:36:47 +020087 .event_names = trx_evt_names,
Erice29b3c82022-10-27 16:54:09 +020088 .cleanup = &trx_fsm_cleanup_cb,
Eric Wild901f6892022-05-07 15:36:47 +020089};
90
91static int trx_udp_open(void *priv, struct osmo_fd *ofd, const char *host_local,
92 uint16_t port_local, const char *host_remote, uint16_t port_remote,
93 int (*cb)(struct osmo_fd *fd, unsigned int what))
94{
95 int rc;
96
97 ofd->data = priv;
98 ofd->fd = -1;
99 ofd->cb = cb;
100
101 /* Init UDP Connection */
102 rc = osmo_sock_init2_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, 0, host_local, port_local,
103 host_remote, port_remote,
104 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
105 return rc;
106}
107
108static void trx_udp_close(struct osmo_fd *ofd)
109{
110 if (ofd->fd > 0) {
111 osmo_fd_unregister(ofd);
112 close(ofd->fd);
113 ofd->fd = -1;
114 }
115}
116
117/* ------------------------------------------------------------------------ */
118/* Control (CTRL) interface handlers */
119/* ------------------------------------------------------------------------ */
120/* Commands on the Per-ARFCN Control Interface */
121/* */
122/* The per-ARFCN control interface uses a command-response protocol. */
123/* Commands are NULL-terminated ASCII strings, one per UDP socket. */
124/* Each command has a corresponding response. */
125/* Every command is of the form: */
126/* */
127/* CMD <cmdtype> [params] */
128/* */
129/* The <cmdtype> is the actual command. */
130/* Parameters are optional depending on the commands type. */
131/* Every response is of the form: */
132/* */
133/* RSP <cmdtype> <status> [result] */
134/* */
135/* The <status> is 0 for success and a non-zero error code for failure. */
136/* Successful responses may include results, depending on the command type. */
137/* ------------------------------------------------------------------------ */
138
139static void trx_ctrl_timer_cb(void *data);
140
141/* Send first CTRL message and start timer */
142static void trx_ctrl_send(struct trx_instance *trx)
143{
144 struct trx_ctrl_msg *tcm;
145
146 if (llist_empty(&trx->trx_ctrl_list))
147 return;
148 tcm = llist_entry(trx->trx_ctrl_list.next, struct trx_ctrl_msg, list);
149
Eric Wild901f6892022-05-07 15:36:47 +0200150 /* Send command */
Erice29b3c82022-10-27 16:54:09 +0200151 LOGPFSML(trx->fi, LOGL_DEBUG, "Sending control '%s'\n", tcm->cmd);
Erica378a1d2022-07-19 21:12:58 +0200152 send(trx->trx_ofd_ctrl.fd, tcm->cmd, strlen(tcm->cmd) + 1, 0);
Eric Wild901f6892022-05-07 15:36:47 +0200153
154 /* Trigger state machine */
Erice29b3c82022-10-27 16:54:09 +0200155 if (trx->fi->state != TRX_STATE_RSP_WAIT) {
156 trx->prev_state = trx->fi->state;
157 osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_RSP_WAIT, 0, 0);
Eric Wild901f6892022-05-07 15:36:47 +0200158 }
159
160 /* Start expire timer */
161 trx->trx_ctrl_timer.data = trx;
162 trx->trx_ctrl_timer.cb = trx_ctrl_timer_cb;
163 osmo_timer_schedule(&trx->trx_ctrl_timer, 2, 0);
164}
165
166static void trx_ctrl_timer_cb(void *data)
167{
168 struct trx_instance *trx = (struct trx_instance *) data;
169 struct trx_ctrl_msg *tcm;
170
171 /* Queue may be cleaned at this moment */
172 if (llist_empty(&trx->trx_ctrl_list))
173 return;
174
Erice29b3c82022-10-27 16:54:09 +0200175 LOGPFSML(trx->fi, LOGL_NOTICE, "No response from transceiver...\n");
Eric Wild901f6892022-05-07 15:36:47 +0200176
177 tcm = llist_entry(trx->trx_ctrl_list.next, struct trx_ctrl_msg, list);
178 if (++tcm->retry_cnt > 3) {
Erice29b3c82022-10-27 16:54:09 +0200179 LOGPFSML(trx->fi, LOGL_NOTICE, "Transceiver offline\n");
180 osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_OFFLINE, 0, 0);
181 osmo_fsm_inst_term(trx->fi, OSMO_FSM_TERM_TIMEOUT, NULL);
Eric Wild901f6892022-05-07 15:36:47 +0200182 return;
183 }
184
185 /* Attempt to send a command again */
186 trx_ctrl_send(trx);
187}
188
189/* Add a new CTRL command to the trx_ctrl_list */
190static int trx_ctrl_cmd(struct trx_instance *trx, int critical,
191 const char *cmd, const char *fmt, ...)
192{
193 struct trx_ctrl_msg *tcm;
194 int len, pending = 0;
195 va_list ap;
196
197 /* TODO: make sure that transceiver online */
198
199 if (!llist_empty(&trx->trx_ctrl_list))
200 pending = 1;
201
202 /* Allocate a message */
203 tcm = talloc_zero(trx, struct trx_ctrl_msg);
204 if (!tcm)
205 return -ENOMEM;
206
207 /* Fill in command arguments */
208 if (fmt && fmt[0]) {
209 len = snprintf(tcm->cmd, sizeof(tcm->cmd) - 1, "CMD %s ", cmd);
210 va_start(ap, fmt);
211 vsnprintf(tcm->cmd + len, sizeof(tcm->cmd) - len - 1, fmt, ap);
212 va_end(ap);
213 } else {
214 snprintf(tcm->cmd, sizeof(tcm->cmd) - 1, "CMD %s", cmd);
215 }
216
217 tcm->cmd_len = strlen(cmd);
218 tcm->critical = critical;
219 llist_add_tail(&tcm->list, &trx->trx_ctrl_list);
Erice29b3c82022-10-27 16:54:09 +0200220 LOGPFSML(trx->fi, LOGL_INFO, "Adding new control '%s'\n", tcm->cmd);
Eric Wild901f6892022-05-07 15:36:47 +0200221
222 /* Send message, if no pending messages */
223 if (!pending)
224 trx_ctrl_send(trx);
225
226 return 0;
227}
228
229/*
230 * Power Control
231 *
232 * ECHO is used to check transceiver availability.
233 * CMD ECHO
234 * RSP ECHO <status>
235 *
236 * POWEROFF shuts off transmitter power and stops the demodulator.
237 * CMD POWEROFF
238 * RSP POWEROFF <status>
239 *
240 * POWERON starts the transmitter and starts the demodulator.
241 * Initial power level is very low.
242 * This command fails if the transmitter and receiver are not yet tuned.
243 * This command fails if the transmit or receive frequency creates a conflict
244 * with another ARFCN that is already running.
245 * If the transceiver is already on, it response with success to this command.
246 * CMD POWERON
247 * RSP POWERON <status>
248 */
249
Eric Wild901f6892022-05-07 15:36:47 +0200250int trx_if_cmd_echo(struct trx_instance *trx)
251{
252 return trx_ctrl_cmd(trx, 1, "ECHO", "");
253}
254
255int trx_if_cmd_poweroff(struct trx_instance *trx)
256{
257 return trx_ctrl_cmd(trx, 1, "POWEROFF", "");
258}
259
260int trx_if_cmd_poweron(struct trx_instance *trx)
261{
262 if (trx->powered_up) {
263 /* FIXME: this should be handled by the FSM, not here! */
Erice29b3c82022-10-27 16:54:09 +0200264 LOGPFSML(trx->fi, LOGL_ERROR, "Suppressing POWERON as we're already powered up\n");
Eric Wild901f6892022-05-07 15:36:47 +0200265 return -EAGAIN;
266 }
267 return trx_ctrl_cmd(trx, 1, "POWERON", "");
268}
269
270/*
271 * Timeslot Control
272 *
273 * SETSLOT sets the format of the uplink timeslots in the ARFCN.
274 * The <timeslot> indicates the timeslot of interest.
275 * The <chantype> indicates the type of channel that occupies the timeslot.
276 * A chantype of zero indicates the timeslot is off.
277 * CMD SETSLOT <timeslot> <chantype>
278 * RSP SETSLOT <status> <timeslot> <chantype>
279 */
280
Erice29b3c82022-10-27 16:54:09 +0200281int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn,
282 enum gsm_phys_chan_config pchan)
Eric Wild901f6892022-05-07 15:36:47 +0200283{
Erice29b3c82022-10-27 16:54:09 +0200284 /* Values correspond to 'enum ChannelCombination' in osmo-trx.git */
285 static const uint8_t chan_types[_GSM_PCHAN_MAX] = {
286 [GSM_PCHAN_UNKNOWN] = 0,
287 [GSM_PCHAN_NONE] = 0,
288 [GSM_PCHAN_CCCH] = 4,
289 [GSM_PCHAN_CCCH_SDCCH4] = 5,
290 [GSM_PCHAN_CCCH_SDCCH4_CBCH] = 5,
291 [GSM_PCHAN_TCH_F] = 1,
292 [GSM_PCHAN_TCH_H] = 3,
293 [GSM_PCHAN_SDCCH8_SACCH8C] = 7,
294 [GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 7,
295 [GSM_PCHAN_PDCH] = 13,
296 };
297
298 return trx_ctrl_cmd(trx, 1, "SETSLOT", "%u %u", tn, chan_types[pchan]);
Eric Wild901f6892022-05-07 15:36:47 +0200299}
300
301/*
302 * Tuning Control
303 *
304 * (RX/TX)TUNE tunes the receiver to a given frequency in kHz.
305 * This command fails if the receiver is already running.
306 * (To re-tune you stop the radio, re-tune, and restart.)
307 * This command fails if the transmit or receive frequency
308 * creates a conflict with another ARFCN that is already running.
309 * CMD (RX/TX)TUNE <kHz>
310 * RSP (RX/TX)TUNE <status> <kHz>
311 */
312
313int trx_if_cmd_rxtune(struct trx_instance *trx, uint16_t band_arfcn)
314{
315 uint16_t freq10;
316
317 /* RX is downlink on MS side */
318 freq10 = gsm_arfcn2freq10(band_arfcn, 0);
319 if (freq10 == 0xffff) {
Erice29b3c82022-10-27 16:54:09 +0200320 LOGPFSML(trx->fi, LOGL_ERROR, "ARFCN %d not defined\n", band_arfcn);
Eric Wild901f6892022-05-07 15:36:47 +0200321 return -ENOTSUP;
322 }
323
324 return trx_ctrl_cmd(trx, 1, "RXTUNE", "%u", freq10 * 100);
325}
326
327int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t band_arfcn)
328{
329 uint16_t freq10;
330
331 /* TX is uplink on MS side */
332 freq10 = gsm_arfcn2freq10(band_arfcn, 1);
333 if (freq10 == 0xffff) {
Erice29b3c82022-10-27 16:54:09 +0200334 LOGPFSML(trx->fi, LOGL_ERROR, "ARFCN %d not defined\n", band_arfcn);
Eric Wild901f6892022-05-07 15:36:47 +0200335 return -ENOTSUP;
336 }
337
338 return trx_ctrl_cmd(trx, 1, "TXTUNE", "%u", freq10 * 100);
339}
340
Eric847de6d2022-10-27 16:54:53 +0200341int trx_if_cmd_sync(struct trx_instance *trx)
342{
343 return trx_ctrl_cmd(trx, 1, "SYNC", 0);
344}
345
Eric Wild901f6892022-05-07 15:36:47 +0200346/*
347 * Power measurement
348 *
349 * MEASURE instructs the transceiver to perform a power
350 * measurement on specified frequency. After receiving this
351 * request, transceiver should quickly re-tune to requested
352 * frequency, measure power level and re-tune back to the
353 * previous frequency.
354 * CMD MEASURE <kHz>
355 * RSP MEASURE <status> <kHz> <dB>
356 */
357
358int trx_if_cmd_measure(struct trx_instance *trx,
359 uint16_t band_arfcn_start, uint16_t band_arfcn_stop)
360{
361 uint16_t freq10;
362
363 /* Update ARFCN range for measurement */
364 trx->pm_band_arfcn_start = band_arfcn_start;
365 trx->pm_band_arfcn_stop = band_arfcn_stop;
366
367 /* Calculate a frequency for current ARFCN (DL) */
368 freq10 = gsm_arfcn2freq10(band_arfcn_start, 0);
369 if (freq10 == 0xffff) {
Erice29b3c82022-10-27 16:54:09 +0200370 LOGPFSML(trx->fi, LOGL_ERROR, "ARFCN %d not defined\n", band_arfcn_start);
Eric Wild901f6892022-05-07 15:36:47 +0200371 return -ENOTSUP;
372 }
373
374 return trx_ctrl_cmd(trx, 1, "MEASURE", "%u", freq10 * 100);
375}
376
377static void trx_if_measure_rsp_cb(struct trx_instance *trx, char *resp)
378{
Erice29b3c82022-10-27 16:54:09 +0200379 struct trxcon_inst *trxcon = trx->trxcon;
Eric Wild901f6892022-05-07 15:36:47 +0200380 unsigned int freq10;
381 uint16_t band_arfcn;
382 int dbm;
383
384 /* Parse freq. and power level */
385 sscanf(resp, "%u %d", &freq10, &dbm);
386 freq10 /= 100;
387
388 /* Check received ARFCN against expected */
389 band_arfcn = gsm_freq102arfcn((uint16_t) freq10, 0);
390 if (band_arfcn != trx->pm_band_arfcn_start) {
Erice29b3c82022-10-27 16:54:09 +0200391 LOGPFSML(trx->fi, LOGL_ERROR, "Power measurement error: "
Eric Wild901f6892022-05-07 15:36:47 +0200392 "response ARFCN=%u doesn't match expected ARFCN=%u\n",
Erice29b3c82022-10-27 16:54:09 +0200393 band_arfcn & ~ARFCN_FLAG_MASK,
394 trx->pm_band_arfcn_start & ~ARFCN_FLAG_MASK);
Eric Wild901f6892022-05-07 15:36:47 +0200395 return;
396 }
397
Erice29b3c82022-10-27 16:54:09 +0200398 struct trxcon_param_full_power_scan_res res = {
399 .last_result = band_arfcn == trx->pm_band_arfcn_stop,
400 .band_arfcn = band_arfcn,
401 .dbm = dbm,
402 };
403
404 osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FULL_POWER_SCAN_RES, &res);
Eric Wild901f6892022-05-07 15:36:47 +0200405
406 /* Schedule a next measurement */
407 if (band_arfcn != trx->pm_band_arfcn_stop)
408 trx_if_cmd_measure(trx, ++band_arfcn, trx->pm_band_arfcn_stop);
409}
410
411/*
412 * Timing Advance control
413 *
414 * SETTA instructs the transceiver to transmit bursts in
415 * advance calculated from requested TA value. This value is
416 * normally between 0 and 63, with each step representing
417 * an advance of one bit period (about 3.69 microseconds).
418 * Since OsmocomBB has a special feature, which allows one
419 * to spoof the distance from BTS, the range is extended.
420 * CMD SETTA <-128..127>
421 * RSP SETTA <status> <TA>
422 */
423
424int trx_if_cmd_setta(struct trx_instance *trx, int8_t ta)
425{
426 return trx_ctrl_cmd(trx, 0, "SETTA", "%d", ta);
427}
428
429/*
430 * Frequency Hopping parameters indication.
431 *
432 * SETFH instructs transceiver to enable frequency hopping mode
433 * using the given HSN, MAIO, and Mobile Allocation parameters.
434 *
435 * CMD SETFH <HSN> <MAIO> <RXF1> <TXF1> [... <RXFN> <TXFN>]
436 *
437 * where <RXFN> and <TXFN> is a pair of Rx/Tx frequencies (in kHz)
438 * corresponding to one ARFCN the Mobile Allocation. Note that the
439 * channel list is expected to be sorted in ascending order.
440 */
441
Erice29b3c82022-10-27 16:54:09 +0200442int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn, uint8_t maio,
443 const uint16_t *ma, size_t ma_len)
Eric Wild901f6892022-05-07 15:36:47 +0200444{
445 /* Reserve some room for CMD SETFH <HSN> <MAIO> */
446 char ma_buf[TRXC_BUF_SIZE - 24];
447 size_t ma_buf_len = sizeof(ma_buf) - 1;
448 uint16_t rx_freq, tx_freq;
449 char *ptr;
450 int i, rc;
451
452 /* Make sure that Mobile Allocation has at least one ARFCN */
453 if (!ma_len || ma == NULL) {
Erice29b3c82022-10-27 16:54:09 +0200454 LOGPFSML(trx->fi, LOGL_ERROR, "Mobile Allocation is empty?!?\n");
Eric Wild901f6892022-05-07 15:36:47 +0200455 return -EINVAL;
456 }
457
458 /* Compose a sequence of Rx/Tx frequencies (mobile allocation) */
459 for (i = 0, ptr = ma_buf; i < ma_len; i++) {
460 /* Convert ARFCN to a pair of Rx/Tx frequencies (Hz * 10) */
461 rx_freq = gsm_arfcn2freq10(ma[i], 0); /* Rx: Downlink */
462 tx_freq = gsm_arfcn2freq10(ma[i], 1); /* Tx: Uplink */
463 if (rx_freq == 0xffff || tx_freq == 0xffff) {
Erice29b3c82022-10-27 16:54:09 +0200464 LOGPFSML(trx->fi, LOGL_ERROR, "Failed to convert ARFCN %u "
Eric Wild901f6892022-05-07 15:36:47 +0200465 "to a pair of Rx/Tx frequencies\n",
466 ma[i] & ~ARFCN_FLAG_MASK);
467 return -EINVAL;
468 }
469
470 /* Append a pair of Rx/Tx frequencies (in kHz) to the buffer */
471 rc = snprintf(ptr, ma_buf_len, "%u %u ", rx_freq * 100, tx_freq * 100);
472 if (rc < 0 || rc > ma_buf_len) { /* Prevent buffer overflow */
Erice29b3c82022-10-27 16:54:09 +0200473 LOGPFSML(trx->fi, LOGL_ERROR, "Not enough room to encode "
Eric Wild901f6892022-05-07 15:36:47 +0200474 "Mobile Allocation (N=%zu)\n", ma_len);
475 return -ENOSPC;
476 }
477
478 /* Move pointer */
479 ma_buf_len -= rc;
480 ptr += rc;
481 }
482
483 /* Overwrite the last space */
484 *(ptr - 1) = '\0';
485
486 return trx_ctrl_cmd(trx, 1, "SETFH", "%u %u %s", hsn, maio, ma_buf);
487}
488
489/* Get response from CTRL socket */
490static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
491{
492 struct trx_instance *trx = ofd->data;
493 struct trx_ctrl_msg *tcm;
494 int resp, rsp_len;
495 char buf[TRXC_BUF_SIZE], *p;
Erica378a1d2022-07-19 21:12:58 +0200496 ssize_t read_len;
Eric Wild901f6892022-05-07 15:36:47 +0200497
Erica378a1d2022-07-19 21:12:58 +0200498 read_len = read(ofd->fd, buf, sizeof(buf) - 1);
499 if (read_len <= 0) {
Erice29b3c82022-10-27 16:54:09 +0200500 LOGPFSML(trx->fi, LOGL_ERROR, "read() failed with rc=%zd\n", read_len);
Erica378a1d2022-07-19 21:12:58 +0200501 return read_len;
502 }
503 buf[read_len] = '\0';
Eric Wild901f6892022-05-07 15:36:47 +0200504
505 if (!!strncmp(buf, "RSP ", 4)) {
Erice29b3c82022-10-27 16:54:09 +0200506 LOGPFSML(trx->fi, LOGL_NOTICE, "Unknown message on CTRL port: %s\n", buf);
Eric Wild901f6892022-05-07 15:36:47 +0200507 return 0;
508 }
509
510 /* Calculate the length of response item */
511 p = strchr(buf + 4, ' ');
512 rsp_len = p ? p - buf - 4 : strlen(buf) - 4;
513
Erice29b3c82022-10-27 16:54:09 +0200514 LOGPFSML(trx->fi, LOGL_INFO, "Response message: '%s'\n", buf);
Eric Wild901f6892022-05-07 15:36:47 +0200515
516 /* Abort expire timer */
Erice29b3c82022-10-27 16:54:09 +0200517 osmo_timer_del(&trx->trx_ctrl_timer);
Eric Wild901f6892022-05-07 15:36:47 +0200518
519 /* Get command for response message */
520 if (llist_empty(&trx->trx_ctrl_list)) {
Erice29b3c82022-10-27 16:54:09 +0200521 LOGPFSML(trx->fi, LOGL_NOTICE, "Response message without command\n");
Eric Wild901f6892022-05-07 15:36:47 +0200522 return -EINVAL;
523 }
524
525 tcm = llist_entry(trx->trx_ctrl_list.next,
526 struct trx_ctrl_msg, list);
527
528 /* Check if response matches command */
529 if (!!strncmp(buf + 4, tcm->cmd + 4, rsp_len)) {
Erice29b3c82022-10-27 16:54:09 +0200530 LOGPFSML(trx->fi, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR,
Eric Wild901f6892022-05-07 15:36:47 +0200531 "Response message '%s' does not match command "
532 "message '%s'\n", buf, tcm->cmd);
533 goto rsp_error;
534 }
535
536 /* Check for response code */
537 sscanf(p + 1, "%d", &resp);
538 if (resp) {
Erice29b3c82022-10-27 16:54:09 +0200539 LOGPFSML(trx->fi, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR,
Eric Wild901f6892022-05-07 15:36:47 +0200540 "Transceiver rejected TRX command with "
541 "response: '%s'\n", buf);
542
543 if (tcm->critical)
544 goto rsp_error;
545 }
546
547 /* Trigger state machine */
548 if (!strncmp(tcm->cmd + 4, "POWERON", 7)) {
549 trx->powered_up = true;
Erice29b3c82022-10-27 16:54:09 +0200550 osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_ACTIVE, 0, 0);
Eric Wild901f6892022-05-07 15:36:47 +0200551 }
552 else if (!strncmp(tcm->cmd + 4, "POWEROFF", 8)) {
553 trx->powered_up = false;
Erice29b3c82022-10-27 16:54:09 +0200554 osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_IDLE, 0, 0);
Eric Wild901f6892022-05-07 15:36:47 +0200555 }
556 else if (!strncmp(tcm->cmd + 4, "MEASURE", 7))
557 trx_if_measure_rsp_cb(trx, buf + 14);
558 else if (!strncmp(tcm->cmd + 4, "ECHO", 4))
Erice29b3c82022-10-27 16:54:09 +0200559 osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_IDLE, 0, 0);
Eric Wild901f6892022-05-07 15:36:47 +0200560 else
Erice29b3c82022-10-27 16:54:09 +0200561 osmo_fsm_inst_state_chg(trx->fi, trx->prev_state, 0, 0);
Eric Wild901f6892022-05-07 15:36:47 +0200562
563 /* Remove command from list */
564 llist_del(&tcm->list);
565 talloc_free(tcm);
566
567 /* Send next message, if any */
568 trx_ctrl_send(trx);
569
570 return 0;
571
572rsp_error:
Erice29b3c82022-10-27 16:54:09 +0200573 osmo_fsm_inst_term(trx->fi, OSMO_FSM_TERM_ERROR, NULL);
Eric Wild901f6892022-05-07 15:36:47 +0200574 return -EIO;
575}
576
577/* ------------------------------------------------------------------------ */
578/* Data interface handlers */
579/* ------------------------------------------------------------------------ */
580/* DATA interface */
581/* */
582/* Messages on the data interface carry one radio burst per UDP message. */
583/* */
584/* Received Data Burst: */
585/* 1 byte timeslot index */
586/* 4 bytes GSM frame number, BE */
587/* 1 byte RSSI in -dBm */
588/* 2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, BE */
589/* 148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1" */
590/* 2 bytes are not used, but being sent by OsmoTRX */
591/* */
592/* Transmit Data Burst: */
593/* 1 byte timeslot index */
594/* 4 bytes GSM frame number, BE */
595/* 1 byte transmit level wrt ARFCN max, -dB (attenuation) */
596/* 148 bytes output symbol values, 0 & 1 */
597/* ------------------------------------------------------------------------ */
598
599static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
600{
601 struct trx_instance *trx = ofd->data;
Erice29b3c82022-10-27 16:54:09 +0200602 struct trxcon_inst *trxcon = trx->trxcon;
603 struct l1sched_meas_set meas;
Eric Wild901f6892022-05-07 15:36:47 +0200604 uint8_t buf[TRXD_BUF_SIZE];
605 sbit_t bits[148];
606 int8_t rssi, tn;
607 int16_t toa256;
608 uint32_t fn;
609 ssize_t read_len;
610
Erica378a1d2022-07-19 21:12:58 +0200611 read_len = read(ofd->fd, buf, sizeof(buf));
612 if (read_len <= 0) {
Erice29b3c82022-10-27 16:54:09 +0200613 LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR, "read() failed with rc=%zd\n", read_len);
Erica378a1d2022-07-19 21:12:58 +0200614 return read_len;
615 }
616
Erice29b3c82022-10-27 16:54:09 +0200617 if (read_len < (8 + 148)) { /* TRXDv0 header + GMSK burst */
618 LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR,
619 "Got data message with invalid length '%zd'\n", read_len);
Erica378a1d2022-07-19 21:12:58 +0200620 return -EINVAL;
621 }
Erice29b3c82022-10-27 16:54:09 +0200622
Erica378a1d2022-07-19 21:12:58 +0200623 tn = buf[0];
624 fn = osmo_load32be(buf + 1);
Erice29b3c82022-10-27 16:54:09 +0200625 rssi = -(int8_t) buf[5];
626 toa256 = ((int16_t) (buf[6] << 8) | buf[7]);
Erica378a1d2022-07-19 21:12:58 +0200627
Eric847de6d2022-10-27 16:54:53 +0200628 memcpy(bits, buf + 8, 148);
Eric Wild901f6892022-05-07 15:36:47 +0200629
630 if (tn >= 8) {
Erice29b3c82022-10-27 16:54:09 +0200631 LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR, "Illegal TS %d\n", tn);
Eric Wild901f6892022-05-07 15:36:47 +0200632 return -EINVAL;
633 }
634
635 if (fn >= 2715648) {
Erice29b3c82022-10-27 16:54:09 +0200636 LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR, "Illegal FN %u\n", fn);
Eric Wild901f6892022-05-07 15:36:47 +0200637 return -EINVAL;
638 }
639
Erice29b3c82022-10-27 16:54:09 +0200640 LOGPFSMSL(trx->fi, DTRXD, LOGL_DEBUG,
641 "RX burst tn=%u fn=%u rssi=%d toa=%d\n",
642 tn, fn, rssi, toa256);
Eric Wild901f6892022-05-07 15:36:47 +0200643
644 /* Group the measurements together */
Erice29b3c82022-10-27 16:54:09 +0200645 meas = (struct l1sched_meas_set) {
Eric Wild901f6892022-05-07 15:36:47 +0200646 .toa256 = toa256,
647 .rssi = rssi,
648 .fn = fn,
649 };
650
651 /* Poke scheduler */
Erice29b3c82022-10-27 16:54:09 +0200652 l1sched_handle_rx_burst(trxcon->sched, tn, fn, bits, 148, &meas);
Eric Wild901f6892022-05-07 15:36:47 +0200653
654 /* Correct local clock counter */
655 if (fn % 51 == 0)
Erice29b3c82022-10-27 16:54:09 +0200656 l1sched_clck_handle(trxcon->sched, fn);
Eric Wild901f6892022-05-07 15:36:47 +0200657
658 return 0;
659}
660
Erice29b3c82022-10-27 16:54:09 +0200661int trx_if_tx_burst(struct trx_instance *trx,
662 const struct l1sched_burst_req *br)
Eric Wild901f6892022-05-07 15:36:47 +0200663{
Erica378a1d2022-07-19 21:12:58 +0200664 uint8_t buf[TRXD_BUF_SIZE];
Erice29b3c82022-10-27 16:54:09 +0200665 size_t length;
Erica378a1d2022-07-19 21:12:58 +0200666
667 /**
668 * We must be sure that we have clock,
669 * and we have sent all control data
670 *
671 * TODO: introduce proper state machines for both
672 * transceiver and its TRXC interface.
673 */
674#if 0
Erice29b3c82022-10-27 16:54:09 +0200675 if (trx->fi->state != TRX_STATE_ACTIVE) {
676 LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR,
677 "Ignoring TX data, transceiver isn't ready\n");
Erica378a1d2022-07-19 21:12:58 +0200678 return -EAGAIN;
679 }
680#endif
681
Erice29b3c82022-10-27 16:54:09 +0200682 LOGPFSMSL(trx->fi, DTRXD, LOGL_DEBUG,
683 "TX burst tn=%u fn=%u pwr=%u\n",
684 br->tn, br->fn, br->pwr);
Erica378a1d2022-07-19 21:12:58 +0200685
Erice29b3c82022-10-27 16:54:09 +0200686 buf[0] = br->tn;
687 osmo_store32be(br->fn, buf + 1);
688 buf[5] = br->pwr;
689 length = 6;
Erica378a1d2022-07-19 21:12:58 +0200690
691 /* Copy ubits {0,1} */
Erice29b3c82022-10-27 16:54:09 +0200692 if (br->burst_len != 0) {
693 memcpy(buf + 6, br->burst, br->burst_len);
694 length += br->burst_len;
695 }
Erica378a1d2022-07-19 21:12:58 +0200696
697 /* Send data to transceiver */
Erice29b3c82022-10-27 16:54:09 +0200698 send(trx->trx_ofd_data.fd, buf, length, 0);
Erica378a1d2022-07-19 21:12:58 +0200699
Eric Wild901f6892022-05-07 15:36:47 +0200700 return 0;
701}
702
703/* Init TRX interface (TRXC, TRXD sockets and FSM) */
Erice29b3c82022-10-27 16:54:09 +0200704struct trx_instance *trx_if_open(struct trxcon_inst *trxcon,
Eric Wild901f6892022-05-07 15:36:47 +0200705 const char *local_host, const char *remote_host,
706 uint16_t base_port)
707{
Erice29b3c82022-10-27 16:54:09 +0200708 const unsigned int offset = trxcon->id * 2;
Eric Wild901f6892022-05-07 15:36:47 +0200709 struct trx_instance *trx;
Erice29b3c82022-10-27 16:54:09 +0200710 struct osmo_fsm_inst *fi;
Eric Wild901f6892022-05-07 15:36:47 +0200711 int rc;
712
Erice29b3c82022-10-27 16:54:09 +0200713 LOGPFSML(trxcon->fi, LOGL_NOTICE, "Init transceiver interface "
714 "(%s:%u/%u)\n", remote_host, base_port, trxcon->id);
Eric Wild901f6892022-05-07 15:36:47 +0200715
Erice29b3c82022-10-27 16:54:09 +0200716 /* Allocate a new dedicated state machine */
717 fi = osmo_fsm_inst_alloc_child(&trx_fsm, trxcon->fi, TRXCON_EV_PHYIF_FAILURE);
718 if (fi == NULL) {
719 LOGPFSML(trxcon->fi, LOGL_ERROR, "Failed to allocate an instance "
720 "of FSM '%s'\n", trx_fsm.name);
Eric Wild901f6892022-05-07 15:36:47 +0200721 return NULL;
722 }
723
Erice29b3c82022-10-27 16:54:09 +0200724 trx = talloc_zero(fi, struct trx_instance);
725 if (!trx) {
726 LOGPFSML(trxcon->fi, LOGL_ERROR, "Failed to allocate memory\n");
727 osmo_fsm_inst_free(fi);
Eric Wild901f6892022-05-07 15:36:47 +0200728 return NULL;
729 }
730
731 /* Initialize CTRL queue */
732 INIT_LLIST_HEAD(&trx->trx_ctrl_list);
733
Erica378a1d2022-07-19 21:12:58 +0200734 /* Open sockets */
Erice29b3c82022-10-27 16:54:09 +0200735 rc = trx_udp_open(trx, &trx->trx_ofd_ctrl, /* TRXC */
736 local_host, base_port + 101 + offset,
737 remote_host, base_port + 1 + offset,
Erica378a1d2022-07-19 21:12:58 +0200738 trx_ctrl_read_cb);
739 if (rc < 0)
740 goto udp_error;
Eric Wild901f6892022-05-07 15:36:47 +0200741
Erice29b3c82022-10-27 16:54:09 +0200742 rc = trx_udp_open(trx, &trx->trx_ofd_data, /* TRXD */
743 local_host, base_port + 102 + offset,
744 remote_host, base_port + 2 + offset,
Erica378a1d2022-07-19 21:12:58 +0200745 trx_data_rx_cb);
746 if (rc < 0)
747 goto udp_error;
Erice29b3c82022-10-27 16:54:09 +0200748
749 trx->trxcon = trxcon;
750 fi->priv = trx;
751 trx->fi = fi;
752
Eric Wild901f6892022-05-07 15:36:47 +0200753 return trx;
Erica378a1d2022-07-19 21:12:58 +0200754
755udp_error:
Erice29b3c82022-10-27 16:54:09 +0200756 LOGPFSML(trx->fi, LOGL_ERROR, "Couldn't establish UDP connection\n");
757 osmo_fsm_inst_free(trx->fi);
Erica378a1d2022-07-19 21:12:58 +0200758 return NULL;
Eric Wild901f6892022-05-07 15:36:47 +0200759}
760
761/* Flush pending control messages */
762void trx_if_flush_ctrl(struct trx_instance *trx)
763{
764 struct trx_ctrl_msg *tcm;
765
766 /* Reset state machine */
Erice29b3c82022-10-27 16:54:09 +0200767 osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_IDLE, 0, 0);
Eric Wild901f6892022-05-07 15:36:47 +0200768
769 /* Clear command queue */
770 while (!llist_empty(&trx->trx_ctrl_list)) {
771 tcm = llist_entry(trx->trx_ctrl_list.next,
772 struct trx_ctrl_msg, list);
773 llist_del(&tcm->list);
774 talloc_free(tcm);
775 }
776}
777
778void trx_if_close(struct trx_instance *trx)
779{
Erice29b3c82022-10-27 16:54:09 +0200780 if (trx == NULL || trx->fi == NULL)
781 return;
782 osmo_fsm_inst_term(trx->fi, OSMO_FSM_TERM_REQUEST, NULL);
783}
784
785static void trx_fsm_cleanup_cb(struct osmo_fsm_inst *fi,
786 enum osmo_fsm_term_cause cause)
787{
788 static const char cmd_poweroff[] = "CMD POWEROFF";
789 struct trx_instance *trx = fi->priv;
790
Eric Wild901f6892022-05-07 15:36:47 +0200791 /* May be unallocated due to init error */
792 if (!trx)
793 return;
794
Erice29b3c82022-10-27 16:54:09 +0200795 LOGPFSML(fi, LOGL_NOTICE, "Shutdown transceiver interface\n");
796
797 /* Abort TRXC response timer (if pending) */
798 osmo_timer_del(&trx->trx_ctrl_timer);
Eric Wild901f6892022-05-07 15:36:47 +0200799
800 /* Flush CTRL message list */
801 trx_if_flush_ctrl(trx);
802
Erice29b3c82022-10-27 16:54:09 +0200803 /* Power off if the transceiver is up */
804 if (trx->powered_up && trx->trx_ofd_ctrl.fd >= 0)
805 send(trx->trx_ofd_ctrl.fd, &cmd_poweroff[0], sizeof(cmd_poweroff), 0);
806
Eric Wild901f6892022-05-07 15:36:47 +0200807 /* Close sockets */
Erica378a1d2022-07-19 21:12:58 +0200808 trx_udp_close(&trx->trx_ofd_ctrl);
809 trx_udp_close(&trx->trx_ofd_data);
Eric Wild901f6892022-05-07 15:36:47 +0200810
811 /* Free memory */
Erice29b3c82022-10-27 16:54:09 +0200812 trx->fi->priv = NULL;
Eric Wild901f6892022-05-07 15:36:47 +0200813 talloc_free(trx);
814}
815
816static __attribute__((constructor)) void on_dso_load(void)
817{
818 OSMO_ASSERT(osmo_fsm_register(&trx_fsm) == 0);
819}