static int foo(void) { struct osmo_fsm_inst *fi; LOGP(DMAIN, LOGL_INFO, "Checking FSM allocation\n"); fi = osmo_fsm_inst_alloc(&fsm, g_ctx, NULL, LOGL_DEBUG, NULL); OSMO_ASSERT(fi); OSMO_ASSERT(fi->fsm == &fsm); OSMO_ASSERT(!strncmp(osmo_fsm_inst_name(fi), fsm.name, strlen(fsm.name))); OSMO_ASSERT(fi->state == ST_NULL); OSMO_ASSERT(fi->log_level == LOGL_DEBUG); /* Try invalid state transition */ osmo_fsm_inst_dispatch(fi, EV_B, (void *) 42); OSMO_ASSERT(fi->state == ST_NULL); /* Legitimate state transition */ osmo_fsm_inst_dispatch(fi, EV_A, (void *) 23); OSMO_ASSERT(fi->state == ST_ONE); /* Legitimate transition with timer */ fsm.timer_cb = test_fsm_tmr_cb; osmo_fsm_inst_dispatch(fi, EV_B, (void *) 42); OSMO_ASSERT(fi->state == ST_TWO); return 0; }
static int m3ua_rx_asp(struct osmo_ss7_asp *asp, struct xua_msg *xua) { int event; /* map from the M3UA message class and message type to the XUA * ASP FSM event number */ event = xua_msg_event_map(xua, m3ua_aspxm_map, ARRAY_SIZE(m3ua_aspxm_map)); if (event < 0) return M3UA_ERR_UNSUPP_MSG_TYPE; /* deliver that event to the ASP FSM */ if (osmo_fsm_inst_dispatch(asp->fi, event, xua) < 0) return M3UA_ERR_UNEXPECTED_MSG; return 0; }
/*! \brief Repeat last SID if possible in case of DTX * \param[in] lchan Logical channel on which we check scheduling * \param[in] dst Buffer to copy last SID into * \returns Number of bytes copied + 1 (to accommodate for extra byte with * payload type), 0 if there's nothing to copy */ uint8_t repeat_last_sid(struct gsm_lchan *lchan, uint8_t *dst, uint32_t fn) { /* FIXME: add EFR support */ if (lchan->tch_mode == GSM48_CMODE_SPEECH_EFR) return 0; if (lchan->tch_mode != GSM48_CMODE_SPEECH_AMR) { if (dtx_sched_optional(lchan, fn)) return 0; } else if (dtx_amr_sid_optional(lchan, fn)) return 0; if (lchan->tch.dtx.len) { if (dtx_dl_amr_enabled(lchan)) { if ((lchan->type == GSM_LCHAN_TCH_H && lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F2) || (lchan->type == GSM_LCHAN_TCH_F && lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1)) { /* advance FSM in case we've just sent SID FIRST to restore silence after FACCH interruption */ osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, E_SID_U, (void *)lchan); dtx_sti_unset(lchan); } else if (lchan->tch.dtx.dl_amr_fsm->state == ST_SID_U) { /* enforce SID UPDATE for next repetition: it might have been altered by FACCH handling */ dtx_sti_set(lchan); lchan->tch.dtx.is_update = true; } } memcpy(dst, lchan->tch.dtx.cache, lchan->tch.dtx.len); lchan->tch.dtx.fn = fn; return lchan->tch.dtx.len + 1; } LOGP(DL1C, LOGL_DEBUG, "Have to send %s frame on TCH but SID buffer " "is empty - sent nothing\n", get_value_string(gsm48_chan_mode_names, lchan->tch_mode)); return 0; }
/*! \brief Send a given xUA message via a given M3UA Application Server * \param[in] as Application Server through which to send \ref xua * \param[in] xua xUA message to be sent * \return 0 on success; negative on error */ int m3ua_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua) { struct msgb *msg; int rc; OSMO_ASSERT(as->cfg.proto == OSMO_SS7_ASP_PROT_M3UA); /* Add RC for this AS */ if (as->cfg.routing_key.context) xua_msg_add_u32(xua, M3UA_IEI_ROUTE_CTX, as->cfg.routing_key.context); msg = m3ua_to_msg(xua); if (!msg) return -1; /* send the msg to the AS for transmission. The AS FSM might * (depending on its state) enqueue it before trnsmission */ rc = osmo_fsm_inst_dispatch(as->fi, XUA_AS_E_TRANSFER_REQ, msg); if (rc < 0) msgb_free(msg); return rc; }
/*! \brief allocate a new instance of a specified FSM as child of * other FSM instance * * This is like \ref osmo_fsm_inst_alloc but using the parent FSM as * talloc context, and inheriting the log level of the parent. * * \param[in] fsm Descriptor of the to-be-allocated FSM * \param[in] parent Parent FSM instance * \param[in] parent_term_event Event to be sent to parent when terminating * \returns newly-allocated, initialized and registered FSM instance */ struct osmo_fsm_inst *osmo_fsm_inst_alloc_child(struct osmo_fsm *fsm, struct osmo_fsm_inst *parent, uint32_t parent_term_event) { struct osmo_fsm_inst *fi; fi = osmo_fsm_inst_alloc(fsm, parent, NULL, parent->log_level, parent->id); if (!fi) { /* indicate immediate termination to caller */ osmo_fsm_inst_dispatch(parent, parent_term_event, NULL); return NULL; } LOGPFSM(fi, "is child of %s\n", osmo_fsm_inst_name(parent)); fi->proc.parent = parent; fi->proc.parent_term_event = parent_term_event; llist_add(&fi->proc.child, &parent->proc.children); return fi; }
/*! \brief Send signal to FSM: with proper check if DIX is enabled for this lchan * \param[in] lchan Logical channel on which we check scheduling * \param[in] e DTX DL AMR FSM Event */ void dtx_dispatch(struct gsm_lchan *lchan, enum dtx_dl_amr_fsm_events e) { if (dtx_dl_amr_enabled(lchan)) osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, e, (void *)lchan); }
/*! \brief Check current state of DTX DL AMR FSM and dispatch necessary events * \param[in] lchan Logical channel on which we check scheduling * \param[in] rtp_pl buffer with RTP data * \param[in] rtp_pl_len length of rtp_pl * \param[in] fn Frame Number for which we check scheduling * \param[in] l1_payload buffer where CMR and CMI prefix should be added * \param[in] marker RTP Marker bit * \param[out] len Length of expected L1 payload * \param[out] ft_out Frame Type to be populated after decoding * \returns 0 in case of success; negative on error */ int dtx_dl_amr_fsm_step(struct gsm_lchan *lchan, const uint8_t *rtp_pl, size_t rtp_pl_len, uint32_t fn, uint8_t *l1_payload, bool marker, uint8_t *len, uint8_t *ft_out) { uint8_t cmr; enum osmo_amr_type ft; enum osmo_amr_quality bfi; int8_t sti, cmi; int rc; if (dtx_dl_amr_enabled(lchan)) { if (lchan->type == GSM_LCHAN_TCH_H && lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F2 && !rtp_pl) { *len = 3; /* SID-FIRST P1 -> P2 completion */ memcpy(l1_payload, lchan->tch.dtx.cache, 2); dtx_dispatch(lchan, E_SID_U); return 0; } } if (!rtp_pl_len) return -EBADMSG; rc = osmo_amr_rtp_dec(rtp_pl, rtp_pl_len, &cmr, &cmi, &ft, &bfi, &sti); if (rc < 0) { LOGP(DRTP, LOGL_ERROR, "failed to decode AMR RTP (length %zu)\n", rtp_pl_len); return rc; } /* only needed for old sysmo firmware: */ *ft_out = ft; /* CMI in downlink tells the L1 encoder which encoding function * it will use, so we have to use the frame type */ if (osmo_amr_is_speech(ft)) cmi = ft; /* populate L1 payload with CMR/CMI - might be ignored by caller: */ amr_set_mode_pref(l1_payload, &lchan->tch.amr_mr, cmi, cmr); /* populate DTX cache with CMR/CMI - overwrite cache which will be either updated or invalidated by caller anyway: */ amr_set_mode_pref(lchan->tch.dtx.cache, &lchan->tch.amr_mr, cmi, cmr); *len = 3 + rtp_pl_len; /* DTX DL is not enabled, move along */ if (!lchan->ts->trx->bts->dtxd) return 0; if (osmo_amr_is_speech(ft)) { /* AMR HR - Inhibition */ if (lchan->type == GSM_LCHAN_TCH_H && marker && lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1) return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, E_INHIB, (void *)lchan); /* AMR FR & HR - generic */ if (marker && (lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1 || lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F2 || lchan->tch.dtx.dl_amr_fsm->state == ST_SID_U )) return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, E_ONSET, (void *)lchan); return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, E_VOICE, (void *)lchan); } if (ft == AMR_SID) { if (lchan->tch.dtx.dl_amr_fsm->state == ST_VOICE) { /* SID FIRST/UPDATE scheduling logic relies on SID FIRST being sent first hence we have to force caching of SID as FIRST regardless of actually decoded type */ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, false); return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, E_SID_F, (void *)lchan); } else dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, sti); return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, sti ? E_SID_U : E_SID_F, (void *)lchan); } if (ft != AMR_NO_DATA) { LOGP(DRTP, LOGL_ERROR, "unsupported AMR FT 0x%02x\n", ft); return -ENOTSUP; } if (marker) osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, E_VOICE, (void *)lchan); *len = 0; return 0; }