/*
 * Copyright (c) 2016 Nordic Semiconductor ASA
 * Copyright (c) 2016 Vinayak Kariappa Chettimada
 *
 * SPDX-License-Identifier: Apache-2.0
 */

enum llcp {
	LLCP_NONE,
	LLCP_CONN_UPD,
	LLCP_CHAN_MAP,

#if defined(CONFIG_BT_CTLR_LE_ENC)
	LLCP_ENCRYPTION,
#endif /* CONFIG_BT_CTLR_LE_ENC */

	LLCP_FEATURE_EXCHANGE,
	LLCP_VERSION_EXCHANGE,
	/* LLCP_TERMINATE, */
	LLCP_CONNECTION_PARAM_REQ,

#if defined(CONFIG_BT_CTLR_LE_PING)
	LLCP_PING,
#endif /* CONFIG_BT_CTLR_LE_PING */

#if defined(CONFIG_BT_CTLR_PHY)
	LLCP_PHY_UPD,
#endif /* CONFIG_BT_CTLR_PHY */
};


struct shdr {
	u32_t ticks_xtal_to_start;
	u32_t ticks_active_to_start;
	u32_t ticks_preempt_to_start;
	u32_t ticks_slot;
};

struct connection {
	struct shdr hdr;

	u8_t  access_addr[4];
	u8_t  crc_init[3];
	u8_t  data_chan_map[5];
	u8_t  chm_update;

	u8_t  data_chan_count:6;
	u8_t  data_chan_sel:1;
	u8_t  role:1;

	union {
		struct {
			u8_t data_chan_hop;
			u8_t data_chan_use;
		};

		u16_t data_chan_id;
	};

	u16_t handle;
	u16_t event_counter;
	u16_t conn_interval;
	u16_t latency;
	u16_t latency_prepare;
	u16_t latency_event;

#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
	u16_t default_tx_octets;
	u16_t max_tx_octets;
	u16_t max_rx_octets;

#if defined(CONFIG_BT_CTLR_PHY)
	u16_t default_tx_time;
	u16_t max_tx_time;
	u16_t max_rx_time;
#endif /* CONFIG_BT_CTLR_PHY */
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */

#if defined(CONFIG_BT_CTLR_PHY)
	u8_t phy_pref_tx:3;
	u8_t phy_tx:3;
	u8_t phy_pref_flags:1;
	u8_t phy_flags:1;
	u8_t phy_tx_time:3;

	u8_t phy_pref_rx:3;
	u8_t phy_rx:3;
#endif /* CONFIG_BT_CTLR_PHY */

	u16_t connect_expire;
	u16_t supervision_reload;
	u16_t supervision_expire;
	u16_t procedure_reload;
	u16_t procedure_expire;

#if defined(CONFIG_BT_CTLR_LE_PING)
	u16_t appto_reload;
	u16_t appto_expire;
	u16_t apto_reload;
	u16_t apto_expire;
#endif /* CONFIG_BT_CTLR_LE_PING */

	union {
		struct {
			u8_t reserved:5;
			u8_t fex_valid:1;
		} common;

		struct {
			u8_t terminate_ack:1;
			u8_t rfu:4;
			u8_t fex_valid:1;
		} master;

		struct {
			u8_t  latency_enabled:1;
			u8_t  latency_cancel:1;
			u8_t  sca:3;
			u8_t  fex_valid:1;
			u32_t window_widening_periodic_us;
			u32_t window_widening_max_us;
			u32_t window_widening_prepare_us;
			u32_t window_widening_event_us;
			u32_t window_size_prepare_us;
			u32_t window_size_event_us;
			u32_t force;
			u32_t ticks_to_offset;
		} slave;
	};

	u8_t  llcp_req;
	u8_t  llcp_ack;
	enum  llcp llcp_type;
	union {
		struct {
			enum {
				LLCP_CUI_STATE_INPROG,
				LLCP_CUI_STATE_USE,
				LLCP_CUI_STATE_SELECT
			} state:2 __packed;
			u8_t  is_internal:1;
			u16_t interval;
			u16_t latency;
			u16_t timeout;
			u16_t instant;
			u32_t win_offset_us;
			u8_t  win_size;
			u16_t *pdu_win_offset;
			u32_t ticks_anchor;
		} conn_upd;
		struct {
			u8_t  initiate;
			u8_t  chm[5];
			u16_t instant;
		} chan_map;

#if defined(CONFIG_BT_CTLR_PHY)
		struct {
			u8_t initiate:1;
			u8_t cmd:1;
			u8_t tx:3;
			u8_t rx:3;
			u16_t instant;
		} phy_upd_ind;
#endif /* CONFIG_BT_CTLR_PHY */

#if defined(CONFIG_BT_CTLR_LE_ENC)
		struct {
			u8_t  initiate;
			u8_t  error_code;
			u8_t  skd[16];
		} encryption;
#endif /* CONFIG_BT_CTLR_LE_ENC */
	} llcp;

	u32_t llcp_features;

	struct {
		u8_t  tx:1;
		u8_t  rx:1;
		u8_t  version_number;
		u16_t company_id;
		u16_t sub_version_number;
	} llcp_version;

	struct {
		u8_t req;
		u8_t ack;
		u8_t reason_own;
		u8_t reason_peer;
		struct {
			struct radio_pdu_node_rx_hdr hdr;
			u8_t reason;
		} radio_pdu_node_rx;
	} llcp_terminate;

#if defined(CONFIG_BT_CTLR_LE_ENC)
	struct {
		u8_t req;
		u8_t ack;
		u8_t ediv[2];
		u8_t rand[8];
		u8_t ltk[16];
	} llcp_enc;
#endif /* CONFIG_BT_CTLR_LE_ENC */

#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
	struct {
		u8_t  req;
		u8_t  ack;
		enum {
			LLCP_CPR_STATE_REQ,
			LLCP_CPR_STATE_RSP,
			LLCP_CPR_STATE_APP_REQ,
			LLCP_CPR_STATE_APP_WAIT,
			LLCP_CPR_STATE_RSP_WAIT,
			LLCP_CPR_STATE_UPD
		} state:3 __packed;
		u8_t  cmd:1;
		u8_t  disabled:1;
		u8_t  status;
		u16_t interval_min;
		u16_t interval_max;
		u16_t latency;
		u16_t timeout;
		u8_t  preferred_periodicity;
		u16_t reference_conn_event_count;
		u16_t offset0;
		u16_t offset1;
		u16_t offset2;
		u16_t offset3;
		u16_t offset4;
		u16_t offset5;
		u16_t *pdu_win_offset0;
		u32_t ticks_ref;
		u32_t ticks_to_offset_next;
	} llcp_conn_param;
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */

#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
	struct {
		u8_t  req;
		u8_t  ack;
		u8_t  state:2;
#define LLCP_LENGTH_STATE_REQ        0
#define LLCP_LENGTH_STATE_ACK_WAIT   1
#define LLCP_LENGTH_STATE_RSP_WAIT   2
#define LLCP_LENGTH_STATE_RESIZE     3
		u8_t  pause_tx:1;
		u16_t rx_octets;
		u16_t tx_octets;
#if defined(CONFIG_BT_CTLR_PHY)
		u16_t rx_time;
		u16_t tx_time;
#endif /* CONFIG_BT_CTLR_PHY */
	} llcp_length;
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */

#if defined(CONFIG_BT_CTLR_PHY)
	struct {
		u8_t req;
		u8_t ack;
		u8_t state:2;
#define LLCP_PHY_STATE_REQ      0
#define LLCP_PHY_STATE_ACK_WAIT 1
#define LLCP_PHY_STATE_RSP_WAIT 2
#define LLCP_PHY_STATE_UPD      3
		u8_t tx:3;
		u8_t rx:3;
		u8_t flags:1;
		u8_t cmd:1;
	} llcp_phy;
#endif /* CONFIG_BT_CTLR_PHY */

	u8_t  sn:1;
	u8_t  nesn:1;
	u8_t  pause_rx:1;
	u8_t  pause_tx:1;
	u8_t  enc_rx:1;
	u8_t  enc_tx:1;
	u8_t  refresh:1;
	u8_t  empty:1;

	struct ccm ccm_rx;
	struct ccm ccm_tx;

	struct radio_pdu_node_tx *pkt_tx_head;
	struct radio_pdu_node_tx *pkt_tx_ctrl;
	struct radio_pdu_node_tx *pkt_tx_ctrl_last;
	struct radio_pdu_node_tx *pkt_tx_data;
	struct radio_pdu_node_tx *pkt_tx_last;
	u8_t  packet_tx_head_len;
	u8_t  packet_tx_head_offset;

#if defined(CONFIG_BT_CTLR_CONN_RSSI)
	u8_t  rssi_latest;
	u8_t  rssi_reported;
	u8_t  rssi_sample_count;
#endif /* CONFIG_BT_CTLR_CONN_RSSI */
};
#define CONNECTION_T_SIZE MROUND(sizeof(struct connection))

struct pdu_data_q_tx {
	u16_t  handle;
	struct radio_pdu_node_tx *node_tx;
};

/* Minimum Rx Data allocation size */
#define PACKET_RX_DATA_SIZE_MIN \
			MROUND(offsetof(struct radio_pdu_node_rx, pdu_data) + \
			(PDU_AC_SIZE_MAX + PDU_AC_SIZE_EXTRA))

/* Minimum Tx Ctrl allocation size */
#define PACKET_TX_CTRL_SIZE_MIN \
			MROUND(offsetof(struct radio_pdu_node_tx, pdu_data) + \
			offsetof(struct pdu_data, lldata) + 27)

/** @todo fix starvation when ctrl rx in radio ISR
 * for multiple connections needs to tx back to peer.
 */
#define PACKET_MEM_COUNT_TX_CTRL	2

#define LL_MEM_CONN (sizeof(struct connection) * RADIO_CONNECTION_CONTEXT_MAX)

#define LL_MEM_RXQ (sizeof(void *) * (RADIO_PACKET_COUNT_RX_MAX + 4))
#define LL_MEM_TXQ (sizeof(struct pdu_data_q_tx) * \
		    (RADIO_PACKET_COUNT_TX_MAX + 2))

#define LL_MEM_RX_POOL_SZ (MROUND(offsetof(struct radio_pdu_node_rx, \
					   pdu_data) + \
				  MAX((PDU_AC_SIZE_MAX + PDU_AC_SIZE_EXTRA), \
				      (offsetof(struct pdu_data, lldata) + \
				       LL_LENGTH_OCTETS_RX_MAX))) * \
			   (RADIO_PACKET_COUNT_RX_MAX + 3))

#define LL_MEM_RX_LINK_POOL (sizeof(void *) * 2 * ((RADIO_PACKET_COUNT_RX_MAX +\
				4) + RADIO_CONNECTION_CONTEXT_MAX))

#define LL_MEM_TX_CTRL_POOL (PACKET_TX_CTRL_SIZE_MIN * PACKET_MEM_COUNT_TX_CTRL)
#define LL_MEM_TX_DATA_POOL ((MROUND(offsetof( \
					struct radio_pdu_node_tx, pdu_data) + \
		   offsetof(struct pdu_data, lldata) + \
				RADIO_PACKET_TX_DATA_SIZE)) \
			* (RADIO_PACKET_COUNT_TX_MAX + 1))

#define LL_MEM_TOTAL (LL_MEM_CONN + LL_MEM_RXQ + (LL_MEM_TXQ * 2) + \
		LL_MEM_RX_POOL_SZ + \
		LL_MEM_RX_LINK_POOL + LL_MEM_TX_CTRL_POOL + LL_MEM_TX_DATA_POOL)
