/*-
 *   BSD LICENSE
 *
 *   Copyright (c) Intel Corporation.
 *   All rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *     * Neither the name of Intel Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef SPDK_NVMF_SPEC_H
#define SPDK_NVMF_SPEC_H

#include "spdk/stdinc.h"

#include "spdk/assert.h"
#include "spdk/nvme_spec.h"

/**
 * \file
 * NVMe over Fabrics specification definitions
 */

#pragma pack(push, 1)

struct spdk_nvmf_capsule_cmd {
	uint8_t		opcode;
	uint8_t		reserved1;
	uint16_t	cid;
	uint8_t		fctype;
	uint8_t		reserved2[35];
	uint8_t		fabric_specific[24];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_capsule_cmd) == 64, "Incorrect size");

/* Fabric Command Set */
#define SPDK_NVME_OPC_FABRIC 0x7f

enum spdk_nvmf_fabric_cmd_types {
	SPDK_NVMF_FABRIC_COMMAND_PROPERTY_SET			= 0x00,
	SPDK_NVMF_FABRIC_COMMAND_CONNECT			= 0x01,
	SPDK_NVMF_FABRIC_COMMAND_PROPERTY_GET			= 0x04,
	SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND		= 0x05,
	SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_RECV		= 0x06,
	SPDK_NVMF_FABRIC_COMMAND_START_VENDOR_SPECIFIC		= 0xC0,
};

enum spdk_nvmf_fabric_cmd_status_code {
	SPDK_NVMF_FABRIC_SC_INCOMPATIBLE_FORMAT		= 0x80,
	SPDK_NVMF_FABRIC_SC_CONTROLLER_BUSY		= 0x81,
	SPDK_NVMF_FABRIC_SC_INVALID_PARAM		= 0x82,
	SPDK_NVMF_FABRIC_SC_RESTART_DISCOVERY		= 0x83,
	SPDK_NVMF_FABRIC_SC_INVALID_HOST		= 0x84,
	SPDK_NVMF_FABRIC_SC_LOG_RESTART_DISCOVERY	= 0x90,
	SPDK_NVMF_FABRIC_SC_AUTH_REQUIRED		= 0x91,
};

/**
 * RDMA Queue Pair service types
 */
enum spdk_nvmf_rdma_qptype {
	/** Reliable connected */
	SPDK_NVMF_RDMA_QPTYPE_RELIABLE_CONNECTED	= 0x1,

	/** Reliable datagram */
	SPDK_NVMF_RDMA_QPTYPE_RELIABLE_DATAGRAM		= 0x2,
};

/**
 * RDMA provider types
 */
enum spdk_nvmf_rdma_prtype {
	/** No provider specified */
	SPDK_NVMF_RDMA_PRTYPE_NONE	= 0x1,

	/** InfiniBand */
	SPDK_NVMF_RDMA_PRTYPE_IB	= 0x2,

	/** RoCE v1 */
	SPDK_NVMF_RDMA_PRTYPE_ROCE	= 0x3,

	/** RoCE v2 */
	SPDK_NVMF_RDMA_PRTYPE_ROCE2	= 0x4,

	/** iWARP */
	SPDK_NVMF_RDMA_PRTYPE_IWARP	= 0x5,
};

/**
 * RDMA connection management service types
 */
enum spdk_nvmf_rdma_cms {
	/** Sockets based endpoint addressing */
	SPDK_NVMF_RDMA_CMS_RDMA_CM	= 0x1,
};

/**
 * NVMe over Fabrics transport types
 */
enum spdk_nvmf_trtype {
	/** RDMA */
	SPDK_NVMF_TRTYPE_RDMA		= 0x1,

	/** Fibre Channel */
	SPDK_NVMF_TRTYPE_FC		= 0x2,

	/** TCP */
	SPDK_NVMF_TRTYPE_TCP		= 0x3,

	/** Intra-host transport (loopback) */
	SPDK_NVMF_TRTYPE_INTRA_HOST	= 0xfe,
};

/**
 * Address family types
 */
enum spdk_nvmf_adrfam {
	/** IPv4 (AF_INET) */
	SPDK_NVMF_ADRFAM_IPV4		= 0x1,

	/** IPv6 (AF_INET6) */
	SPDK_NVMF_ADRFAM_IPV6		= 0x2,

	/** InfiniBand (AF_IB) */
	SPDK_NVMF_ADRFAM_IB		= 0x3,

	/** Fibre Channel address family */
	SPDK_NVMF_ADRFAM_FC		= 0x4,

	/** Intra-host transport (loopback) */
	SPDK_NVMF_ADRFAM_INTRA_HOST	= 0xfe,
};

/**
 * NVM subsystem types
 */
enum spdk_nvmf_subtype {
	/** Discovery type for NVM subsystem */
	SPDK_NVMF_SUBTYPE_DISCOVERY		= 0x1,

	/** NVMe type for NVM subsystem */
	SPDK_NVMF_SUBTYPE_NVME		= 0x2,
};

/**
 * Connections shall be made over a fabric secure channel
 */
enum spdk_nvmf_treq_secure_channel {
	/** Not specified */
	SPDK_NVMF_TREQ_SECURE_CHANNEL_NOT_SPECIFIED	= 0x0,

	/** Required */
	SPDK_NVMF_TREQ_SECURE_CHANNEL_REQUIRED		= 0x1,

	/** Not required */
	SPDK_NVMF_TREQ_SECURE_CHANNEL_NOT_REQUIRED	= 0x2,
};

struct spdk_nvmf_fabric_auth_recv_cmd {
	uint8_t		opcode;
	uint8_t		reserved1;
	uint16_t	cid;
	uint8_t		fctype; /* NVMF_FABRIC_COMMAND_AUTHENTICATION_RECV (0x06) */
	uint8_t		reserved2[19];
	struct spdk_nvme_sgl_descriptor sgl1;
	uint8_t		reserved3;
	uint8_t		spsp0;
	uint8_t		spsp1;
	uint8_t		secp;
	uint32_t	al;
	uint8_t		reserved4[16];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_fabric_auth_recv_cmd) == 64, "Incorrect size");

struct spdk_nvmf_fabric_auth_send_cmd {
	uint8_t		opcode;
	uint8_t		reserved1;
	uint16_t	cid;
	uint8_t		fctype; /* NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND (0x05) */
	uint8_t		reserved2[19];
	struct spdk_nvme_sgl_descriptor sgl1;
	uint8_t		reserved3;
	uint8_t		spsp0;
	uint8_t		spsp1;
	uint8_t		secp;
	uint32_t	tl;
	uint8_t		reserved4[16];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_fabric_auth_send_cmd) == 64, "Incorrect size");

struct spdk_nvmf_fabric_connect_data {
	uint8_t		hostid[16];
	uint16_t	cntlid;
	uint8_t		reserved5[238];
	uint8_t		subnqn[SPDK_NVME_NQN_FIELD_SIZE];
	uint8_t		hostnqn[SPDK_NVME_NQN_FIELD_SIZE];
	uint8_t		reserved6[256];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_fabric_connect_data) == 1024, "Incorrect size");

struct spdk_nvmf_fabric_connect_cmd {
	uint8_t		opcode;
	uint8_t		reserved1;
	uint16_t	cid;
	uint8_t		fctype;
	uint8_t		reserved2[19];
	struct spdk_nvme_sgl_descriptor sgl1;
	uint16_t	recfmt; /* Connect Record Format */
	uint16_t	qid; /* Queue Identifier */
	uint16_t	sqsize; /* Submission Queue Size */
	uint8_t		cattr; /* queue attributes */
	uint8_t		reserved3;
	uint32_t	kato; /* keep alive timeout */
	uint8_t		reserved4[12];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_fabric_connect_cmd) == 64, "Incorrect size");

struct spdk_nvmf_fabric_connect_rsp {
	union {
		struct {
			uint16_t cntlid;
			uint16_t authreq;
		} success;

		struct {
			uint16_t	ipo;
			uint8_t		iattr;
			uint8_t		reserved;
		} invalid;

		uint32_t raw;
	} status_code_specific;

	uint32_t	reserved0;
	uint16_t	sqhd;
	uint16_t	reserved1;
	uint16_t	cid;
	struct spdk_nvme_status status;
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_fabric_connect_rsp) == 16, "Incorrect size");

#define SPDK_NVMF_PROP_SIZE_4	0
#define SPDK_NVMF_PROP_SIZE_8	1

struct spdk_nvmf_fabric_prop_get_cmd {
	uint8_t		opcode;
	uint8_t		reserved1;
	uint16_t	cid;
	uint8_t		fctype;
	uint8_t		reserved2[35];
	struct {
		uint8_t size		: 3;
		uint8_t reserved	: 5;
	} attrib;
	uint8_t		reserved3[3];
	uint32_t	ofst;
	uint8_t		reserved4[16];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_fabric_prop_get_cmd) == 64, "Incorrect size");

struct spdk_nvmf_fabric_prop_get_rsp {
	union {
		uint64_t u64;
		struct {
			uint32_t low;
			uint32_t high;
		} u32;
	} value;

	uint16_t	sqhd;
	uint16_t	reserved0;
	uint16_t	cid;
	struct spdk_nvme_status status;
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_fabric_prop_get_rsp) == 16, "Incorrect size");

struct spdk_nvmf_fabric_prop_set_cmd {
	uint8_t		opcode;
	uint8_t		reserved0;
	uint16_t	cid;
	uint8_t		fctype;
	uint8_t		reserved1[35];
	struct {
		uint8_t size		: 3;
		uint8_t reserved	: 5;
	} attrib;
	uint8_t		reserved2[3];
	uint32_t	ofst;

	union {
		uint64_t u64;
		struct {
			uint32_t low;
			uint32_t high;
		} u32;
	} value;

	uint8_t		reserved4[8];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_fabric_prop_set_cmd) == 64, "Incorrect size");

#define SPDK_NVMF_NQN_MIN_LEN 11 /* The prefix in the spec is 11 characters */
#define SPDK_NVMF_NQN_MAX_LEN 223
#define SPDK_NVMF_NQN_UUID_PRE_LEN 32
#define SPDK_NVMF_UUID_STRING_LEN 36
#define SPDK_NVMF_NQN_UUID_PRE "nqn.2014-08.org.nvmexpress:uuid:"
#define SPDK_NVMF_DISCOVERY_NQN "nqn.2014-08.org.nvmexpress.discovery"

#define SPDK_DOMAIN_LABEL_MAX_LEN 63 /* RFC 1034 max domain label length */

#define SPDK_NVMF_TRSTRING_MAX_LEN 32
#define SPDK_NVMF_TRADDR_MAX_LEN 256
#define SPDK_NVMF_TRSVCID_MAX_LEN 32

/** RDMA transport-specific address subtype */
struct spdk_nvmf_rdma_transport_specific_address_subtype {
	/** RDMA QP service type (\ref spdk_nvmf_rdma_qptype) */
	uint8_t		rdma_qptype;

	/** RDMA provider type (\ref spdk_nvmf_rdma_prtype) */
	uint8_t		rdma_prtype;

	/** RDMA connection management service (\ref spdk_nvmf_rdma_cms) */
	uint8_t		rdma_cms;

	uint8_t		reserved0[5];

	/** RDMA partition key for AF_IB */
	uint16_t	rdma_pkey;

	uint8_t		reserved2[246];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_rdma_transport_specific_address_subtype) == 256,
		   "Incorrect size");

/** TCP Secure Socket Type */
enum spdk_nvme_tcp_secure_socket_type {
	/** No security */
	SPDK_NVME_TCP_SECURITY_NONE			= 0,

	/** TLS (Secure Sockets) */
	SPDK_NVME_TCP_SECURITY_TLS			= 1,
};

/** TCP transport-specific address subtype */
struct spdk_nvme_tcp_transport_specific_address_subtype {
	/** Security type (\ref spdk_nvme_tcp_secure_socket_type) */
	uint8_t		sectype;

	uint8_t		reserved0[255];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_tcp_transport_specific_address_subtype) == 256,
		   "Incorrect size");

/** Transport-specific address subtype */
union spdk_nvmf_transport_specific_address_subtype {
	uint8_t raw[256];

	/** RDMA */
	struct spdk_nvmf_rdma_transport_specific_address_subtype rdma;

	/** TCP */
	struct spdk_nvme_tcp_transport_specific_address_subtype tcp;
};
SPDK_STATIC_ASSERT(sizeof(union spdk_nvmf_transport_specific_address_subtype) == 256,
		   "Incorrect size");

#define SPDK_NVMF_MIN_ADMIN_MAX_SQ_SIZE 32

/**
 * Discovery Log Page entry
 */
struct spdk_nvmf_discovery_log_page_entry {
	/** Transport type (\ref spdk_nvmf_trtype) */
	uint8_t		trtype;

	/** Address family (\ref spdk_nvmf_adrfam) */
	uint8_t		adrfam;

	/** Subsystem type (\ref spdk_nvmf_subtype) */
	uint8_t		subtype;

	/** Transport requirements */
	struct {
		/** Secure channel requirements (\ref spdk_nvmf_treq_secure_channel) */
		uint8_t secure_channel : 2;

		uint8_t reserved : 6;
	} treq;

	/** NVM subsystem port ID */
	uint16_t	portid;

	/** Controller ID */
	uint16_t	cntlid;

	/** Admin max SQ size */
	uint16_t	asqsz;

	uint8_t		reserved0[22];

	/** Transport service identifier */
	uint8_t		trsvcid[SPDK_NVMF_TRSVCID_MAX_LEN];

	uint8_t		reserved1[192];

	/** NVM subsystem qualified name */
	uint8_t		subnqn[256];

	/** Transport address */
	uint8_t		traddr[SPDK_NVMF_TRADDR_MAX_LEN];

	/** Transport-specific address subtype */
	union spdk_nvmf_transport_specific_address_subtype tsas;
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_discovery_log_page_entry) == 1024, "Incorrect size");

struct spdk_nvmf_discovery_log_page {
	uint64_t	genctr;
	uint64_t	numrec;
	uint16_t	recfmt;
	uint8_t		reserved0[1006];
	struct spdk_nvmf_discovery_log_page_entry entries[0];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_discovery_log_page) == 1024, "Incorrect size");

/* RDMA Fabric specific definitions below */

#define SPDK_NVME_SGL_SUBTYPE_INVALIDATE_KEY	0xF

struct spdk_nvmf_rdma_request_private_data {
	uint16_t	recfmt; /* record format */
	uint16_t	qid;	/* queue id */
	uint16_t	hrqsize;	/* host receive queue size */
	uint16_t	hsqsize;	/* host send queue size */
	uint16_t	cntlid;		/* controller id */
	uint8_t		reserved[22];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_rdma_request_private_data) == 32, "Incorrect size");

struct spdk_nvmf_rdma_accept_private_data {
	uint16_t	recfmt; /* record format */
	uint16_t	crqsize;	/* controller receive queue size */
	uint8_t		reserved[28];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_rdma_accept_private_data) == 32, "Incorrect size");

struct spdk_nvmf_rdma_reject_private_data {
	uint16_t	recfmt; /* record format */
	uint16_t	sts; /* status */
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_rdma_reject_private_data) == 4, "Incorrect size");

union spdk_nvmf_rdma_private_data {
	struct spdk_nvmf_rdma_request_private_data	pd_request;
	struct spdk_nvmf_rdma_accept_private_data	pd_accept;
	struct spdk_nvmf_rdma_reject_private_data	pd_reject;
};
SPDK_STATIC_ASSERT(sizeof(union spdk_nvmf_rdma_private_data) == 32, "Incorrect size");

enum spdk_nvmf_rdma_transport_error {
	SPDK_NVMF_RDMA_ERROR_INVALID_PRIVATE_DATA_LENGTH	= 0x1,
	SPDK_NVMF_RDMA_ERROR_INVALID_RECFMT			= 0x2,
	SPDK_NVMF_RDMA_ERROR_INVALID_QID			= 0x3,
	SPDK_NVMF_RDMA_ERROR_INVALID_HSQSIZE			= 0x4,
	SPDK_NVMF_RDMA_ERROR_INVALID_HRQSIZE			= 0x5,
	SPDK_NVMF_RDMA_ERROR_NO_RESOURCES			= 0x6,
	SPDK_NVMF_RDMA_ERROR_INVALID_IRD			= 0x7,
	SPDK_NVMF_RDMA_ERROR_INVALID_ORD			= 0x8,
};

/* TCP transport specific definitions below */

/** NVMe/TCP PDU type */
enum spdk_nvme_tcp_pdu_type {
	/** Initialize Connection Request (ICReq) */
	SPDK_NVME_TCP_PDU_TYPE_IC_REQ			= 0x00,

	/** Initialize Connection Response (ICResp) */
	SPDK_NVME_TCP_PDU_TYPE_IC_RESP			= 0x01,

	/** Terminate Connection Request (TermReq) */
	SPDK_NVME_TCP_PDU_TYPE_H2C_TERM_REQ		= 0x02,

	/** Terminate Connection Response (TermResp) */
	SPDK_NVME_TCP_PDU_TYPE_C2H_TERM_REQ		= 0x03,

	/** Command Capsule (CapsuleCmd) */
	SPDK_NVME_TCP_PDU_TYPE_CAPSULE_CMD		= 0x04,

	/** Response Capsule (CapsuleRsp) */
	SPDK_NVME_TCP_PDU_TYPE_CAPSULE_RESP		= 0x05,

	/** Host To Controller Data (H2CData) */
	SPDK_NVME_TCP_PDU_TYPE_H2C_DATA			= 0x06,

	/** Controller To Host Data (C2HData) */
	SPDK_NVME_TCP_PDU_TYPE_C2H_DATA			= 0x07,

	/** Ready to Transfer (R2T) */
	SPDK_NVME_TCP_PDU_TYPE_R2T			= 0x09,
};

/** Common NVMe/TCP PDU header */
struct spdk_nvme_tcp_common_pdu_hdr {
	/** PDU type (\ref spdk_nvme_tcp_pdu_type) */
	uint8_t				pdu_type;

	/** pdu_type-specific flags */
	uint8_t				flags;

	/** Length of PDU header (not including the Header Digest) */
	uint8_t				hlen;

	/** PDU Data Offset from the start of the PDU */
	uint8_t				pdo;

	/** Total number of bytes in PDU, including pdu_hdr */
	uint32_t			plen;
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_tcp_common_pdu_hdr) == 8, "Incorrect size");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_common_pdu_hdr, pdu_type) == 0,
		   "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_common_pdu_hdr, flags) == 1, "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_common_pdu_hdr, hlen) == 2, "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_common_pdu_hdr, pdo) == 3, "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_common_pdu_hdr, plen) == 4, "Incorrect offset");

#define SPDK_NVME_TCP_CH_FLAGS_HDGSTF		(1u << 0)
#define SPDK_NVME_TCP_CH_FLAGS_DDGSTF		(1u << 1)

/**
 * ICReq
 *
 * common.pdu_type == SPDK_NVME_TCP_PDU_TYPE_IC_REQ
 */
struct spdk_nvme_tcp_ic_req {
	struct spdk_nvme_tcp_common_pdu_hdr	common;
	uint16_t				pfv;
	/** Specifies the data alignment for all PDUs transferred from the controller to the host that contain data */
	uint8_t					hpda;
	union {
		uint8_t				raw;
		struct {
			uint8_t			hdgst_enable : 1;
			uint8_t			ddgst_enable : 1;
			uint8_t			reserved : 6;
		} bits;
	} dgst;
	uint32_t				maxr2t;
	uint8_t					reserved16[112];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_tcp_ic_req) == 128, "Incorrect size");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_ic_req, pfv) == 8, "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_ic_req, hpda) == 10, "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_ic_req, maxr2t) == 12, "Incorrect offset");

#define SPDK_NVME_TCP_CPDA_MAX 31
#define SPDK_NVME_TCP_PDU_PDO_MAX_OFFSET     ((SPDK_NVME_TCP_CPDA_MAX + 1) << 2)

/**
 * ICResp
 *
 * common.pdu_type == SPDK_NVME_TCP_PDU_TYPE_IC_RESP
 */
struct spdk_nvme_tcp_ic_resp {
	struct spdk_nvme_tcp_common_pdu_hdr	common;
	uint16_t				pfv;
	/** Specifies the data alignment for all PDUs transferred from the host to the controller that contain data */
	uint8_t					cpda;
	union {
		uint8_t				raw;
		struct {
			uint8_t			hdgst_enable : 1;
			uint8_t			ddgst_enable : 1;
			uint8_t			reserved : 6;
		} bits;
	} dgst;
	/** Specifies the maximum number of PDU-Data bytes per H2C Data Transfer PDU */
	uint32_t				maxh2cdata;
	uint8_t					reserved16[112];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_tcp_ic_resp) == 128, "Incorrect size");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_ic_resp, pfv) == 8, "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_ic_resp, cpda) == 10, "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_ic_resp, maxh2cdata) == 12, "Incorrect offset");

/**
 * TermReq
 *
 * common.pdu_type == SPDK_NVME_TCP_PDU_TYPE_TERM_REQ
 */
struct spdk_nvme_tcp_term_req_hdr {
	struct spdk_nvme_tcp_common_pdu_hdr	common;
	uint16_t				fes;
	uint8_t					fei[4];
	uint8_t					reserved14[10];
};

SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_tcp_term_req_hdr) == 24, "Incorrect size");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_term_req_hdr, fes) == 8, "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_term_req_hdr, fei) == 10, "Incorrect offset");

enum spdk_nvme_tcp_term_req_fes {
	SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD				= 0x01,
	SPDK_NVME_TCP_TERM_REQ_FES_PDU_SEQUENCE_ERROR				= 0x02,
	SPDK_NVME_TCP_TERM_REQ_FES_HDGST_ERROR					= 0x03,
	SPDK_NVME_TCP_TERM_REQ_FES_DATA_TRANSFER_OUT_OF_RANGE			= 0x04,
	SPDK_NVME_TCP_TERM_REQ_FES_DATA_TRANSFER_LIMIT_EXCEEDED			= 0x05,
	SPDK_NVME_TCP_TERM_REQ_FES_R2T_LIMIT_EXCEEDED				= 0x05,
	SPDK_NVME_TCP_TERM_REQ_FES_INVALID_DATA_UNSUPPORTED_PARAMETER		= 0x06,
};

/* Total length of term req PDU (including PDU header and DATA) in bytes shall not exceed a limit of 152 bytes. */
#define SPDK_NVME_TCP_TERM_REQ_ERROR_DATA_MAX_SIZE	128
#define SPDK_NVME_TCP_TERM_REQ_PDU_MAX_SIZE		(SPDK_NVME_TCP_TERM_REQ_ERROR_DATA_MAX_SIZE + sizeof(struct spdk_nvme_tcp_term_req_hdr))

/**
 * CapsuleCmd
 *
 * common.pdu_type == SPDK_NVME_TCP_PDU_TYPE_CAPSULE_CMD
 */
struct spdk_nvme_tcp_cmd {
	struct spdk_nvme_tcp_common_pdu_hdr	common;
	struct spdk_nvme_cmd			ccsqe;
	/**< icdoff hdgst padding + in-capsule data + ddgst (if enabled) */
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_tcp_cmd) == 72, "Incorrect size");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_cmd, ccsqe) == 8, "Incorrect offset");

/**
 * CapsuleResp
 *
 * common.pdu_type == SPDK_NVME_TCP_PDU_TYPE_CAPSULE_RESP
 */
struct spdk_nvme_tcp_rsp {
	struct spdk_nvme_tcp_common_pdu_hdr	common;
	struct spdk_nvme_cpl			rccqe;
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_tcp_rsp) == 24, "incorrect size");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_rsp, rccqe) == 8, "Incorrect offset");


/**
 * H2CData
 *
 * hdr.pdu_type == SPDK_NVME_TCP_PDU_TYPE_H2C_DATA
 */
struct spdk_nvme_tcp_h2c_data_hdr {
	struct spdk_nvme_tcp_common_pdu_hdr	common;
	uint16_t				cccid;
	uint16_t				ttag;
	uint32_t				datao;
	uint32_t				datal;
	uint8_t					reserved20[4];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_tcp_h2c_data_hdr) == 24, "Incorrect size");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_h2c_data_hdr, cccid) == 8, "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_h2c_data_hdr, ttag) == 10, "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_h2c_data_hdr, datao) == 12, "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_h2c_data_hdr, datal) == 16, "Incorrect offset");

#define SPDK_NVME_TCP_H2C_DATA_FLAGS_LAST_PDU	(1u << 2)
#define SPDK_NVME_TCP_H2C_DATA_FLAGS_SUCCESS	(1u << 3)
#define SPDK_NVME_TCP_H2C_DATA_PDO_MULT		8u

/**
 * C2HData
 *
 * hdr.pdu_type == SPDK_NVME_TCP_PDU_TYPE_C2H_DATA
 */
struct spdk_nvme_tcp_c2h_data_hdr {
	struct spdk_nvme_tcp_common_pdu_hdr	common;
	uint16_t				cccid;
	uint8_t					reserved10[2];
	uint32_t				datao;
	uint32_t				datal;
	uint8_t					reserved20[4];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_tcp_c2h_data_hdr) == 24, "Incorrect size");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_c2h_data_hdr, cccid) == 8, "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_c2h_data_hdr, datao) == 12, "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_c2h_data_hdr, datal) == 16, "Incorrect offset");

#define SPDK_NVME_TCP_C2H_DATA_FLAGS_SUCCESS	(1u << 3)
#define SPDK_NVME_TCP_C2H_DATA_FLAGS_LAST_PDU	(1u << 2)
#define SPDK_NVME_TCP_C2H_DATA_PDO_MULT		8u

/**
 * R2T
 *
 * common.pdu_type == SPDK_NVME_TCP_PDU_TYPE_R2T
 */
struct spdk_nvme_tcp_r2t_hdr {
	struct spdk_nvme_tcp_common_pdu_hdr	common;
	uint16_t				cccid;
	uint16_t				ttag;
	uint32_t				r2to;
	uint32_t				r2tl;
	uint8_t					reserved20[4];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_tcp_r2t_hdr) == 24, "Incorrect size");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_r2t_hdr, cccid) == 8, "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_r2t_hdr, ttag) == 10, "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_r2t_hdr, r2to) == 12, "Incorrect offset");
SPDK_STATIC_ASSERT(offsetof(struct spdk_nvme_tcp_r2t_hdr, r2tl) == 16, "Incorrect offset");

#pragma pack(pop)

#endif /* __NVMF_SPEC_H__ */
