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.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,
	uint4_t		iuup_version,
	IuUP_ProcedureIndicator	procedure_ind,
	uint6_t		header_crc,
	uint10_t	payload_crc,
	IuUP_PDU14_Union u
} with { variant (u) "CROSSTAG(	proc_sending,	ack_nack=IuUP_ACKNACK_CTRL;
				ack,		ack_nack=IuUP_ACKNACK_ACK;
				nack,		ack_nack=IuUP_ACKNACK_NACK)"
};

/* 6.6.2.3.1 Figure 21 */
type record IuUP_PDU14_ProcSending {
	octetstring	payload
};

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

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

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

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

/* 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,

	/* FIXME: Further decode */
	octetstring	payload
};
type record IuUP_InitRfci {
	boolean		lri,
	boolean		li,
	IuUP_RFCI	rfci,
	RecOfU8		len8 optional,
	RecOfU16	len16 optional
} with { variant (len8)		"PRESENCE(li=false)";
	 variant (len16)	"PRESENCE(li=true)"
};
type record of uint8_t RecOfU8;
type record of uint16_t RecOfU16;

/* 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,
		iuup_version := version,
		procedure_ind := IuUP_PRI_INITIALIZATION,
		header_crc := 0,
		payload_crc := 0,
		u := {
			ack := {
				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,
		iuup_version := version,
		procedure_ind := IuUP_PRI_INITIALIZATION,
		header_crc := ?,
		payload_crc := ?,
		u := {
			ack := {
				spare_ext := omit
			}
		}
	}
};

template IuUP_PDU ts_IuUP_INIT(octetstring payload, uint2_t frame_nr := 0, uint4_t version := 0) := {
	type_14 := {
		pdu_type := 14,
		ack_nack := IuUP_ACKNACK_CTRL,
		frame_nr := frame_nr,
		iuup_version := version,
		procedure_ind := IuUP_PRI_INITIALIZATION,
		header_crc := 0,
		payload_crc := 0,
		u := {
			proc_sending := {
				payload := payload
			}
		}
	}
};

template IuUP_PDU tr_IuUP_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,
		iuup_version := version,
		procedure_ind := IuUP_PRI_INITIALIZATION,
		header_crc := ?,
		payload_crc := ?,
		u := {
			proc_sending := {
				payload := payload
			}
		}
	}
};


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)" }
