/*	$NetBSD: iscsi_pdu.h,v 1.4 2017/12/03 19:07:10 christos Exp $	*/

/*-
 * Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Wasabi Systems, Inc.
 *
 * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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_PDU_H
#define _ISCSI_PDU_H

#define BHS_SIZE           48	/* Basic Header Segment (without digest) */
#define PDU_HEADER_SIZE    52	/* PDU Header with Digest */

#define OP_IMMEDIATE       0x40	/* Bit 1 in Opcode field: immediate delivery */
#define OPCODE_MASK        0x3f	/* Mask for opcode */

/* PDU Flags field */

#define FLAG_FINAL         0x80	/* Bit 0: Final PDU in sequence */
#define FLAG_TRANSIT       0x80	/* Bit 0: Transit to next login phase */
#define FLAG_CONTINUE      0x40	/* Bit 1: Continue PDU */
#define FLAG_ACK           0x40	/* Bit 1: Acknowledge */
#define FLAG_READ          0x40	/* Bit 1: Read Data */
#define FLAG_WRITE         0x20	/* Bit 2: Write Data */
#define FLAG_BIDI_OFLO     0x10	/* Bit 3: Bidirectional Read Residual Oflow */
#define FLAG_BIDI_UFLOW    0x08	/* Bit 4: Bidirectional Read Residual Uflow */
#define FLAG_OVERFLOW      0x04	/* Bit 5: Residual Overflow */
#define FLAG_UNDERFLOW     0x02	/* Bit 6: Residual Underflow */
#define FLAG_STATUS        0x01	/* Bit 7: Command Status is valid */

/* CSG/NSG flag field codes */

#define SG_SECURITY_NEGOTIATION           0
#define SG_LOGIN_OPERATIONAL_NEGOTIATION  1
#define SG_FULL_FEATURE_PHASE             3

#define CSG_SHIFT          2	/* shift factor for CSG field */
#define SG_MASK            3	/* mask for CSG (after shift) and NSG */

#define NEXT_PHASE(ph)  ((!ph) ? 1 : 3)	/* no phase 2 */

/* Task Management Function Codes (in Flags byte) */

#define ABORT_TASK            1
#define ABORT_TASK_SET        2
#define CLEAR_ACA             3
#define CLEAR_TASK_SET        4
#define LOGICAL_UNIT_RESET    5
#define TARGET_WARM_RESET     6
#define TARGET_COLD_RESET     7
#define TASK_REASSIGN         8

/* ISID T-Field (first byte) */

#define T_FORMAT_OUI    0x00
#define T_FORMAT_EN     0x40
#define T_FORMAT_RANDOM 0x80


/* Task Attributes */

#define ATTR_UNTAGGED         0
#define ATTR_SIMPLE           1
#define ATTR_ORDERED          2
#define ATTR_HEAD_OF_QUEUE    3
#define ATTR_ACA              4

/* Initiator Opcodes */

#define IOP_NOP_Out              0x00
#define IOP_SCSI_Command         0x01
#define IOP_SCSI_Task_Management 0x02
#define IOP_Login_Request        0x03
#define IOP_Text_Request         0x04
#define IOP_SCSI_Data_out        0x05
#define IOP_Logout_Request       0x06
#define IOP_SNACK_Request        0x10

/* Target Opcodes */

#define TOP_NOP_In               0x20
#define TOP_SCSI_Response        0x21
#define TOP_SCSI_Task_Management 0x22
#define TOP_Login_Response       0x23
#define TOP_Text_Response        0x24
#define TOP_SCSI_Data_in         0x25
#define TOP_Logout_Response      0x26
#define TOP_R2T                  0x31
#define TOP_Asynchronous_Message 0x32
#define TOP_Reject               0x3f

/*
 * The Opcode-dependent fields of the BHS, defined per PDU
 */

/* Command + Response */

struct scsi_command_pdu_s
{
	uint32_t ExpectedDataTransferLength;
	uint32_t CmdSN;
	uint32_t ExpStatSN;
	uint8_t SCSI_CDB[16];
} __packed;

typedef struct scsi_command_pdu_s scsi_command_pdu_t;

struct scsi_response_pdu_s
{
	uint32_t SNACKTag;
	uint32_t StatSN;
	uint32_t ExpCmdSN;
	uint32_t MaxCmdSN;
	uint32_t ExpDataSN;
	uint32_t ReadResidualCount;
	uint32_t ResidualCount;
} __packed;

typedef struct scsi_response_pdu_s scsi_response_pdu_t;


/* Task Management */

struct task_management_req_pdu_s
{
	uint32_t ReferencedTaskTag;
	uint32_t CmdSN;
	uint32_t ExpStatSN;
	uint32_t RefCmdSN;
	uint32_t ExpDataSN;
	uint8_t reserved[8];
} __packed;

typedef struct task_management_req_pdu_s task_management_req_pdu_t;


struct task_management_rsp_pdu_s
{
	uint32_t reserved1;
	uint32_t StatSN;
	uint32_t ExpCmdSN;
	uint32_t MaxCmdSN;
	uint8_t reserved2[12];
} __packed;

typedef struct task_management_rsp_pdu_s task_management_rsp_pdu_t;


/* Data Out & In, R2T */

struct data_out_pdu_s
{
	uint32_t TargetTransferTag;
	uint32_t reserved1;
	uint32_t ExpStatSN;
	uint32_t reserved2;
	uint32_t DataSN;
	uint32_t BufferOffset;
	uint32_t reserved3;
} __packed;

typedef struct data_out_pdu_s data_out_pdu_t;


struct data_in_pdu_s
{
	uint32_t TargetTransferTag;
	uint32_t StatSN;
	uint32_t ExpCmdSN;
	uint32_t MaxCmdSN;
	uint32_t DataSN;
	uint32_t BufferOffset;
	uint32_t ResidualCount;
} __packed;

typedef struct data_in_pdu_s data_in_pdu_t;


struct r2t_pdu_s
{
	uint32_t TargetTransferTag;
	uint32_t StatSN;
	uint32_t ExpCmdSN;
	uint32_t MaxCmdSN;
	uint32_t R2TSN;
	uint32_t BufferOffset;
	uint32_t DesiredDataTransferLength;
} __packed;

typedef struct r2t_pdu_s r2t_pdu_t;


/* Asynch message */

struct asynch_pdu_s
{
	uint32_t reserved1;
	uint32_t StatSN;
	uint32_t ExpCmdSN;
	uint32_t MaxCmdSN;
	uint8_t AsyncEvent;
	uint8_t AsyncVCode;
	uint16_t Parameter1;
	uint16_t Parameter2;
	uint16_t Parameter3;
	uint32_t reserved2;
} __packed;

typedef struct asynch_pdu_s asynch_pdu_t;


/* Text request / response */

struct text_req_pdu_s
{
	uint32_t TargetTransferTag;
	uint32_t CmdSN;
	uint32_t ExpStatSN;
	uint8_t reserved[16];
} __packed;

typedef struct text_req_pdu_s text_req_pdu_t;


struct text_rsp_pdu_s
{
	uint32_t TargetTransferTag;
	uint32_t StatSN;
	uint32_t ExpCmdSN;
	uint32_t MaxCmdSN;
	uint8_t reserved[12];
} __packed;

typedef struct text_rsp_pdu_s text_rsp_pdu_t;


/* Login request / response */

struct login_req_pdu_s
{
	uint16_t CID;
	uint16_t reserved1;
	uint32_t CmdSN;
	uint32_t ExpStatSN;
	uint8_t reserved2[16];
} __packed;

typedef struct login_req_pdu_s login_req_pdu_t;

/* Overlays LUN field in login request and response */
struct login_isid_s
{
	uint8_t ISID_A;
	uint16_t ISID_B;
	uint8_t ISID_C;
	uint16_t ISID_D;
	uint16_t TSIH;
} __packed;

typedef struct login_isid_s login_isid_t;

struct login_rsp_pdu_s
{
	uint32_t reserved1;
	uint32_t StatSN;
	uint32_t ExpCmdSN;
	uint32_t MaxCmdSN;
	uint8_t StatusClass;
	uint8_t StatusDetail;
	uint8_t reserved2[10];
} __packed;

typedef struct login_rsp_pdu_s login_rsp_pdu_t;


/* Logout request / response */

struct logout_req_pdu_s
{
	uint16_t CID;
	uint16_t reserved2;
	uint32_t CmdSN;
	uint32_t ExpStatSN;
	uint8_t reserved3[16];
} __packed;

typedef struct logout_req_pdu_s logout_req_pdu_t;


struct logout_rsp_pdu_s
{
	uint32_t reserved2;
	uint32_t StatSN;
	uint32_t ExpCmdSN;
	uint32_t MaxCmdSN;
	uint32_t reserved3;
	uint16_t Time2Wait;
	uint16_t Time2Retain;
	uint32_t reserved4;
} __packed;

typedef struct logout_rsp_pdu_s logout_rsp_pdu_t;


/* SNACK request */

/* SNACK Types (in Flags field) */

#define SNACK_DATA_NAK     0
#define SNACK_STATUS_NAK   1
#define SNACK_DATA_ACK     2
#define SNACK_RDATA_NAK    3

struct snack_req_pdu_s
{
	uint32_t TargetTransferTag;
	uint32_t reserved1;
	uint32_t ExpStatSN;
	uint8_t reserved2[8];
	uint32_t BegRun;
	uint32_t RunLength;
} __packed;

typedef struct snack_req_pdu_s snack_req_pdu_t;


/* Reject */

#define REJECT_DIGEST_ERROR         2
#define REJECT_SNACK                3
#define REJECT_PROTOCOL_ERROR       4
#define REJECT_CMD_NOT_SUPPORTED    5
#define REJECT_IMMED_COMMAND        6
#define REJECT_TASK_IN_PROGRESS     7
#define REJECT_INVALID_DATA_ACK     8
#define REJECT_INVALID_PDU_FIELD    9
#define REJECT_LONG_OPERATION       10
#define REJECT_NEGOTIATION_RESET    11
#define REJECT_WAITING_FOR_LOGOUT   12


struct reject_pdu_s
{
	uint32_t reserved2;
	uint32_t StatSN;
	uint32_t ExpCmdSN;
	uint32_t MaxCmdSN;
	uint8_t DataSN;
	uint8_t reserved[8];
} __packed;

typedef struct reject_pdu_s reject_pdu_t;


/* NOP Out & In */

struct nop_out_pdu_s
{
	uint32_t TargetTransferTag;
	uint32_t CmdSN;
	uint32_t ExpStatSN;
	uint8_t reserved[16];
} __packed;

typedef struct nop_out_pdu_s nop_out_pdu_t;


struct nop_in_pdu_s
{
	uint32_t TargetTransferTag;
	uint32_t StatSN;
	uint32_t ExpCmdSN;
	uint32_t MaxCmdSN;
	uint8_t reserved3[12];
} __packed;

typedef struct nop_in_pdu_s nop_in_pdu_t;


/*
 * The complete PDU Header.
 */

struct pdu_header_s
{
	uint8_t pduh_Opcode;
	uint8_t pduh_Flags;
	uint8_t pduh_OpcodeSpecific[2];
	uint8_t pduh_TotalAHSLength;
	uint8_t pduh_DataSegmentLength[3];
	uint64_t pduh_LUN;
	uint32_t pduh_InitiatorTaskTag;
	union
	{
		scsi_command_pdu_t command;
		scsi_response_pdu_t response;
		task_management_req_pdu_t task_req;
		task_management_rsp_pdu_t task_rsp;
		data_out_pdu_t data_out;
		data_in_pdu_t data_in;
		r2t_pdu_t r2t;
		asynch_pdu_t asynch;
		text_req_pdu_t text_req;
		text_rsp_pdu_t text_rsp;
		login_req_pdu_t login_req;
		login_rsp_pdu_t login_rsp;
		logout_req_pdu_t logout_req;
		logout_rsp_pdu_t logout_rsp;
		snack_req_pdu_t snack;
		reject_pdu_t reject;
		nop_out_pdu_t nop_out;
		nop_in_pdu_t nop_in;
	} pduh_p;
	uint32_t pduh_HeaderDigest;
} __packed;

typedef struct pdu_header_s pdu_header_t;

#endif /* !_ISCSI_PDU_H */
