Change in osmo-bsc[master]: doc: charts: illustrate new plan for ts and lchans

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Neels Hofmeyr gerrit-no-reply at lists.osmocom.org
Thu Jun 7 01:01:45 UTC 2018


Neels Hofmeyr has uploaded this change for review. ( https://gerrit.osmocom.org/9489


Change subject: doc: charts: illustrate new plan for ts and lchans
......................................................................

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
---
M doc/Makefile.am
M doc/assignment.msc
A doc/handover-inter-bsc-mo.msc
A doc/handover-inter-bsc-mt.msc
M doc/handover.msc
A doc/lchan-fsm.dot
M doc/lchan-release.msc
A doc/lchan.msc
M doc/ms-channel-request.msc
A doc/timeslot-fsm.dot
A doc/timeslot.msc
A doc/ts-and-lchan-fsm-lifecycle.msc
12 files changed, 1,098 insertions(+), 466 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-bsc refs/changes/89/9489/1

diff --git a/doc/Makefile.am b/doc/Makefile.am
index 5fe5674..ca0470d 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -7,11 +7,24 @@
 	$(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
diff --git a/doc/assignment.msc b/doc/assignment.msc
index d470d01..9f10ea1 100644
--- a/doc/assignment.msc
+++ b/doc/assignment.msc
@@ -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"];
 }
diff --git a/doc/handover-inter-bsc-mo.msc b/doc/handover-inter-bsc-mo.msc
new file mode 100644
index 0000000..9aff7a7
--- /dev/null
+++ b/doc/handover-inter-bsc-mo.msc
@@ -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)"];
+}
diff --git a/doc/handover-inter-bsc-mt.msc b/doc/handover-inter-bsc-mt.msc
new file mode 100644
index 0000000..88a52da
--- /dev/null
+++ b/doc/handover-inter-bsc-mt.msc
@@ -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"];
+}
diff --git a/doc/handover.msc b/doc/handover.msc
index 8862dd9..7529de6 100644
--- a/doc/handover.msc
+++ b/doc/handover.msc
@@ -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)"];
 	...;
-	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!"];
+	--- [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 => 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"];
 }
diff --git a/doc/lchan-fsm.dot b/doc/lchan-fsm.dot
new file mode 100644
index 0000000..dbb283c
--- /dev/null
+++ b/doc/lchan-fsm.dot
@@ -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]
+
+}
diff --git a/doc/lchan-release.msc b/doc/lchan-release.msc
index f75b559..017c9cf 100644
--- a/doc/lchan-release.msc
+++ b/doc/lchan-release.msc
@@ -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"];
 }
diff --git a/doc/lchan.msc b/doc/lchan.msc
new file mode 100644
index 0000000..9b7d663
--- /dev/null
+++ b/doc/lchan.msc
@@ -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()"];
+}
diff --git a/doc/ms-channel-request.msc b/doc/ms-channel-request.msc
index c0ef60f..1c5b4bf 100644
--- a/doc/ms-channel-request.msc
+++ b/doc/ms-channel-request.msc
@@ -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 @@
 		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"];
 }
diff --git a/doc/timeslot-fsm.dot b/doc/timeslot-fsm.dot
new file mode 100644
index 0000000..79e56c9
--- /dev/null
+++ b/doc/timeslot-fsm.dot
@@ -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]
+}
diff --git a/doc/timeslot.msc b/doc/timeslot.msc
new file mode 100644
index 0000000..9a8c360
--- /dev/null
+++ b/doc/timeslot.msc
@@ -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"];
+	...;
+
+}
diff --git a/doc/ts-and-lchan-fsm-lifecycle.msc b/doc/ts-and-lchan-fsm-lifecycle.msc
new file mode 100644
index 0000000..79d32c5
--- /dev/null
+++ b/doc/ts-and-lchan-fsm-lifecycle.msc
@@ -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'"];
+
+}

-- 
To view, visit https://gerrit.osmocom.org/9489
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-bsc
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: I18d60de5ee932c962aad0a532965a55d570bb936
Gerrit-Change-Number: 9489
Gerrit-PatchSet: 1
Gerrit-Owner: Neels Hofmeyr <nhofmeyr at sysmocom.de>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20180607/8aa045a0/attachment.htm>


More information about the gerrit-log mailing list