/*
 * AEAD algorithm self tests
 */
int _fips_qcrypto_aead_selftest(struct fips_selftest_data *selftest_d)
{
	int rc = 0, err, tv_index, num_tv, authsize, buf_length;
	struct crypto_aead *tfm;
	struct aead_request *aead_req;
	struct _fips_completion fips_completion;
	struct scatterlist fips_sg, fips_assoc_sg;
	char *k_align_src = NULL;
	struct _fips_test_vector_aead tv_aead;

	num_tv = (sizeof(fips_test_vector_aead)) /
		(sizeof(struct _fips_test_vector_aead));

	/* One-by-one testing */
	for (tv_index = 0; tv_index < num_tv; tv_index++) {

		memcpy(&tv_aead, &fips_test_vector_aead[tv_index],
			(sizeof(struct _fips_test_vector_aead)));

		if (tv_aead.pln_txt_len > tv_aead.enc_txt_len)
			buf_length = tv_aead.pln_txt_len;
		else
			buf_length = tv_aead.enc_txt_len;

		/* Single buffer allocation for in place operation */
		k_align_src = kzalloc(buf_length, GFP_KERNEL);
		if (k_align_src == NULL) {
			pr_err("qcrypto:, Failed to allocate memory for k_align_src %ld\n",
				PTR_ERR(k_align_src));
			return -ENOMEM;
		}
		memcpy(&k_align_src[0], tv_aead.pln_txt,
			tv_aead.pln_txt_len);

		/* use_sw flags are set in dtsi file which makes
		default Linux API calls to go to s/w crypto instead
		of h/w crypto. This code makes sure that all selftests
		calls always go to h/w, independent of DTSI flags. */
		if (selftest_d->prefix_aead_algo) {
			if (_fips_get_alg_cra_name(tv_aead.mod_alg,
				selftest_d->algo_prefix,
				strlen(tv_aead.mod_alg))) {
				rc = -1;
				pr_err("Algo Name is too long for tv %d\n",
					tv_index);
				goto clr_buf;
			}
		}
		tfm = crypto_alloc_aead(tv_aead.mod_alg, 0, 0);
		if (IS_ERR(tfm)) {
			pr_err("qcrypto: %s algorithm not found\n",
				tv_aead.mod_alg);
			rc = -ENOMEM;
			goto clr_buf;
		}
		aead_req = aead_request_alloc(tfm, GFP_KERNEL);
		if (!aead_req) {
			pr_err("qcrypto:aead_request_alloc failed\n");
			rc = -ENOMEM;
			goto clr_tfm;
		}
		rc = qcrypto_aead_set_device(aead_req, selftest_d->ce_device);
		if (rc != 0) {
			pr_err("%s qcrypto_cipher_set_device failed with err %d\n",
				__func__, rc);
			goto clr_aead_req;
		}
		init_completion(&fips_completion.completion);
		aead_request_set_callback(aead_req,
			CRYPTO_TFM_REQ_MAY_BACKLOG,
			_fips_cb, &fips_completion);
		crypto_aead_clear_flags(tfm, ~0);
		rc = crypto_aead_setkey(tfm, tv_aead.key, tv_aead.klen);
		if (rc) {
			pr_err("qcrypto:crypto_aead_setkey failed\n");
			goto clr_aead_req;
		}
		authsize = abs(tv_aead.enc_txt_len - tv_aead.pln_txt_len);
		rc = crypto_aead_setauthsize(tfm, authsize);
		if (rc) {
			pr_err("qcrypto:crypto_aead_setauthsize failed\n");
			goto clr_aead_req;
		}
		sg_init_one(&fips_sg, k_align_src,
		tv_aead.pln_txt_len + authsize);
		aead_request_set_crypt(aead_req, &fips_sg, &fips_sg,
			tv_aead.pln_txt_len , tv_aead.iv);
		sg_init_one(&fips_assoc_sg, tv_aead.assoc, tv_aead.alen);
		aead_request_set_assoc(aead_req, &fips_assoc_sg, tv_aead.alen);
		/**** Encryption test ****/
		rc = crypto_aead_encrypt(aead_req);
		if (rc == -EINPROGRESS || rc == -EBUSY) {
			rc = wait_for_completion_interruptible(
				&fips_completion.completion);
			err = fips_completion.err;
			if (!rc && !err) {
				INIT_COMPLETION(fips_completion.completion);
			} else {
				pr_err("qcrypto:aead:ENC, wait_for_completion failed\n");
				goto clr_aead_req;
			}

		}
		if (memcmp(k_align_src, tv_aead.enc_txt, tv_aead.enc_txt_len)) {
			rc = -1;
			goto clr_aead_req;
		}

		/** Decryption test **/
		init_completion(&fips_completion.completion);
		aead_request_set_callback(aead_req,
			CRYPTO_TFM_REQ_MAY_BACKLOG,
			_fips_cb, &fips_completion);
		crypto_aead_clear_flags(tfm, ~0);
		rc = crypto_aead_setkey(tfm, tv_aead.key, tv_aead.klen);
		if (rc) {
			pr_err("qcrypto:aead:DEC, crypto_aead_setkey failed\n");
			goto clr_aead_req;
		}

		authsize = abs(tv_aead.enc_txt_len - tv_aead.pln_txt_len);
		rc = crypto_aead_setauthsize(tfm, authsize);
		if (rc) {
			pr_err("qcrypto:aead:DEC, crypto_aead_setauthsize failed\n");
			goto clr_aead_req;
		}

		sg_init_one(&fips_sg, k_align_src,
			tv_aead.enc_txt_len + authsize);
		aead_request_set_crypt(aead_req, &fips_sg, &fips_sg,
			tv_aead.enc_txt_len, tv_aead.iv);
		sg_init_one(&fips_assoc_sg, tv_aead.assoc, tv_aead.alen);
		aead_request_set_assoc(aead_req, &fips_assoc_sg,
			tv_aead.alen);
		rc = crypto_aead_decrypt(aead_req);
		if (rc == -EINPROGRESS || rc == -EBUSY) {
			rc = wait_for_completion_interruptible(
				&fips_completion.completion);
			err = fips_completion.err;
			if (!rc && !err) {
				INIT_COMPLETION(fips_completion.completion);
			} else {
				pr_err("qcrypto:aead:DEC, wait_for_completion failed\n");
				goto clr_aead_req;
			}

		}

		if (memcmp(k_align_src, tv_aead.pln_txt, tv_aead.pln_txt_len)) {
			rc = -1;
			goto clr_aead_req;
		}
clr_aead_req:
		aead_request_free(aead_req);
clr_tfm:
		crypto_free_aead(tfm);
clr_buf:
		kzfree(k_align_src);
	/* In case of any failure, return error */
		if (rc)
			return rc;
	}
	return rc;
}
int cryptodev_cipher_init(struct cipher_data *out, const char *alg_name,
				uint8_t *keyp, size_t keylen, int stream, int aead)
{
	int ret;

	if (aead == 0) {
		struct ablkcipher_alg *alg;

		out->async.s = crypto_alloc_ablkcipher(alg_name, 0, 0);
		if (unlikely(IS_ERR(out->async.s))) {
			ddebug(1, "Failed to load cipher %s", alg_name);
				return -EINVAL;
		}

		alg = crypto_ablkcipher_alg(out->async.s);
		if (alg != NULL) {
			/* Was correct key length supplied? */
			if (alg->max_keysize > 0 &&
					unlikely((keylen < alg->min_keysize) ||
					(keylen > alg->max_keysize))) {
				ddebug(1, "Wrong keylen '%zu' for algorithm '%s'. Use %u to %u.",
						keylen, alg_name, alg->min_keysize, alg->max_keysize);
				ret = -EINVAL;
				goto error;
			}
		}

		out->blocksize = crypto_ablkcipher_blocksize(out->async.s);
		out->ivsize = crypto_ablkcipher_ivsize(out->async.s);
		out->alignmask = crypto_ablkcipher_alignmask(out->async.s);

		ret = crypto_ablkcipher_setkey(out->async.s, keyp, keylen);
	} else {
		out->async.as = crypto_alloc_aead(alg_name, 0, 0);
		if (unlikely(IS_ERR(out->async.as))) {
			ddebug(1, "Failed to load cipher %s", alg_name);
			return -EINVAL;
		}

		out->blocksize = crypto_aead_blocksize(out->async.as);
		out->ivsize = crypto_aead_ivsize(out->async.as);
		out->alignmask = crypto_aead_alignmask(out->async.as);

		ret = crypto_aead_setkey(out->async.as, keyp, keylen);
	}

	if (unlikely(ret)) {
		ddebug(1, "Setting key failed for %s-%zu.", alg_name, keylen*8);
		ret = -EINVAL;
		goto error;
	}

	out->stream = stream;
	out->aead = aead;

	out->async.result = kzalloc(sizeof(*out->async.result), GFP_KERNEL);
	if (unlikely(!out->async.result)) {
		ret = -ENOMEM;
		goto error;
	}

	init_completion(&out->async.result->completion);

	if (aead == 0) {
		out->async.request = ablkcipher_request_alloc(out->async.s, GFP_KERNEL);
		if (unlikely(!out->async.request)) {
			derr(1, "error allocating async crypto request");
			ret = -ENOMEM;
			goto error;
		}

		ablkcipher_request_set_callback(out->async.request,
					CRYPTO_TFM_REQ_MAY_BACKLOG,
					cryptodev_complete, out->async.result);
	} else {
		out->async.arequest = aead_request_alloc(out->async.as, GFP_KERNEL);
		if (unlikely(!out->async.arequest)) {
			derr(1, "error allocating async crypto request");
			ret = -ENOMEM;
			goto error;
		}

		aead_request_set_callback(out->async.arequest,
					CRYPTO_TFM_REQ_MAY_BACKLOG,
					cryptodev_complete, out->async.result);
	}

	out->init = 1;
	return 0;
error:
	if (aead == 0) {
		if (out->async.request)
			ablkcipher_request_free(out->async.request);
		if (out->async.s)
			crypto_free_ablkcipher(out->async.s);
	} else {
		if (out->async.arequest)
			aead_request_free(out->async.arequest);
		if (out->async.as)
			crypto_free_aead(out->async.as);
	}
	kfree(out->async.result);

	return ret;
}