doc: charts: illustrate new plan for ts and lchans

Add lchan and timeslot FSM charts to illustrate planning of how osmo-bsc should
handle lchan assignment and release.

Modify assignment, handover, lchan-release charts according to the new plan.

Change-Id: I18d60de5ee932c962aad0a532965a55d570bb936
This commit is contained in:
Neels Hofmeyr 2018-05-28 14:29:49 +02:00 committed by Harald Welte
parent c4bb31dcad
commit 44fcc9f11e
12 changed files with 1098 additions and 466 deletions

View File

@ -7,11 +7,24 @@ msc: \
$(builddir)/assignment.png \
$(builddir)/lchan-release.png \
$(builddir)/ms-channel-request.png \
$(builddir)/timeslot.png \
$(builddir)/lchan.png \
$(builddir)/ts-and-lchan-fsm-lifecycle.png \
$(builddir)/handover-inter-bsc-mo.png \
$(builddir)/handover-inter-bsc-mt.png \
$(NULL)
dot: \
$(builddir)/timeslot-fsm.png \
$(builddir)/lchan-fsm.png \
$(NULL)
$(builddir)/%.png: $(srcdir)/%.msc
mscgen -T png -o $@ $<
$(builddir)/%.png: $(srcdir)/%.dot
dot -Tpng $< > $@
.PHONY: poll
poll:
while true; do $(MAKE) msc; sleep 1; done
while true; do $(MAKE) msc dot; sleep 1; done

View File

@ -1,190 +1,126 @@
msc {
hscale=3;
ms [label="MS"], bts [label="BTS"], bsc[label="BSC"], bsc_gscon[label="BSC conn FSM"], bsc_mgcp[label="BSC mgcp FSM"], mgw[label="MGW"], msc_[label="MSC"];
ms [label="MS/BTS"], bsc_lchan[label="BSC lchan FSM"],
bsc_gscon[label="BSC conn FSM"], bsc_mgcp[label="BSC mgcp FSM"], mgw_msc[label="MGW/MSC"];
ms note msc_ [label="lchan allocation sequence for BSSMAP Assignment Request"];
ms note mgw_msc [label="lchan allocation sequence for BSSMAP Assignment Request"];
bsc <= msc_ [label="BSSMAP Assignment Request"];
bsc box bsc [label="bssmap_handle_assignm_req()"];
bsc -> bsc_gscon [label="GSCON_EV_A_ASSIGNMENT_CMD"];
bsc_gscon <= mgw_msc [label="BSSMAP Assignment Request"];
bsc_gscon abox bsc_gscon [label="ST_ASSIGNMENT_\nWAIT_LCHAN"];
--- [label="is the chan_mode a speech mode?"];
bsc_lchan <- bsc_gscon [label="lchan_select_by_chan_mode(chan_mode)"];
|||;
--- [label="IF returned lchan is NULL"];
bsc_gscon => mgw_msc [label="BSSMAP Assignment Failure"];
bsc_gscon abox bsc_gscon [label="ST_ACTIVE"];
---;
|||;
bsc_gscon box bsc_gscon [label="store lchan pointer in conn->lchan_for_assignment"];
bsc_lchan <- bsc_gscon [label="lchan_activate(FOR_ASSIGNMENT)"];
...;
|||;
--- [label="on lchan FSM error or timeout"];
bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ALLOC_ERROR"];
bsc_gscon box bsc_gscon [label="'forget' all about conn->lchan_for_assignment"];
bsc_gscon => mgw_msc [label="BSSMAP Assignment Failure"];
bsc_gscon abox bsc_gscon [label="ST_ACTIVE"];
--- [label="END: 'on error'"];
...;
...;
bsc_gscon abox bsc_gscon [label="ST_WAIT_CRCX_BTS (MGCP_MGW_TIMEOUT = 4s)"];
--- [label="IF lchan FSM decides that it is an lchan for speech"];
bsc_lchan -> bsc_gscon [label="GSCON_EV_ENSURE_MGW_ENDPOINT"];
--- [label="IF there is an MGW endpoint for the BTS already (conn->user_plane.fi_bts)"];
bsc_gscon -> bsc_lchan [label="LCHAN_EV_MGW_ENDPOINT_AVAILABLE"];
--- [label="ELSE: no MGW endpoint for the BTS side yet"];
bsc_gscon abox bsc_gscon [label="ST_ASSIGNMENT_\nWAIT_CRCX_BTS"];
bsc_gscon box bsc_gscon [label="assignment_created_mgw_endpoint = true"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_create()"];
bsc_mgcp => mgw [label="CRCX (for BTS)"];
bsc_mgcp abox bsc_mgcp [label="ST_CRCX (MGCP_MGW_TIMEOUT = 4s)"];
bsc_gscon note bsc_mgcp [label="two timeouts running in parallel"];
bsc_gscon note bsc_mgcp [label="note: #define MGCP_MGW_TIMEOUT exists twice,
once in libosmo-mgcp-client,
once in bsc_subscr_conn_fsm.c"];
bsc_mgcp -> bsc_gscon [label="mgcp_conn_create() exits"];
bsc_gscon -> bsc [label="bssmap_handle_assignm_req() exits"];
bsc_mgcp abox bsc_mgcp [label="ST_CRCX_RESP (MGCP_MGW_TIMEOUT = 4s)"];
bsc_mgcp => mgw_msc [label="CRCX (for BTS)"];
bsc_gscon note bsc_mgcp [label="conn FSM relies on mgcp FSM timeout"];
...;
--- [label="On Timeout"];
bsc_gscon note bsc_gscon [label="The conn FSM likely timeouts first"];
bsc_gscon => msc_ [label="BSSMAP Assignment Failure"];
bsc_gscon abox bsc_gscon [label="ST_ACTIVE"];
bsc_mgcp note bsc_mgcp [label="The MGCP FSM will timeout right after that, and terminate itself,
emitting the parent_term event set upon mgcp_conn_create():"];
bsc_mgcp note bsc_mgcp [label="On timeouit, the MGCP FSM will terminate, emitting the parent_term
event set upon mgcp_conn_create():"];
bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_FAIL_BTS"];
bsc_gscon note bsc_gscon [label="GSCON_EV_MGW_FAIL_BTS is handled by the conn FSM allstate
handler. It sets conn->user_plane.fi_bts = NULL. There is code
that would emit a BSSMAP Assignment Failure, but not in
ST_ACTIVE"];
--- [label="end: 'On Timeout'"];
handler. It sets conn->user_plane.fi_bts = NULL."];
bsc_gscon -> bsc_lchan [label="LCHAN_EV_MGW_ENDPOINT_ERROR"];
bsc_lchan note bsc_gscon [label="conn FSM timeout handler exits and relies on the lchan FSM
signalling error, which should actually happen immediately:"];
bsc_gscon <- bsc_lchan [label="GSCON_EV_LCHAN_ALLOC_ERROR"];
bsc_gscon abox bsc_gscon [label="ST_ACTIVE"];
bsc_gscon box bsc_gscon [label="'forget' all about conn->lchan_for_assignment"];
bsc_gscon => mgw_msc [label="BSSMAP Assignment Failure"];
--- [label="END: 'On Timeout'"];
...;
bsc_mgcp <= mgw [label="CRCX OK (for BTS)"];
bsc_mgcp <= mgw_msc [label="CRCX OK (for BTS)"];
bsc_mgcp box bsc_mgcp [label="libosmo-mgcp-client fsm_crcx_resp_cb()"];
bsc_mgcp abox bsc_mgcp [label="ST_READY"];
bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_CRCX_RESP_BTS"];
--- [label="end: 'is the chan_mode a speech mode?'"];
bsc_gscon note bsc_gscon [label="for mode=sign, we're still handling GSCON_EV_A_ASSIGNMENT_CMD;
for speech mode, we're handling GSCON_EV_MGW_CRCX_RESP_BTS"];
bsc <- bsc_gscon [label="gsm0808_assign_req()"];
bsc box bsc [label="lchan_alloc(): pick available lchan"];
bsc box bsc [label="rsl_chan_activate_lchan()"];
--- [label="is the chosen lchan on dynamic timeslot that is currently used as PDCH?"];
bts <= bsc [label="i) RSL RF Chan Release of PDCH (Osmocom dyn TS)"];
bts <= bsc [label="OR ii) RSL PDCH Deact (ip.access dyn TS)"];
bsc -> bsc_gscon [label="gsm0808_assign_req() returns early"];
bsc_gscon abox bsc_gscon [label="ST_WAIT_ASS_COMPL (GSM0808_T10_VALUE=6s)"];
bsc_gscon abox bsc_gscon [label="ST_ASSIGNMENT_\nWAIT_LCHAN"];
bsc_gscon -> bsc_lchan [label="LCHAN_EV_MGW_ENDPOINT_AVAILABLE"];
--- [label="END: lchan FSM decides that it is an lchan for speech"];
...;
bts note bsc_gscon [linecolor="red",
label="Osmocom style dyn TS use lchan->act_timer to watch over RF Chan Release, but there
seems to be no timer watching over PDCH Deact!"];
...;
bts => bsc [label="i) RSL RF Chan Release ACK (Osmocom dyn TS)"];
bts => bsc [label="OR ii) RSL PDCH Deact ACK (ip.access dyn TS)"];
bsc box bsc [label="rsl_chan_activate_lchan() re-invoked"];
bsc box bsc [label="lchan->state = LCHAN_S_ACT_REQ"];
bts <= bsc [label="RSL Chan Activ"];
--- [label="else (no dyn TS switchover)"];
bsc box bsc [label="lchan->state = LCHAN_S_ACT_REQ"];
bts <= bsc [label="RSL Chan Activ"];
bsc -> bsc_gscon [label="gsm0808_assign_req() returns"];
bsc_gscon abox bsc_gscon [label="ST_WAIT_ASS_COMPL (GSM0808_T10_VALUE=6s)"];
---;
bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ACTIVE"];
bsc_gscon abox bsc_gscon [label="ST_ASSIGNMENT_\nWAIT_COMPLETE\nT10, 6s"];
ms <= bsc_gscon [label="RR Assignment"];
...;
--- [label="On Timeout"];
bsc_gscon => msc_ [label="BSSMAP Assignment Failure"];
bsc_gscon => mgw_msc [label="BSSMAP Assignment Failure"];
bsc_gscon -> bsc_lchan [label="LCHAN_EV_LCHAN_RELEASE"];
bsc_gscon box bsc_gscon [label="'forget' all about conn->lchan_for_assignment"];
--- [label="IF assignment_created_mgw_endpoint == true"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"];
bsc_gscon note bsc_mgcp [label="If the MGW endpoint didn't exist before the Assignment, release
it now. If there was one before this, it is probably still in use by a previous lchan, so
keep it in place."];
bsc_gscon abox bsc_gscon [label="ST_ACTIVE"];
bsc_gscon note bsc_mgcp [linecolor="red",
label="The mgcp FSM from CRCX above apparently lacks a cleanup action for this case.
It should be cleaned up eventually when the conn is torn down, but we should
release RTP endpoints as soon as possible."];
--- [label="end: 'On Timeout'"];
--- [label="END: 'On Timeout'"];
...;
bts => bsc [label="RSL Chan Activ ACK"];
bsc box bsc [label="rsl_rx_chan_act_ack()"];
bsc box bsc [label="Stop lchan->act_timer"];
bsc box bsc [label="lchan->state = LCHAN_S_ACTIVE"];
bsc -> bsc [label="S_LCHAN_ACTIVATE_ACK"];
bsc box bsc [label="bsc_api.c handle_chan_ack()"];
ms <= bsc [label="RR Assignment Command"];
...;
ms note bsc_gscon [label="We rely on the overall conn FSM ST_WAIT_ASS_COMPL timeout."];
...;
ms => bsc [label="RR Assignment Complete"];
bsc box bsc [label="handle_ass_compl()"];
--- [label="Release old lchan"];
bsc box bsc [label="_lchan_handle_release(sacch_deact=0)"];
bsc box bsc [label="rsl_release_sapis_from(start=1)"];
bts <= bsc [label="RSL Release Request (Local End)..."];
bts <= bsc [label="...for each SAPI except link_id=0"];
bsc box bsc [label="rsl_release_request(link_id=0)"];
bts <= bsc [label="RSL Release Request (Local End) for link_id=0"];
bsc box bsc [label="_lchan_handle_release() returns here, the remaining release is asynchronous;
see `End: 'Release old lchan'` below."];
...;
bts note bsc_gscon [linecolor="red",
label="There seems to be no timer watching over RSL Release Request!"];
...;
bts => bsc [label="RSL Release Confirm..."];
bts => bsc [label="...for each SAPI and link_id=0"];
bsc abox bsc [label="start T3111"];
...;
bsc box bsc [label="T3111 expires"];
bsc abox bsc [label="Start lchan->act_timer with lchan_deact_tmr_cb"];
bts <= bsc [label="RSL RF Channel Release"];
...;
--- [label="On timeout"];
bsc box bsc [label="lchan_deact_tmr_cb()"];
bsc box bsc [label="rsl_lchan_mark_broken(): state=LCHAN_S_BROKEN"];
bsc box bsc [label="lchan_free()"];
bsc -> bsc [label="S_LCHAN_UNEXPECTED_RELEASE"];
bsc box bsc [label="bsc_api.c handle_release()"];
bsc box bsc [label="bsc->assign_fail()"];
bsc -> bsc_gscon [label="GSCON_EV_RR_ASS_FAIL"];
bsc note bsc_gscon [linecolor="orange",
label="The name 'RR_ASS_FAIL' might suggest the event means an actual RR Assignment
Failure message being received. Maybe this should be called GSCON_EV_ASSIGNMENT_ERROR."];
...;
bsc box bsc [label="bsc->clear_request()"];
bsc box bsc [label="bsc_clear_request encodes a BSSMAP Clear Request message and passes it on
to the conn FSM as data argument via:"];
bsc -> bsc_gscon [label="GSCON_EV_TX_SCCP"];
bsc_gscon => msc_ [label="BSSMAP Clear Request"];
bsc note bsc_gscon [linecolor="red",
label="Instead of sending an arbitrary message, the conn FSM should
be explicitly instructed to clear the connection, to be able
to notice if the MSC failed to respond to the Clear Request.
Currently, this relies on the MSC responding with a Clear
Command, hopefully, some time later."];
--- [label="End: 'On timeout'"];
...;
bts => bsc [label="RSL RF Channel Release Ack"];
bsc box bsc [label="Stop lchan->act_timer"];
bsc box bsc [label="Stop lchan->T3111"];
--- [label="End: 'Release old lchan'"];
bsc box bsc [label="still in handle_ass_compl()"];
bsc note bsc [label="officially take over new lchan: conn->lchan = conn->secondary_lchan"];
--- [label="is BTS using IPA Abis? (osmo-bts, ip.access)"];
bts <= bsc [label="IPACC CRCX"];
---;
bsc -> bsc [label="handle_ass_compl() calls bsc_api->assign_compl()"];
--- [label="is BTS using IPA Abis? (osmo-bts, ip.access) && conn->user_plane.rtp_ip"];
bsc box bsc [label="bsc_assign_compl()"];
bsc note bsc [label="set ass_compl.valid = true,
postponing GSCON_EV_RR_ASS_COMPL until after the
IPACC MDCX ACK received in osmo_bsc_audio.c"];
bsc box bsc [label="exit early: bsc_assign_compl()"];
bsc box bsc [label="exit early: handle_ass_compl()"];
bsc box bsc [label="osmo_bsc_audio.c"];
bts => bsc [label="IPACC CRCX ACK"];
bts <= bsc [label="IPACC MDCX"];
bts => bsc [label="IPACC MDCX ACK"];
bsc box bsc [label="handle_abisip_signal()"];
bsc -> bsc_gscon [label="GSCON_EV_RR_ASS_COMPL"];
--- [label="else"];
bsc box bsc [label="bsc_assign_compl()"];
bsc -> bsc_gscon [label="GSCON_EV_RR_ASS_COMPL"];
--- ;
--- [label="is chan_mode a speech mode?"];
ms => bsc_gscon [label="RR Assignment Complete"];
bsc_gscon -> bsc_lchan [label="OLD lchan: LCHAN_EV_LCHAN_RELEASE"];
bsc_gscon box bsc_gscon [label="conn->lchan = conn->lchan_for_assignment"];
--- [label="IF: chan_mode a speech mode?"];
bsc_gscon abox bsc_gscon [label="ST_WAIT_MDCX_BTS"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_modify()"];
bsc_mgcp note bsc_mgcp [label="same mgcp FSM as above, for BTS side"];
bsc_mgcp => mgw [label="MDCX (for BTS)"];
bsc_mgcp <= mgw [label="MDCX OK"];
bsc_mgcp abox bsc_mgcp [label="ST_MDCX_RESP"];
bsc_mgcp => mgw_msc [label="MDCX (for BTS)"];
...;
--- [label="On Timeout"];
bsc_gscon -> bsc_lchan [label="LCHAN_EV_RELEASE"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"];
bsc_gscon => mgw_msc [label="BSSMAP Assignment Failure"];
bsc_gscon abox bsc_gscon [label="ST_WAIT_CLEAR_CMD"];
bsc_gscon => mgw_msc [label="BSSMAP Clear Request"];
--- [label="END: 'On Timeout'"];
...;
bsc_mgcp <= mgw_msc [label="MDCX OK"];
bsc_mgcp abox bsc_mgcp [label="ST_READY"];
bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_MDCX_RESP_BTS"];
bsc_gscon abox bsc_gscon [label="ST_WAIT_CRCX_MSC"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_create()"];
bsc_mgcp note bsc_mgcp [label="second mgcp FSM for MSC side"];
bsc_mgcp => mgw [label="CRCX (for MSC)"];
bsc_mgcp <= mgw [label="CRCX OK (for MSC)"];
bsc_mgcp => mgw_msc [label="CRCX (for MSC)"];
...;
--- [label="On Timeout"];
bsc_gscon -> bsc_lchan [label="LCHAN_EV_RELEASE"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"];
bsc_gscon => mgw_msc [label="BSSMAP Assignment Failure"];
bsc_gscon abox bsc_gscon [label="ST_WAIT_CLEAR_CMD"];
bsc_gscon => mgw_msc [label="BSSMAP Clear Request"];
--- [label="END: 'On Timeout'"];
...;
bsc_mgcp <= mgw_msc [label="CRCX OK (for MSC)"];
bsc_gscon <- bsc_mgcp [label="GSCON_EV_MGW_CRCX_RESP_MSC"];
---;
--- [label="END: chan_mode a speech mode?"];
bsc_gscon => msc_ [label="BSSMAP Assignment Complete"];
bsc_gscon => mgw_msc [label="BSSMAP Assignment Complete"];
bsc_gscon abox bsc_gscon [label="ST_ACTIVE"];
}

View File

@ -0,0 +1,37 @@
msc {
hscale=2;
ms [label="MS via BTS"], bsc_lchan[label="BSC lchan FSM"], bsc_gscon[label="BSC conn FSM"],
msc_[label="MSC"];
ms note msc_ [label="inter-BSC Handover to another BSS"];
bsc_gscon abox bsc_gscon [label="ST_ACTIVE"];
bsc_gscon box bsc_gscon [label="bsc_handover_start(): init conn->ho"];
bsc_gscon -> bsc_gscon [label="GSCON_EV_HO_START (inter-BSC MO)"];
bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MO_\nWAIT_HO_CMD\nT7"];
bsc_gscon => msc_ [label="BSSMAP Handover Required"];
...;
--- [label="On Timeout"];
ms note bsc_gscon [label="MS happily continues on the old lchan."];
bsc_gscon abox bsc_gscon [label="ST_ACTIVE"];
bsc_gscon box bsc_gscon [label="handover_end(fail)"];
--- [label="END: 'On Timeout'"];
...;
bsc_gscon <= msc_ [label="BSSMAP Handover Command"];
bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MO_\nWAIT_CLEAR_CMD\nT8"];
ms <= bsc_gscon [label="RR Handover Command"];
...;
--- [label="On Timeout"];
ms note bsc_gscon [label="MS happily continues on the old lchan."];
bsc_gscon abox bsc_gscon [label="ST_ACTIVE"];
bsc_gscon box bsc_gscon [label="handover_end(fail)"];
--- [label="END: 'On Timeout'"];
...;
msc_ note msc_ [label="Remote BSS reported Handover Complete to the MSC, this connection has been
superseded."];
bsc_gscon <= msc_ [label="BSSMAP Clear Command"];
bsc_gscon abox bsc_gscon [label="ST_CLEARING"];
bsc_gscon => msc_ [label="BSSMAP Clear Complete"];
bsc_lchan <- bsc_gscon [label="LCHAN_EV_RELEASE"];
ms <=> bsc_lchan [label="release procedure (async)"];
}

View File

@ -0,0 +1,154 @@
msc {
hscale=3;
ms [label="MS via BTS"], bsc_lchan[label="BSC lchan FSM"], bsc_gscon[label="BSC conn FSM"],
bsc_mgcp[label="BSC mgcp FSM"], mgw_msc[label="MGW/MSC"];
ms note mgw_msc [label="inter-BSC Handover, from remote BSS"];
bsc_gscon <= mgw_msc [label="N-Connect: BSSMAP Handover Request"];
bsc_gscon box bsc_gscon [label="bsc_subscr_con_allocate()"];
bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MT_WAIT_LCHAN"];
bsc_gscon box bsc_gscon [label="lchan_select_by_chan_mode()"];
bsc_lchan <- bsc_gscon [label="lchan_activate(lchan, FOR_HANDOVER)"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_TS_READY"];
...;
--- [label="on no lchan, lchan FSM error or timeout"];
bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ALLOC_ERROR"];
bsc_gscon box bsc_gscon [label="handover_end(fail)"];
bsc_gscon => mgw_msc [label="BSSMAP Handover Failure"];
ms note bsc_gscon [label="MS happily continues on the old lchan."];
--- [label="END: 'on error'"];
...;
--- [label="IF lchan FSM decides that it is an lchan for speech"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_MGW_ENDPOINT_AVAILABLE"];
bsc_lchan -> bsc_gscon [label="GSCON_EV_ENSURE_MGW_ENDPOINT"];
bsc_gscon abox bsc_gscon [label="ST_HANDOVER_\nWAIT_CRCX_BTS"];
bsc_gscon box bsc_gscon [label="handover_created_mgw_endpoint = true"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_create()"];
bsc_mgcp => mgw_msc [label="CRCX (for BTS)"];
bsc_mgcp abox bsc_mgcp [label="ST_CRCX_RESP (MGCP_MGW_TIMEOUT = 4s)"];
bsc_gscon note bsc_mgcp [label="conn FSM relies on mgcp FSM timeout"];
...;
--- [label="On Timeout"];
bsc_mgcp note bsc_mgcp [label="On timeout, the MGCP FSM will terminate, emitting the parent_term
event set upon mgcp_conn_create():"];
bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_FAIL_BTS"];
bsc_gscon note bsc_gscon [label="GSCON_EV_MGW_FAIL_BTS is handled by the conn FSM allstate
handler. It sets conn->user_plane.fi_bts = NULL."];
bsc_gscon -> bsc_lchan [label="LCHAN_EV_MGW_ENDPOINT_ERROR"];
bsc_lchan note bsc_gscon [label="conn FSM error handler exits and relies on the lchan FSM
signalling error, which should actually happen immediately:"];
bsc_gscon <- bsc_lchan [label="GSCON_EV_LCHAN_ALLOC_ERROR"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"];
bsc_gscon box bsc_gscon [label="handover_end(fail)"];
bsc_gscon => mgw_msc [label="BSSMAP Handover Failure"];
ms note bsc_gscon [label="MS happily continues on the old lchan."];
--- [label="END: 'On Timeout'"];
...;
bsc_mgcp <= mgw_msc [label="CRCX OK (for BTS)"];
bsc_mgcp box bsc_mgcp [label="libosmo-mgcp-client fsm_crcx_resp_cb()"];
bsc_mgcp abox bsc_mgcp [label="ST_READY"];
bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_CRCX_RESP_BTS"];
bsc_gscon -> bsc_lchan [label="LCHAN_EV_MGW_ENDPOINT_AVAILABLE"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_ACTIV_ACK"];
bsc_gscon note bsc_gscon [label="MSC-side CRCX needs from Handover Required the RTP IP address
and port of the MSC's MGW; from MGW CRCX ACK (BTS) the conn->user_plane.mgw_endpoint; The
Call-ID, which we actually derive from the SCCP connection ID"];
bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MT_WAIT_CRCX_MSC"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_create()"];
bsc_mgcp note bsc_mgcp [label="second mgcp FSM for MSC side"];
bsc_mgcp => mgw_msc [label="CRCX (for MSC)"];
...;
--- [label="On Timeout"];
bsc_mgcp note bsc_mgcp [label="On timeout, the MGCP FSM will terminate, emitting the parent_term
event set upon mgcp_conn_create():"];
bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_FAIL_MSC"];
bsc_gscon note bsc_gscon [label="GSCON_EV_MGW_FAIL_MSC is handled by the conn FSM allstate
handler. It sets conn->user_plane.fi_msc = NULL."];
bsc_gscon -> bsc_lchan [label="LCHAN_EV_RELEASE"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete() (FSM for BTS)"];
bsc_gscon box bsc_gscon [label="handover_end(fail)"];
bsc_gscon => mgw_msc [label="BSSMAP Handover Failure"];
ms note bsc_gscon [label="MS happily continues on the old lchan."];
--- [label="END: 'On Timeout'"];
...;
bsc_mgcp <= mgw_msc [label="CRCX OK (for MSC)"];
bsc_gscon <- bsc_mgcp [label="GSCON_EV_MGW_CRCX_RESP_MSC"];
bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MT_WAIT_LCHAN"];
--- [label="END: chan_mode a speech mode?"];
--- [label="END: lchan FSM decides that it is an lchan for speech"];
...;
...;
bsc_lchan note bsc_lchan [label="TODO: when does the MS send RLL Establish Ind? I guess like
this:"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_RLL_ESTABLISH"];
ms => bsc_lchan [label="RLL Establish Ind"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_ACTIVE"];
bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ACTIVE"];
bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MT_\nWAIT_HO_ACCEPT\nsanity timer?"];
bsc_gscon box bsc_gscon [label="Compose RR Handover Command"];
bsc_gscon => mgw_msc [label="BSSMAP Handover Request Acknowledge"];
mgw_msc note mgw_msc [label="MSC forwards the RR HO Cmd to the remote BSS"];
...;
--- [label="On timeout"];
bsc_lchan <- bsc_gscon [label="NEW lchan: LCHAN_EV_RELEASE"];
ms <=> bsc_lchan [label="release procedure (async)"];
bsc_gscon box bsc_gscon [label="handover_end(fail)"];
bsc_gscon abox bsc_gscon [label="ST_WAIT_CLEAR_CMD"];
bsc_gscon => mgw_msc [label="BSSMAP Clear Request"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"];
ms note bsc_gscon [label="MS happily continues on the old lchan."];
--- [label="END: On timeout"];
...;
ms => bsc_gscon [label="RR Handover Accept"];
bsc_gscon => mgw_msc [label="BSSMAP Handover Detect"];
mgw_msc note mgw_msc [label="MSC switches call to new path"];
bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MT_WAIT_SABM\nT3105"];
...;
ms => bsc_gscon [label="SABM"];
bsc_lchan note bsc_lchan [label="SABM: Layer 2 message containing ?"];
bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MT_\nWAIT_MDCX_BTS\nT?"];
bsc_lchan note bsc_lchan [label="TODO: what is UA?"];
ms <= bsc_gscon [label="RR UA"];
bsc_gscon note bsc_gscon [label="TODO: at what point do we know the information needed for BTS
MDCX?"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_modify()"];
bsc_mgcp abox bsc_mgcp [label="ST_MDCX_RESP"];
bsc_mgcp => mgw_msc [label="MDCX (for BTS)"];
...;
--- [label="Should the Handover Complete arrive early"];
ms => bsc_gscon [label="RR Handover Complete"];
bsc_gscon box bsc_gscon [label="handover_complete_received = true"];
---;
...;
--- [label="On timeout of the mgcp FSM"];
bsc_gscon note mgw_msc [label="MGCP FSM terminates"];
bsc_gscon <- bsc_mgcp [label="GSCON_EV_MGW_FAIL_BTS"];
bsc_lchan note bsc_gscon [label="The phone has already taken on the new lchan, but now we happen
to not be able to use it. The only sensible thing is to end the conn."];
bsc_gscon abox bsc_gscon [label="ST_WAIT_CLEAR_CMD"];
bsc_gscon => mgw_msc [label="BSSMAP Clear Request\n(Equipment Failure)"];
bsc_lchan <- bsc_gscon [label="NEW lchan: LCHAN_EV_RELEASE"];
ms <=> bsc_lchan [label="release procedure (async)"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"];
--- [label="END: On timeout of the mgcp FSM"];
...;
bsc_mgcp <= mgw_msc [label="MDCX OK"];
bsc_mgcp abox bsc_mgcp [label="ST_READY"];
bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_MDCX_RESP_BTS"];
--- [label="IF !handover_complete_received"];
bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MT_WAIT_HO_COMPL\nT?"];
--- [label="ELSE"];
bsc_gscon -> bsc_gscon [label="gscon_handover_post_complete()"];
---;
...;
ms => bsc_gscon [label="RR Handover Complete"];
bsc_gscon box bsc_gscon [label="gscon_handover_post_complete()"];
bsc_gscon => mgw_msc [label="BSSMAP Handover Complete"];
bsc_gscon note bsc_gscon [label="handover_end(success), conn->ho = NULL"];
bsc_gscon abox bsc_gscon [label="ST_ACTIVE"];
}

View File

@ -1,170 +1,123 @@
# Handover between cells, intra-BSC
msc {
hscale=3;
ms [label="MS"], bts [label="BTS"], bsc[label="BSC"], bsc_gscon[label="BSC conn FSM"], bsc_mgcp[label="BSC mgcp FSM"], mgw[label="MGW"];
ms [label="MS via BTS"], bsc_lchan[label="BSC lchan FSM"], bsc_gscon[label="BSC conn FSM"],
bsc_mgcp[label="BSC mgcp FSM"], mgw_msc[label="MGW/MSC"];
ms note mgw [label="intra-BSC Handover sequence"];
ms note mgw_msc [label="intra-BSC Handover sequence"];
bsc_gscon abox bsc_gscon [label="ST_ACTIVE"];
bsc box bsc [label="bsc_handover_start(): init conn->ho"];
bsc -> bsc_gscon [label="GSCON_EV_HO_START"];
bsc <- bsc_gscon [label="bsc_handover_start_gscon()"];
bsc box bsc [label="lchan_alloc(): pick available lchan"];
bsc box bsc [label="rsl_chan_activate_lchan()"];
--- [label="is the chosen lchan on dynamic timeslot that is currently used as PDCH?"];
bts <= bsc [label="i) RSL RF Chan Release of PDCH (Osmocom dyn TS)"];
bts <= bsc [label="OR ii) RSL PDCH Deact (ip.access dyn TS)"];
bsc -> bsc_gscon [label="bsc_handover_start_gscon() returns early"];
bsc_gscon abox bsc_gscon [label="ST_WAIT_HO_COMPL (no timeout, relies on T3103 below)"];
bsc_gscon box bsc_gscon [label="bsc_handover_start(): init conn->ho"];
bsc_gscon -> bsc_gscon [label="GSCON_EV_HO_START (intra-BSC)"];
bsc_gscon abox bsc_gscon [label="ST_HANDOVER_\nWAIT_LCHAN"];
bsc_lchan <- bsc_gscon [label="lchan_activate(lchan, FOR_HANDOVER)"];
...;
--- [label="on lchan FSM error or timeout"];
bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ALLOC_ERROR"];
bsc_gscon box bsc_gscon [label="handover_end(fail)"];
ms note bsc_gscon [label="MS happily continues on the old lchan."];
bsc_gscon abox bsc_gscon [label="ST_ACTIVE"];
--- [label="END: 'on error'"];
...;
bts note bsc_gscon [linecolor="red",
label="Osmocom style dyn TS use lchan->act_timer to watch over RF Chan Release, but there
seems to be no timer watching over PDCH Deact!"];
...;
bts => bsc [label="i) RSL RF Chan Release ACK (Osmocom dyn TS)"];
bts => bsc [label="OR ii) RSL PDCH Deact ACK (ip.access dyn TS)"];
bsc box bsc [label="rsl_chan_activate_lchan() re-invoked"];
bts <= bsc [label="RSL Chan Activ"];
--- [label="else (no dyn TS switchover)"];
bts <= bsc [label="RSL Chan Activ"];
bsc -> bsc_gscon [label="bsc_handover_start_gscon() returns"];
--- [label="IF lchan FSM decides that it is an lchan for speech"];
bsc_lchan -> bsc_gscon [label="GSCON_EV_ENSURE_MGW_ENDPOINT"];
--- [label="IF there is an MGW endpoint for the BTS already (conn->user_plane.fi_bts)"];
bsc_gscon box bsc_gscon [label="handover_created_mgw_endpoint = false"];
bsc_gscon -> bsc_lchan [label="LCHAN_EV_MGW_ENDPOINT_AVAILABLE"];
--- [label="ELSE: no MGW endpoint for the BTS side yet"];
bsc_gscon abox bsc_gscon [label="ST_HANDOVER_\nWAIT_CRCX_BTS"];
bsc_gscon box bsc_gscon [label="handover_created_mgw_endpoint = true"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_create()"];
bsc_mgcp => mgw_msc [label="CRCX (for BTS)"];
bsc_mgcp abox bsc_mgcp [label="ST_CRCX_RESP (MGCP_MGW_TIMEOUT = 4s)"];
bsc_gscon note bsc_mgcp [label="conn FSM relies on mgcp FSM timeout"];
...;
--- [label="On Timeout"];
bsc_mgcp note bsc_mgcp [label="On timeout, the MGCP FSM will terminate, emitting the parent_term
event set upon mgcp_conn_create():"];
bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_FAIL_BTS"];
bsc_gscon note bsc_gscon [label="GSCON_EV_MGW_FAIL_BTS is handled by the conn FSM allstate
handler. It sets conn->user_plane.fi_bts = NULL."];
bsc_gscon -> bsc_lchan [label="LCHAN_EV_MGW_ENDPOINT_ERROR"];
bsc_lchan note bsc_gscon [label="conn FSM error handler exits and relies on the lchan FSM
signalling error, which should actually happen immediately:"];
bsc_gscon <- bsc_lchan [label="GSCON_EV_LCHAN_ALLOC_ERROR"];
bsc_gscon box bsc_gscon [label="handover_end(fail)"];
--- [label="IF handover_created_mgw_endpoint == true"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"];
---;
bsc_gscon abox bsc_gscon [label="ST_WAIT_HO_COMPL (no timeout, relies on T3103 below)"];
ms note bsc_gscon [label="MS happily continues on the old lchan."];
bsc_gscon abox bsc_gscon [label="ST_ACTIVE"];
--- [label="END: 'On Timeout'"];
...;
bsc_mgcp <= mgw_msc [label="CRCX OK (for BTS)"];
bsc_mgcp box bsc_mgcp [label="libosmo-mgcp-client fsm_crcx_resp_cb()"];
bsc_mgcp abox bsc_mgcp [label="ST_READY"];
bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_CRCX_RESP_BTS"];
bsc_gscon abox bsc_gscon [label="ST_HANDOVER_\nWAIT_LCHAN"];
bsc_gscon -> bsc_lchan [label="LCHAN_EV_MGW_ENDPOINT_AVAILABLE"];
--- [label="END: lchan FSM decides that it is an lchan for speech"];
...;
bts note bsc_gscon [linecolor="red",
label="There seems to be no timer watching out for RSL Chan Activ ACK/NACK!"];
...;
bts => bsc [label="RSL Chan Activ ACK"];
bsc box bsc [label="rsl_rx_chan_act_ack()"];
bsc box bsc [label="Stop lchan->act_timer"];
bsc box bsc [label="lchan->state = LCHAN_S_ACTIVE"];
bsc -> bsc [label="S_LCHAN_ACTIVATE_ACK"];
bsc box bsc [label="handover_logic.c ho_logic_sig_cb()"];
bsc box bsc [label="ho_chan_activ_ack()"];
bsc note bsc [label="gsm48_send_ho_cmd()"];
ms <= bsc [label="RR Handover Command"];
bsc abox bsc [label="start T3103"];
--- [label="is BTS using IPA Abis? (osmo-bts, ip.access)"];
bts <= bsc [label="IPACC CRCX"];
bsc -> bsc [label="ho_chan_activ_ack() returns"];
bts note bsc [linecolor="red",
label="There seems to be no timer watching over IPACC CRCX ACK/NACK!
If no response is received, we simply ignore that fact and carry on as if
everything was fine."];
...;
bts note bsc [label="The IPACC CRCX and MDCX ACKs may come back at any time:
before or after the Handover Detect, before or after Handover Complete."];
bts note bsc_mgcp [linecolor="red",
label="The CRCX ACK contains vital information for routing the RTP stream.
If the CRCX ACK were very slow, we would not know which RTP/RTPC ports
to point the MGW at, below at mgcp_conn_modify()!
Even though this being unrealistic, we must make sure to receive a CRCX ACK."];
...;
bsc box bsc [label="osmo_bsc_audio.c"];
bts => bsc [label="IPACC CRCX ACK"];
bts <= bsc [label="IPACC MDCX"];
...;
bts note bsc [linecolor="red",
label="There seems to be no timer watching over IPACC MDCX ACK/NACK!
If no response is received, we simply ignore that fact and carry on as if
everything was fine."];
...;
bts => bsc [label="IPACC MDCX ACK"];
bts note bsc [label="IPACC MDCX ACK triggers no events or actions"];
---;
...;
ms => bsc [label="RR Handover Detect"];
bsc -> bsc [label="S_LCHAN_HANDOVER_DETECT"];
bsc box bsc [label="ho_rsl_detect(): no action, only logging"];
bsc note bsc_gscon [label="Handover Detect triggers no events or actions"];
bsc note bsc_gscon [linecolor="red",
label="upon Handover Detect, we should already start re-routing the RTP!
Instead we wait for Handover Complete."];
bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ACTIVE"];
bsc_gscon abox bsc_gscon [label="ST_HANDOVER_\nWAIT_DETECT\nT3103"];
bsc_gscon box bsc_gscon [label="gsm48_send_ho_cmd()"];
ms <= bsc_gscon [label="RR Handover Command"];
...;
ms => bsc [label="RR Handover Complete"];
bsc -> bsc [label="S_LCHAN_HANDOVER_COMPL"];
bsc box bsc [label="handover_logic.c ho_logic_sig_cb()"];
bsc box bsc [label="ho_gsm48_ho_compl()"];
bsc box bsc [label="stop T3103"];
bts note bsc_gscon [label="If anything goes wrong from this point on, we will not move back
to the old lchan: would be pointless after Handover Complete."];
bsc note bsc [label="officially take over new lchan: conn->lchan = ho->new_lchan"];
--- [label="Release old lchan"];
bsc box bsc [label="_lchan_handle_release(sacch_deact=0)"];
bsc box bsc [label="rsl_release_sapis_from(start=1)"];
bts <= bsc [label="RSL Release Request (Local End)..."];
bts <= bsc [label="...for each SAPI except link_id=0"];
bsc box bsc [label="rsl_release_request(link_id=0)"];
bts <= bsc [label="RSL Release Request (Local End) for link_id=0"];
bsc box bsc [label="_lchan_handle_release() returns here, the remaining release is asynchronous;
see `End: 'Release old lchan'` below."];
...;
bts note bsc_gscon [linecolor="red",
label="There seems to be no timer watching over RSL Release Request!"];
...;
bts => bsc [label="RSL Release Confirm..."];
bts => bsc [label="...for each SAPI and link_id=0"];
bsc abox bsc [label="start T3111"];
...;
bsc box bsc [label="T3111 expires"];
bsc abox bsc [label="Start lchan->act_timer with lchan_deact_tmr_cb"];
bts <= bsc [label="RSL RF Channel Release"];
...;
--- [label="On timeout"];
bsc box bsc [label="lchan_deact_tmr_cb()"];
bsc box bsc [label="rsl_lchan_mark_broken(): state=LCHAN_S_BROKEN"];
bsc box bsc [label="lchan_free()"];
bsc -> bsc [label="S_LCHAN_UNEXPECTED_RELEASE"];
bsc box bsc [label="bsc_api.c handle_release()"];
bsc box bsc [label="bsc->clear_request()"];
bsc box bsc [label="bsc_clear_request encodes a BSSMAP Clear Request message and passes it on
to the conn FSM as data argument via:"];
bsc -> bsc_gscon [label="GSCON_EV_TX_SCCP"];
bsc_gscon rbox bsc_gscon [label="BSSMAP Clear Request to MSC"];
bsc note bsc_gscon [linecolor="red",
label="During Handover, we actually release the entire conn just because we failed to
gracefully release the old lchan. That is obviously nonsense."];
bsc note bsc [label="Stop T3101 (but was not active in this code path)"];
bsc -> bsc [label="S_CHALLOC_FREED"];
--- [label="End: 'On timeout'"];
...;
bts => bsc [label="RSL RF Channel Release Ack"];
bsc box bsc [label="Stop lchan->act_timer"];
bsc box bsc [label="Stop lchan->T3111"];
bsc_lchan <- bsc_gscon [label="NEW lchan: LCHAN_EV_RELEASE"];
ms <=> bsc_lchan [label="release procedure (async)"];
bsc_gscon box bsc_gscon [label="handover_end(fail)"];
--- [label="IF handover_created_mgw_endpoint == true"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"];
---;
ms note bsc_gscon [label="MS happily continues on the old lchan."];
bsc_gscon abox bsc_gscon [label="ST_ACTIVE"];
--- [label="END: On timeout"];
...;
ms => bsc_gscon [label="RR Handover Detect"];
bsc box bsc [label="still in ho_gsm48_ho_compl()"];
bsc note bsc [label="handover_free(), conn->ho = NULL"];
bsc -> bsc_gscon [label="GSCON_EV_HO_COMPL"];
bsc note bsc_gscon [linecolor="orange",
label="Handover information is cleared before signalling the conn FSM.
That means the conn FSM cannot possibly log sensible information about exactly
which Handover has just completed."];
bsc_gscon abox bsc_gscon [label="ST_WAIT_MDCX_BTS_HO
(MGCP_MGW_TIMEOUT=4s with MGCP_MGW_HO_TIMEOUT_TIMER_NR)"];
bsc_gscon abox bsc_gscon [label="ST_HANDOVER_\nWAIT_MDCX_BTS\ncontinue T3103"];
bsc_gscon -> bsc_lchan [label="OLD lchan: LCHAN_EV_RELEASE"];
ms <=> bsc_lchan [label="release procedure (async)"];
bsc_lchan note bsc_gscon [label="officially take over new lchan: conn->lchan = ho->new_lchan"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_modify()"];
bsc_mgcp note bsc_mgcp [label="mgcp FSM that was established for old lchan, for BTS side"];
bsc_mgcp => mgw [label="MDCX (for BTS)"];
bsc_mgcp abox bsc_mgcp [label="ST_MDCX_RESP"];
bsc_mgcp => mgw_msc [label="MDCX (for BTS)"];
...;
bsc_gscon note mgw [
label="If we get no MDCX ACK, the MGCP FSM terminates, and emits GSCON_EV_MGW_FAIL_BTS.
Besides invalidating the MGCP FSM pointer, this event has no
effect in ST_WAIT_MDCX_BTS_HO, and we rely on above conn FSM
timeout instead."];
bsc_gscon note bsc_gscon [linecolor="red",
label="A timeout of ST_WAIT_MDCX_BTS_HO simply transitions back to ST_ACTIVE!
Even though the MGW failed, we carry on as if everything were fine."];
--- [label="Should the Handover Complete arrive early"];
ms => bsc_gscon [label="RR Handover Complete"];
bsc_gscon box bsc_gscon [label="handover_complete_received = true"];
---;
...;
bsc_mgcp <= mgw [label="MDCX OK"];
--- [label="On timeout of the mgcp FSM"];
bsc_gscon note mgw_msc [label="MGCP FSM terminates"];
bsc_gscon <- bsc_mgcp [label="GSCON_EV_MGW_FAIL_BTS"];
bsc_lchan note bsc_gscon [label="The phone has already taken on the new lchan, but now we happen
to not be able to use it. The only sensible thing is to end the conn."];
bsc_gscon abox bsc_gscon [label="ST_WAIT_CLEAR_CMD"];
bsc_gscon => mgw_msc [label="BSSMAP Clear Request\n(Equipment Failure)"];
bsc_lchan <- bsc_gscon [label="NEW lchan: LCHAN_EV_RELEASE"];
ms <=> bsc_lchan [label="release procedure (async)"];
bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"];
...;
bsc_mgcp <= mgw_msc [label="MDCX OK"];
bsc_mgcp abox bsc_mgcp [label="ST_READY"];
bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_MDCX_RESP_BTS"];
--- [label="IF !handover_complete_received"];
bsc_gscon abox bsc_gscon [label="ST_HANDOVER_\nWAIT_COMPLETE\ncontinue T3103"];
--- [label="ELSE"];
bsc_gscon -> bsc_gscon [label="gscon_handover_post_complete()"];
---;
...;
ms => bsc_gscon [label="RR Handover Complete"];
bsc_gscon box bsc_gscon [label="gscon_handover_post_complete()"];
bsc_gscon note bsc_gscon [label="handover_end(success), conn->ho = NULL"];
bsc_gscon abox bsc_gscon [label="ST_ACTIVE"];
}

53
doc/lchan-fsm.dot Normal file
View File

@ -0,0 +1,53 @@
digraph G {
rankdir=TB;
invisible [style="invisible"]
UNUSED [penwidth=3.0]
WAIT_TS_READY
WAIT_MGW_ENDPOINT_AVAILABLE
WAIT_ACTIV_ACK
WAIT_IPACC_CRCX_ACK
WAIT_IPACC_MDCX_ACK
WAIT_RLL_ESTABLISH
ACTIVE [penwidth=3.0]
WAIT_SAPIS_RELEASED
WAIT_BEFORE_RF_RELEASE
WAIT_RF_RELEASE_ACK
WAIT_AFTER_ERROR
BORKEN
ts [label="timeslot FSM",shape=box3d];
gscon [label="conn FSM",shape=box3d];
UNUSED -> WAIT_TS_READY [label="lchan_allocate()"]
WAIT_TS_READY -> WAIT_ACTIV_ACK
WAIT_ACTIV_ACK -> WAIT_RLL_ESTABLISH
WAIT_RLL_ESTABLISH -> WAIT_MGW_ENDPOINT_AVAILABLE [label="TCH"]
WAIT_MGW_ENDPOINT_AVAILABLE -> WAIT_IPACC_CRCX_ACK [label="IPACC BTS"]
WAIT_MGW_ENDPOINT_AVAILABLE -> ACTIVE
WAIT_IPACC_CRCX_ACK -> WAIT_IPACC_MDCX_ACK
WAIT_IPACC_MDCX_ACK -> ACTIVE
WAIT_RLL_ESTABLISH -> ACTIVE [label="non-TCH"];
WAIT_RLL_ESTABLISH -> WAIT_RF_RELEASE_ACK [label="timeout",style=dashed,constraint=false]
ACTIVE -> WAIT_SAPIS_RELEASED [label="LCHAN_EV_\nRELEASE"]
WAIT_SAPIS_RELEASED -> WAIT_BEFORE_RF_RELEASE
WAIT_SAPIS_RELEASED -> WAIT_RF_RELEASE_ACK [label="timeout",style=dashed,constraint=false]
WAIT_BEFORE_RF_RELEASE -> WAIT_RF_RELEASE_ACK [label="T3111"]
WAIT_RF_RELEASE_ACK -> UNUSED
WAIT_RF_RELEASE_ACK -> WAIT_AFTER_ERROR [label="release was\ndue to error"]
WAIT_AFTER_ERROR -> UNUSED [label="T3111+2s"]
WAIT_TS_READY -> ts [label="TS_EV_\nLCHAN_\nREQUESTED",style=dotted,penwidth=3]
UNUSED -> ts [label="TS_EV_\nLCHAN_\nUNUSED",style=dotted,penwidth=3]
ts -> WAIT_TS_READY [label="LCHAN_EV_\nTS_READY",style=dotted]
WAIT_TS_READY -> UNUSED [label="error/timeout",style=dashed,constraint=false]
{WAIT_ACTIV_ACK,WAIT_RF_RELEASE_ACK} -> BORKEN [label="error/timeout",style=dashed]
{WAIT_MGW_ENDPOINT_AVAILABLE,WAIT_IPACC_CRCX_ACK,WAIT_IPACC_MDCX_ACK} -> WAIT_SAPIS_RELEASED [label=error,style=dashed]
WAIT_TS_READY -> gscon [label="GSCON_EV_\nENSURE_\nMGW_ENDPOINT",style=dotted]
gscon -> WAIT_MGW_ENDPOINT_AVAILABLE [label="LCHAN_EV_\nMGW_ENDPOINT_\n{AVAILABLE,ERROR}",style=dotted]
}

View File

@ -1,149 +1,83 @@
msc {
hscale=2;
ms [label="MS"], bts [label="BTS"], bsc[label="BSC"], bsc_gscon[label="BSC conn FSM"];
ms [label="MS"], bts [label="BTS"], bsc[label="BSC"], bsc_lchan[label="BSC lchan FSM"],
bsc_gscon[label="BSC conn FSM"], msc_[label="MSC"];
ms note bsc_gscon [label="various lchan release scenarios"];
ms rbox bsc_gscon [label="IF BSC releases, from BSSMAP Clear Request"];
bsc note bsc [label="lchan_release() may be called with sacch_deact=true or false.
Currently, the only time lchan_release(sacch_deact=true) is invoked is upon BSSMAP Clear
Command, i.e. when the MSC instructs to stop using an active lchan.
Some error handling code paths however directly invoke
rsl_rf_chan_release(error=1, SACCH_DEACTIVATE)."];
---;
bsc_gscon note bsc_gscon [label="Rx: BSSMAP Clear Request from MSC"];
bsc <- bsc_gscon [label="gsm0808_clear()"];
bsc box bsc [label="lchan_release(sacch_deact=1)"];
bsc box bsc [label="lchan->state = LCHAN_S_REL_REQ"];
bsc box bsc [label="_lchan_handle_release(sacch_deact=1)"];
bsc box bsc [label="rsl_release_sapis_from(start=1)"];
bts <= bsc [label="RSL Release Request (Local End)..."];
bts <= bsc [label="...for each SAPI, except link_id=0"];
ms <= bsc [label="RR Channel Release"];
ms note bsc [label="There is no ACK for RR Channel Release"];
bsc box bsc [label="rsl_deact_sacch()"];
bts <= bsc [label="RSL Deactivate SACCH"];
bsc abox bsc [label="Start T3109 (net->T3109, t3109_expired())"];
ms rbox msc_ [label="MSC releases"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_ACTIVE"];
bsc_gscon abox bsc_gscon [label="ST_ACTIVE"];
bsc_gscon <= msc_ [label="BSSMAP Clear Command"];
bsc_gscon abox bsc_gscon [label="ST_CLEARING"];
bsc_gscon => msc_ [label="BSSMAP Clear Complete"];
bsc_gscon -> bsc_lchan [label="LCHAN_EV_RELEASE"];
--- [label="IF SAPIs besides SAPI[0] are active"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nSAPIS_RELEASED\nT3109"];
bts <= bsc_lchan [label="RSL Release Request (Local End)..."];
bts <= bsc_lchan [label="...for each SAPI, except link_id=0"];
ms <= bsc_lchan [label="RR Channel Release"];
bts <= bsc_lchan [label="RSL Deactivate SACCH",ID="if appropriate pchan"];
...;
--- [label="If T3109 expires"];
bsc box bsc [label="t3109_expired()"];
bsc box bsc [label="rsl_rf_chan_release(error=1)"];
bts <= bsc [label="RSL Release Request (Local End)..."];
bts <= bsc [label="...for each SAPI, except link_id=0"];
bsc box bsc [label="lchan->state = LCHAN_S_REL_REQ"];
bts <= bsc [label="RSL RF Channel Release"];
---;
bts => bsc_lchan [label="RSL Release ACKs"];
--- [label="END: SAPIs besides SAPI[0] are active"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nBEFORE_RF_RELEASE\nT3111"];
bsc_lchan -> bsc_gscon [label="GSCON_EV_FORGET_LCHAN"];
bsc_gscon note bsc_gscon [label="has already forgotten the lchan above."];
...;
bsc rbox bsc [label="continue in the 'Common' part"];
--- [label="END: 'BSSMAP Clear Request'"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nRF_RELEASE_ACK\n4s"];
bts <= bsc_lchan [label="RSL RF Channel Release"];
...;
bts => bsc_lchan [label="RSL RF Channel Release ACK"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"];
...;
...;
ms rbox bsc_gscon [label="IF BSC releases, from implicitly unused lchan"];
bsc note bsc [label="The BSC may release old unused lchans after Handover, or release lchans
after some error condition."];
bsc note bsc [label="BSC decides to release an unused lchan"];
bsc box bsc [label="lchan_release(sacch_deact=0)"];
bsc box bsc [label="lchan->state = LCHAN_S_REL_REQ"];
bsc box bsc [label="_lchan_handle_release(sacch_deact=0)"];
bts <= bsc [label="RSL Release Request (Local End)..."];
bts <= bsc [label="...for all SAPIs"];
ms rbox msc_ [label="BSC releases, outside of conn FSM's flow"];
bsc -> bsc_lchan [label="LCHAN_EV_RELEASE"];
--- [label="IF SAPIs besides SAPI[0] are active"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nSAPIS_RELEASED\nT3109"];
bts <= bsc_lchan [label="RSL Release Request (Local End)..."];
bts <= bsc_lchan [label="...for each SAPI, except link_id=0"];
ms <= bsc_lchan [label="RR Channel Release",ID="if conn is present"];
bts <= bsc_lchan [label="RSL Deactivate SACCH",ID="if appropriate pchan"];
...;
bts note bsc_gscon [linecolor="red",
label="There seems to be no timer watching over RSL Release Request!"];
bts => bsc_lchan [label="RSL Release ACKs"];
--- [label="END: SAPIs besides SAPI[0] are active"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nBEFORE_RF_RELEASE\nT3111"];
bsc_lchan -> bsc_gscon [label="GSCON_EV_FORGET_LCHAN"];
bsc_gscon note bsc_gscon [label="conn FSM notices that its primary lchan is gone"];
bsc_gscon => msc_ [label="BSSMAP Clear Request"];
bsc_gscon abox bsc_gscon [label="ST_WAIT_CLEAR_CMD"];
...;
bts => bsc [label="RSL Release Confirm..."];
bts => bsc [label="...for all SAPIs"];
bsc rbox bsc [label="continue in the 'Common' part"];
---;
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nRF_RELEASE_ACK\n4s"];
bts <= bsc_lchan [label="RSL RF Channel Release"];
...;
bts => bsc_lchan [label="RSL RF Channel Release ACK"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"];
...;
bsc_gscon <= msc_ [label="BSSMAP Clear Command"];
bsc_gscon abox bsc_gscon [label="ST_CLEARING"];
bsc_gscon => msc_ [label="BSSMAP Clear Complete"];
...;
...;
ms rbox bsc_gscon [label="IF MS releases"];
ms rbox msc_ [label="MS releases"];
ms => bts [label="DISC"];
bts => bsc [label="RLL Release Ind..."];
bts => bsc [label="...for each SAPI"];
bsc rbox bsc [label="continue in the 'Common' part"];
---;
bts => bsc_lchan [label="RLL Release Ind..."];
bts => bsc_lchan [label="...for each SAPI"];
bsc_lchan note bsc_lchan [label="The lchan FSM notices when all SAPIs have been released"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nBEFORE_RF_RELEASE\nT3111"];
...;
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nRF_RELEASE_ACK\n4s"];
bts <= bsc_lchan [label="RSL RF Channel Release"];
bsc_lchan -> bsc_gscon [label="GSCON_EV_FORGET_LCHAN"];
bsc_gscon note bsc_gscon [label="conn FSM notices that its primary lchan is gone"];
bsc_gscon => msc_ [label="BSSMAP Clear Request"];
bsc_gscon abox bsc_gscon [label="ST_WAIT_CLEAR_CMD"];
...;
ms rbox bsc_gscon [label="Common"];
--- [label="for each SAPI (?)"];
bts => bsc [label="RLL Release Confirm / RLL Release Ind"];
bsc box bsc [label="abis_rsl_rx_rll()"];
bsc box bsc [label="mark lchan->sapis[link_id] = LCHAN_SAPI_UNUSED"];
bsc box bsc [label="rll_indication()"];
bsc box bsc [label="for each bsc_rll_req matching this link_id:
disable timer, call cb(BSC_RLLR_IND_REL_IND)"];
bsc box bsc [label="rsl_handle_release()"];
--- [label="IF all SAPIs are unused"];
bsc box bsc [label="Stop T3109"];
bsc note bsc [label="T3109 was started if the MSC requested the release"];
bsc abox bsc [label="Start T3111 (net->T3111 value, t3111_expired())"];
--- [label="END: all SAPIs are unused"];
bsc -> bsc_gscon [label="GSCON_EV_RLL_REL_IND (only if RLL Release Ind)"];
--- [label="END: for each SAPI"];
bts => bsc_lchan [label="RSL RF Channel Release ACK"];
...;
bsc box bsc [label="T3111 expires"];
bsc box bsc [label="rsl_rf_chan_release()"];
bsc box bsc [label="Stop T3109"];
bsc note bsc [label="[If lchan->state is LCHAN_S_REL_ERR, don't do anything]"];
bsc abox bsc [label="Start lchan->act_timer (4s, lchan_deact_tmr_cb())"];
bts <= bsc [label="RSL RF Channel Release"];
...;
--- [label="IF lchan->act_timer expires"];
bsc box bsc [label="lchan_deact_tmr_cb()"];
bsc box bsc [label="rsl_lchan_mark_broken(): lchan->state = LCHAN_S_BROKEN"];
bsc box bsc [label="lchan_free() (see below)"];
--- [label="END: 'lchan->act_timer expires'"];
...;
bts => bsc [label="RSL RF Channel Release ACK"];
bsc box bsc [label="rsl_rx_rf_chan_rel_ack()"];
bsc box bsc [label="Stop lchan->act_timer"];
bsc box bsc [label="Stop T3111"];
--- [label="IF lchan->state == LCHAN_S_BROKEN"];
bsc note bsc [label="If an ACK comes in late, for specific BTS models, we may choose to 'repair'
the lchan so that it is usable again, by calling do_lchan_free() directly (see below)."];
bsc box bsc [label="rsl_rx_rf_chan_rel_ack() exits here and none of below actions happen.
The lchan remains LCHAN_S_BROKEN indefinitely."];
--- [label="END: lchan->state == LCHAN_S_BROKEN"];
bsc box bsc [label="do_lchan_free()"];
--- [label="IF lchan->state == LCHAN_S_REL_ERR"];
bsc note bsc [label="If release failed, we take the lchan back into operation after due
timeout"];
bsc abox bsc [label="Start lchan->error_timer (T3111+2, error_timeout_cb())"];
bsc note bsc [label="do_lchan_free() continues, async:"];
...;
bsc box bsc [label="error_timeout_cb()"];
bsc box bsc [label="lchan->state = LCHAN_S_NONE"];
bsc box bsc [label="dyn TS: activate PDCH..."];
--- [label="ELSE"];
bsc box bsc [label="lchan->state = LCHAN_S_NONE"];
--- [label="END: lchan->state == LCHAN_S_REL_ERR"];
bsc box bsc [label="lchan_free()"];
--- [label="IF conn is still associated (and not dyn TS in switchover)"];
bsc -> bsc [label="S_LCHAN_UNEXPECTED_RELEASE"];
bsc box bsc [label="handle_release()"];
bsc box bsc [label="Stop T10"];
bsc note bsc [linecolor=orange,label="conn->T10 is actually dead code, it is never started.
Instead, the conn FSM starts ST_WAIT_ASS_COMPL with a T10 value."];
bsc -> bsc_gscon [label="GSCON_EV_RR_ASS_FAIL"];
bsc -> bsc_gscon [label="GSCON_EV_TX_SCCP: BSSMAP Clear Request"];
bsc box bsc [label="bsc_clear_handover()"];
bsc box bsc [label="Stop T3103"];
bsc box bsc [label="free handover struct"];
bsc box bsc [label="lchan->conn = NULL"];
--- [label="END: 'conn is still associated'"];
bsc box bsc [label="Stop T3101"];
bsc note bsc [label="T3101 is started when sending an RR Immediate Assignment"];
bsc -> bsc [label="S_CHALLOC_FREED"];
bsc -> bsc [label="rll_lchan_signal()"];
bsc box bsc [label="for each bsc_rll_req matching this lchan:
disable timer, call cb(BSC_RLLR_IND_REL_IND)"];
bsc_gscon <= msc_ [label="BSSMAP Clear Command"];
bsc_gscon => msc_ [label="BSSMAP Clear Complete"];
}

306
doc/lchan.msc Normal file
View File

@ -0,0 +1,306 @@
msc {
hscale=2;
bts [label="MS/BTS"], bsc[label="BSC"], bsc_ts [label="BSC timeslot FSM"],
bsc_lchan[label="BSC lchan FSM"], bsc_gscon[label="BSC conn FSM"],
mgw_msc[label="MGW/MSC"];
bts box mgw_msc [label="lchan allocation sequence"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"];
bts rbox mgw_msc [label="Channel Request from MS"];
bts => bsc [label="RSL Channel Request"];
bsc box bsc [label="lchan_select_by_type(chan_type)"];
bsc -> bsc_lchan [label="lchan_activate(lchan, FOR_MS_CHANNEL_REQUEST)"];
bsc_lchan rbox bsc_lchan [label="Continue at\nlchan_activate()\n"];
|||;
|||;
bts rbox mgw_msc [label="Channel Request from BSSMAP Assignment"];
bsc_gscon <= mgw_msc [label="BSSMAP Assignment request"];
bsc_gscon box bsc_gscon [label="lchan_select_by_chan_mode(chan_mode)"];
bsc_lchan <- bsc_gscon [label="lchan_activate(lchan, FOR_ASSIGNMENT)"];
bsc_lchan rbox bsc_lchan [label="Continue at\nlchan_activate()\n"];
|||;
|||;
bts rbox mgw_msc [label="Channel Request from Handover Decision"];
bsc note bsc [label="target lchan typically already chosen by Handover Decision"];
bsc -> bsc_gscon [label="GSCON_EV_HO_START (intra-BSC)"];
bsc_lchan <- bsc_gscon [label="lchan_activate(lchan, FOR_HANDOVER)"];
bsc_lchan rbox bsc_lchan [label="Continue at\nlchan_activate()\n"];
|||;
|||;
bts rbox mgw_msc [label="Channel Request from intra-BSC-MT-Handover"];
bsc_gscon <- mgw_msc [label="BSSMAP Handover Request"];
bsc_gscon box bsc_gscon [label="lchan_select_by_chan_mode(chan_mode)"];
bsc box bsc [label="lchan_activate(lchan, FOR_HANDOVER)"];
bsc_lchan rbox bsc_lchan [label="Continue at\nlchan_activate()\n"];
|||;
|||;
bts rbox mgw_msc [label="lchan_activate()"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_\nWAIT_TS_READY\n(timeout: ? s, Tnnnn)"];
|||;
|||;
--- [label="TCH?"];
bsc_lchan note bsc_gscon [label="This is skipped when FOR_MS_CHANNEL_REQUEST. If the MS requests
a TCH lchan, and we end up actually giving it a TCH because no SDCCH are available, we
can not set up an RTP stream because there is not even an L3 conn yet."];
bsc_lchan note bsc_gscon [label="The lchan FSM asks the conn FSM to have an MGW endpoint ready as
early as possible. Either the conn already has such MGW endpoint from a previous lchan,
in which case it immediately replies, or it requests one from the MGW, in which case we
wait for a response in 'TCH? (2)' below."];
bsc_lchan -> bsc_gscon [label="GSCON_EV_ENSURE_MGW_ENDPOINT"];
--- [label="IF conn has user_plane.fi_bts in state ST_READY"];
bsc_lchan <- bsc_gscon [label="LCHAN_EV_MGW_ENDPOINT_AVAILABLE"];
bsc_lchan box bsc_lchan [label="mgw_endpoint_available = true"];
bsc_lchan note bsc_lchan [label="lchan_activate() continues"];
--- [label="ELSE (no MGW endpoint available yet)"];
bsc_gscon => mgw_msc [label="CRCX (for BTS) via mgcp_conn_create()"];
bsc_gscon abox bsc_gscon [label="ST_WAIT_CRCX_BTS\n(timeout: ? s, Tnnnn)"];
bsc_lchan <- bsc_gscon [label="(event dispatch returns)"];
bsc_lchan note bsc_lchan [label="lchan_activate() continues"];
...;
bsc_gscon note bsc_gscon [label="async:"];
bsc_gscon <= mgw_msc [label="CRCX OK (for BTS)"];
bsc_lchan <- bsc_gscon [label="LCHAN_EV_MGW_ENDPOINT_AVAILABLE"];
bsc_lchan box bsc_lchan [label="mgw_endpoint_available = true"];
bsc_lchan note bsc_lchan [label="As soon as we reach LCHAN_ST_WAIT_MGW_ENDPOINT_AVAILABLE, this triggers
immedate action (s.b.), but until then, only the flag gets set to true."];
...;
--- [label="CRCX timeout"];
bsc_gscon note bsc_gscon [label="conn FSM should fire on CRCX timeout"];
bsc_lchan <- bsc_gscon [label="LCHAN_EV_MGW_ENDPOINT_ERROR"];
bsc_gscon note bsc_gscon [label="conn FSM should not assume anything and wait for
GSCON_EV_LCHAN_ALLOC_ERROR"];
bsc_lchan rbox bsc_lchan [label="Do 'On any error'"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"];
bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_UNUSED"];
--- [label="END: 'TCH?'"];
|||;
|||;
bsc_lchan box bsc_lchan [label="lchan_activate() exits"];
bsc_lchan note bsc_lchan [label="still in\nlchan_request()\nLCHAN_ST_WAIT_\nTS_READY"];
bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_REQUESTED"];
...;
--- [label="on error from TS or timeout:"];
bsc_ts -> bsc_lchan [label="LCHAN_EV_TS_ERROR"];
bsc_lchan rbox bsc_lchan [label="Do 'On any error'"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"];
bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_UNUSED"];
---;
...;
bsc_ts abox bsc_ts [label="TS_ST_IN_USE"];
bsc_ts -> bsc_lchan [label="LCHAN_EV_TS_READY"];
bsc_lchan box bsc_lchan [label="lchan_fsm_\npre_lchan_activ()"];
|||;
|||;
bts rbox mgw_msc [label="mode FOR_MS_CHANNEL_REQUEST"];
bts note bsc_lchan [label="This is the simple case where the MS requested a channel, and there is no
L3 conn to the MSC; no matter if this is SDDCH or a TCH channel type, we will not prepare
an RTP stream."];
bsc_lchan note bsc_lchan [label="still in lchan_fsm_\npre_lchan_activ()"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nACTIV_ACK\n(timeout: ? s, Tnnnn)"];
bts <= bsc_lchan [label="RSL Chan Activ (RSL_ACT_INTRA_IMM_ASS)"];
bts note bsc_lchan [label="If any errors occur from now on, we don't want to send an RR Immediate
Assignment Reject anymore."];
bsc_lchan box bsc_lchan [label="sent_chan_activ = true"];
...;
--- [label="on timeout"];
bsc_lchan rbox bsc_lchan [label="Continue at: 'On any error', 'unrecoverable'"];
---;
...;
bts => bsc_lchan [label="RSL Chan Activ ACK"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nRLL_ESTABLISH\nT3101"];
bsc_lchan note bsc_lchan [label="Now the lchan is assigned, but has no L3 conn yet. On errors,
this will either go into graceful release or into broken state, but will not trigger any
events to a (non-existing) conn."];
...;
--- [label="on timeout"];
bts <= bsc_lchan [label="RSL RF Channel Release"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_RF_RELEASE_ACK\n(T?, 4s)"];
---;
...;
bts => bsc_lchan [label="RLL Establish Ind"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_ACTIVE"];
|||;
|||;
bts rbox mgw_msc [label="modes FOR_ASSIGNMENT and FOR_HANDOVER"];
bsc_lchan note bsc_lchan [label="still in lchan_fsm_\npre_lchan_activ()"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nACTIV_ACK\n(timeout: ? s, Tnnnn)"];
bts <= bsc_lchan [label="RSL Chan Activ (RSL_ACT_INTRA_NORM_ASS)",ID=FOR_ASSIGNMENT];
bts <= bsc_lchan [label="RSL Chan Activ (RSL_ACT_INTER_ASYNC)",ID=FOR_HANDOVER];
...;
--- [label="on timeout"];
bsc_lchan rbox bsc_lchan [label="Continue at: 'On any error', 'unrecoverable'"];
---;
bts => bsc_lchan [label="RSL Chan Activ ACK"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nRLL_ESTABLISH\nT3101"];
...;
--- [label="on timeout"];
bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ALLOC_ERROR"];
bsc_lchan -> bsc_lchan [label="lchan_fsm_pre_rf_release()"];
---;
...;
bts => bsc_lchan [label="RLL Establish Indication"];
|||;
--- [label="TCH? (2)"];
--- [label="mgw_endpoint_available == false?"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nMGW_ENDPOINT_\nAVAILABLE"];
bsc_lchan note bsc_lchan [label="rely on conn FSM timeout; apply only a long sanity timeout."];
...;
bsc_gscon <= mgw_msc [label="CRCX OK (for BTS)"];
bsc_lchan <- bsc_gscon [label="LCHAN_EV_MGW_ENDPOINT_AVAILABLE"];
bsc_lchan box bsc_lchan [label="mgw_endpoint_available = true"];
bsc_lchan <- bsc_lchan [label="re-invoke lchan_fsm_pre_lchan_activ()"];
--- [label="END: 'TCH? (2)'"];
|||;
--- [label="is BTS using IPA Abis? (osmo-bts, ip.access)"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nIPACC_CRCX_ACK\n(timeout: ? s, Tnnnn)"];
bts <= bsc_lchan [label="IPACC CRCX"];
...;
--- [label="on timeout"];
bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ALLOC_ERROR"];
bsc_lchan -> bsc_lchan [label="lchan_graceful_release()"];
---;
...;
bts => bsc_lchan [label="IPACC CRCX ACK"];
bts note bsc_lchan [label="The IPACC CRCX ACK tells us what port the IPA Abis based BTS has
assigned to this lchan. AoIP: we need to forward this to the MGW (BTS side) with an MDCX;
SCCPlite: we forward this to the MSC during BSSMAP Assignment Complete (TODO: is this
correct??)"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nIPACC_MDCX_ACK\n(timeout: ? s, Tnnnn)"];
bts <= bsc_lchan [label="IPACC MDCX"];
bts note bsc_lchan [label="The IPACC MDCX tells IPA Abis based BTSes the IP address and RTP port
assigned by the BTS side of the MGW. AoIP: the MGW CRCX (BTS) must thus happen before
this; SCCPlite: the RTP port is already known from the timeslot+multiplex information."];
...;
--- [label="on timeout"];
bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ALLOC_ERROR"];
bsc_lchan -> bsc_lchan [label="lchan_graceful_release()"];
---;
...;
bts => bsc_lchan [label="IPACC MDCX ACK"];
--- [label="END: is BTS using IPA Abis? (osmo-bts, ip.access)"];
|||;
bsc_lchan abox bsc_lchan [label="LCHAN_ST_ACTIVE"];
bsc_lchan box bsc_lchan [label="lchan_fsm_post_lchan_activ()"];
bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ACTIVE"];
bts <= bsc_gscon [label="RR Assignment",ID="BSSMAP Assignment Request"];
bts <= bsc_gscon [label="RR Handover Command",ID="intra-BSC HO"];
bsc_gscon => mgw_msc [label="BSSMAP Handover\nRequest Acknowledge",ID="inter-BSC-MT HO"];
...;
---[label="On error"];
bsc_lchan rbox bsc_lchan [label="Continue at 'When the lchan is no longer used'"];
---;
...;
bts => bsc_gscon [label="RR Assignment Complete",ID="BSSMAP Assignment Request"];
bts => bsc_gscon [label="RR Handover Detect",ID="intra-BSC HO"];
bts => bsc_gscon [label="RR Handover Accept",ID="inter-BSC-MT HO"];
bsc_gscon note bsc_gscon [label="conn FSM takes care of MGW endpoints for BTS side (possibly
redirect) and MSC side (possibly create). More information in e.g. assignment.msc and
handover.msc"];
...;
...;
...;
bts rbox mgw_msc [label="When the lchan is no longer used"];
--- [label="IF the MS or BTS release the lchan"];
bts -> bsc_lchan [label="RLL Release Ind for SAPI=0"];
--- [label="IF the BSC other than the conn FSM decides to release"];
bsc -> bsc_lchan [label="LCHAN_EV_RELEASE"];
--- [label="IF the MSC or conn FSM release the lchan"];
bsc_lchan <- bsc_gscon [label="LCHAN_EV_RELEASE"];
---;
bsc note bsc_gscon [label="The LCHAN_EV_RELEASE's data pointer possibly indicates an error
cause"];
bsc_lchan note bsc_gscon [label="If the conn FSM requested a release, it probably has already
forgotten about this lchan. However, if the MS/BTS initiated the release, make sure the conn FSM
is informed:"];
bsc_lchan box bsc_lchan [label="lchan_graceful_release()"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nSAPIS_RELEASED\nT3109"];
--- [label="TCH and got as far as Chan Activ Ack?"];
bts <= bsc_lchan [label="RSL Deactivate SACCH"];
---;
bts <= bsc_lchan [label="RLL Release Request (Local End)..."];
bts <= bsc_lchan [label="...for all SAPIs except [0]"];
|||;
--- [label="SAPI[0] in use?"];
bsc_lchan note bsc_lchan [label="for bts->nokia.no_loc_rel_cnf we do not expect Release Confirm
messages and this state immediately advances to lchan_fsm_pre_rf_release()"];
...;
--- [label="on timeout"];
bsc_lchan box bsc_lchan [label="Anyway try RF Channel Release, continue
with lchan_fsm_wait_before_rf_release()"];
---;
...;
bts => bsc_lchan [label="RLL Release Confirm..."];
bts => bsc_lchan [label="...for each SAPI except [0]"];
bsc_lchan box bsc_lchan [label="Stay in\nLCHAN_ST_WAIT_\nSAPIS_RELEASED\nuntil only SAPI[0] remains active"];
--- [label="END: 'SAPI[0] in use?'"];
|||;
bsc_lchan box bsc_lchan [label="lchan_fsm_wait_before_rf_release()"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nBEFORE_RF_RELEASE\nT3111"];
bsc_lchan -> bsc_gscon [label="GSCON_EV_FORGET_LCHAN (data=lchan)"];
bsc_gscon note bsc_gscon [label="conn FSM immediately forgets about the lchan"];
bsc_gscon => mgw_msc [label="BSSMAP Clear Request?"];
...;
bsc_lchan box bsc_lchan [label="T3111 expires"];
bsc_lchan box bsc_lchan [label="lchan_fsm_pre_rf_release()"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nRF_RELEASE_ACK\nT3111"];
bsc_lchan box bsc_lchan [label="for each bsc_rll_req matching this lchan: disable timer, call
cb(BSC_RLLR_IND_REL_IND)"];
bts <= bsc_lchan [label="RSL RF Channel Release"];
...;
--- [label="on timeout"];
bsc_lchan rbox bsc_lchan [label="Continue at: 'On any error', 'unrecoverable'"];
---;
...;
bts => bsc_lchan [label="RSL RF Channel Release Ack"];
bsc_lchan box bsc_lchan [label="lchan_fsm_post_rf_release()"];
|||;
--- [label="IF an error cause was indicated on LCHAN_EV_RELEASE"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nAFTER_ERROR\n(timeout: T3111+2 s, T?)"];
...;
bsc_lchan box bsc_lchan [label="timer expires"];
--- [label="END: 'an error cause was indicated on LCHAN_EV_RELEASE'"];
|||;
bsc_lchan box bsc_lchan [label="lchan_fsm_release_complete()"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"];
bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_UNUSED"];
bsc_ts abox bsc_ts [label="TS_ST_UNUSED"];
|||;
|||;
bts rbox mgw_msc [label="On any error"];
|||;
--- [label="IF FOR_MS_CHANNEL_REQUEST && !sent_chan_activ"];
bts <= bsc_lchan [label="RR Immediate Assign Reject"];
|||;
--- [label="IF FOR_ASSIGNMENT or FOR_HANDOVER"];
bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ALLOC_ERROR"];
bsc_gscon note bsc_gscon [label="conn FSM shall immediately 'forget' the lchan"];
bsc_gscon => mgw_msc [label="BSSMAP\nAssignment Failure",ID=FOR_ASSIGNMENT];
bsc_gscon => mgw_msc [label="BSSMAP\nHandover Failure",ID="inter-BSC-MT HO"];
---;
|||;
--- [label="IF unrecoverable error"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_BORKEN"];
bsc_lchan note bsc_lchan [label="The broken state usually stays around
until the BTS disconnects."];
...;
bts note bsc_lchan [label="If an ACK comes in late, for specific BTS models, we may choose to
'repair' the lchan so that it is usable again."];
bts -> bsc_lchan [label="Chan Release ACK"];
bsc_lchan -> bsc_lchan [label="lchan_fsm_post_rf_release()"];
}

View File

@ -1,8 +1,8 @@
msc {
hscale=3;
ms [label="MS"], bts [label="BTS"], bsc[label="BSC"], bsc_gscon[label="BSC conn FSM"];
hscale=2;
ms [label="MS"], bts [label="BTS"], bsc[label="BSC"], bsc_lchan[label="BSC lchan FSM"];
ms note bsc_gscon [label="lchan allocation sequence for RSL Channel Request"];
ms note bsc_lchan [label="lchan allocation sequence for RSL Channel Request"];
ms => bts [label="RR Channel Request"];
bts => bsc [label="RSL Channel Request"];
@ -13,51 +13,47 @@ msc {
longer concerned (rsl_rx_pchan_rqd())."];
bsc note bsc [label="Always try to allocate an SDCCH regardless of the requested type, only if no
SDCCH is available, look for the actually requested channel type."];
bsc box bsc [label="lchan_alloc(SDCCH, allow_bigger=0)"];
bsc box bsc [label="lchan_select_by_type(SDCCH)"];
--- [label="IF no lchan is available (neither SDCCH nor requested type)"];
bsc note bsc [label="Figure out T3122 value from bts->T3122, network->T3122 or
GSM_T3122_DEFAULT"];
bsc box bsc [label="rsl_send_imm_ass_rej()"];
bsc box bsc [label="rsl_send_imm_ass_rej(wait_ind=T3122)"];
bsc note bsc [label="..."];
bts <= bsc [label="RR Immediate Assign Reject"];
ms <= bts [label="RR Immediate Assign Reject (possibly grouped with up to 4 others)"];
bsc note bsc [label="rsl_rx_pchan_rqd() exits, no channel is allocated."];
---;
--- [label="END: no lchan is available"];
bsc box bsc [label="Store RACH data in lchan->rqd_ref, rqd_ta"];
bsc abox bsc [label="Start lchan->act_timer (4s, lchan_act_tmr_cb())"];
bsc box bsc [label="rsl_chan_activate_lchan(RSL_ACT_INTRA_IMM_ASS)"];
--- [label="is the chosen lchan on dynamic timeslot that is currently used as PDCH?"];
bsc box bsc [linecolor=red,label="Osmocom style dyn TS use the lchan->act_timer for an RSL RF
Channel Release, to release PDCH mode. This will actually overwrite above act_timer!"];
bts <= bsc [label="i) RSL RF Chan Release of PDCH (Osmocom dyn TS)"];
bts <= bsc [label="OR ii) RSL PDCH Deact (ip.access dyn TS)"];
bsc -> bsc_gscon [label="gsm0808_assign_req() returns early"];
bsc_gscon abox bsc_gscon [label="ST_WAIT_ASS_COMPL (GSM0808_T10_VALUE=6s)"];
bsc -> bsc_lchan [label="lchan_allocate(FOR_MS_CHANNEL_REQUEST)"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_ACTIV_ACK\nT3103"];
bsc_lchan note bsc_lchan [label="The lchan FSM knows that FOR_MS_CHANNEL_REQUEST is about
Immediate Assignment."];
bts <= bsc_lchan [label="RSL Chan Activ (Immediate Assignment)"];
...;
bts note bsc_gscon [linecolor="red",
label="Osmocom style dyn TS use lchan->act_timer to watch over RF Chan Release, but there
seems to be no timer watching over PDCH Deact!"];
...;
bts => bsc [label="i) RSL RF Chan Release ACK (Osmocom dyn TS)"];
bts => bsc [label="OR ii) RSL PDCH Deact ACK (ip.access dyn TS)"];
bsc box bsc [label="rsl_chan_activate_lchan() re-invoked"];
--- [label="on any error"];
bts <= bsc_lchan [label="RR Immediate Assign Reject"];
ms <= bts [label="RR Immediate Assign Reject (possibly grouped with up to 4 others)"];
---;
bsc box bsc [label="lchan->state = LCHAN_S_ACT_REQ"];
bts <= bsc [label="RSL Chan Activ: Immediate Assignment"];
...;
bsc note bsc [label="Timeout of lchan->act_timer causes the
lchan->state to go to LCHAN_S_BROKEN, but no events or actions
are triggered."];
bts => bsc_lchan [label="RSL Chan Activ ACK"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nRLL_ESTABLISH\ncontinue T3103"];
...;
bts => bsc [label="RSL Chan Activ ACK"];
bsc box bsc [label="rsl_rx_chan_act_ack()"];
bsc box bsc [label="Stop lchan->act_timer"];
bsc box bsc [label="lchan->state = LCHAN_S_ACTIVE"];
bsc -> bsc [label="S_LCHAN_ACTIVATE_ACK (has no effect)"];
bsc note bsc [label="Since this was an Immediate Assignment, no further action is required on
behalf of the BSC. The MS is now free to use the lchan."];
--- [label="on timeout"];
bsc_lchan box bsc_lchan [label="lchan_error_release(deact_sacch=true)"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_RF_RELEASE_ACK"];
bts <= bsc_lchan [label="RLL Release Request (Local End)..."];
bts <= bsc_lchan [label="...for all SAPIs including [0]"];
bts <= bsc_lchan [label="RSL Deactivate SACCH"];
bts <= bsc_lchan [label="RSL RF Channel Release"];
...;
bts => bsc_lchan [label="RSL RF Channel Release ACK"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_AFTER_ERROR"];
...;
bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"];
---;
ms => bsc_lchan [label="RLL Establish Ind"];
bsc_lchan box bsc_lchan [label="associate lchan FSM with new conn FSM"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_ACTIVE"];
}

36
doc/timeslot-fsm.dot Normal file
View File

@ -0,0 +1,36 @@
digraph G {
rankdir=TB;
invisible [style="invisible"]
invisible2 [style="invisible"]
NOT_INITIALIZED
lchan [label="lchan FSM",shape=box3d];
UNUSED
IN_USE
BORKEN
PDCH
WAIT_PDCH_ACT
WAIT_PDCH_DEACT
invisible -> NOT_INITIALIZED [label="OML\nOpstart ACK",style=dotted]
invisible2 -> NOT_INITIALIZED [label="RSL\nbootstrapped",style=dotted]
NOT_INITIALIZED -> UNUSED [label="OML+RSL ready"]
UNUSED -> IN_USE [label="first\nlchan\nrequested\nby lchan\nFSM"]
IN_USE -> UNUSED [label="last lchan\nunused"]
UNUSED -> PDCH [label="onenter:\ndedicated PDCH\nand GPRS\nis enabled"]
UNUSED -> WAIT_PDCH_ACT [label="onenter:\ndyn TS\nand GPRS\nis enabled"]
WAIT_PDCH_ACT -> PDCH [label="dyn TS:\nPDCH activated"]
PDCH -> WAIT_PDCH_DEACT [label="dyn TS:\nlchan of specific\npchan requested"]
WAIT_PDCH_DEACT -> UNUSED [label="lchan\nunused\n(e.g. error)",style=dashed]
WAIT_PDCH_DEACT -> IN_USE [label="dyn TS:\nPDCH released"]
lchan -> {UNUSED} [label="TS_EV_LCHAN_\nREQUESTED",style=dotted]
{IN_USE} -> lchan [label="LCHAN_EV_\nTS_READY",style=dotted]
lchan -> IN_USE [label="TS_EV_LCHAN_\nUNUSED",style=dotted]
{WAIT_PDCH_ACT,WAIT_PDCH_DEACT} -> BORKEN [label=timeout,style=dashed]
}

98
doc/timeslot.msc Normal file
View File

@ -0,0 +1,98 @@
msc {
hscale=2;
bts [label="MS/BTS"], bsc[label="BSC"], bsc_ts[label="BSC timeslot FSM"], bsc_lchan[label="BSC lchan FSM"];
bsc_ts abox bsc_ts [label="NOT_INITIALIZED (no timeout)"];
...;
bsc note bsc_ts [label="OML and RSL may be established in any order"];
bts => bsc_ts [label="OML: Channel OPSTART ACK"];
bsc -> bsc_ts [label="RSL bootstrapped"];
bsc_ts abox bsc_ts [label="UNUSED (no timeout)"];
|||;
bts rbox bsc_lchan [label="UNUSED, onenter"];
bsc_ts abox bsc_ts [label="UNUSED"];
--- [label="GPRS enabled?"];
--- [label="IF: dedicated PDCH?"];
bsc_ts abox bsc_ts [label="PDCH (no timeout)"];
|||;
--- [label="IF: dynamic timeslot"];
bsc_ts abox bsc_ts [label="WAIT_PDCH_ACT (?s, Tnnnn)"];
bts <= bsc_ts [label="RSL Chan Activ of PDCH",ID="Osmocom style"];
bts <= bsc_ts [label="RSL PDCH Act",ID="ip.access style"];
...;
--- [label="timeout:"];
bsc_ts abox bsc_ts [label="BORKEN"];
---;
...;
bts => bsc_ts [label="RSL RF Chan Activ ACK",ID="Osmocom style"];
bts => bsc_ts [label="RSL PDCH Act ACK",ID="ip.access style"];
bsc_ts abox bsc_ts [label="PDCH (no timeout)"];
--- [label="END: GPRS enabled?"];
...;
...;
bts rbox bsc_lchan [label="UNUSED, on event"];
bsc_ts abox bsc_ts [label="UNUSED"];
bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_REQUESTED (data=lchan)"];
bsc_ts abox bsc_ts [label="IN_USE"];
bsc_ts -> bsc_lchan [label="LCHAN_EV_TS_READY"];
bts <= bsc_lchan [label="RSL Chan Activ (and so on)"];
...;
bts rbox bsc_lchan [label="IN_USE, second lchan"];
bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_REQUESTED (data=lchan)"];
bsc_ts -> bsc_lchan [label="LCHAN_EV_TS_READY"];
bts <= bsc_lchan [label="RSL Chan Activ (and so on)"];
...;
...;
bts rbox bsc_lchan [label="IN_USE, when lchan FSM releases (both regularly, or due to error)"];
bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_UNUSED (data=lchan)"];
--- [label="IF all lchan->fi->state == LCHAN_ST_UNUSED"];
bsc_ts abox bsc_ts [label="UNUSED"];
---;
...;
...;
bts rbox bsc_lchan [label="PDCH on lchan request"];
bsc_ts note bsc_lchan [label="TS_EV_LCHAN_REQUESTED should only come in on
lchans where it makes sense, both from TS kind as well as not
conflicting with other users of the lchan."];
bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_REQUESTED"];
bsc_ts abox bsc_ts [label="WAIT_PDCH_DEACT (?s, Tnnnn)"];
bts <= bsc_ts [label="RSL RF Chan Release of PDCH",ID="Osmocom style"];
bts <= bsc_ts [label="RSL PDCH Deact",ID="ip.access style"];
...;
--- [label="timeout:"];
bsc_ts abox bsc_ts [label="BORKEN"];
bsc_ts -> bsc_lchan [label="LCHAN_EV_TS_ERROR"];
---;
...;
bts => bsc_ts [label="RSL RF Chan Release ACK",ID="Osmocom style"];
bts => bsc_ts [label="RSL PDCH Deact ACK",ID="ip.access style"];
--- [label="IF all lchan->fi->state == LCHAN_ST_UNUSED"];
bsc_ts note bsc_lchan [label="If the lchan FSM decided to give up in the
meantime, nr of active lchans might have dropped back to zero."];
bsc_ts abox bsc_ts [label="UNUSED"];
bsc_ts note bsc_ts [label="onenter at UNUSED state will trigger back to
PDCH mode"];
|||;
--- [label="IF at least one lchan->state != LCHAN_ST_UNUSED"];
bsc_ts abox bsc_ts [label="IN_USE"];
bsc_ts rbox bsc_ts [label="Continue at 'IN_USE' above"];
...;
...;
bts rbox bsc_lchan [label="on erratic event"];
bsc_ts -> bsc_lchan [label="LCHAN_EV_TS_ERROR"];
bsc_lchan box bsc_lchan [label="release lchan"];
...;
bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_UNUSED"];
bsc_ts note bsc_ts [label="log error but ignore"];
...;
}

View File

@ -0,0 +1,116 @@
msc {
hscale=2;
bts [label="MS/BTS"], bsc[label="BSC"], bsc_ts[label="BSC timeslot FSM"], bsc_lchan[label="BSC lchan FSM"];
bsc box bsc [label="gsm_bts_alloc()"];
bsc box bsc [label="bts->c0 = gsm_bts_trx_alloc()"];
bsc -> bsc_ts;
bsc_ts box bsc_ts [label="trx->ts[*].fi = osmo_fsm_inst_alloc(timeslot_fsm)"];
bsc_ts abox bsc_ts [label="TS_ST_NOT_INITIALIZED"];
bsc -> bsc_lchan;
bsc_lchan box bsc_lchan [label="ts->lchan[*].ts = ts;\nts->lchan[*].nr = i;"];
bsc_lchan box bsc_lchan [label="ts->lchan[*].fi = NULL"];
bsc_ts note bsc_lchan [label="lchan_select() will only pick lchans from initialized timeslots of
the right pchan kind. lchan_select() shall OSMO_ASSERT(lchan->fi)."];
...;
...;
bts rbox bsc_lchan [label="reading config file"];
...;
bsc box bsc [label="timeslot N"];
bsc box bsc [label="phys_chan_config X"];
bsc_ts box bsc_ts [label="ts->pchan_from_config = X"];
bsc_ts note bsc_ts [label="still TS_ST_NOT_INITIALIZED"];
...;
bsc box bsc [label="trx 1..*"];
bsc box bsc [label="bts->trx_list add gsm_bts_trx_alloc()"];
bsc_ts rbox bsc_lchan [label="same as for c0 above"];
...;
...;
bts rbox bsc_lchan [label="Starting Operation"];
bts => bsc_ts [label="OML Channel OPSTART ACK"];
bsc_ts box bsc_ts [label="ts_on_oml_opstart()"];
bsc_ts box bsc_ts [label="ts->pchan_on_init = pchan_from_config"];
--- [label="IF dedicated TS"];
bsc_ts box bsc_ts [label="ts->pchan = ts->pchan_on_init"];
--- [label="ELSE: dyn TS"];
bsc_ts box bsc_ts [label="ts->pchan = NONE"];
--- [label="END: dyn TS"];
bsc_ts note bsc_lchan [label="Normally, the lchan FSM never terminates. Logic dictates that
the lchan is a child of the timeslot FSM, but it's not actually of functional importance
beyond basic sanity. Parent term event: TS_EV_LCHAN_UNUSED"];
bsc_ts box bsc_ts [label="Determine N = maximum number of lchans applicable to pchan_on_init"];
bsc_ts -> bsc_lchan;
bsc_lchan box bsc_lchan [label="ts->lchan[all N].type = osmo_fsm_inst_alloc(lchan_fsm)"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED\n(initial state)"];
bsc_ts -> bsc_ts [label="ts_check_init()"];
...;
bsc -> bsc_ts [label="RSL bootstrapped"];
bsc_ts -> bsc_ts [label="ts_check_init()"];
...;
bsc_ts box bsc_ts [label="ts_check_init()"];
--- [label="as soon as both OML and RSL are ready:"];
bsc_ts box bsc_ts [label="ts_on_init()"];
bsc_ts abox bsc_ts [label="TS_ST_UNUSED"];
--- [label="dyn TS"];
bsc_ts box bsc_ts [label="onenter of TS_ST_UNUSED:"];
bsc_ts abox bsc_ts [label="TS_ST_WAIT_PDCH_ACT"];
...;
bsc_ts abox bsc_ts [label="PDCH"];
--- [label="END: dyn TS"];
--- [label="END: OML and RSL ready"];
...;
bsc box bsc [label="lchan_select() picks an unused lchan"];
bsc -> bsc_lchan [label="lchan_allocate()"];
bsc_lchan -> bsc_ts [label="TS_EV_LCHAN_REQUESTED"];
--- [label="dyn TS"];
bsc_ts note bsc_ts [label="possibly switch from PDCH...\n(see timeslot FSM)"];
bsc_ts box bsc_ts [label="ts->pchan =\n requested GSM_PCHAN_XXX type"];
---;
bsc_ts -> bsc_lchan [label="LCHAN_EV_TS_READY"];
bsc_lchan note bsc_lchan [label="RSL Chan Alloc and so fort..."];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_ACTIVE"];
...;
bsc -> bsc_lchan [label="LCHAN_EV_RELEASE"];
bsc_lchan note bsc_lchan [label="...RSL RF Chan Release..."];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"];
bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_UNUSED"];
bsc_ts abox bsc_ts [label="TS_ST_UNUSED"];
--- [label="dyn TS"];
bsc_ts note bsc_ts [label="possibly switch to PDCH"];
---;
...;
...;
bts rbox bsc_lchan [label="BTS RSL is dropped"];
bsc box bsc [label="ipaccess_drop_rsl()"];
bsc -> bsc_ts [label="ts[*]:"];
bsc_ts abox bsc_ts [label="TS_ST_NOT_INITIALIZED"];
bsc_ts note bsc_lchan [label="If it's just the RSL being dropped, transition lchan FSMs to
LCHAN_ST_UNUSED, but keep them allocated. Unless OML is re-established, any vty pchan
modifications must not take effect."];
bsc_ts -> bsc_lchan [label="lchan[*]:"];
bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"];
bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_UNUSED (ignored)"];
...;
--- [label="when RSL comes back later:"];
bsc -> bsc_ts [label="RSL bootstrapped"];
bsc_ts box bsc_ts [label="ts_check_init()"];
bsc_ts rbox bsc_ts [label="see ts_check_init() above"];
...;
...;
bts rbox bsc_lchan [label="BTS OML is dropped"];
bsc note bsc [label="As part of OML drop, RSL is also dropped:"];
bsc box bsc [label="ipaccess_drop_rsl()"];
bsc -> bsc_ts [label="ts[*]:"];
bsc_ts abox bsc_ts [label="TS_ST_NOT_INITIALIZED"];
bsc rbox bsc [label="see 'BTS RSL is dropped' above"];
bsc -> bsc_ts [label="ts[*]:"];
bsc_ts -> bsc_lchan [label="lchan[*]:"];
bsc_lchan box bsc_lchan [label="osmo_fsm_inst_term()"];
bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_UNUSED (ignored)"];
bsc_lchan box bsc_lchan [label="lchan->fi = NULL"];
bsc rbox bsc [label="Continue at 'Starting Operation'"];
}