add simplistic IuUP FSM and strip/add IuUP headers

This should really be using the FSM in libosmocore/laforge/iu_up: take the best
of both sides and integate in the libosmocore FSM implementation, then use it
here.
- in libosmocore, the FSM definition is nicer.
- here, we have correct header checksums.

This patch here also adds RTP header stripping/adding functionality, after
introducing using msgb to pass data around.

Change-Id: Ibc70e0aa00476926dd1f4ea8139c34f31f9cdfa3
diff --git a/include/osmocom/mgcp/iuup_protocol.h b/include/osmocom/mgcp/iuup_protocol.h
new file mode 100644
index 0000000..f4aec1f
--- /dev/null
+++ b/include/osmocom/mgcp/iuup_protocol.h
@@ -0,0 +1,117 @@
+/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
+/* IuUP protocol handling, minimal implementation */
+
+#pragma once
+
+#include <osmocom/core/endian.h>
+#include <osmocom/core/msgb.h>
+
+#define OSMO_IUUP_HEADROOM 32
+
+enum osmo_iuup_pdu_type {
+	OSMO_IUUP_PDU_DATA_WITH_CRC = 0,
+	OSMO_IUUP_PDU_CONTROL_PROCEDURE = 14,
+};
+
+enum osmo_iuup_acknack {
+	OSMO_IUUP_ACKNACK_PROCEDURE = 0,
+	OSMO_IUUP_ACKNACK_ACK = 1,
+	OSMO_IUUP_ACKNACK_NACK = 2,
+};
+
+enum osmo_iuup_procedure {
+	OSMO_IUUP_PROC_INITIALIZATION = 0,
+	OSMO_IUUP_PROC_RATE_CONTROL = 1,
+	OSMO_IUUP_PROC_TIME_ALIGNMENT = 2,
+	OSMO_IUUP_PROC_ERROR_EVENT = 3,
+};
+
+enum osmo_iuup_frame_good {
+	OSMO_IUUP_FRAME_GOOD = 0,
+	OSMO_IUUP_FRAME_BAD = 1,
+	OSMO_IUUP_FRAME_BAD_DUE_TO_RADIO = 2,
+};
+
+struct osmo_iuup_hdr_ctrl {
+#if OSMO_IS_BIG_ENDIAN
+	uint8_t pdu_type:4,
+		ack_nack:2,
+		frame_nr:2;
+	uint8_t mode_version:4,
+		procedure:4;
+	uint8_t header_crc:6,
+		payload_crc_hi:2;
+	uint8_t payload_crc_lo;
+	uint8_t payload[0];
+#elif OSMO_IS_LITTLE_ENDIAN
+	uint8_t frame_nr:2,
+		ack_nack:2,
+		pdu_type:4;
+	uint8_t procedure:4,
+		mode_version:4;
+	uint8_t payload_crc_hi:2,
+		header_crc:6;
+	uint8_t payload_crc_lo;
+	uint8_t payload[0];
+#endif
+} __attribute__((packed));
+
+union osmo_iuup_hdr_ctrl_payload {
+	struct {
+#if OSMO_IS_BIG_ENDIAN
+	uint8_t spare:3,
+		iptis_present:1,
+		subflows:3,
+		chain:1;
+#elif OSMO_IS_LITTLE_ENDIAN
+	uint8_t spare:3,
+		iptis_present:1,
+		subflows:3,
+		chain:1;
+#endif
+	} initialization;
+
+	struct {
+#if OSMO_IS_BIG_ENDIAN
+	uint8_t error_distance:2,
+		error_cause:6;
+#elif OSMO_IS_LITTLE_ENDIAN
+	uint8_t error_cause:6,
+		error_distance:2;
+#endif
+	} error_event;
+};
+
+extern const struct value_string osmo_iuup_error_cause_names[];
+static inline const char *osmo_iuup_error_cause_name(uint8_t val)
+{ return get_value_string(osmo_iuup_error_cause_names, val); }
+
+struct osmo_iuup_hdr_data {
+#if OSMO_IS_BIG_ENDIAN
+	uint8_t pdu_type:4,
+		frame_nr:4;
+	uint8_t frame_good:2,
+		rfci:6;
+	uint8_t header_crc:6,
+		payload_crc_hi:2;
+	uint8_t payload_crc_lo;
+#elif OSMO_IS_LITTLE_ENDIAN
+	uint8_t frame_nr:4,
+		pdu_type:4;
+	uint8_t rfci:6,
+		frame_good:2;
+	uint8_t payload_crc_hi:2,
+		header_crc:6;
+	uint8_t payload_crc_lo;
+#endif
+	uint8_t payload[0];
+} __attribute__((packed));
+
+int osmo_iuup_classify(bool log_errors,
+		       const char *log_label,
+		       struct msgb *pdu,
+		       struct osmo_iuup_hdr_ctrl **is_ctrl,
+		       struct osmo_iuup_hdr_data **is_data);
+bool osmo_iuup_is_init(struct msgb *pdu);
+void osmo_iuup_make_init_ack(struct msgb *ack);
+void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len);