/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2012 The FreeBSD Foundation
 *
 * This software was developed by Edward Tomasz Napierala under sponsorship
 * from the FreeBSD Foundation.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 ISCSI_PROTO_H
#define	ISCSI_PROTO_H

#ifndef CTASSERT
#define CTASSERT(x)		_CTASSERT(x, __LINE__)
#define _CTASSERT(x, y)		__CTASSERT(x, y)
#define __CTASSERT(x, y)	typedef char __assert_ ## y [(x) ? 1 : -1]
#endif

#define	ISCSI_SNGT(x, y)	((int32_t)(x) - (int32_t)(y) > 0)
#define	ISCSI_SNLT(x, y)	((int32_t)(x) - (int32_t)(y) < 0)

#define	ISCSI_BHS_SIZE			48
#define	ISCSI_HEADER_DIGEST_SIZE	4
#define	ISCSI_DATA_DIGEST_SIZE		4

#define	ISCSI_BHS_OPCODE_IMMEDIATE	0x40

#define	ISCSI_BHS_OPCODE_NOP_OUT	0x00
#define	ISCSI_BHS_OPCODE_SCSI_COMMAND	0x01
#define	ISCSI_BHS_OPCODE_TASK_REQUEST	0x02
#define	ISCSI_BHS_OPCODE_LOGIN_REQUEST	0x03
#define	ISCSI_BHS_OPCODE_TEXT_REQUEST	0x04
#define	ISCSI_BHS_OPCODE_SCSI_DATA_OUT	0x05
#define	ISCSI_BHS_OPCODE_LOGOUT_REQUEST	0x06

#define	ISCSI_BHS_OPCODE_NOP_IN		0x20
#define	ISCSI_BHS_OPCODE_SCSI_RESPONSE	0x21
#define	ISCSI_BHS_OPCODE_TASK_RESPONSE	0x22
#define	ISCSI_BHS_OPCODE_LOGIN_RESPONSE	0x23
#define	ISCSI_BHS_OPCODE_TEXT_RESPONSE	0x24
#define	ISCSI_BHS_OPCODE_SCSI_DATA_IN	0x25
#define	ISCSI_BHS_OPCODE_LOGOUT_RESPONSE	0x26
#define	ISCSI_BHS_OPCODE_R2T		0x31
#define	ISCSI_BHS_OPCODE_ASYNC_MESSAGE	0x32
#define	ISCSI_BHS_OPCODE_REJECT		0x3f

struct iscsi_bhs {
	uint8_t		bhs_opcode;
	uint8_t		bhs_opcode_specific1[3];
	uint8_t		bhs_total_ahs_len;
	uint8_t		bhs_data_segment_len[3];
	uint64_t	bhs_lun;
	uint8_t		bhs_inititator_task_tag[4];
	uint8_t		bhs_opcode_specific4[28];
};
CTASSERT(sizeof(struct iscsi_bhs) == ISCSI_BHS_SIZE);

#define	BHSSC_FLAGS_F		0x80
#define	BHSSC_FLAGS_R		0x40
#define	BHSSC_FLAGS_W		0x20
#define	BHSSC_FLAGS_ATTR	0x07

#define	BHSSC_FLAGS_ATTR_UNTAGGED	0
#define	BHSSC_FLAGS_ATTR_SIMPLE		1
#define	BHSSC_FLAGS_ATTR_ORDERED	2
#define	BHSSC_FLAGS_ATTR_HOQ		3
#define	BHSSC_FLAGS_ATTR_ACA		4

#define	BHSSC_PRI_MASK		0xf0
#define	BHSSC_PRI_SHIFT		4

struct iscsi_bhs_scsi_command {
	uint8_t		bhssc_opcode;
	uint8_t		bhssc_flags;
	uint8_t		bhssc_pri;
	uint8_t		bhssc_reserved;
	uint8_t		bhssc_total_ahs_len;
	uint8_t		bhssc_data_segment_len[3];
	uint64_t	bhssc_lun;
	uint32_t	bhssc_initiator_task_tag;
	uint32_t	bhssc_expected_data_transfer_length;
	uint32_t	bhssc_cmdsn;
	uint32_t	bhssc_expstatsn;
	uint8_t		bhssc_cdb[16];
};
CTASSERT(sizeof(struct iscsi_bhs_scsi_command) == ISCSI_BHS_SIZE);

#define	BHSSR_FLAGS_RESIDUAL_UNDERFLOW		0x02
#define	BHSSR_FLAGS_RESIDUAL_OVERFLOW		0x04

#define	BHSSR_RESPONSE_COMMAND_COMPLETED	0x00

struct iscsi_bhs_scsi_response {
	uint8_t		bhssr_opcode;
	uint8_t		bhssr_flags;
	uint8_t		bhssr_response;
	uint8_t		bhssr_status;
	uint8_t		bhssr_total_ahs_len;
	uint8_t		bhssr_data_segment_len[3];
	uint16_t	bhssr_status_qualifier;
	uint16_t	bhssr_reserved;
	uint32_t	bhssr_reserved2;
	uint32_t	bhssr_initiator_task_tag;
	uint32_t	bhssr_snack_tag;
	uint32_t	bhssr_statsn;
	uint32_t	bhssr_expcmdsn;
	uint32_t	bhssr_maxcmdsn;
	uint32_t	bhssr_expdatasn;
	uint32_t	bhssr_bidirectional_read_residual_count;
	uint32_t	bhssr_residual_count;
};
CTASSERT(sizeof(struct iscsi_bhs_scsi_response) == ISCSI_BHS_SIZE);

#define	BHSTMR_FUNCTION_ABORT_TASK		1
#define	BHSTMR_FUNCTION_ABORT_TASK_SET		2
#define	BHSTMR_FUNCTION_CLEAR_ACA		3
#define	BHSTMR_FUNCTION_CLEAR_TASK_SET		4
#define	BHSTMR_FUNCTION_LOGICAL_UNIT_RESET	5
#define	BHSTMR_FUNCTION_TARGET_WARM_RESET	6
#define	BHSTMR_FUNCTION_TARGET_COLD_RESET	7
#define	BHSTMR_FUNCTION_TASK_REASSIGN		8
#define	BHSTMR_FUNCTION_QUERY_TASK		9
#define	BHSTMR_FUNCTION_QUERY_TASK_SET		10
#define	BHSTMR_FUNCTION_I_T_NEXUS_RESET		11
#define	BHSTMR_FUNCTION_QUERY_ASYNC_EVENT	12

struct iscsi_bhs_task_management_request {
	uint8_t		bhstmr_opcode;
	uint8_t		bhstmr_function;
	uint8_t		bhstmr_reserved[2];
	uint8_t		bhstmr_total_ahs_len;
	uint8_t		bhstmr_data_segment_len[3];
	uint64_t	bhstmr_lun;
	uint32_t	bhstmr_initiator_task_tag;
	uint32_t	bhstmr_referenced_task_tag;
	uint32_t	bhstmr_cmdsn;
	uint32_t	bhstmr_expstatsn;
	uint32_t	bhstmr_refcmdsn;
	uint32_t	bhstmr_expdatasn;
	uint64_t	bhstmr_reserved2;
};
CTASSERT(sizeof(struct iscsi_bhs_task_management_request) == ISCSI_BHS_SIZE);

#define	BHSTMR_RESPONSE_FUNCTION_COMPLETE	0
#define	BHSTMR_RESPONSE_TASK_DOES_NOT_EXIST	1
#define	BHSTMR_RESPONSE_LUN_DOES_NOT_EXIST	2
#define	BHSTMR_RESPONSE_TASK_STILL_ALLEGIANT	3
#define	BHSTMR_RESPONSE_TASK_ALL_REASS_NOT_SUPP	4
#define	BHSTMR_RESPONSE_FUNCTION_NOT_SUPPORTED	5
#define	BHSTMR_RESPONSE_FUNCTION_AUTH_FAIL	6
#define	BHSTMR_RESPONSE_FUNCTION_SUCCEEDED	7
#define	BHSTMR_RESPONSE_FUNCTION_REJECTED	255

struct iscsi_bhs_task_management_response {
	uint8_t		bhstmr_opcode;
	uint8_t		bhstmr_flags;
	uint8_t		bhstmr_response;
	uint8_t		bhstmr_reserved;
	uint8_t		bhstmr_total_ahs_len;
	uint8_t		bhstmr_data_segment_len[3];
	uint8_t		bhstmr_additional_reponse_information[3];
	uint8_t		bhstmr_reserved2[5];
	uint32_t	bhstmr_initiator_task_tag;
	uint32_t	bhstmr_reserved3;
	uint32_t	bhstmr_statsn;
	uint32_t	bhstmr_expcmdsn;
	uint32_t	bhstmr_maxcmdsn;
	uint8_t		bhstmr_reserved4[12];
};
CTASSERT(sizeof(struct iscsi_bhs_task_management_response) == ISCSI_BHS_SIZE);

#define	BHSLR_FLAGS_TRANSIT		0x80
#define	BHSLR_FLAGS_CONTINUE		0x40

#define	BHSLR_STAGE_SECURITY_NEGOTIATION	0
#define	BHSLR_STAGE_OPERATIONAL_NEGOTIATION	1
#define	BHSLR_STAGE_FULL_FEATURE_PHASE		3 /* Yes, 3. */

struct iscsi_bhs_login_request {
	uint8_t		bhslr_opcode;
	uint8_t		bhslr_flags;
	uint8_t		bhslr_version_max;
	uint8_t		bhslr_version_min;
	uint8_t		bhslr_total_ahs_len;
	uint8_t		bhslr_data_segment_len[3];
	uint8_t		bhslr_isid[6];
	uint16_t	bhslr_tsih;
	uint32_t	bhslr_initiator_task_tag;
	uint16_t	bhslr_cid;
	uint16_t	bhslr_reserved;
	uint32_t	bhslr_cmdsn;
	uint32_t	bhslr_expstatsn;
	uint8_t		bhslr_reserved2[16];
};
CTASSERT(sizeof(struct iscsi_bhs_login_request) == ISCSI_BHS_SIZE);

struct iscsi_bhs_login_response {
	uint8_t		bhslr_opcode;
	uint8_t		bhslr_flags;
	uint8_t		bhslr_version_max;
	uint8_t		bhslr_version_active;
	uint8_t		bhslr_total_ahs_len;
	uint8_t		bhslr_data_segment_len[3];
	uint8_t		bhslr_isid[6];
	uint16_t	bhslr_tsih;
	uint32_t	bhslr_initiator_task_tag;
	uint32_t	bhslr_reserved;
	uint32_t	bhslr_statsn;
	uint32_t	bhslr_expcmdsn;
	uint32_t	bhslr_maxcmdsn;
	uint8_t		bhslr_status_class;
	uint8_t		bhslr_status_detail;
	uint16_t	bhslr_reserved2;
	uint8_t		bhslr_reserved3[8];
};
CTASSERT(sizeof(struct iscsi_bhs_login_response) == ISCSI_BHS_SIZE);

#define	BHSTR_FLAGS_FINAL		0x80
#define	BHSTR_FLAGS_CONTINUE		0x40

struct iscsi_bhs_text_request {
	uint8_t		bhstr_opcode;
	uint8_t		bhstr_flags;
	uint16_t	bhstr_reserved;
	uint8_t		bhstr_total_ahs_len;
	uint8_t		bhstr_data_segment_len[3];
	uint64_t	bhstr_lun;
	uint32_t	bhstr_initiator_task_tag;
	uint32_t	bhstr_target_transfer_tag;
	uint32_t	bhstr_cmdsn;
	uint32_t	bhstr_expstatsn;
	uint8_t		bhstr_reserved2[16];
};
CTASSERT(sizeof(struct iscsi_bhs_text_request) == ISCSI_BHS_SIZE);

struct iscsi_bhs_text_response {
	uint8_t		bhstr_opcode;
	uint8_t		bhstr_flags;
	uint16_t	bhstr_reserved;
	uint8_t		bhstr_total_ahs_len;
	uint8_t		bhstr_data_segment_len[3];
	uint64_t	bhstr_lun;
	uint32_t	bhstr_initiator_task_tag;
	uint32_t	bhstr_target_transfer_tag;
	uint32_t	bhstr_statsn;
	uint32_t	bhstr_expcmdsn;
	uint32_t	bhstr_maxcmdsn;
	uint8_t		bhstr_reserved2[12];
};
CTASSERT(sizeof(struct iscsi_bhs_text_response) == ISCSI_BHS_SIZE);

#define	BHSDO_FLAGS_F	0x80

struct iscsi_bhs_data_out {
	uint8_t		bhsdo_opcode;
	uint8_t		bhsdo_flags;
	uint8_t		bhsdo_reserved[2];
	uint8_t		bhsdo_total_ahs_len;
	uint8_t		bhsdo_data_segment_len[3];
	uint64_t	bhsdo_lun;
	uint32_t	bhsdo_initiator_task_tag;
	uint32_t	bhsdo_target_transfer_tag;
	uint32_t	bhsdo_reserved2;
	uint32_t	bhsdo_expstatsn;
	uint32_t	bhsdo_reserved3;
	uint32_t	bhsdo_datasn;
	uint32_t	bhsdo_buffer_offset;
	uint32_t	bhsdo_reserved4;
};
CTASSERT(sizeof(struct iscsi_bhs_data_out) == ISCSI_BHS_SIZE);

#define	BHSDI_FLAGS_F	0x80
#define	BHSDI_FLAGS_A	0x40
#define	BHSDI_FLAGS_O	0x04
#define	BHSDI_FLAGS_U	0x02
#define	BHSDI_FLAGS_S	0x01

struct iscsi_bhs_data_in {
	uint8_t		bhsdi_opcode;
	uint8_t		bhsdi_flags;
	uint8_t		bhsdi_reserved;
	uint8_t		bhsdi_status;
	uint8_t		bhsdi_total_ahs_len;
	uint8_t		bhsdi_data_segment_len[3];
	uint64_t	bhsdi_lun;
	uint32_t	bhsdi_initiator_task_tag;
	uint32_t	bhsdi_target_transfer_tag;
	uint32_t	bhsdi_statsn;
	uint32_t	bhsdi_expcmdsn;
	uint32_t	bhsdi_maxcmdsn;
	uint32_t	bhsdi_datasn;
	uint32_t	bhsdi_buffer_offset;
	uint32_t	bhsdi_residual_count;
};
CTASSERT(sizeof(struct iscsi_bhs_data_in) == ISCSI_BHS_SIZE);

struct iscsi_bhs_r2t {
	uint8_t		bhsr2t_opcode;
	uint8_t		bhsr2t_flags;
	uint16_t	bhsr2t_reserved;
	uint8_t		bhsr2t_total_ahs_len;
	uint8_t		bhsr2t_data_segment_len[3];
	uint64_t	bhsr2t_lun;
	uint32_t	bhsr2t_initiator_task_tag;
	uint32_t	bhsr2t_target_transfer_tag;
	uint32_t	bhsr2t_statsn;
	uint32_t	bhsr2t_expcmdsn;
	uint32_t	bhsr2t_maxcmdsn;
	uint32_t	bhsr2t_r2tsn;
	uint32_t	bhsr2t_buffer_offset;
	uint32_t	bhsr2t_desired_data_transfer_length;
};
CTASSERT(sizeof(struct iscsi_bhs_r2t) == ISCSI_BHS_SIZE);

struct iscsi_bhs_nop_out {
	uint8_t		bhsno_opcode;
	uint8_t		bhsno_flags;
	uint16_t	bhsno_reserved;
	uint8_t		bhsno_total_ahs_len;
	uint8_t		bhsno_data_segment_len[3];
	uint64_t	bhsno_lun;
	uint32_t	bhsno_initiator_task_tag;
	uint32_t	bhsno_target_transfer_tag;
	uint32_t	bhsno_cmdsn;
	uint32_t	bhsno_expstatsn;
	uint8_t		bhsno_reserved2[16];
};
CTASSERT(sizeof(struct iscsi_bhs_nop_out) == ISCSI_BHS_SIZE);

struct iscsi_bhs_nop_in {
	uint8_t		bhsni_opcode;
	uint8_t		bhsni_flags;
	uint16_t	bhsni_reserved;
	uint8_t		bhsni_total_ahs_len;
	uint8_t		bhsni_data_segment_len[3];
	uint64_t	bhsni_lun;
	uint32_t	bhsni_initiator_task_tag;
	uint32_t	bhsni_target_transfer_tag;
	uint32_t	bhsni_statsn;
	uint32_t	bhsni_expcmdsn;
	uint32_t	bhsni_maxcmdsn;
	uint8_t		bhsno_reserved2[12];
};
CTASSERT(sizeof(struct iscsi_bhs_nop_in) == ISCSI_BHS_SIZE);

#define	BHSLR_REASON_CLOSE_SESSION		0
#define	BHSLR_REASON_CLOSE_CONNECTION		1
#define	BHSLR_REASON_REMOVE_FOR_RECOVERY	2

struct iscsi_bhs_logout_request {
	uint8_t		bhslr_opcode;
	uint8_t		bhslr_reason;
	uint16_t	bhslr_reserved;
	uint8_t		bhslr_total_ahs_len;
	uint8_t		bhslr_data_segment_len[3];
	uint64_t	bhslr_reserved2;
	uint32_t	bhslr_initiator_task_tag;
	uint16_t	bhslr_cid;
	uint16_t	bhslr_reserved3;
	uint32_t	bhslr_cmdsn;
	uint32_t	bhslr_expstatsn;
	uint8_t		bhslr_reserved4[16];
};
CTASSERT(sizeof(struct iscsi_bhs_logout_request) == ISCSI_BHS_SIZE);

#define	BHSLR_RESPONSE_CLOSED_SUCCESSFULLY	0
#define	BHSLR_RESPONSE_RECOVERY_NOT_SUPPORTED	2

struct iscsi_bhs_logout_response {
	uint8_t		bhslr_opcode;
	uint8_t		bhslr_flags;
	uint8_t		bhslr_response;
	uint8_t		bhslr_reserved;
	uint8_t		bhslr_total_ahs_len;
	uint8_t		bhslr_data_segment_len[3];
	uint64_t	bhslr_reserved2;
	uint32_t	bhslr_initiator_task_tag;
	uint32_t	bhslr_reserved3;
	uint32_t	bhslr_statsn;
	uint32_t	bhslr_expcmdsn;
	uint32_t	bhslr_maxcmdsn;
	uint32_t	bhslr_reserved4;
	uint16_t	bhslr_time2wait;
	uint16_t	bhslr_time2retain;
	uint32_t	bhslr_reserved5;
};
CTASSERT(sizeof(struct iscsi_bhs_logout_response) == ISCSI_BHS_SIZE);

#define	BHSAM_EVENT_TARGET_REQUESTS_LOGOUT		1
#define	BHSAM_EVENT_TARGET_TERMINATES_CONNECTION	2
#define	BHSAM_EVENT_TARGET_TERMINATES_SESSION		3

struct iscsi_bhs_asynchronous_message {
	uint8_t		bhsam_opcode;
	uint8_t		bhsam_flags;
	uint16_t	bhsam_reserved;
	uint8_t		bhsam_total_ahs_len;
	uint8_t		bhsam_data_segment_len[3];
	uint64_t	bhsam_lun;
	uint32_t	bhsam_0xffffffff;
	uint32_t	bhsam_reserved2;
	uint32_t	bhsam_statsn;
	uint32_t	bhsam_expcmdsn;
	uint32_t	bhsam_maxcmdsn;
	uint8_t		bhsam_async_event;
	uint8_t		bhsam_async_vcode;
	uint16_t	bhsam_parameter1;
	uint16_t	bhsam_parameter2;
	uint16_t	bhsam_parameter3;
	uint32_t	bhsam_reserved3;
};
CTASSERT(sizeof(struct iscsi_bhs_asynchronous_message) == ISCSI_BHS_SIZE);

#define BHSSR_REASON_DATA_DIGEST_ERROR	0x02
#define BHSSR_PROTOCOL_ERROR		0x04
#define BHSSR_COMMAND_NOT_SUPPORTED	0x05
#define BHSSR_INVALID_PDU_FIELD		0x09

struct iscsi_bhs_reject {
	uint8_t		bhsr_opcode;
	uint8_t		bhsr_flags;
	uint8_t		bhsr_reason;
	uint8_t		bhsr_reserved;
	uint8_t		bhsr_total_ahs_len;
	uint8_t		bhsr_data_segment_len[3];
	uint64_t	bhsr_reserved2;
	uint32_t	bhsr_0xffffffff;
	uint32_t	bhsr_reserved3;
	uint32_t	bhsr_statsn;
	uint32_t	bhsr_expcmdsn;
	uint32_t	bhsr_maxcmdsn;
	uint32_t	bhsr_datasn_r2tsn;
	uint32_t	bhsr_reserved4;
	uint32_t	bhsr_reserved5;
};
CTASSERT(sizeof(struct iscsi_bhs_reject) == ISCSI_BHS_SIZE);

#endif /* !ISCSI_PROTO_H */
