module IuUP_Types {

/* Definition of abstract types for the IuUP protocol as specified in
 * 3GPP TS 25.415.  Uses the TITAN "RAW" codec to auto-generate encoder/decoder
 * functions.
 *
 * (C) 2017 by Harald Welte <laforge@gnumonks.org>
 * All rights reserved.
 *
 * Released under the terms of GNU General Public License, Version 2 or
 * (at your option) any later version.
 */

import from Osmocom_Types all;
import from General_Types all;

/* See TS 25.415 6.6.3.1 */
type uint4_t IuUP_PDU_Type;

/* See TS 25.415 6.6.3.2 */
type enumerated IuUP_AckNack {
	IuUP_ACKNACK_CTRL	(0),
	IuUP_ACKNACK_ACK	(1),
	IuUP_ACKNACK_NACK	(2),
	IuUP_ACKNACK_RESERVED	(3)
} with { variant "FIELDLENGTH(2)" };

/* See TS 25.415 6.6.3.3 */
type uint4_t IuUP_FrameNr;

/* See TS 25.415 6.6.3.5 */
type enumerated IuUP_FQC {
	IuUP_FQC_GOOD		(0),
	IuUP_FQC_BAD		(1),
	IuUP_FQC_BAD_RADIO	(2),
	IuUP_FQC_SPARE		(3)
} with { variant "FIELDLENGTH(2)" };

/* See TS 25.415 6.6.3.6 */
type uint6_t IuUP_RFCI;

/* See TS 25.415 6.6.3.7 */
type enumerated IuUP_ProcedureIndicator {
	IuUP_PRI_INITIALIZATION		(0),
	IuUP_PRI_RATE_CONTROL		(1),
	IuUP_PRI_TIME_ALIGNMENT		(2),
	IuUP_PRI_ERROR_EVENT		(3)
	/* reserved */
} with { variant "FIELDLENGTH(4)" };

/* See TS 25.415 6.6.3.13 */
type uint6_t IuUP_NumOfRfciInd;

/* See TS 25.415 6.6.3.15 */
type enumerated IuUP_ErrorDistance {
	IuUP_ERR_DIST_LOCAL		(0),
	IuUP_ERR_DIST_FIRST_FW		(1),
	IuUP_ERR_DIST_SECOND_FW		(2),
	IuUP_ERR_DIST_RESERVED		(3)
} with { variant "FIELDLENGTH(2)" };

/* See TS 25.415 6.6.3.16 */
type enumerated IuUP_ErrorCause {
	/* Syntactical protocol errors */
	IuUP_CAUSE_CRC_ERROR_HEADER	(0),
	IuUP_CAUSE_CRC_ERROR_PAYLOAD	(1),
	IuUP_CAUSE_UNEXP_FRAME_NR	(2),
	IuUP_CAUSE_FRAME_LOSS		(3),
	IuUP_CAUSE_PDU_TYPE_UNKNOWN	(4),
	IuUP_CAUSE_UNKNOWN_PROCEDURE	(5),
	IuUP_CAUSE_UNKNOWN_RES_VAL	(6),
	IuUP_CAUSE_UNKNOWN_FIELD	(7),
	IuUP_CAUSE_FRAME_TOO_SHORT	(8),
	IuUP_CAUSE_MISSING_FIELD	(9),
	/* Semantical protocol errors */
	IuUP_CAUSE_UNEXPECTED_PDU_TYPE	(16),
	IuUP_CAUSE_UNEXPECTED_PROCEDURE	(18),
	IuUP_CAUSE_UNEXPECTED_RFCI	(19),
	IuUP_CAUSE_UNEXPECTED_VALUE	(20),
	/* Other Errors */
	IuUP_CAUSE_INIT_FAIL		(42),
	IuUP_CAUSE_INIT_FAIL_NET_TMR_EXP	(43),
	IuUP_CAUSE_INIT_FAIL_FERR_REP_NACK	(44),
	IuUP_CAUSE_RATE_CONTROL_FAIL	(45),
	IuUP_CAUSE_ERROR_EVENT_FAIL	(46),
	IuUP_CAUSE_TIME_ALIGN_NOTSUPP	(47),
	IuUP_CAUSE_REQ_ALIGN_NOTPOSS	(48),
	IuUP_CAUSE_IU_UP_VERS_NOTSUPP	(49)
} with { variant "FIELDLENGTH(6)" };

/* See TS 25.415 6.6.3.18 */
type uint8_t IuUP_TimeAlignment;

/* See TS 25.415 6.6.3.24 */
type uint4_t IuUP_IPTI;

/* See TS 25.415 6.6.2.1 */
type record IuUP_PDU_Type_0 {
	IuUP_PDU_Type	pdu_type,	/* 0 */
	IuUP_FrameNr	frame_nr,
	IuUP_FQC	fqc,
	IuUP_RFCI	rfci,
	uint6_t		header_crc,
	uint10_t	payload_crc,
	octetstring	payload
};

/* See TS 25.415 6.6.2.2 */
type record IuUP_PDU_Type_1 {
	IuUP_PDU_Type	pdu_type,	/* 1 */
	IuUP_FrameNr	frame_nr,
	IuUP_FQC	fqc,
	IuUP_RFCI	rfci,
	uint6_t		header_crc,
	BIT2		spare,
	octetstring	payload
};

/* See TS 25.415 6.6.6.2.3 */
type record IuUP_PDU_Type_14 {
	IuUP_PDU_Type	pdu_type,
	IuUP_AckNack	ack_nack,
	uint2_t		frame_nr,
	IuUP_PDU14_Union u
} with { variant (u) "CROSSTAG(proc,	ack_nack=IuUP_ACKNACK_CTRL;
			       ack,	ack_nack=IuUP_ACKNACK_ACK;
			       nack,	ack_nack=IuUP_ACKNACK_NACK)"
};

type record IuUP_PDU_Type_14_Common_Hdr {
	uint4_t		iuup_version,
	IuUP_ProcedureIndicator	procedure_ind,
	uint6_t		header_crc,
	uint10_t	payload_crc
};

type union IuUP_PDU14_Union {
	IuUP_PDU14_ProcSending	proc,
	IuUP_PDU14_ACK		ack,
	IuUP_PDU14_NACK		nack
};

/* 6.6.2.3.1 Figure 21 */
type record IuUP_PDU14_ProcSending {
	IuUP_PDU_Type_14_Common_Hdr hdr,
	IuUP_PDU14_ProcSendingUnion u
} with {
	variant (u) "CROSSTAG(init,      hdr.procedure_ind=IuUP_PRI_INITIALIZATION;
			      rate_ctrl, hdr.procedure_ind=IuUP_PRI_RATE_CONTROL;
			      time_alignment, hdr.procedure_ind=IuUP_PRI_TIME_ALIGNMENT;
			      error_event, hdr.procedure_ind=IuUP_PRI_ERROR_EVENT;
			      other,     OTHERWISE)"
};

/* 6.6.2.3.2 Figure 22 */
type record IuUP_PDU14_ACK {
	IuUP_PDU_Type_14_Common_Hdr hdr,
	octetstring	spare_ext optional
};

/* 6.6.2.3.3 Figure 23 */
type record IuUP_PDU14_NACK {
	IuUP_PDU_Type_14_Common_Hdr hdr,
	IuUP_ErrorCause	err_cause,
	BIT2		spare,
	octetstring	spare_ext optional
};

type union IuUP_PDU14_ProcSendingUnion {
	IuUP_PDU14_ProcSending_INIT		init,
	IuUP_PDU14_ProcSending_RATE_CTRL	rate_ctrl,
	IuUP_PDU14_ProcSending_TIME_ALIGNMENT	time_alignment,
	IuUP_PDU14_ProcSending_ERROR_EVENT	error_event,
	octetstring 				other
};

/* 6.6.2.3.4.1 Initialisation */
type record IuUP_PDU14_ProcSending_INIT {
	BIT3		spare,
	boolean		ti,
	uint3_t		subflows_per_rfci,
	boolean		chain_ind,
	IuUP_InitRfci	rfci,
	IuUP_IPTI_List	IPTIs optional,
	BIT16		versions_supported,
	uint4_t		data_pdu_type,
	BIT4		spare2
} with {
	variant (IPTIs) "PRESENCE(ti=true)"
	variant (versions_supported) "BITORDER(lsb)"
	variant (versions_supported) "BYTEORDER(last)"
};
type record IuUP_InitRfci {
	boolean		lri,
	boolean		li,
	IuUP_RFCI	rfci_id,
	RecOfU8		len8 optional,
	RecOfU16	len16 optional,
	IuUP_InitRfci	rfci optional
} with {
	variant (len8)	"PRESENCE(li=false)";
	variant (len16)	"PRESENCE(li=true)"
	variant (rfci)	"PRESENCE(lri=false)"
};
type record of IuUP_IPTI IuUP_IPTI_List with {
	variant "FIELDLENGTH(3)"
	variant "PADDING(yes)"
	};
type record of uint8_t RecOfU8 with { variant "FIELDLENGTH(3)" };
type record of uint16_t RecOfU16 with { variant "FIELDLENGTH(3)" };
type record of IuUP_InitRfci IuUP_InitRfciList;

/* 6.6.2.3.4.2.1 Rate Control procedure */
type record IuUP_PDU14_ProcSending_RATE_CTRL {
	BIT2		spare,
	uint6_t		nr_of_rfci_ind,
	bitstring	rfci_ind
} with { variant (nr_of_rfci_ind) "LENGTHTO(rfci_ind)"
	 variant (nr_of_rfci_ind) "UNIT(bits)"
};

/* 6.6.2.3.4.3 Time Alignment */
type record IuUP_PDU14_ProcSending_TIME_ALIGNMENT {
	uint8_t		time_alignment,
	octetstring	spare optional
};

/* 6.6.2.3.4.4 Error Event */
type record IuUP_PDU14_ProcSending_ERROR_EVENT {
	IuUP_ErrorDistance	distance,
	IuUP_ErrorCause		cause
};


type union IuUP_PDU {
	IuUP_PDU_Type_0		type_0,
	IuUP_PDU_Type_1		type_1,
	IuUP_PDU_Type_14	type_14
} with { variant "TAG(	type_0,		pdu_type = 0;
			type_1,		pdu_type = 1;
			type_14,	pdu_type = 14;)" };

/* hand-written C++ functions */
external function f_enc_IuUP_PDU(in IuUP_PDU msg) return octetstring;
external function f_IuUP_compute_crc_header(in octetstring inp) return uint6_t;
external function f_IuUP_compute_crc_payload(in octetstring inp) return uint10_t;

/* auto-generated */
external function dec_IuUP_PDU(in octetstring stream) return IuUP_PDU
	with { extension "prototype(convert) decode(RAW)" };

template IuUP_PDU ts_IuUP_INIT_ACK(uint2_t frame_nr, uint4_t version) := {
	type_14 := {
		pdu_type := 14,
		ack_nack := IuUP_ACKNACK_ACK,
		frame_nr := frame_nr,
		u := {
			ack := {
				hdr := {
					iuup_version := version,
					procedure_ind := IuUP_PRI_INITIALIZATION,
					header_crc := 0,
					payload_crc := 0
				},
				spare_ext := omit
			}
		}
	}
};

template IuUP_PDU tr_IuUP_INIT_ACK(template uint2_t frame_nr := ?, template uint4_t version := ?) := {
	type_14 := {
		pdu_type := 14,
		ack_nack := IuUP_ACKNACK_ACK,
		frame_nr := frame_nr,
		u := {
			ack := {
				hdr := {
					iuup_version := version,
					procedure_ind := IuUP_PRI_INITIALIZATION,
					header_crc := ?,
					payload_crc := ?
				},
				spare_ext := omit
			}
		}
	}
};

template (value) IuUP_InitRfci ts_IuUP_InitRfci(
			template (value) boolean lri,
			template (value) boolean li,
			template (value) IuUP_RFCI rfci_id,
			template (omit) RecOfU8 len8,
			template (omit) RecOfU16 len16,
			template (omit) IuUP_InitRfci rfci := omit) := {
	lri := lri,
	li := li,
	rfci_id := rfci_id,
	len8 := len8,
	len16 := len16,
	rfci := rfci
}

template (value) IuUP_PDU14_ProcSending_INIT ts_IuUP_PDU14_ProcSending_INIT(
			template (value) boolean ti := true,
			template (value) uint3_t subflows_per_rfci := 3,
			template (value) boolean chain_ind := false,
			template (value) IuUP_InitRfci rfci := ts_IuUP_InitRfci(false, false, 0, {81, 103, 60}, omit,
									ts_IuUP_InitRfci(false, false, 1, {39, 0, 0}, omit,
										ts_IuUP_InitRfci(true, false, 2, {0, 0, 0}, omit, omit))),
			template (omit) IuUP_IPTI_List IPTIs := {1, 7, 1},
			template (value) BIT16 versions_supported := '0000000000000001'B,
			template (value) uint4_t data_pdu_type := 0) := {
	spare := '000'B,
	ti := ti,
	subflows_per_rfci := subflows_per_rfci,
	chain_ind := chain_ind,
	rfci := rfci,
	IPTIs := IPTIs,
	versions_supported := versions_supported,
	data_pdu_type := data_pdu_type,
	spare2 := '0000'B
}

template (present) IuUP_PDU14_ProcSending_INIT tr_IuUP_PDU14_ProcSending_INIT(
			template (present) boolean ti := ?,
			template (present) uint3_t subflows_per_rfci := ?,
			template (present) boolean chain_ind := ?,
			template (present) IuUP_InitRfci rfci := ?,
			template IuUP_IPTI_List IPTIs := *,
			template (present) BIT16 versions_supported := ?,
			template (present) uint4_t data_pdu_type := ?) := {
	spare := '000'B,
	ti := ti,
	subflows_per_rfci := subflows_per_rfci,
	chain_ind := chain_ind,
	rfci := rfci,
	IPTIs := IPTIs,
	versions_supported := versions_supported,
	data_pdu_type := data_pdu_type,
	spare2 := '0000'B
}

template (value) IuUP_PDU ts_IuUP_INIT(template (value) IuUP_PDU14_ProcSending_INIT init, uint2_t frame_nr := 0, uint4_t version := 0) := {
	type_14 := {
		pdu_type := 14,
		ack_nack := IuUP_ACKNACK_CTRL,
		frame_nr := frame_nr,
		u := {
			proc := {
				hdr := {
					iuup_version := version,
					procedure_ind := IuUP_PRI_INITIALIZATION,
					header_crc := 0,
					payload_crc := 0
				},
				u := {
					init := init
				}
			}
		}
	}
};

template IuUP_PDU tr_IuUP_INIT(template (present) IuUP_PDU14_ProcSending_INIT init := ?, template octetstring payload := ?, template uint2_t frame_nr := ?,
				template uint4_t version := ?) := {
	type_14 := {
		pdu_type := 14,
		ack_nack := IuUP_ACKNACK_CTRL,
		frame_nr := frame_nr,
		u := {
			proc := {
				hdr := {
					iuup_version := version,
					procedure_ind := IuUP_PRI_INITIALIZATION,
					header_crc := ?,
					payload_crc := ?
				},
				u := {
					init := init
				}
			}
		}
	}
};


template IuUP_PDU ts_IuUP_Type0(IuUP_FrameNr frame_nr, IuUP_RFCI rfci, octetstring payload,
				IuUP_FQC fqc := IuUP_FQC_GOOD) := {
	type_0 := {
		pdu_type := 0,
		frame_nr := frame_nr,
		fqc := fqc,
		rfci := rfci,
		header_crc := 0,
		payload_crc := 0,
		payload := payload
	}
};

template IuUP_PDU tr_IuUP_Type0(template IuUP_FrameNr frame_nr := ?, template IuUP_RFCI rfci := ?,
				template IuUP_FQC fqc := ?) := {
	type_0 := {
		pdu_type := 0,
		frame_nr := frame_nr,
		fqc := fqc,
		rfci := rfci,
		header_crc := ?,
		payload_crc := ?,
		payload := ?
	}
};

template IuUP_PDU ts_IuUP_Type1(IuUP_FrameNr frame_nr, IuUP_RFCI rfci, octetstring payload,
				IuUP_FQC fqc := IuUP_FQC_GOOD) := {
	type_1 := {
		pdu_type := 1,
		frame_nr := frame_nr,
		fqc := fqc,
		rfci := rfci,
		header_crc := 0,
		spare := '00'B,
		payload := payload
	}
};


template IuUP_PDU tr_IuUP_Type1(template IuUP_FrameNr frame_nr := ?, template IuUP_RFCI rfci := ?,
				template IuUP_FQC fqc := ?) := {
	type_1 := {
		pdu_type := 1,
		frame_nr := frame_nr,
		fqc := fqc,
		rfci := rfci,
		header_crc := ?,
		spare := ?,
		payload := ?
	}
};



} with { encode "RAW" ; variant "FIELDORDER(msb)" }