#ifndef _GSM48_MM_H
#define _GSM48_MM_H

struct gsm_settings;

/* GSM 04.07 9.2.2 */
#define GSM48_MMXX_MASK			0xf00
#define GSM48_MMCC_CLASS		0x100
#define GSM48_MMSS_CLASS		0x200
#define GSM48_MMSMS_CLASS		0x300
#define GSM48_MMGCC_CLASS		0x500
#define GSM48_MMBCC_CLASS		0x600
#define GSM48_MMXX_REL_IND		0x022
#define GSM48_MMCC_EST_REQ		0x110
#define GSM48_MMCC_EST_IND		0x112
#define GSM48_MMCC_EST_CNF		0x111
#define GSM48_MMCC_REL_REQ		0x120
#define GSM48_MMCC_REL_IND		0x122
#define GSM48_MMCC_DATA_REQ		0x130
#define GSM48_MMCC_DATA_IND		0x132
#define GSM48_MMCC_UNIT_DATA_REQ	0x140
#define GSM48_MMCC_UNIT_DATA_IND	0x142
#define GSM48_MMCC_SYNC_IND		0x152
#define GSM48_MMCC_REEST_REQ		0x160
#define GSM48_MMCC_REEST_CNF		0x161
#define GSM48_MMCC_ERR_IND		0x172
#define GSM48_MMCC_PROMPT_IND		0x182
#define GSM48_MMCC_PROMPT_REJ		0x184
#define GSM48_MMSS_EST_REQ		0x210
#define GSM48_MMSS_EST_IND		0x212
#define GSM48_MMSS_EST_CNF		0x211
#define GSM48_MMSS_REL_REQ		0x220
#define GSM48_MMSS_REL_IND		0x222
#define GSM48_MMSS_DATA_REQ		0x230
#define GSM48_MMSS_DATA_IND		0x232
#define GSM48_MMSS_UNIT_DATA_REQ	0x240
#define GSM48_MMSS_UNIT_DATA_IND	0x242
#define GSM48_MMSS_REEST_REQ		0x260
#define GSM48_MMSS_REEST_CNF		0x261
#define GSM48_MMSS_ERR_IND		0x272
#define GSM48_MMSS_PROMPT_IND		0x282
#define GSM48_MMSS_PROMPT_REJ		0x284
#define GSM48_MMSMS_EST_REQ		0x310
#define GSM48_MMSMS_EST_IND		0x312
#define GSM48_MMSMS_EST_CNF		0x311
#define GSM48_MMSMS_REL_REQ		0x320
#define GSM48_MMSMS_REL_IND		0x322
#define GSM48_MMSMS_DATA_REQ		0x330
#define GSM48_MMSMS_DATA_IND		0x332
#define GSM48_MMSMS_UNIT_DATA_REQ	0x340
#define GSM48_MMSMS_UNIT_DATA_IND	0x342
#define GSM48_MMSMS_REEST_REQ		0x360
#define GSM48_MMSMS_REEST_CNF		0x361
#define GSM48_MMSMS_ERR_IND		0x372
#define GSM48_MMSMS_PROMPT_IND		0x382
#define GSM48_MMSMS_PROMPT_REJ		0x384
/* MM messages for Voice Group/Broadcast Calls */
#define GSM48_MMGCC_EST_REQ		0x510
#define GSM48_MMGCC_EST_CNF		0x511
#define GSM48_MMGCC_REL_REQ		0x520
#define GSM48_MMGCC_REL_IND		0x522
#define GSM48_MMGCC_DATA_REQ		0x530
#define GSM48_MMGCC_DATA_IND		0x532
#define GSM48_MMGCC_UNIT_DATA_REQ	0x540
#define GSM48_MMGCC_UNIT_DATA_IND	0x542
#define GSM48_MMGCC_REEST_REQ		0x560
#define GSM48_MMGCC_REEST_CNF		0x561
#define GSM48_MMGCC_ERR_IND		0x572
#define GSM48_MMGCC_NOTIF_IND		0x582
#define GSM48_MMGCC_GROUP_REQ		0x590
#define GSM48_MMGCC_GROUP_CNF		0x591
#define GSM48_MMGCC_UPLINK_REQ		0x5a0
#define GSM48_MMGCC_UPLINK_CNF		0x5a1
#define GSM48_MMGCC_UPLINK_REL_REQ	0x5a8
#define GSM48_MMGCC_UPLINK_REL_IND	0x5aa
#define GSM48_MMGCC_UPLINK_FREE_IND	0x5b2
#define GSM48_MMGCC_UPLINK_BUSY_IND	0x5b6
#define GSM48_MMBCC_EST_REQ		0x610
#define GSM48_MMBCC_EST_CNF		0x611
#define GSM48_MMBCC_REL_REQ		0x620
#define GSM48_MMBCC_REL_IND		0x622
#define GSM48_MMBCC_DATA_REQ		0x630
#define GSM48_MMBCC_DATA_IND		0x632
#define GSM48_MMBCC_UNIT_DATA_REQ	0x640
#define GSM48_MMBCC_UNIT_DATA_IND	0x642
#define GSM48_MMBCC_REEST_REQ		0x660
#define GSM48_MMBCC_REEST_CNF		0x661
#define GSM48_MMBCC_ERR_IND		0x672
#define GSM48_MMBCC_NOTIF_IND		0x682
#define GSM48_MMBCC_GROUP_REQ		0x690
#define GSM48_MMBCC_GROUP_CNF		0x691
#define GSM48_MMBCC_UPLINK_REQ		0x6a0
#define GSM48_MMBCC_UPLINK_CNF		0x6a1
#define GSM48_MMBCC_UPLINK_REL_REQ	0x6a8
#define GSM48_MMBCC_UPLINK_REL_IND	0x6aa
#define GSM48_MMBCC_UPLINK_FREE_IND	0x6b2
#define GSM48_MMBCC_UPLINK_BUSY_IND	0x6b6


#define MMXX_ALLOC_SIZE			256
#define MMXX_ALLOC_HEADROOM		64

#define MMXX_NOTIFY_SETUP		0
#define MMXX_NOTIFY_RELEASE		1

/* MMxx-SAP header */
struct gsm48_mmxx_hdr {
	uint16_t		msg_type; /* MMxx_* primitive */
	uint32_t		ref; /* reference to transaction */
	uint32_t		transaction_id; /* transaction identifier */
	uint8_t			sapi; /* sapi */
	uint8_t			emergency; /* emergency type of call */
	uint8_t			cause; /* cause used for release */
	uint8_t			notify; /* notify ongoing ASCI call */
	bool			ch_desc_present; /* notifies channel */
	struct gsm48_chan_desc	ch_desc; /* group channel */
} __attribute__((packed));

/* GSM 6.1.2 */
#define	GSM48_MMR_REG_REQ		0x01
#define	GSM48_MMR_REG_CNF		0x02
#define	GSM48_MMR_NREG_REQ		0x03
#define	GSM48_MMR_NREG_IND		0x04

/* MMR-SAP header */
struct gsm48_mmr {
	uint8_t		msg_type;
	uint8_t		cause;
} __attribute__((packed));

/* GSM 04.07 9.2.1 */
#define GSM48_MMXX_ST_IDLE		0
#define GSM48_MMXX_ST_CONN_PEND		1
#define GSM48_MMXX_ST_DEDICATED		2
#define GSM48_MMXX_ST_CONN_SUSP		3
#define GSM48_MMXX_ST_REESTPEND		4

/* GSM 04.08 4.1.2.1 */
#define	GSM48_MM_ST_NULL		0
#define GSM48_MM_ST_LOC_UPD_INIT	3
#define GSM48_MM_ST_WAIT_OUT_MM_CONN	5
#define GSM48_MM_ST_MM_CONN_ACTIVE	6
#define GSM48_MM_ST_IMSI_DETACH_INIT	7
#define GSM48_MM_ST_PROCESS_CM_SERV_P	8
#define GSM48_MM_ST_WAIT_NETWORK_CMD	9
#define GSM48_MM_ST_LOC_UPD_REJ		10
#define GSM48_MM_ST_WAIT_RR_CONN_LUPD	13
#define GSM48_MM_ST_WAIT_RR_CONN_MM_CON	14
#define GSM48_MM_ST_WAIT_RR_CONN_IMSI_D	15
#define GSM48_MM_ST_WAIT_REEST		17
#define GSM48_MM_ST_WAIT_RR_ACTIVE	18
#define GSM48_MM_ST_MM_IDLE		19
#define GSM48_MM_ST_WAIT_ADD_OUT_MM_CON	20
#define GSM48_MM_ST_MM_CONN_ACTIVE_VGCS	21
#define GSM48_MM_ST_WAIT_RR_CONN_VGCS	22
#define GSM48_MM_ST_LOC_UPD_PEND	23
#define GSM48_MM_ST_IMSI_DETACH_PEND	24
#define GSM48_MM_ST_RR_CONN_RELEASE_NA	25

/* GSM 04.08 4.1.2.1 */
#define GSM48_MM_SST_NORMAL_SERVICE	1
#define GSM48_MM_SST_ATTEMPT_UPDATE	2
#define GSM48_MM_SST_LIMITED_SERVICE	3
#define GSM48_MM_SST_NO_IMSI		4
#define GSM48_MM_SST_NO_CELL_AVAIL	5
#define GSM48_MM_SST_LOC_UPD_NEEDED	6
#define GSM48_MM_SST_PLMN_SEARCH	7
#define GSM48_MM_SST_PLMN_SEARCH_NORMAL	8
#define GSM48_MM_SST_RX_VGCS_NORMAL	9
#define GSM48_MM_SST_RX_VGCS_LIMITED	10

/* MM events */
#define GSM48_MM_EVENT_CELL_SELECTED	1
#define GSM48_MM_EVENT_NO_CELL_FOUND	2
#define GSM48_MM_EVENT_TIMEOUT_T3210	3
#define GSM48_MM_EVENT_TIMEOUT_T3211	4
#define GSM48_MM_EVENT_TIMEOUT_T3212	5
#define GSM48_MM_EVENT_TIMEOUT_T3213	6
#define GSM48_MM_EVENT_TIMEOUT_T3220	7
#define GSM48_MM_EVENT_TIMEOUT_T3230	8
#define GSM48_MM_EVENT_TIMEOUT_T3240	9
#define GSM48_MM_EVENT_IMSI_DETACH	10
#define GSM48_MM_EVENT_POWER_OFF	11
#define GSM48_MM_EVENT_PAGING		12
#define GSM48_MM_EVENT_AUTH_RESPONSE	13
#define GSM48_MM_EVENT_SYSINFO		14
#define GSM48_MM_EVENT_USER_PLMN_SEL	15
#define GSM48_MM_EVENT_LOST_COVERAGE	16
#define GSM48_MM_EVENT_NOTIFICATION	17
#define GSM48_MM_EVENT_UPLINK_FREE	18
#define GSM48_MM_EVENT_UPLINK_BUSY	19

/* message for MM events */
struct gsm48_mm_event {
	uint32_t		msg_type;

	union {
		/* GSM48_MM_EVENT_AUTH_RESPONSE */
		uint8_t			sres[4];
		/* GSM48_MM_EVENT_NOTIFICATION */
		struct {
			uint8_t			gcr[5];
			bool			ch_desc_present;
			struct gsm48_chan_desc	ch_desc;
			bool			gone;
		} __attribute__((packed)) notification;
	};
} __attribute__((packed));

/* GSM 04.08 MM timers */
#define GSM_T3210_MS    20, 0
#define GSM_T3211_MS    15, 0
/* T3212 is given by SYSTEM INFORMATION */
#define GSM_T3213_MS    4, 0
#define GSM_T3220_MS    5, 0
#define GSM_T3230_MS    15, 0
#define GSM_T3240_MS    10, 0
#define GSM_T3241_MS    300, 0

/* MM sublayer instance */
struct gsm48_mmlayer {
	struct osmocom_ms	*ms;
	int			state;
	int			substate;

	/* queue for RR-SAP, MMxx-SAP, MMR-SAP, events message upwards */
	struct llist_head       rr_upqueue;
	struct llist_head       mmxx_upqueue;
	struct llist_head       mmr_downqueue;
	struct llist_head       event_queue;

	/* timers */
	struct osmo_timer_list	t3210, t3211, t3212, t3213;
	struct osmo_timer_list	t3220, t3230, t3240;
	int			t3212_value;
	int			start_t3211; /* remember to start timer */

	/* list of MM connections */
	struct llist_head	mm_conn;

	/* network name */
	char			name_short[32];
	char			name_long[32];

	/* location update */
	uint8_t			lupd_pending;	/* current pending loc. upd. */
	uint8_t			lupd_type;	/* current coded type */
	uint8_t			lupd_attempt;	/* attempt counter */
	uint8_t			lupd_ra_failure;/* random access failed */
	uint8_t			lupd_rej_cause;	/* cause of last reject */
	uint8_t			lupd_periodic;	/* periodic update pending */
	uint8_t			lupd_retry;	/* pending T3211/T3213 to */
	struct osmo_location_area_id lupd_lai;

	/* imsi detach */
	uint8_t			delay_detach;	/* do detach when possible */

	/* other */
	uint8_t			est_cause; /* cause of establishment msg */
	int			mr_substate;	/* rem most recent substate */
	uint8_t			power_off_idle; /* waits for IDLE before po */

	/* sapi 3 */
	int			sapi3_link;

	/* VGCS additional states */
	struct {
		bool			enabled;		/* We are in group/broadcast mode. */
		bool			group_call;		/* This is a group call, not a broadcast call. */
		uint32_t		callref;		/* Callref of this call. */
		bool			normal_service;		/* Service state before group transmit mode. */
	} vgcs;
};

/* MM connection entry */
struct gsm48_mm_conn {
	struct llist_head	list;
	struct gsm48_mmlayer	*mm;

	/* ref and protocol form a unique tuple */
	uint32_t		ref; /* reference to trans */
	uint8_t			protocol;
	uint8_t			transaction_id;
	uint8_t			sapi;

	int			state;
};

int gsm48_encode_mi_lv(struct osmocom_ms *ms, struct msgb *msg, uint8_t mi_type, bool emergency_imsi);
int gsm48_encode_mi_tlv(struct osmocom_ms *ms, struct msgb *msg, uint8_t mi_type, bool emergency_imsi);
uint8_t gsm48_current_pwr_lev(struct gsm_settings *set, uint16_t arfcn);
int gsm48_mm_init(struct osmocom_ms *ms);
int gsm48_mm_exit(struct osmocom_ms *ms);
struct msgb *gsm48_mmr_msgb_alloc(int msg_type);
struct msgb *gsm48_mmevent_msgb_alloc(int msg_type);
int gsm48_mmevent_msg(struct osmocom_ms *ms, struct msgb *msg);
int gsm48_mmr_downmsg(struct osmocom_ms *ms, struct msgb *msg);
int gsm48_rr_dequeue(struct osmocom_ms *ms);
int gsm48_mmxx_dequeue(struct osmocom_ms *ms);
int gsm48_mmr_dequeue(struct osmocom_ms *ms);
int gsm48_mmevent_dequeue(struct osmocom_ms *ms);
int gsm48_mmxx_downmsg(struct osmocom_ms *ms, struct msgb *msg);
struct msgb *gsm48_mmxx_msgb_alloc(int msg_type, uint32_t ref,
	uint8_t transaction_id, uint8_t sapi);
const char *get_mmr_name(int value);
const char *get_mm_name(int value);
const char *get_mmxx_name(int value);
extern const char *gsm48_mm_state_names[];
extern const char *gsm48_mm_substate_names[];

#endif /* _GSM48_MM_H */
