/*-
 *   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_OPAL_INTERNAL_H
#define SPDK_OPAL_INTERNAL_H

#include "spdk/opal_spec.h"
#include "spdk/opal.h"
#include "spdk/scsi_spec.h"

#define IO_BUFFER_LENGTH		2048
#define MAX_TOKS			64
#define OPAL_KEY_MAX			256
#define OPAL_UID_LENGTH			8
#define OPAL_MAX_LRS			8 /* minimum 8 defined by spec */

#define SPDK_OPAL_TPER_TIMEOUT		600 /* seconds */

#define GENERIC_HOST_SESSION_NUM	0x69

#define OPAL_INVAL_PARAM		12

#define SPDK_DTAERROR_NO_METHOD_STATUS	0x89

enum opal_token_type {
	OPAL_DTA_TOKENID_BYTESTRING	= 0xE0,
	OPAL_DTA_TOKENID_SINT		= 0xE1,
	OPAL_DTA_TOKENID_UINT		= 0xE2,
	OPAL_DTA_TOKENID_TOKEN		= 0xE3, /* actual token is returned */
	OPAL_DTA_TOKENID_INVALID	= 0X0,
};

enum opal_atom_width {
	OPAL_WIDTH_TINY,    /* 1 byte in length */
	OPAL_WIDTH_SHORT,   /* a 1-byte header and contain up to 15 bytes of data */
	OPAL_WIDTH_MEDIUM,  /* a 2-byte header and contain up to 2047 bytes of data */
	OPAL_WIDTH_LONG,    /* a 4-byte header and which contain up to 16,777,215 bytes of data */
	OPAL_WIDTH_TOKEN
};

enum opal_uid_enum {
	/* users */
	UID_SMUID,
	UID_THISSP,
	UID_ADMINSP,
	UID_LOCKINGSP,
	UID_ANYBODY,
	UID_SID,
	UID_ADMIN1,
	UID_USER1,
	UID_USER2,

	/* tables */
	UID_LOCKINGRANGE_GLOBAL,
	UID_LOCKINGRANGE_ACE_RDLOCKED,
	UID_LOCKINGRANGE_ACE_WRLOCKED,
	UID_MBRCONTROL,
	UID_MBR,
	UID_AUTHORITY_TABLE,
	UID_C_PIN_TABLE,
	UID_LOCKING_INFO_TABLE,
	UID_PSID,

	/* C_PIN_TABLE object ID's */
	UID_C_PIN_MSID,
	UID_C_PIN_SID,
	UID_C_PIN_ADMIN1,
	UID_C_PIN_USER1,

	/* half UID's (only first 4 bytes used) */
	UID_HALF_AUTHORITY_OBJ_REF,
	UID_HALF_BOOLEAN_ACE,
};

/* enum for indexing the spdk_opal_method array */
enum opal_method_enum {
	PROPERTIES_METHOD,
	STARTSESSION_METHOD,
	REVERT_METHOD,
	ACTIVATE_METHOD,
	NEXT_METHOD,
	GETACL_METHOD,
	GENKEY_METHOD,
	REVERTSP_METHOD,
	GET_METHOD,
	SET_METHOD,
	AUTHENTICATE_METHOD,
	RANDOM_METHOD,
};

union spdk_discovery0_features {
	struct spdk_d0_tper_features tper;
	struct spdk_d0_locking_features locking;
	struct spdk_d0_geo_features geometry;
	struct spdk_d0_datastore_features datastore;
	struct spdk_d0_sum  sumode;
	struct spdk_d0_opal_v200 opalv200;
	struct spdk_d0_opal_v100 opalv100;
};

struct opal_common_session {
	uint32_t sum; /* single user mode */
	uint32_t who;
	struct spdk_opal_key *opal_key;
};

struct spdk_opal_locking_session {
	struct opal_common_session session;
	uint32_t l_state;
};

struct spdk_opal_new_pw_session {
	struct opal_common_session old_session;
	struct opal_common_session new_session;
};

const uint8_t spdk_opal_uid[][OPAL_UID_LENGTH] = {
	/* users */
	[UID_SMUID] = /* Session Manager UID */
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff },
	[UID_THISSP] =
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
	[UID_ADMINSP] =
	{ 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x00, 0x01 },
	[UID_LOCKINGSP] =
	{ 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x00, 0x02 },
	[UID_ANYBODY] =
	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01 },
	[UID_SID] = /* Security Identifier UID */
	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06 },
	[UID_ADMIN1] =
	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x01, 0x00, 0x01 },
	[UID_USER1] =
	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x03, 0x00, 0x01 },
	[UID_USER2] =
	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x03, 0x00, 0x02 },

	/* tables */
	[UID_LOCKINGRANGE_GLOBAL] =
	{ 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00, 0x01 },
	[UID_LOCKINGRANGE_ACE_RDLOCKED] =
	{ 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0xE0, 0x01 },
	[UID_LOCKINGRANGE_ACE_WRLOCKED] =
	{ 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0xE8, 0x01 },
	[UID_MBRCONTROL] =
	{ 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x00, 0x01 },
	[UID_MBR] =
	{ 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00 },
	[UID_AUTHORITY_TABLE] =
	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00},
	[UID_C_PIN_TABLE] =
	{ 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00},
	[UID_LOCKING_INFO_TABLE] =
	{ 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x01 },
	[UID_PSID] =
	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x01, 0xff, 0x01 },

	/* C_PIN_TABLE object ID's */
	[UID_C_PIN_MSID] =
	{ 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x84, 0x02},
	[UID_C_PIN_SID] =
	{ 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01},
	[UID_C_PIN_ADMIN1] =
	{ 0x00, 0x00, 0x00, 0x0B, 0x00, 0x01, 0x00, 0x01},
	[UID_C_PIN_USER1] =
	{ 0x00, 0x00, 0x00, 0x0B, 0x00, 0x03, 0x00, 0x01},

	/* half UID's (only first 4 bytes used) */
	[UID_HALF_AUTHORITY_OBJ_REF] =
	{ 0x00, 0x00, 0x0C, 0x05, 0xff, 0xff, 0xff, 0xff },
	[UID_HALF_BOOLEAN_ACE] =
	{ 0x00, 0x00, 0x04, 0x0E, 0xff, 0xff, 0xff, 0xff },
};

/*
 * TCG Storage SSC Methods.
 */
const uint8_t spdk_opal_method[][OPAL_UID_LENGTH] = {
	[PROPERTIES_METHOD] =
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01 },
	[STARTSESSION_METHOD] =
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02 },
	[REVERT_METHOD] =
	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x02 },
	[ACTIVATE_METHOD] =
	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x03 },
	[NEXT_METHOD] =
	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08 },
	[GETACL_METHOD] =
	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0d },
	[GENKEY_METHOD] =
	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10 },
	[REVERTSP_METHOD] =
	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11 },
	[GET_METHOD] =
	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x16 },
	[SET_METHOD] =
	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17 },
	[AUTHENTICATE_METHOD] =
	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1c },
	[RANDOM_METHOD] =
	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x01 },
};

/*
 * Response token
 */
struct spdk_opal_resp_token {
	const uint8_t *pos;
	uint8_t _padding[7];
	union {
		uint64_t unsigned_num;
		int64_t signed_num;
	} stored;
	size_t len; /* header + data */
	enum opal_token_type type;
	enum opal_atom_width width;
};

struct spdk_opal_resp_parsed {
	int num;
	struct spdk_opal_resp_token resp_tokens[MAX_TOKS];
};

struct spdk_opal_key {
	uint8_t locking_range;
	uint8_t key_len;
	uint8_t _padding[6];
	uint8_t key[OPAL_KEY_MAX];
};

struct opal_locking_range_setup_session {
	uint8_t id;
	uint8_t _padding[7];
	uint64_t range_start;
	uint64_t range_length;
	bool read_lock_enabled;
	bool write_lock_enabled;
	struct opal_common_session session;
};

/* header of a response */
struct spdk_opal_header {
	struct spdk_opal_compacket com_packet;
	struct spdk_opal_packet packet;
	struct spdk_opal_data_subpacket sub_packet;
};

struct spdk_opal_dev {
	bool supported;
	void *dev_handler;

	uint16_t comid;
	uint32_t hsn;
	uint32_t tsn;
	uint64_t align;
	uint64_t lowest_lba;

	size_t cmd_pos;
	uint8_t cmd[IO_BUFFER_LENGTH];
	uint8_t resp[IO_BUFFER_LENGTH];

	struct spdk_opal_resp_parsed parsed_resp;
	size_t prev_d_len;
	void *prev_data;

	struct spdk_opal_info *opal_info;

	uint64_t timeout;   /* seconds */
	uint8_t max_ranges; /* max locking range number */
	struct spdk_opal_locking_range_info *locking_range_info[OPAL_MAX_LRS];

	pthread_mutex_t mutex_lock; /* some structs are accessed by current thread only */
	spdk_opal_revert_cb revert_cb_fn;
	void *ctx;  /* user context data */
};

#endif
