blob: 0243fd444e590d43540b253496fa459795a36003 [file] [log] [blame]
Erice29b3c82022-10-27 16:54:09 +02001/*
2 * OsmocomBB <-> SDR connection bridge
3 *
4 * (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
5 * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
6 *
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 */
20
21#include <stdio.h>
22#include <stdint.h>
23#include <string.h>
24#include <errno.h>
25
26#include <arpa/inet.h>
27
28#include <osmocom/core/fsm.h>
29#include <osmocom/core/msgb.h>
30#include <osmocom/core/talloc.h>
31
32#include <osmocom/bb/trxcon/trxcon.h>
33#include <osmocom/bb/trxcon/trx_if.h>
34#include <osmocom/bb/trxcon/logging.h>
35#include <osmocom/bb/trxcon/l1ctl.h>
36#include <osmocom/bb/trxcon/l1ctl_server.h>
37#include <osmocom/bb/trxcon/l1ctl_proto.h>
38#include <osmocom/bb/l1sched/l1sched.h>
39#include <osmocom/bb/l1sched/logging.h>
40
41#define S(x) (1 << (x))
42
43static void trxcon_allstate_action(struct osmo_fsm_inst *fi,
44 uint32_t event, void *data)
45{
46 struct trxcon_inst *trxcon = fi->priv;
47
48 switch (event) {
49 case TRXCON_EV_PHYIF_FAILURE:
50 trxcon->phyif = NULL;
51 osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
52 break;
53 case TRXCON_EV_L2IF_FAILURE:
54 trxcon->l2if = NULL;
55 osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
56 break;
57 case TRXCON_EV_RESET_FULL_REQ:
58 if (fi->state != TRXCON_ST_RESET)
59 osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0);
60 l1sched_reset(trxcon->sched, true);
61 trx_if_cmd_poweroff(trxcon->phyif);
62 trx_if_cmd_echo(trxcon->phyif);
63 break;
64 case TRXCON_EV_RESET_SCHED_REQ:
65 l1sched_reset(trxcon->sched, false);
66 break;
67 case TRXCON_EV_SET_PHY_CONFIG_REQ:
68 {
69 const struct trxcon_param_set_phy_config_req *req = data;
70
71 switch (req->type) {
72 case TRXCON_PHY_CFGT_PCHAN_COMB:
73 trx_if_cmd_setslot(trxcon->phyif,
74 req->pchan_comb.tn,
75 req->pchan_comb.pchan);
76 break;
77 case TRXCON_PHY_CFGT_TX_PARAMS:
78 if (trxcon->l1p.ta != req->tx_params.timing_advance)
79 trx_if_cmd_setta(trxcon->phyif, req->tx_params.timing_advance);
80 trxcon->l1p.tx_power = req->tx_params.tx_power;
81 trxcon->l1p.ta = req->tx_params.timing_advance;
82 break;
83 }
84 break;
85 }
86 case TRXCON_EV_UPDATE_SACCH_CACHE_REQ:
87 {
88 const struct trxcon_param_tx_traffic_data_req *req = data;
89
90 if (req->link_id != L1SCHED_CH_LID_SACCH) {
91 LOGPFSML(fi, LOGL_ERROR, "Unexpected link_id=0x%02x\n", req->link_id);
92 break;
93 }
94 if (req->data_len != GSM_MACBLOCK_LEN) {
95 LOGPFSML(fi, LOGL_ERROR, "Unexpected data length=%zu\n", req->data_len);
96 break;
97 }
98
99 memcpy(&trxcon->sched->sacch_cache[0], req->data, req->data_len);
100 break;
101 }
102 default:
103 OSMO_ASSERT(0);
104 }
105}
106
107static int trxcon_timer_cb(struct osmo_fsm_inst *fi)
108{
109 struct trxcon_inst *trxcon = fi->priv;
110
111 switch (fi->state) {
112 case TRXCON_ST_FBSB_SEARCH:
113 l1ctl_tx_fbsb_fail(trxcon->l2if, trxcon->l1p.band_arfcn);
114 osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0);
115 return 0;
116 default:
117 OSMO_ASSERT(0);
118 }
119}
120
121static void trxcon_st_reset_action(struct osmo_fsm_inst *fi,
122 uint32_t event, void *data)
123{
124 struct trxcon_inst *trxcon = fi->priv;
125
126 switch (event) {
127 case TRXCON_EV_FBSB_SEARCH_REQ:
128 {
129 const struct trxcon_param_fbsb_search_req *req = data;
130 const struct trx_instance *trx = trxcon->phyif;
131
132 osmo_fsm_inst_state_chg_ms(fi, TRXCON_ST_FBSB_SEARCH, req->timeout_ms, 0);
133
134 l1sched_configure_ts(trxcon->sched, 0, req->pchan_config);
135
136 /* Only if current ARFCN differs */
Eric847de6d2022-10-27 16:54:53 +0200137 // if (trxcon->l1p.band_arfcn != req->band_arfcn) {
Erice29b3c82022-10-27 16:54:09 +0200138 /* Update current ARFCN */
139 trxcon->l1p.band_arfcn = req->band_arfcn;
140
141 /* Tune transceiver to required ARFCN */
142 trx_if_cmd_rxtune(trxcon->phyif, req->band_arfcn);
143 trx_if_cmd_txtune(trxcon->phyif, req->band_arfcn);
Eric847de6d2022-10-27 16:54:53 +0200144 // }
Erice29b3c82022-10-27 16:54:09 +0200145
146 /* Transceiver might have been powered on before, e.g.
147 * in case of sending L1CTL_FBSB_REQ due to signal loss. */
Eric847de6d2022-10-27 16:54:53 +0200148 trx_if_cmd_sync(trxcon->phyif);
149
Erice29b3c82022-10-27 16:54:09 +0200150 if (!trx->powered_up)
151 trx_if_cmd_poweron(trxcon->phyif);
152 break;
153 }
154 case TRXCON_EV_FULL_POWER_SCAN_REQ:
155 {
156 const struct trxcon_param_full_power_scan_req *req = data;
157
158 osmo_fsm_inst_state_chg(fi, TRXCON_ST_FULL_POWER_SCAN, 0, 0); /* TODO: timeout */
159 trx_if_cmd_measure(trxcon->phyif, req->band_arfcn_start, req->band_arfcn_stop);
160 break;
161 }
162 default:
163 OSMO_ASSERT(0);
164 }
165}
166
167static void trxcon_st_full_power_scan_action(struct osmo_fsm_inst *fi,
168 uint32_t event, void *data)
169{
170 struct trxcon_inst *trxcon = fi->priv;
171
172 switch (event) {
173 case TRXCON_EV_FULL_POWER_SCAN_RES:
174 {
175 const struct trxcon_param_full_power_scan_res *res = data;
176
177 l1ctl_tx_pm_conf(trxcon->l2if, res->band_arfcn, res->dbm, res->last_result);
178 break;
179 }
180 case TRXCON_EV_FULL_POWER_SCAN_REQ:
181 {
182 const struct trxcon_param_full_power_scan_req *req = data;
183
184 trx_if_cmd_measure(trxcon->phyif, req->band_arfcn_start, req->band_arfcn_stop);
185 break;
186 }
187 default:
188 OSMO_ASSERT(0);
189 }
190}
191
192static void trxcon_st_fbsb_search_action(struct osmo_fsm_inst *fi,
193 uint32_t event, void *data)
194{
195 struct trxcon_inst *trxcon = fi->priv;
196
197 switch (event) {
198 case TRXCON_EV_FBSB_SEARCH_RES:
199 osmo_fsm_inst_state_chg(fi, TRXCON_ST_BCCH_CCCH, 0, 0);
200 l1ctl_tx_fbsb_conf(trxcon->l2if,
201 trxcon->l1p.band_arfcn,
202 trxcon->sched->bsic);
203 break;
204 default:
205 OSMO_ASSERT(0);
206 }
207}
208
209static void handle_tx_access_burst_req(struct osmo_fsm_inst *fi,
210 const struct trxcon_param_tx_access_burst_req *req)
211{
212 struct trxcon_inst *trxcon = fi->priv;
213 enum l1sched_ts_prim_type prim_type;
214 const struct l1sched_ts_prim *prim;
215
216 const struct l1sched_ts_prim_rach rach = {
217 .synch_seq = req->synch_seq,
218 .offset = req->offset,
219 .ra = req->ra,
220 };
221
222 prim_type = req->is_11bit ? L1SCHED_PRIM_RACH11 : L1SCHED_PRIM_RACH8;
223 prim = l1sched_prim_push(trxcon->sched, prim_type,
224 req->chan_nr, req->link_id,
225 (const uint8_t *)&rach, sizeof(rach));
226 if (prim == NULL)
227 LOGPFSML(fi, LOGL_ERROR, "Failed to enqueue a prim\n");
228}
229
230static void trxcon_st_bcch_ccch_action(struct osmo_fsm_inst *fi,
231 uint32_t event, void *data)
232{
233 struct trxcon_inst *trxcon = fi->priv;
234 struct l1sched_ts *ts;
235 int rc;
236
237 switch (event) {
238 case TRXCON_EV_TX_ACCESS_BURST_REQ:
239 handle_tx_access_burst_req(fi, data);
240 break;
241 case TRXCON_EV_SET_CCCH_MODE_REQ:
242 {
243 struct trxcon_param_set_ccch_tch_mode_req *req = data;
244 enum gsm_phys_chan_config chan_config = req->mode;
245
246 /* Make sure that TS0 is allocated and configured */
247 ts = trxcon->sched->ts[0];
248 if (ts == NULL || ts->mf_layout == NULL) {
249 LOGPFSML(fi, LOGL_ERROR, "TS0 is not configured\n");
250 return;
251 }
252
253 /* Do nothing if the current mode matches required */
254 if (ts->mf_layout->chan_config != chan_config)
255 l1sched_configure_ts(trxcon->sched, 0, chan_config);
256 req->applied = true;
257 break;
258 }
259 case TRXCON_EV_DEDICATED_ESTABLISH_REQ:
260 {
261 const struct trxcon_param_dedicated_establish_req *req = data;
262 enum gsm_phys_chan_config config;
263
264 config = l1sched_chan_nr2pchan_config(req->chan_nr);
265 if (config == GSM_PCHAN_NONE) {
266 LOGPFSML(fi, LOGL_ERROR, "Failed to determine channel config\n");
267 return;
268 }
269
270 if (req->hopping) {
271 /* Apply the freq. hopping parameters */
272 rc = trx_if_cmd_setfh(trxcon->phyif,
273 req->h1.hsn, req->h1.maio,
274 &req->h1.ma[0], req->h1.n);
275 if (rc)
276 return;
277
278 /* Set current ARFCN to an invalid value */
279 trxcon->l1p.band_arfcn = 0xffff;
280 } else {
281 /* Tune transceiver to required ARFCN */
282 if (trx_if_cmd_rxtune(trxcon->phyif, req->h0.band_arfcn))
283 return;
284 if (trx_if_cmd_txtune(trxcon->phyif, req->h0.band_arfcn))
285 return;
286
287 /* Update current ARFCN */
288 trxcon->l1p.band_arfcn = req->h0.band_arfcn;
289 }
290
291 rc = l1sched_configure_ts(trxcon->sched, req->chan_nr & 0x07, config);
292 if (rc)
293 return;
294 ts = trxcon->sched->ts[req->chan_nr & 0x07];
295 OSMO_ASSERT(ts != NULL);
296
297 l1sched_deactivate_all_lchans(ts);
298
299 /* Activate only requested lchans */
300 rc = l1sched_set_lchans(ts, req->chan_nr, 1, req->tch_mode, req->tsc);
301 if (rc) {
302 LOGPFSML(fi, LOGL_ERROR, "Failed to activate requested lchans\n");
303 return;
304 }
305
306 if (config == GSM_PCHAN_PDCH)
307 osmo_fsm_inst_state_chg(fi, TRXCON_ST_PACKET_DATA, 0, 0);
308 else
309 osmo_fsm_inst_state_chg(fi, TRXCON_ST_DEDICATED, 0, 0);
310 break;
311 }
312 case TRXCON_EV_RX_DATA_IND:
313 {
314 const struct trxcon_param_rx_traffic_data_ind *ind = data;
315 const struct l1ctl_info_dl dl_hdr = {
316 .chan_nr = ind->chan_nr,
317 .link_id = ind->link_id,
318 .frame_nr = htonl(ind->frame_nr),
319 .band_arfcn = htons(trxcon->l1p.band_arfcn),
320 .fire_crc = ind->data_len > 0 ? 0 : 2,
321 .rx_level = dbm2rxlev(ind->rssi),
322 .num_biterr = ind->n_errors,
323 /* TODO: set proper .snr */
324 };
325
326 l1ctl_tx_dt_ind(trxcon->l2if, &dl_hdr, ind->data, ind->data_len, false);
327 break;
328 }
329 default:
330 OSMO_ASSERT(0);
331 }
332}
333
334static void trxcon_st_dedicated_action(struct osmo_fsm_inst *fi,
335 uint32_t event, void *data)
336{
337 struct trxcon_inst *trxcon = fi->priv;
338
339 switch (event) {
340 case TRXCON_EV_TX_ACCESS_BURST_REQ:
341 handle_tx_access_burst_req(fi, data);
342 break;
343 case TRXCON_EV_DEDICATED_RELEASE_REQ:
344 l1sched_reset(trxcon->sched, false);
345 osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0);
346 break;
347 case TRXCON_EV_SET_TCH_MODE_REQ:
348 {
349 struct trxcon_param_set_ccch_tch_mode_req *req = data;
350 unsigned int tn;
351
352 /* Iterate over timeslot list */
353 for (tn = 0; tn < ARRAY_SIZE(trxcon->sched->ts); tn++) {
354 struct l1sched_ts *ts = trxcon->sched->ts[tn];
355 struct l1sched_lchan_state *lchan;
356
357 /* Timeslot is not allocated */
358 if (ts == NULL || ts->mf_layout == NULL)
359 continue;
360
361 /* Iterate over all allocated lchans */
362 llist_for_each_entry(lchan, &ts->lchans, list) {
363 /* Omit inactive channels */
364 if (!lchan->active)
365 continue;
366 lchan->tch_mode = req->mode;
367 if (req->mode == GSM48_CMODE_SPEECH_AMR) {
368 uint8_t bmask = req->amr.codecs_bitmask;
369 int n = 0;
370 int acum = 0;
371 int pos;
372 while ((pos = ffs(bmask)) != 0) {
373 acum += pos;
374 LOGPFSML(fi, LOGL_DEBUG,
375 LOGP_LCHAN_NAME_FMT " AMR codec[%u] = %u\n",
376 LOGP_LCHAN_NAME_ARGS(lchan), n, acum - 1);
377 lchan->amr.codec[n++] = acum - 1;
378 bmask >>= pos;
379 }
380 if (n == 0) {
381 LOGPFSML(fi, LOGL_ERROR,
382 LOGP_LCHAN_NAME_FMT " Empty AMR codec mode bitmask!\n",
383 LOGP_LCHAN_NAME_ARGS(lchan));
384 continue;
385 }
386 lchan->amr.codecs = n;
387 lchan->amr.dl_ft = req->amr.start_codec;
388 lchan->amr.dl_cmr = req->amr.start_codec;
389 lchan->amr.ul_ft = req->amr.start_codec;
390 lchan->amr.ul_cmr = req->amr.start_codec;
391 }
392 req->applied = true;
393 }
394 }
395 break;
396 }
397 case TRXCON_EV_CRYPTO_REQ:
398 {
399 const struct trxcon_param_crypto_req *req = data;
400 unsigned int tn = req->chan_nr & 0x07;
401 struct l1sched_ts *ts;
402
403 /* Make sure that required TS is allocated and configured */
404 ts = trxcon->sched->ts[tn];
405 if (ts == NULL || ts->mf_layout == NULL) {
406 LOGPFSML(fi, LOGL_ERROR, "TS%u is not configured\n", tn);
407 return;
408 }
409
410 if (l1sched_start_ciphering(ts, req->a5_algo, req->key, req->key_len) != 0) {
411 LOGPFSML(fi, LOGL_ERROR, "Failed to configure ciphering\n");
412 return;
413 }
414 break;
415 }
416 case TRXCON_EV_TX_TRAFFIC_REQ:
417 case TRXCON_EV_TX_DATA_REQ:
418 {
419 const struct trxcon_param_tx_traffic_data_req *req = data;
420 struct l1sched_ts_prim *prim;
421
422 prim = l1sched_prim_push(trxcon->sched, L1SCHED_PRIM_DATA,
423 req->chan_nr, req->link_id,
424 req->data, req->data_len);
425 if (prim == NULL) {
426 LOGPFSML(fi, LOGL_ERROR, "Failed to enqueue a prim\n");
427 return;
428 }
429 break;
430 }
431 case TRXCON_EV_RX_TRAFFIC_IND:
432 case TRXCON_EV_RX_DATA_IND:
433 {
434 const struct trxcon_param_rx_traffic_data_ind *ind = data;
435 const struct l1ctl_info_dl dl_hdr = {
436 .chan_nr = ind->chan_nr,
437 .link_id = ind->link_id,
438 .frame_nr = htonl(ind->frame_nr),
439 .band_arfcn = htons(trxcon->l1p.band_arfcn),
440 .fire_crc = ind->data_len > 0 ? 0 : 2,
441 .rx_level = dbm2rxlev(ind->rssi),
442 .num_biterr = ind->n_errors,
443 /* TODO: set proper .snr */
444 };
445
446 l1ctl_tx_dt_ind(trxcon->l2if, &dl_hdr,
447 ind->data, ind->data_len,
448 event == TRXCON_EV_RX_TRAFFIC_IND);
449 break;
450 }
451 default:
452 OSMO_ASSERT(0);
453 }
454}
455
456static void trxcon_st_packet_data_action(struct osmo_fsm_inst *fi,
457 uint32_t event, void *data)
458{
459 struct trxcon_inst *trxcon = fi->priv;
460
461 switch (event) {
462 case TRXCON_EV_TX_ACCESS_BURST_REQ:
463 handle_tx_access_burst_req(fi, data);
464 break;
465 case TRXCON_EV_RX_TRAFFIC_IND:
466 LOGPFSML(fi, LOGL_NOTICE, "Rx PDTCH/D message\n");
467 break;
468 case TRXCON_EV_RX_DATA_IND:
469 LOGPFSML(fi, LOGL_NOTICE, "Rx PTCCH/D message\n");
470 break;
471 case TRXCON_EV_DEDICATED_RELEASE_REQ:
472 l1sched_reset(trxcon->sched, false);
473 osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0);
474 break;
475 default:
476 OSMO_ASSERT(0);
477 }
478}
479
480static void trxcon_fsm_pre_term_cb(struct osmo_fsm_inst *fi,
481 enum osmo_fsm_term_cause cause)
482{
483 struct trxcon_inst *trxcon = fi->priv;
484
485 if (trxcon == NULL)
486 return;
487
488 /* Shutdown the scheduler */
489 if (trxcon->sched != NULL)
490 l1sched_free(trxcon->sched);
491 /* Close active connections */
492 if (trxcon->l2if != NULL) {
493 /* Avoid use-after-free: both *fi and *trxcon are children of
494 * the L2IF (L1CTL connection), so we need to re-parent *fi
495 * to NULL before calling l1ctl_client_conn_close(). */
496 talloc_steal(NULL, fi);
497 l1ctl_client_conn_close(trxcon->l2if);
498 }
499 if (trxcon->phyif != NULL)
500 trx_if_close(trxcon->phyif);
501
502 talloc_free(trxcon);
503 fi->priv = NULL;
504}
505
506static const struct osmo_fsm_state trxcon_fsm_states[] = {
507 [TRXCON_ST_RESET] = {
508 .name = "RESET",
509 .out_state_mask = S(TRXCON_ST_FBSB_SEARCH)
510 | S(TRXCON_ST_FULL_POWER_SCAN),
511 .in_event_mask = S(TRXCON_EV_FBSB_SEARCH_REQ)
512 | S(TRXCON_EV_FULL_POWER_SCAN_REQ),
513 .action = &trxcon_st_reset_action,
514 },
515 [TRXCON_ST_FULL_POWER_SCAN] = {
516 .name = "FULL_POWER_SCAN",
517 .out_state_mask = S(TRXCON_ST_RESET),
518 .in_event_mask = S(TRXCON_EV_FULL_POWER_SCAN_RES)
519 | S(TRXCON_EV_FULL_POWER_SCAN_REQ),
520 .action = &trxcon_st_full_power_scan_action,
521 },
522 [TRXCON_ST_FBSB_SEARCH] = {
523 .name = "FBSB_SEARCH",
524 .out_state_mask = S(TRXCON_ST_RESET)
525 | S(TRXCON_ST_BCCH_CCCH),
526 .in_event_mask = S(TRXCON_EV_FBSB_SEARCH_RES),
527 .action = &trxcon_st_fbsb_search_action,
528 },
529 [TRXCON_ST_BCCH_CCCH] = {
530 .name = "BCCH_CCCH",
531 .out_state_mask = S(TRXCON_ST_RESET)
532 | S(TRXCON_ST_FBSB_SEARCH)
533 | S(TRXCON_ST_DEDICATED)
534 | S(TRXCON_ST_PACKET_DATA),
535 .in_event_mask = S(TRXCON_EV_RX_DATA_IND)
536 | S(TRXCON_EV_SET_CCCH_MODE_REQ)
537 | S(TRXCON_EV_TX_ACCESS_BURST_REQ)
538 | S(TRXCON_EV_DEDICATED_ESTABLISH_REQ),
539 .action = &trxcon_st_bcch_ccch_action,
540 },
541 [TRXCON_ST_DEDICATED] = {
542 .name = "DEDICATED",
543 .out_state_mask = S(TRXCON_ST_RESET)
544 | S(TRXCON_ST_FBSB_SEARCH)
545 | S(TRXCON_ST_BCCH_CCCH),
546 .in_event_mask = S(TRXCON_EV_DEDICATED_RELEASE_REQ)
547 | S(TRXCON_EV_TX_ACCESS_BURST_REQ)
548 | S(TRXCON_EV_SET_TCH_MODE_REQ)
549 | S(TRXCON_EV_TX_TRAFFIC_REQ)
550 | S(TRXCON_EV_RX_TRAFFIC_IND)
551 | S(TRXCON_EV_TX_DATA_REQ)
552 | S(TRXCON_EV_RX_DATA_IND)
553 | S(TRXCON_EV_CRYPTO_REQ),
554 .action = &trxcon_st_dedicated_action,
555 },
556 [TRXCON_ST_PACKET_DATA] = {
557 .name = "PACKET_DATA",
558 .out_state_mask = S(TRXCON_ST_RESET)
559 | S(TRXCON_ST_FBSB_SEARCH)
560 | S(TRXCON_ST_BCCH_CCCH),
561 .in_event_mask = S(TRXCON_EV_DEDICATED_RELEASE_REQ)
562 | S(TRXCON_EV_TX_ACCESS_BURST_REQ)
563 | S(TRXCON_EV_RX_TRAFFIC_IND)
564 | S(TRXCON_EV_RX_DATA_IND),
565 .action = &trxcon_st_packet_data_action,
566 },
567};
568
569static const struct value_string trxcon_fsm_event_names[] = {
570 OSMO_VALUE_STRING(TRXCON_EV_PHYIF_FAILURE),
571 OSMO_VALUE_STRING(TRXCON_EV_L2IF_FAILURE),
572 OSMO_VALUE_STRING(TRXCON_EV_RESET_FULL_REQ),
573 OSMO_VALUE_STRING(TRXCON_EV_RESET_SCHED_REQ),
574 OSMO_VALUE_STRING(TRXCON_EV_FULL_POWER_SCAN_REQ),
575 OSMO_VALUE_STRING(TRXCON_EV_FULL_POWER_SCAN_RES),
576 OSMO_VALUE_STRING(TRXCON_EV_FBSB_SEARCH_REQ),
577 OSMO_VALUE_STRING(TRXCON_EV_FBSB_SEARCH_RES),
578 OSMO_VALUE_STRING(TRXCON_EV_SET_CCCH_MODE_REQ),
579 OSMO_VALUE_STRING(TRXCON_EV_SET_TCH_MODE_REQ),
580 OSMO_VALUE_STRING(TRXCON_EV_SET_PHY_CONFIG_REQ),
581 OSMO_VALUE_STRING(TRXCON_EV_TX_ACCESS_BURST_REQ),
582 OSMO_VALUE_STRING(TRXCON_EV_UPDATE_SACCH_CACHE_REQ),
583 OSMO_VALUE_STRING(TRXCON_EV_DEDICATED_ESTABLISH_REQ),
584 OSMO_VALUE_STRING(TRXCON_EV_DEDICATED_RELEASE_REQ),
585 OSMO_VALUE_STRING(TRXCON_EV_TX_TRAFFIC_REQ),
586 OSMO_VALUE_STRING(TRXCON_EV_RX_TRAFFIC_IND),
587 OSMO_VALUE_STRING(TRXCON_EV_TX_DATA_REQ),
588 OSMO_VALUE_STRING(TRXCON_EV_RX_DATA_IND),
589 OSMO_VALUE_STRING(TRXCON_EV_CRYPTO_REQ),
590 { 0, NULL }
591};
592
593struct osmo_fsm trxcon_fsm_def = {
594 .name = "trxcon",
595 .states = trxcon_fsm_states,
596 .num_states = ARRAY_SIZE(trxcon_fsm_states),
597 .log_subsys = DAPP,
598 .event_names = trxcon_fsm_event_names,
599 .allstate_event_mask = S(TRXCON_EV_PHYIF_FAILURE)
600 | S(TRXCON_EV_L2IF_FAILURE)
601 | S(TRXCON_EV_RESET_FULL_REQ)
602 | S(TRXCON_EV_RESET_SCHED_REQ)
603 | S(TRXCON_EV_SET_PHY_CONFIG_REQ)
604 | S(TRXCON_EV_UPDATE_SACCH_CACHE_REQ),
605 .allstate_action = &trxcon_allstate_action,
606 .timer_cb = &trxcon_timer_cb,
607 .pre_term = &trxcon_fsm_pre_term_cb,
608};
609
610static __attribute__((constructor)) void on_dso_load(void)
611{
612 OSMO_ASSERT(osmo_fsm_register(&trxcon_fsm_def) == 0);
613}