/* GPRS SNDCP XID field encoding/decoding as per 3GPP TS 44.065 */

/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 * All Rights Reserved
 *
 * Author: Philipp Maier
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <stdint.h>
#include <osmocom/core/linuxlist.h>

#define DEFAULT_SNDCP_VERSION 0	/* See 3GPP TS 44.065, clause 8 */
#define MAX_ENTITIES 32		/* 3GPP TS 44.065 reserves 5 bit
				 * for compression enitity number */

#define MAX_COMP 16	/* Maximum number of possible pcomp/dcomp values */
#define MAX_NSAPI 11	/* Maximum number usable NSAPIs */
#define MAX_ROHC 16	/* Maximum number of ROHC compression profiles */

/* According to: 3GPP TS 44.065, 6.5.1.1.4 Algorithm identifier */
enum gprs_sndcp_hdr_comp_algo {
	RFC_1144,		/* TCP/IP header compression, see also 6.5.2 */
	RFC_2507,		/* TCP/UDP/IP header compression, see also: 6.5.3 */
	ROHC			/* Robust Header Compression, see also 6.5.4 */
};

/* According to: 3GPP TS 44.065, 6.5.1.1.4 Algorithm identifier */
enum gprs_sndcp_data_comp_algo {
	V42BIS,			/* V.42bis data compression, see also 6.6.2 */
	V44			/* V44 data compression, see also: 6.6.3 */
};

union gprs_sndcp_comp_algo {
	enum gprs_sndcp_hdr_comp_algo pcomp;
	enum gprs_sndcp_data_comp_algo dcomp;
};

/* According to: 3GPP TS 44.065, 6.5.1.1 Format of the protocol control
 * information compression field (Figure 7) and 3GPP TS 44.065, 
 * 6.6.1.1 Format of the data compression field (Figure 9) */
struct gprs_sndcp_comp_field {
	struct llist_head list;

	/* Propose bit (P), see also: 6.5.1.1.2 and 6.6.1.1.2 */
	unsigned int p;

	/* Entity number, see also: 6.5.1.1.3 and 6.6.1.1.3 */
	unsigned int entity;

	/* Algorithm identifier, see also: 6.5.1.1.4 and 6.6.1.1.4 */
	union gprs_sndcp_comp_algo algo;

	/* Number of contained PCOMP / DCOMP values */
	uint8_t comp_len;

	/* PCOMP / DCOMP values, see also: 6.5.1.1.5 and 6.6.1.1.5 */
	uint8_t comp[MAX_COMP];

	/* Note: Only one of the following struct pointers may,
	   be used. Unused pointers must be set to NULL! */
	struct gprs_sndcp_pcomp_rfc1144_params *rfc1144_params;
	struct gprs_sndcp_pcomp_rfc2507_params *rfc2507_params;
	struct gprs_sndcp_pcomp_rohc_params *rohc_params;
	struct gprs_sndcp_dcomp_v42bis_params *v42bis_params;
	struct gprs_sndcp_dcomp_v44_params *v44_params;
};

/* According to: 3GPP TS 44.065, 8 SNDCP XID parameters */
enum gprs_sndcp_xid_param_types {
	SNDCP_XID_VERSION_NUMBER,
	SNDCP_XID_DATA_COMPRESSION,	/* See also: subclause 6.6.1 */
	SNDCP_XID_PROTOCOL_COMPRESSION,	/* See also: subclause 6.5.1 */
	SNDCP_XID_INVALID_COMPRESSION   /* Not part of the spec; this means we found an invalid value */
};

/* According to: 3GPP TS 44.065, 6.5.2.1 Parameters (Table 5) */
struct gprs_sndcp_pcomp_rfc1144_params {
	uint8_t nsapi_len;		/* Number of applicable NSAPIs
					 * (default 0) */
	uint8_t nsapi[MAX_NSAPI];	/* Applicable NSAPIs (default 0) */
	int s01;			/* (default 15) */
};

/* According to: 3GPP TS 44.065, 6.5.2.2 Assignment of PCOMP values */
enum gprs_sndcp_pcomp_rfc1144_pcomp {
	RFC1144_PCOMP1,			/* Uncompressed TCP */
	RFC1144_PCOMP2,			/* Compressed TCP */
	RFC1144_PCOMP_NUM		/* Number of pcomp values */
};

/* According to: 3GPP TS 44.065, 6.5.3.1 Parameters (Table 6) */
struct gprs_sndcp_pcomp_rfc2507_params {
	uint8_t nsapi_len;		/* Number of applicable NSAPIs
					 * (default 0) */
	uint8_t nsapi[MAX_NSAPI];	/* Applicable NSAPIs (default 0) */
	int f_max_period;		/* (default 256) */
	int f_max_time;			/* (default 5) */
	int max_header;			/* (default 168) */
	int tcp_space;			/* (default 15) */
	int non_tcp_space;		/* (default 15) */
};

/* According to: 3GPP TS 44.065, 6.5.3.2 Assignment of PCOMP values for RFC2507 */
enum gprs_sndcp_pcomp_rfc2507_pcomp {
	RFC2507_PCOMP1,			/* Full Header */
	RFC2507_PCOMP2,			/* Compressed TCP */
	RFC2507_PCOMP3,			/* Compressed TCP non delta */
	RFC2507_PCOMP4,			/* Compressed non TCP */
	RFC2507_PCOMP5,			/* Context state */
	RFC2507_PCOMP_NUM		/* Number of pcomp values */
};

/* According to: 3GPP TS 44.065, 6.5.4.1 Parameter (Table 10) */
struct gprs_sndcp_pcomp_rohc_params {
	uint8_t nsapi_len;		/* Number of applicable NSAPIs
					 * (default 0) */
	uint8_t nsapi[MAX_NSAPI];	/* Applicable NSAPIs (default 0) */
	int max_cid;			/* (default 15) */
	int max_header;			/* (default 168) */
	uint8_t profile_len;		/* (default 1) */
	uint16_t profile[MAX_ROHC];	/* (default 0, ROHC uncompressed) */
};

/* According to: 3GPP TS 44.065, 6.5.4.2 Assignment of PCOMP values for ROHC */
enum gprs_sndcp_pcomp_rohc_pcomp {
	ROHC_PCOMP1,			/* ROHC small CIDs */
	ROHC_PCOMP2,			/* ROHC large CIDs */
	ROHC_PCOMP_NUM			/* Number of pcomp values */
};

/* ROHC compression profiles, see also:
   http://www.iana.org/assignments/rohc-pro-ids/rohc-pro-ids.xhtml */
enum gprs_sndcp_xid_rohc_profiles {
	ROHC_UNCOMPRESSED = 0x0000,	/* ROHC uncompressed    [RFC5795] */
	ROHC_RTP = 0x0001,		/* ROHC RTP             [RFC3095] */
	ROHCV2_RTP = 0x0101,		/* ROHCv2 RTP           [RFC5225] */
	ROHC_UDP = 0x0002,		/* ROHC UDP             [RFC3095] */
	ROHCv2_UDP = 0x0102,		/* ROHCv2 UDP           [RFC5225] */
	ROHC_ESP = 0x0003,		/* ROHC ESP             [RFC3095] */
	ROHCV2_ESP = 0x0103,		/* ROHCv2 ESP           [RFC5225] */
	ROHC_IP = 0x0004,		/* ROHC IP              [RFC3843] */
	ROHCV2_IP = 0x0104,		/* ROHCv2 IP            [RFC5225] */
	ROHC_LLA = 0x0005,		/* ROHC LLA             [RFC4362] */
	ROHC_LLA_WITH_R_MODE = 0x0105,	/* ROHC LLA with R-mode [RFC3408] */
	ROHC_TCP = 0x0006,		/* ROHC TCP             [RFC6846] */
	ROHC_RTP_UDP_LITE = 0x0007,	/* ROHC RTP/UDP-Lite    [RFC4019] */
	ROHCV2_RTP_UDP_LITE = 0x0107,	/* ROHCv2 RTP/UDP-Lite  [RFC5225] */
	ROHC_UDP_LITE = 0x0008,		/* ROHC UDP-Lite        [RFC4019] */
	ROHCV2_UDP_LITE = 0x0108,	/* ROHCv2 UDP-Lite      [RFC5225] */
};

/* According to: 3GPP TS 44.065, 6.6.2.1 Parameters (Table 7a) */
struct gprs_sndcp_dcomp_v42bis_params {
	uint8_t nsapi_len;		/* Number of applicable NSAPIs
					 * (default 0) */
	uint8_t nsapi[MAX_NSAPI];	/* Applicable NSAPIs (default 0) */
	int p0;				/* (default 3) */
	int p1;				/* (default 2048) */
	int p2;				/* (default 20) */

};

/* According to: 3GPP TS 44.065, 6.6.2.2 Assignment of DCOMP values */
enum gprs_sndcp_dcomp_v42bis_dcomp {
	V42BIS_DCOMP1,			/* V.42bis enabled */
	V42BIS_DCOMP_NUM		/* Number of dcomp values */
};

/* According to: 3GPP TS 44.065, 6.6.3.1 Parameters (Table 7c) */
struct gprs_sndcp_dcomp_v44_params {
	uint8_t nsapi_len;		/* Number of applicable NSAPIs
					 * (default 0) */
	uint8_t nsapi[MAX_NSAPI];	/* Applicable NSAPIs (default 0) */
	int c0;				/* (default 10000000) */
	int p0;				/* (default 3) */
	int p1t;			/* Refer to subclause 6.6.3.1.4 */
	int p1r;			/* Refer to subclause 6.6.3.1.5 */
	int p3t;			/* (default 3 x p1t) */
	int p3r;			/* (default 3 x p1r) */
};

/* According to: 3GPP TS 44.065, 6.6.3.2 Assignment of DCOMP values */
enum gprs_sndcp_dcomp_v44_dcomp {
	V44_DCOMP1,			/* Packet method compressed */
	V44_DCOMP2,			/* Multi packet method compressed */
	V44_DCOMP_NUM			/* Number of dcomp values */
};

/* Transform a list with compression fields into an SNDCP-XID message (dst) */
int gprs_sndcp_compile_xid(uint8_t *dst, unsigned int dst_maxlen,
			   const struct llist_head *comp_fields, int version);

/* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */
struct llist_head *gprs_sndcp_parse_xid(int *version,
					const void *ctx,
					const uint8_t *src,
					unsigned int src_len,
					const struct llist_head
					*comp_fields_req);

/* Find out to which compression class the specified comp-field belongs
 * (header compression or data compression?) */
enum gprs_sndcp_xid_param_types gprs_sndcp_get_compression_class(
				const struct gprs_sndcp_comp_field *comp_field);

/* Dump a list with SNDCP-XID fields (Debug) */
void gprs_sndcp_dump_comp_fields(const struct llist_head *comp_fields,
				 unsigned int logl);