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()"];
}
