msc {
	hscale=2;
	ms [label="MS/BTS"], ts [label="BSC timeslot FSM"],
	lchan[label="BSC lchan FSM"], rtp[label="BSC lchan RTP FSM"],mgwep[label="BSC MGW endpoint FSM"];

	ms box mgwep [label="lchan allocation sequence"];
	lchan abox lchan [label="LCHAN_ST_UNUSED"];
	...;
	lchan rbox lchan [label="lchan_activate(activate_info)"];
	lchan note lchan [label="Dispatching event to make sure the lchan FSM permits activation."];
	lchan -> lchan [label="LCHAN_EV_ACTIVATE\ndata = activate_info"];
	lchan abox lchan [label="LCHAN_ST_\nWAIT_TS_READY"];
	ts <- lchan [label="TS_EV_LCHAN_REQUESTED"];
	ts rbox ts [label="Most details omitted. See timeslot FSM diagrams."];
	ts note ts [label="A dyn TS may be in PDCH mode and will asynchronously switch off PDCH first. A
		non-dynamic TS is ready immediately."];
	|||;
	--- [label="IF requires_voice_stream"];
	lchan -> rtp [label="lchan_rtp_fsm_start()"];
	rtp abox rtp [label="allocate\n LCHAN_RTP_ST_\nWAIT_MGW_ENDPOINT_\nAVAILABLE"];
	--- [label="IF no endpoint-CI yet"];
	rtp box rtp [label="gscon_ensure_mgw_endpoint()"];
	rtp -> mgwep [label="mgw_endpoint_ci_add(to-BTS)"];
	rtp -> mgwep [label="CRCX to-BTS"];
	mgwep rbox mgwep [label="MGCP: CRCX"];
	...;
	mgwep rbox mgwep [label="MGCP: CRCX OK"];
	rtp <- mgwep [label="LCHAN_RTP_EV_MGW_ENDPOINT_AVAILABLE"];
	rtp note mgwep [label="The CRCX OK has assigned us a new endpoint CI number"];
	rtp abox rtp [label="LCHAN_RTP_ST_WAIT_LCHAN_READY"];
	--- [label="END: no endpoint-CI yet"];
	--- [label="END: requires_voice_stream"];
	|||;
	...;
	ts -> lchan [label="LCHAN_EV_TS_READY"];
	lchan abox lchan [label="LCHAN_ST_\nWAIT_ACTIV_ACK"];
	--- [label="IF FOR_MS_CHANNEL_REQUEST"];
	ms <= lchan [label="RSL Chan Activ (RSL_ACT_INTRA_IMM_ASS)"];
	--- [label="ELSE: FOR_ASSIGNMENT"];
	ms <= lchan [label="RSL Chan Activ (RSL_ACT_INTRA_NORM_ASS)"];
	--- [label="ELSE: FOR_HANDOVER"];
	ms <= lchan [label="RSL Chan Activ (RSL_ACT_INTER_ASYNC)"];
	--- [label="END"];
	...;
	ms rbox lchan [label="On timeout or Chan Activ NACK, see: 'On any error', 'unrecoverable'"];
	...;
	ms => lchan [label="RSL Chan Activ ACK"];
	lchan box lchan [label="lchan_fsm_post_activ_ack()"];

	--- [label="IF FOR_MS_CHANNEL_REQUEST"];
	ms <= lchan [label="RR Immediate Assignment"];
	--- [label="ELSE: FOR_ASSIGNMENT"];
	lchan rbox lchan [label="dispatch\nASSIGNMENT_EV_\nLCHAN_ACTIVE\n(see Assignment FSM diagrams)"];
	ms <= lchan [label="RR Assignment Command"];
	--- [label="ELSE: FOR_HANDOVER"];
	lchan rbox lchan [label="dispatch\nHO_EV_LCHAN_ACTIVE\n(see Handover FSM diagrams)"];
	--- [label="END"];


	lchan abox lchan [label="LCHAN_ST_WAIT_\nRLL_RTP_ESTABLISH\nT3101"];
	|||;
	|||;
	--- [label="IF requires_voice_stream"];
	lchan -> rtp [label="LCHAN_RTP_EV_LCHAN_READY"];
	|||;
	--- [label="IF ip.access style BTS"];
	rtp abox rtp [label="LCHAN_RTP_ST_WAIT_IPACC_CRCX_ACK"];
	ms <= rtp [label="IPACC CRCX"];
	...;
	ms => rtp [label="IPACC CRCX ACK (BTS RTP port info)"];
	--- [label="END ip.access style BTS"];
	|||;
	rtp box rtp [label="lchan_rtp_fsm_switch_rtp()"];
	|||;
	--- [label="IF wait_before_switching_rtp"];
	rtp note rtp [label="During Handover, wait for HO DETECT before redirecting an existing endpoint
		CI towards the new lchan."];
	rtp abox rtp [label="LCHAN_RTP_ST_WAIT_READY_TO_SWITCH_RTP"];
	...;
	ms => rtp [label="HO DETECT (via Handover FSM)"];
	--- [label="END: wait_before_switching_rtp"];
	|||;
	rtp abox rtp [label="LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED"];
	rtp box rtp [label="connect_mgw_endpoint_to_lchan()"];
	rtp -> mgwep [label="MDCX to-BTS"];
	mgwep rbox mgwep [label="MGCP: MDCX"];
	...;
	mgwep rbox mgwep [label="MGCP: MDCX OK"];
	rtp <- mgwep [label="LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED"];
	--- [label="IF ip.access style BTS"];
	rtp abox rtp [label="LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK"];
	ms <= rtp [label="IPACC MDCX (MGW RTP port info)"];
	...;
	ms => rtp [label="IPACC MDCX ACK"];
	--- [label="END ip.access style BTS"];
	rtp abox rtp [label="LCHAN_RTP_ST_READY"];
	lchan <- rtp [label="LCHAN_EV_RTP_READY"];
	rtp note rtp [label="RTP FSM stays ready for Rollback until final establish event"];
	...;
	lchan -> rtp [label="LCHAN_RTP_EV_ESTABLISHED\nvia gscon_change_primary_lchan()"];
	rtp abox rtp [label="LCHAN_RTP_ST_\nESTABLISHED"];
	--- [label="END: requires_voice_stream"];
	|||;
	|||;

	...;
	ms => lchan [label="RLL Establish Ind"];
	lchan abox lchan [label="LCHAN_ST_\nESTABLISHED"];
	lchan box lchan [label="lchan_on_fully_established()"];
	--- [label="IF FOR_MS_CHANNEL_REQUEST"];
	ms note lchan [label="No action required. The MS will have sent an L3 message in the RLL
		Establish Ind and is then free to dispatch DTAP."];
	--- [label="ELSE: FOR_ASSIGNMENT"];
	lchan rbox lchan [label="dispatch\nASSIGNMENT_EV_\nLCHAN_ESTABLISHED\n(see Assignment FSM diagrams)"];
	--- [label="ELSE: FOR_HANDOVER"];
	lchan rbox lchan [label="dispatch\nHO_EV_LCHAN_ESTABLISHED\n(see Handover FSM diagrams)"];
	--- [label="END"];
	...;
	--- [label="IF requires_voice_stream"];
	lchan rbox lchan [label="Assignment or Handover FSM:"];
	lchan -> mgwep [label="CRCX/MDCX to-MSC"];
	...;
	lchan <- mgwep [label="OK"];
	lchan box lchan [label="gscon_change_primary_lchan()"];
	lchan -> rtp [label="LCHAN_RTP_EV_ESTABLISHED"];
	rtp abox rtp [label="LCHAN_RTP_ST_\nESTABLISHED"];
	rtp box rtp [label="Forget any Rollback info"];
	--- [label="END: requires_voice_stream"];

	...;
	...;
	...;

	ms rbox mgwep [label="On Mode Modify (e.g. change a TCH lchan from signalling to voice)"];
	lchan abox lchan [label="LCHAN_ST_\nWAIT_RR_CHAN_\nMODE_MODIFY_ACK"];
	ms <= lchan [label="RR Chan Mode Modif"];
	...;
	ms => lchan [label="RR Chan Mode Modif Ack"];
	lchan abox lchan [label="LCHAN_ST_\nWAIT_RSL_CHAN_\nMODE_MODIFY_ACK"];
	ms <= lchan [label="RSL MT Mode Modify Req"];
	...;
	ms => lchan [label="RSL MT Mode Modify Ack"];
	--- [label="IF adding RTP stream"];
	lchan abox lchan [label="LCHAN_ST_WAIT_\nRLL_RTP_ESTABLISH\nT3101"];
	lchan rbox rtp [label="See above at 'LCHAN_RTP_EV_LCHAN_READY'"];
	--- [label="IF not adding RTP stream"];
	lchan abox lchan [label="LCHAN_ST_\nESTABLISHED"];
	--- [label="END: whether adding voice stream"];

	...;
	...;
	...;

	ms rbox mgwep [label="When the MS or BTS release the lchan"];
	lchan abox lchan [label="LCHAN_ST_\nESTABLISHED"];
	ms -> lchan [label="RLL Release Ind for SAPI=0"];
	lchan abox lchan [label="LCHAN_ST_WAIT_RLL_RTP_RELEASED"];
	lchan rbox lchan [label="Continue at 'common release' below"];
	...;
	...;
	ms rbox mgwep [label="When the BSC decides to release the lchan"];
	lchan box lchan [label="lchan_release()"];
	lchan abox lchan [label="LCHAN_ST_WAIT_RLL_RTP_RELEASED"];
	ms <= lchan [label="RR Release"];
	lchan rbox lchan [label="common release"];
	--- [label="IF RTP FSM present"];
	lchan -> rtp [label="LCHAN_RTP_EV_RELEASE"];
	--- [label="END: RTP FSM present"];
	ms <= lchan [label="RSL Deactivate SACCH"];
	ms <= lchan [label="RSL Release Request (Local End)",ID="for each SAPI except [0]"];
	lchan note lchan [label="for ms->nokia.no_loc_rel_cnf we do not expect Release Confirm
		messages and immediately mark all SAPIs as released"];

	...;
	lchan <- rtp [label="LCHAN_EV_RTP_RELEASED"];
	...;
	ms => lchan [label="RLL Release Confirm",ID="for each SAPI except [0]"];
	...;
	lchan box lchan [label="Stay in\nLCHAN_ST_WAIT_\nRLL_RTP_RELEASED\nuntil only SAPI[0] remains active"];
	lchan abox lchan [label="LCHAN_ST_WAIT_\nBEFORE_RF_RELEASE\nT3111"];
	...;
	lchan box lchan [label="T3111 expires"];
	lchan box lchan [label="lchan_fsm_pre_rf_release()"];
	lchan abox lchan [label="LCHAN_ST_WAIT_\nRF_RELEASE_ACK\nT3111"];
	ms <= lchan [label="RSL RF Channel Release"];
	...;
	lchan rbox lchan [label="On timeout, continue at: 'On any error', 'unrecoverable'"];
	...;
	ms => lchan [label="RSL RF Channel Release Ack"];
	|||;
	--- [label="IF release_in_error"];
	lchan abox lchan [label="LCHAN_ST_WAIT_\nAFTER_ERROR\n(timeout: T3111+2 s, X3111)"];
	...;
	lchan box lchan [label="timer expires"];
	--- [label="END: release_in_error"];
	|||;
	lchan abox lchan [label="LCHAN_ST_UNUSED"];
	ts <- lchan [label="TS_EV_LCHAN_UNUSED"];
	|||;
	|||;
	|||;

	ms rbox mgwep [label="On any error"];
	|||;
	--- [label="IF FOR_MS_CHANNEL_REQUEST"];
	ms <= lchan [label="RR Immediate Assign Reject"];
	--- [label="ELSE: FOR_ASSIGNMENT"];
	lchan rbox lchan [label="dispatch\nASSIGNMENT_EV_\nLCHAN_ERROR\n(see Assignment FSM diagrams)"];
	--- [label="ELSE: FOR_HANDOVER"];
	lchan rbox lchan [label="dispatch\nHO_EV_LCHAN_ERROR\n(see Handover FSM diagrams)"];
	--- [label="END"];
	|||;
	--- [label="IF fi_rtp present"];
	lchan -> rtp [label="LCHAN_RTP_EV_ROLLBACK"];
	rtp rbox rtp [label="If to-BTS is not established yet, ROLLBACK is synonymous to LCHAN_RTP_EV_RELEASE"];
	rtp rbox rtp [label="If there is no old_lchan, just DLCX instead"];
	rtp abox rtp [label="LCHAN_RTP_ST_ROLLBACK"];
	rtp box rtp [label="connect_mgw_endpoint_to_lchan()\nusing old_lchan"];
	rtp -> mgwep [label="MDCX to-BTS"];
	mgwep rbox mgwep [label="MGCP: MDCX"];
	...;
	mgwep rbox mgwep [label="MGCP: MDCX OK"];
	rtp <- mgwep [label="LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED"];
	rtp abox rtp [label="terminate"];
	lchan <- rtp [label="LCHAN_EV_RTP_RELEASED"];
	--- [label="END: fi_rtp present"];
	|||;
	|||;
	--- [label="IF unrecoverable error"];
	lchan abox lchan [label="LCHAN_ST_BORKEN"];
	ms note lchan [label="The broken state usually stays around
		until the BTS disconnects."];
	...;
	ms note 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."];
	ms -> lchan [label="RF Chan Release ACK"];
	lchan rbox lchan [label="continue above at\nLCHAN_ST_WAIT_\nAFTER_ERROR"];
}
