/*-
 * This file is provided under a dual BSD/GPLv2 license. When using or
 * redistributing this file, you may do so under either license.
 *
 *   BSD LICENSE
 *
 * Copyright 2008-2016 Freescale Semiconductor Inc.
 * Copyright 2016 NXP.
 *
 * 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 the above-listed copyright holders nor the
 * names of any contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 *   GPL LICENSE SUMMARY
 *
 * ALTERNATIVELY, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") as published by the Free Software
 * Foundation, either version 2 of that License or (at your option) any
 * later version.
 *
 * 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 HOLDERS 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 __RTA_PROTOCOL_CMD_H__
#define __RTA_PROTOCOL_CMD_H__

extern enum rta_sec_era rta_sec_era;

static inline int
__rta_ssl_proto(uint16_t protoinfo)
{
	switch (protoinfo) {
	case OP_PCL_SSL30_RC4_40_MD5_2:
	case OP_PCL_SSL30_RC4_128_MD5_2:
	case OP_PCL_SSL30_RC4_128_SHA_5:
	case OP_PCL_SSL30_RC4_40_MD5_3:
	case OP_PCL_SSL30_RC4_128_MD5_3:
	case OP_PCL_SSL30_RC4_128_SHA:
	case OP_PCL_SSL30_RC4_128_MD5:
	case OP_PCL_SSL30_RC4_40_SHA:
	case OP_PCL_SSL30_RC4_40_MD5:
	case OP_PCL_SSL30_RC4_128_SHA_2:
	case OP_PCL_SSL30_RC4_128_SHA_3:
	case OP_PCL_SSL30_RC4_128_SHA_4:
	case OP_PCL_SSL30_RC4_128_SHA_6:
	case OP_PCL_SSL30_RC4_128_SHA_7:
	case OP_PCL_SSL30_RC4_128_SHA_8:
	case OP_PCL_SSL30_RC4_128_SHA_9:
	case OP_PCL_SSL30_RC4_128_SHA_10:
	case OP_PCL_TLS_ECDHE_PSK_RC4_128_SHA:
		if (rta_sec_era == RTA_SEC_ERA_7)
			return -EINVAL;
		/* fall through if not Era 7 */
	case OP_PCL_SSL30_DES40_CBC_SHA:
	case OP_PCL_SSL30_DES_CBC_SHA_2:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_5:
	case OP_PCL_SSL30_DES40_CBC_SHA_2:
	case OP_PCL_SSL30_DES_CBC_SHA_3:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_6:
	case OP_PCL_SSL30_DES40_CBC_SHA_3:
	case OP_PCL_SSL30_DES_CBC_SHA_4:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_7:
	case OP_PCL_SSL30_DES40_CBC_SHA_4:
	case OP_PCL_SSL30_DES_CBC_SHA_5:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_8:
	case OP_PCL_SSL30_DES40_CBC_SHA_5:
	case OP_PCL_SSL30_DES_CBC_SHA_6:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_9:
	case OP_PCL_SSL30_DES40_CBC_SHA_6:
	case OP_PCL_SSL30_DES_CBC_SHA_7:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_10:
	case OP_PCL_SSL30_DES_CBC_SHA:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA:
	case OP_PCL_SSL30_DES_CBC_MD5:
	case OP_PCL_SSL30_3DES_EDE_CBC_MD5:
	case OP_PCL_SSL30_DES40_CBC_SHA_7:
	case OP_PCL_SSL30_DES40_CBC_MD5:
	case OP_PCL_SSL30_AES_128_CBC_SHA:
	case OP_PCL_SSL30_AES_128_CBC_SHA_2:
	case OP_PCL_SSL30_AES_128_CBC_SHA_3:
	case OP_PCL_SSL30_AES_128_CBC_SHA_4:
	case OP_PCL_SSL30_AES_128_CBC_SHA_5:
	case OP_PCL_SSL30_AES_128_CBC_SHA_6:
	case OP_PCL_SSL30_AES_256_CBC_SHA:
	case OP_PCL_SSL30_AES_256_CBC_SHA_2:
	case OP_PCL_SSL30_AES_256_CBC_SHA_3:
	case OP_PCL_SSL30_AES_256_CBC_SHA_4:
	case OP_PCL_SSL30_AES_256_CBC_SHA_5:
	case OP_PCL_SSL30_AES_256_CBC_SHA_6:
	case OP_PCL_TLS12_AES_128_CBC_SHA256_2:
	case OP_PCL_TLS12_AES_128_CBC_SHA256_3:
	case OP_PCL_TLS12_AES_128_CBC_SHA256_4:
	case OP_PCL_TLS12_AES_128_CBC_SHA256_5:
	case OP_PCL_TLS12_AES_256_CBC_SHA256_2:
	case OP_PCL_TLS12_AES_256_CBC_SHA256_3:
	case OP_PCL_TLS12_AES_256_CBC_SHA256_4:
	case OP_PCL_TLS12_AES_256_CBC_SHA256_5:
	case OP_PCL_TLS12_AES_128_CBC_SHA256_6:
	case OP_PCL_TLS12_AES_256_CBC_SHA256_6:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_2:
	case OP_PCL_SSL30_AES_128_CBC_SHA_7:
	case OP_PCL_SSL30_AES_256_CBC_SHA_7:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_3:
	case OP_PCL_SSL30_AES_128_CBC_SHA_8:
	case OP_PCL_SSL30_AES_256_CBC_SHA_8:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_4:
	case OP_PCL_SSL30_AES_128_CBC_SHA_9:
	case OP_PCL_SSL30_AES_256_CBC_SHA_9:
	case OP_PCL_SSL30_AES_128_GCM_SHA256_1:
	case OP_PCL_SSL30_AES_256_GCM_SHA384_1:
	case OP_PCL_SSL30_AES_128_GCM_SHA256_2:
	case OP_PCL_SSL30_AES_256_GCM_SHA384_2:
	case OP_PCL_SSL30_AES_128_GCM_SHA256_3:
	case OP_PCL_SSL30_AES_256_GCM_SHA384_3:
	case OP_PCL_SSL30_AES_128_GCM_SHA256_4:
	case OP_PCL_SSL30_AES_256_GCM_SHA384_4:
	case OP_PCL_SSL30_AES_128_GCM_SHA256_5:
	case OP_PCL_SSL30_AES_256_GCM_SHA384_5:
	case OP_PCL_SSL30_AES_128_GCM_SHA256_6:
	case OP_PCL_TLS_DH_ANON_AES_256_GCM_SHA384:
	case OP_PCL_TLS_PSK_AES_128_GCM_SHA256:
	case OP_PCL_TLS_PSK_AES_256_GCM_SHA384:
	case OP_PCL_TLS_DHE_PSK_AES_128_GCM_SHA256:
	case OP_PCL_TLS_DHE_PSK_AES_256_GCM_SHA384:
	case OP_PCL_TLS_RSA_PSK_AES_128_GCM_SHA256:
	case OP_PCL_TLS_RSA_PSK_AES_256_GCM_SHA384:
	case OP_PCL_TLS_PSK_AES_128_CBC_SHA256:
	case OP_PCL_TLS_PSK_AES_256_CBC_SHA384:
	case OP_PCL_TLS_DHE_PSK_AES_128_CBC_SHA256:
	case OP_PCL_TLS_DHE_PSK_AES_256_CBC_SHA384:
	case OP_PCL_TLS_RSA_PSK_AES_128_CBC_SHA256:
	case OP_PCL_TLS_RSA_PSK_AES_256_CBC_SHA384:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_11:
	case OP_PCL_SSL30_AES_128_CBC_SHA_10:
	case OP_PCL_SSL30_AES_256_CBC_SHA_10:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_12:
	case OP_PCL_SSL30_AES_128_CBC_SHA_11:
	case OP_PCL_SSL30_AES_256_CBC_SHA_11:
	case OP_PCL_SSL30_AES_128_CBC_SHA_12:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_13:
	case OP_PCL_SSL30_AES_256_CBC_SHA_12:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_14:
	case OP_PCL_SSL30_AES_128_CBC_SHA_13:
	case OP_PCL_SSL30_AES_256_CBC_SHA_13:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_15:
	case OP_PCL_SSL30_AES_128_CBC_SHA_14:
	case OP_PCL_SSL30_AES_256_CBC_SHA_14:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_16:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_17:
	case OP_PCL_SSL30_3DES_EDE_CBC_SHA_18:
	case OP_PCL_SSL30_AES_128_CBC_SHA_15:
	case OP_PCL_SSL30_AES_128_CBC_SHA_16:
	case OP_PCL_SSL30_AES_128_CBC_SHA_17:
	case OP_PCL_SSL30_AES_256_CBC_SHA_15:
	case OP_PCL_SSL30_AES_256_CBC_SHA_16:
	case OP_PCL_SSL30_AES_256_CBC_SHA_17:
	case OP_PCL_TLS_ECDHE_ECDSA_AES_128_CBC_SHA256:
	case OP_PCL_TLS_ECDHE_ECDSA_AES_256_CBC_SHA384:
	case OP_PCL_TLS_ECDH_ECDSA_AES_128_CBC_SHA256:
	case OP_PCL_TLS_ECDH_ECDSA_AES_256_CBC_SHA384:
	case OP_PCL_TLS_ECDHE_RSA_AES_128_CBC_SHA256:
	case OP_PCL_TLS_ECDHE_RSA_AES_256_CBC_SHA384:
	case OP_PCL_TLS_ECDH_RSA_AES_128_CBC_SHA256:
	case OP_PCL_TLS_ECDH_RSA_AES_256_CBC_SHA384:
	case OP_PCL_TLS_ECDHE_ECDSA_AES_128_GCM_SHA256:
	case OP_PCL_TLS_ECDHE_ECDSA_AES_256_GCM_SHA384:
	case OP_PCL_TLS_ECDH_ECDSA_AES_128_GCM_SHA256:
	case OP_PCL_TLS_ECDH_ECDSA_AES_256_GCM_SHA384:
	case OP_PCL_TLS_ECDHE_RSA_AES_128_GCM_SHA256:
	case OP_PCL_TLS_ECDHE_RSA_AES_256_GCM_SHA384:
	case OP_PCL_TLS_ECDH_RSA_AES_128_GCM_SHA256:
	case OP_PCL_TLS_ECDH_RSA_AES_256_GCM_SHA384:
	case OP_PCL_TLS_ECDHE_PSK_3DES_EDE_CBC_SHA:
	case OP_PCL_TLS_ECDHE_PSK_AES_128_CBC_SHA:
	case OP_PCL_TLS_ECDHE_PSK_AES_256_CBC_SHA:
	case OP_PCL_TLS_ECDHE_PSK_AES_128_CBC_SHA256:
	case OP_PCL_TLS_ECDHE_PSK_AES_256_CBC_SHA384:
	case OP_PCL_TLS12_3DES_EDE_CBC_MD5:
	case OP_PCL_TLS12_3DES_EDE_CBC_SHA160:
	case OP_PCL_TLS12_3DES_EDE_CBC_SHA224:
	case OP_PCL_TLS12_3DES_EDE_CBC_SHA256:
	case OP_PCL_TLS12_3DES_EDE_CBC_SHA384:
	case OP_PCL_TLS12_3DES_EDE_CBC_SHA512:
	case OP_PCL_TLS12_AES_128_CBC_SHA160:
	case OP_PCL_TLS12_AES_128_CBC_SHA224:
	case OP_PCL_TLS12_AES_128_CBC_SHA256:
	case OP_PCL_TLS12_AES_128_CBC_SHA384:
	case OP_PCL_TLS12_AES_128_CBC_SHA512:
	case OP_PCL_TLS12_AES_192_CBC_SHA160:
	case OP_PCL_TLS12_AES_192_CBC_SHA224:
	case OP_PCL_TLS12_AES_192_CBC_SHA256:
	case OP_PCL_TLS12_AES_192_CBC_SHA512:
	case OP_PCL_TLS12_AES_256_CBC_SHA160:
	case OP_PCL_TLS12_AES_256_CBC_SHA224:
	case OP_PCL_TLS12_AES_256_CBC_SHA256:
	case OP_PCL_TLS12_AES_256_CBC_SHA384:
	case OP_PCL_TLS12_AES_256_CBC_SHA512:
	case OP_PCL_TLS_PVT_AES_192_CBC_SHA160:
	case OP_PCL_TLS_PVT_AES_192_CBC_SHA384:
	case OP_PCL_TLS_PVT_AES_192_CBC_SHA224:
	case OP_PCL_TLS_PVT_AES_192_CBC_SHA512:
	case OP_PCL_TLS_PVT_AES_192_CBC_SHA256:
	case OP_PCL_TLS_PVT_MASTER_SECRET_PRF_FE:
	case OP_PCL_TLS_PVT_MASTER_SECRET_PRF_FF:
		return 0;
	}

	return -EINVAL;
}

static inline int
__rta_ike_proto(uint16_t protoinfo)
{
	switch (protoinfo) {
	case OP_PCL_IKE_HMAC_MD5:
	case OP_PCL_IKE_HMAC_SHA1:
	case OP_PCL_IKE_HMAC_AES128_CBC:
	case OP_PCL_IKE_HMAC_SHA256:
	case OP_PCL_IKE_HMAC_SHA384:
	case OP_PCL_IKE_HMAC_SHA512:
	case OP_PCL_IKE_HMAC_AES128_CMAC:
		return 0;
	}

	return -EINVAL;
}

static inline int
__rta_ipsec_proto(uint16_t protoinfo)
{
	uint16_t proto_cls1 = protoinfo & OP_PCL_IPSEC_CIPHER_MASK;
	uint16_t proto_cls2 = protoinfo & OP_PCL_IPSEC_AUTH_MASK;

	switch (proto_cls1) {
	case OP_PCL_IPSEC_AES_NULL_WITH_GMAC:
		if (rta_sec_era < RTA_SEC_ERA_2)
			return -EINVAL;
		/* no break */
	case OP_PCL_IPSEC_AES_CCM8:
	case OP_PCL_IPSEC_AES_CCM12:
	case OP_PCL_IPSEC_AES_CCM16:
	case OP_PCL_IPSEC_AES_GCM8:
	case OP_PCL_IPSEC_AES_GCM12:
	case OP_PCL_IPSEC_AES_GCM16:
		/* CCM, GCM, GMAC require PROTINFO[7:0] = 0 */
		if (proto_cls2 == OP_PCL_IPSEC_HMAC_NULL)
			return 0;
		return -EINVAL;
	case OP_PCL_IPSEC_NULL:
		if (rta_sec_era < RTA_SEC_ERA_2)
			return -EINVAL;
		/* no break */
	case OP_PCL_IPSEC_DES_IV64:
	case OP_PCL_IPSEC_DES:
	case OP_PCL_IPSEC_3DES:
	case OP_PCL_IPSEC_AES_CBC:
	case OP_PCL_IPSEC_AES_CTR:
		break;
	default:
		return -EINVAL;
	}

	switch (proto_cls2) {
	case OP_PCL_IPSEC_HMAC_NULL:
	case OP_PCL_IPSEC_HMAC_MD5_96:
	case OP_PCL_IPSEC_HMAC_SHA1_96:
	case OP_PCL_IPSEC_AES_XCBC_MAC_96:
	case OP_PCL_IPSEC_HMAC_MD5_128:
	case OP_PCL_IPSEC_HMAC_SHA1_160:
	case OP_PCL_IPSEC_AES_CMAC_96:
	case OP_PCL_IPSEC_HMAC_SHA2_256_128:
	case OP_PCL_IPSEC_HMAC_SHA2_384_192:
	case OP_PCL_IPSEC_HMAC_SHA2_512_256:
		return 0;
	}

	return -EINVAL;
}

static inline int
__rta_srtp_proto(uint16_t protoinfo)
{
	uint16_t proto_cls1 = protoinfo & OP_PCL_SRTP_CIPHER_MASK;
	uint16_t proto_cls2 = protoinfo & OP_PCL_SRTP_AUTH_MASK;

	switch (proto_cls1) {
	case OP_PCL_SRTP_AES_CTR:
		switch (proto_cls2) {
		case OP_PCL_SRTP_HMAC_SHA1_160:
			return 0;
		}
		/* no break */
	}

	return -EINVAL;
}

static inline int
__rta_macsec_proto(uint16_t protoinfo)
{
	switch (protoinfo) {
	case OP_PCL_MACSEC:
		return 0;
	}

	return -EINVAL;
}

static inline int
__rta_wifi_proto(uint16_t protoinfo)
{
	switch (protoinfo) {
	case OP_PCL_WIFI:
		return 0;
	}

	return -EINVAL;
}

static inline int
__rta_wimax_proto(uint16_t protoinfo)
{
	switch (protoinfo) {
	case OP_PCL_WIMAX_OFDM:
	case OP_PCL_WIMAX_OFDMA:
		return 0;
	}

	return -EINVAL;
}

/* Allowed blob proto flags for each SEC Era */
static const uint32_t proto_blob_flags[] = {
	OP_PCL_BLOB_FORMAT_MASK | OP_PCL_BLOB_BLACK,
	OP_PCL_BLOB_FORMAT_MASK | OP_PCL_BLOB_BLACK | OP_PCL_BLOB_TKEK |
		OP_PCL_BLOB_EKT | OP_PCL_BLOB_REG_MASK,
	OP_PCL_BLOB_FORMAT_MASK | OP_PCL_BLOB_BLACK | OP_PCL_BLOB_TKEK |
		OP_PCL_BLOB_EKT | OP_PCL_BLOB_REG_MASK,
	OP_PCL_BLOB_FORMAT_MASK | OP_PCL_BLOB_BLACK | OP_PCL_BLOB_TKEK |
		OP_PCL_BLOB_EKT | OP_PCL_BLOB_REG_MASK | OP_PCL_BLOB_SEC_MEM,
	OP_PCL_BLOB_FORMAT_MASK | OP_PCL_BLOB_BLACK | OP_PCL_BLOB_TKEK |
		OP_PCL_BLOB_EKT | OP_PCL_BLOB_REG_MASK | OP_PCL_BLOB_SEC_MEM
};

static inline int
__rta_blob_proto(uint16_t protoinfo)
{
	if (protoinfo & ~proto_blob_flags[rta_sec_era])
		return -EINVAL;

	switch (protoinfo & OP_PCL_BLOB_FORMAT_MASK) {
	case OP_PCL_BLOB_FORMAT_NORMAL:
	case OP_PCL_BLOB_FORMAT_MASTER_VER:
	case OP_PCL_BLOB_FORMAT_TEST:
		break;
	default:
		return -EINVAL;
	}

	switch (protoinfo & OP_PCL_BLOB_REG_MASK) {
	case OP_PCL_BLOB_AFHA_SBOX:
		if (rta_sec_era < RTA_SEC_ERA_3)
			return -EINVAL;
		/* no break */
	case OP_PCL_BLOB_REG_MEMORY:
	case OP_PCL_BLOB_REG_KEY1:
	case OP_PCL_BLOB_REG_KEY2:
	case OP_PCL_BLOB_REG_SPLIT:
	case OP_PCL_BLOB_REG_PKE:
		return 0;
	}

	return -EINVAL;
}

static inline int
__rta_dlc_proto(uint16_t protoinfo)
{
	if ((rta_sec_era < RTA_SEC_ERA_2) &&
	    (protoinfo & (OP_PCL_PKPROT_DSA_MSG | OP_PCL_PKPROT_HASH_MASK |
	     OP_PCL_PKPROT_EKT_Z | OP_PCL_PKPROT_DECRYPT_Z |
	     OP_PCL_PKPROT_DECRYPT_PRI)))
		return -EINVAL;

	switch (protoinfo & OP_PCL_PKPROT_HASH_MASK) {
	case OP_PCL_PKPROT_HASH_MD5:
	case OP_PCL_PKPROT_HASH_SHA1:
	case OP_PCL_PKPROT_HASH_SHA224:
	case OP_PCL_PKPROT_HASH_SHA256:
	case OP_PCL_PKPROT_HASH_SHA384:
	case OP_PCL_PKPROT_HASH_SHA512:
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static inline int
__rta_rsa_enc_proto(uint16_t protoinfo)
{
	switch (protoinfo & OP_PCL_RSAPROT_OP_MASK) {
	case OP_PCL_RSAPROT_OP_ENC_F_IN:
		if ((protoinfo & OP_PCL_RSAPROT_FFF_MASK) !=
		    OP_PCL_RSAPROT_FFF_RED)
			return -EINVAL;
		break;
	case OP_PCL_RSAPROT_OP_ENC_F_OUT:
		switch (protoinfo & OP_PCL_RSAPROT_FFF_MASK) {
		case OP_PCL_RSAPROT_FFF_RED:
		case OP_PCL_RSAPROT_FFF_ENC:
		case OP_PCL_RSAPROT_FFF_EKT:
		case OP_PCL_RSAPROT_FFF_TK_ENC:
		case OP_PCL_RSAPROT_FFF_TK_EKT:
			break;
		default:
			return -EINVAL;
		}
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static inline int
__rta_rsa_dec_proto(uint16_t protoinfo)
{
	switch (protoinfo & OP_PCL_RSAPROT_OP_MASK) {
	case OP_PCL_RSAPROT_OP_DEC_ND:
	case OP_PCL_RSAPROT_OP_DEC_PQD:
	case OP_PCL_RSAPROT_OP_DEC_PQDPDQC:
		break;
	default:
		return -EINVAL;
	}

	switch (protoinfo & OP_PCL_RSAPROT_PPP_MASK) {
	case OP_PCL_RSAPROT_PPP_RED:
	case OP_PCL_RSAPROT_PPP_ENC:
	case OP_PCL_RSAPROT_PPP_EKT:
	case OP_PCL_RSAPROT_PPP_TK_ENC:
	case OP_PCL_RSAPROT_PPP_TK_EKT:
		break;
	default:
		return -EINVAL;
	}

	if (protoinfo & OP_PCL_RSAPROT_FMT_PKCSV15)
		switch (protoinfo & OP_PCL_RSAPROT_FFF_MASK) {
		case OP_PCL_RSAPROT_FFF_RED:
		case OP_PCL_RSAPROT_FFF_ENC:
		case OP_PCL_RSAPROT_FFF_EKT:
		case OP_PCL_RSAPROT_FFF_TK_ENC:
		case OP_PCL_RSAPROT_FFF_TK_EKT:
			break;
		default:
			return -EINVAL;
		}

	return 0;
}

/*
 * DKP Protocol - Restrictions on key (SRC,DST) combinations
 * For e.g. key_in_out[0][0] = 1 means (SRC=IMM,DST=IMM) combination is allowed
 */
static const uint8_t key_in_out[4][4] = { {1, 0, 0, 0},
					  {1, 1, 1, 1},
					  {1, 0, 1, 0},
					  {1, 0, 0, 1} };

static inline int
__rta_dkp_proto(uint16_t protoinfo)
{
	int key_src = (protoinfo & OP_PCL_DKP_SRC_MASK) >> OP_PCL_DKP_SRC_SHIFT;
	int key_dst = (protoinfo & OP_PCL_DKP_DST_MASK) >> OP_PCL_DKP_DST_SHIFT;

	if (!key_in_out[key_src][key_dst]) {
		pr_err("PROTO_DESC: Invalid DKP key (SRC,DST)\n");
		return -EINVAL;
	}

	return 0;
}


static inline int
__rta_3g_dcrc_proto(uint16_t protoinfo)
{
	if (rta_sec_era == RTA_SEC_ERA_7)
		return -EINVAL;

	switch (protoinfo) {
	case OP_PCL_3G_DCRC_CRC7:
	case OP_PCL_3G_DCRC_CRC11:
		return 0;
	}

	return -EINVAL;
}

static inline int
__rta_3g_rlc_proto(uint16_t protoinfo)
{
	if (rta_sec_era == RTA_SEC_ERA_7)
		return -EINVAL;

	switch (protoinfo) {
	case OP_PCL_3G_RLC_NULL:
	case OP_PCL_3G_RLC_KASUMI:
	case OP_PCL_3G_RLC_SNOW:
		return 0;
	}

	return -EINVAL;
}

static inline int
__rta_lte_pdcp_proto(uint16_t protoinfo)
{
	if (rta_sec_era == RTA_SEC_ERA_7)
		return -EINVAL;

	switch (protoinfo) {
	case OP_PCL_LTE_ZUC:
		if (rta_sec_era < RTA_SEC_ERA_5)
			break;
	case OP_PCL_LTE_NULL:
	case OP_PCL_LTE_SNOW:
	case OP_PCL_LTE_AES:
		return 0;
	}

	return -EINVAL;
}

static inline int
__rta_lte_pdcp_mixed_proto(uint16_t protoinfo)
{
	switch (protoinfo & OP_PCL_LTE_MIXED_AUTH_MASK) {
	case OP_PCL_LTE_MIXED_AUTH_NULL:
	case OP_PCL_LTE_MIXED_AUTH_SNOW:
	case OP_PCL_LTE_MIXED_AUTH_AES:
	case OP_PCL_LTE_MIXED_AUTH_ZUC:
		break;
	default:
		return -EINVAL;
	}

	switch (protoinfo & OP_PCL_LTE_MIXED_ENC_MASK) {
	case OP_PCL_LTE_MIXED_ENC_NULL:
	case OP_PCL_LTE_MIXED_ENC_SNOW:
	case OP_PCL_LTE_MIXED_ENC_AES:
	case OP_PCL_LTE_MIXED_ENC_ZUC:
		return 0;
	}

	return -EINVAL;
}

struct proto_map {
	uint32_t optype;
	uint32_t protid;
	int (*protoinfo_func)(uint16_t);
};

static const struct proto_map proto_table[] = {
/*1*/	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_SSL30_PRF,	 __rta_ssl_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_TLS10_PRF,	 __rta_ssl_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_TLS11_PRF,	 __rta_ssl_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_TLS12_PRF,	 __rta_ssl_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_DTLS10_PRF,	 __rta_ssl_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_IKEV1_PRF,	 __rta_ike_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_IKEV2_PRF,	 __rta_ike_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_PUBLICKEYPAIR, __rta_dlc_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_DSASIGN,	 __rta_dlc_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_DSAVERIFY,	 __rta_dlc_proto},
	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_IPSEC,         __rta_ipsec_proto},
	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_SRTP,	         __rta_srtp_proto},
	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_SSL30,	 __rta_ssl_proto},
	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_TLS10,	 __rta_ssl_proto},
	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_TLS11,	 __rta_ssl_proto},
	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_TLS12,	 __rta_ssl_proto},
	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_DTLS10,	 __rta_ssl_proto},
	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_MACSEC,        __rta_macsec_proto},
	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_WIFI,          __rta_wifi_proto},
	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_WIMAX,         __rta_wimax_proto},
/*21*/	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_BLOB,          __rta_blob_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_DIFFIEHELLMAN, __rta_dlc_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_RSAENCRYPT,	 __rta_rsa_enc_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_RSADECRYPT,	 __rta_rsa_dec_proto},
	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_3G_DCRC,       __rta_3g_dcrc_proto},
	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_3G_RLC_PDU,    __rta_3g_rlc_proto},
	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_3G_RLC_SDU,    __rta_3g_rlc_proto},
	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_LTE_PDCP_USER, __rta_lte_pdcp_proto},
/*29*/	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_LTE_PDCP_CTRL, __rta_lte_pdcp_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_DKP_MD5,       __rta_dkp_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_DKP_SHA1,      __rta_dkp_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_DKP_SHA224,    __rta_dkp_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_DKP_SHA256,    __rta_dkp_proto},
	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_DKP_SHA384,    __rta_dkp_proto},
/*35*/	{OP_TYPE_UNI_PROTOCOL,   OP_PCLID_DKP_SHA512,    __rta_dkp_proto},
	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_PUBLICKEYPAIR, __rta_dlc_proto},
/*37*/	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_DSASIGN,	 __rta_dlc_proto},
/*38*/	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_LTE_PDCP_CTRL_MIXED,
	 __rta_lte_pdcp_mixed_proto},
	{OP_TYPE_DECAP_PROTOCOL, OP_PCLID_IPSEC_NEW,     __rta_ipsec_proto},
};

/*
 * Allowed OPERATION protocols for each SEC Era.
 * Values represent the number of entries from proto_table[] that are supported.
 */
static const unsigned int proto_table_sz[] = {21, 29, 29, 29, 29, 35, 37, 39};

static inline int
rta_proto_operation(struct program *program, uint32_t optype,
				      uint32_t protid, uint16_t protoinfo)
{
	uint32_t opcode = CMD_OPERATION;
	unsigned int i, found = 0;
	uint32_t optype_tmp = optype;
	unsigned int start_pc = program->current_pc;
	int ret = -EINVAL;

	for (i = 0; i < proto_table_sz[rta_sec_era]; i++) {
		/* clear last bit in optype to match also decap proto */
		optype_tmp &= (uint32_t)~(1 << OP_TYPE_SHIFT);
		if (optype_tmp == proto_table[i].optype) {
			if (proto_table[i].protid == protid) {
				/* nothing else to verify */
				if (proto_table[i].protoinfo_func == NULL) {
					found = 1;
					break;
				}
				/* check protoinfo */
				ret = (*proto_table[i].protoinfo_func)
						(protoinfo);
				if (ret < 0) {
					pr_err("PROTO_DESC: Bad PROTO Type. SEC Program Line: %d\n",
					       program->current_pc);
					goto err;
				}
				found = 1;
				break;
			}
		}
	}
	if (!found) {
		pr_err("PROTO_DESC: Operation Type Mismatch. SEC Program Line: %d\n",
		       program->current_pc);
		goto err;
	}

	__rta_out32(program, opcode | optype | protid | protoinfo);
	program->current_instruction++;
	return (int)start_pc;

 err:
	program->first_error_pc = start_pc;
	program->current_instruction++;
	return ret;
}

static inline int
rta_dkp_proto(struct program *program, uint32_t protid,
				uint16_t key_src, uint16_t key_dst,
				uint16_t keylen, uint64_t key,
				enum rta_data_type key_type)
{
	unsigned int start_pc = program->current_pc;
	unsigned int in_words = 0, out_words = 0;
	int ret;

	key_src &= OP_PCL_DKP_SRC_MASK;
	key_dst &= OP_PCL_DKP_DST_MASK;
	keylen &= OP_PCL_DKP_KEY_MASK;

	ret = rta_proto_operation(program, OP_TYPE_UNI_PROTOCOL, protid,
				  key_src | key_dst | keylen);
	if (ret < 0)
		return ret;

	if ((key_src == OP_PCL_DKP_SRC_PTR) ||
	    (key_src == OP_PCL_DKP_SRC_SGF)) {
		__rta_out64(program, program->ps, key);
		in_words = program->ps ? 2 : 1;
	} else if (key_src == OP_PCL_DKP_SRC_IMM) {
		__rta_inline_data(program, key, inline_flags(key_type), keylen);
		in_words = (unsigned int)((keylen + 3) / 4);
	}

	if ((key_dst == OP_PCL_DKP_DST_PTR) ||
	    (key_dst == OP_PCL_DKP_DST_SGF)) {
		out_words = in_words;
	} else  if (key_dst == OP_PCL_DKP_DST_IMM) {
		out_words = split_key_len(protid) / 4;
	}

	if (out_words < in_words) {
		pr_err("PROTO_DESC: DKP doesn't currently support a smaller descriptor\n");
		program->first_error_pc = start_pc;
		return -EINVAL;
	}

	/* If needed, reserve space in resulting descriptor for derived key */
	program->current_pc += (out_words - in_words);

	return (int)start_pc;
}

#endif /* __RTA_PROTOCOL_CMD_H__ */
