/*
 * Copyright (c) 2006-2019, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2019-04-23     tyx          the first version
 */

#include <rtthread.h>
#include <rtdevice.h>
#include <hwcrypto.h>

/**
 * @brief           Setting context type (Direct calls are not recommended)
 *
 * @param ctx       Crypto context
 * @param type      Types of settings
 *
 * @return          RT_EOK on success.
 */
rt_err_t rt_hwcrypto_set_type(struct rt_hwcrypto_ctx *ctx, hwcrypto_type type)
{
    if (ctx)
    {
        /* Is it the same category? */
        if ((ctx->type & HWCRYPTO_MAIN_TYPE_MASK) == (type & HWCRYPTO_MAIN_TYPE_MASK))
        {
            ctx->type = type;
            return RT_EOK;
        }
        /* Context is empty type */
        else if (ctx->type == HWCRYPTO_TYPE_NULL)
        {
            ctx->type = type;
            return RT_EOK;
        }
        else
        {
            return -RT_ERROR;
        }
    }
    return -RT_EINVAL;
}

/**
 * @brief           Reset context type (Direct calls are not recommended)
 *
 * @param ctx       Crypto context
 *
 */
void rt_hwcrypto_ctx_reset(struct rt_hwcrypto_ctx *ctx)
{
    if (ctx && ctx->device->ops->reset)
    {
        ctx->device->ops->reset(ctx);
    }
}

/**
 * @brief           Init crypto context
 *
 * @param ctx       The context to initialize
 * @param device    Hardware crypto device
 * @param type      Type of context
 * @param obj_size  Size of context object
 *
 * @return          RT_EOK on success.
 */
rt_err_t rt_hwcrypto_ctx_init(struct rt_hwcrypto_ctx *ctx, struct rt_hwcrypto_device *device, hwcrypto_type type)
{
    rt_err_t err;

    /* Setting context type */
    rt_hwcrypto_set_type(ctx, type);
    ctx->device = device;
    /* Create hardware context */
    err = ctx->device->ops->create(ctx);
    if (err != RT_EOK)
    {
        return err;
    }
    return RT_EOK;
}

/**
 * @brief           Create crypto context
 *
 * @param device    Hardware crypto device
 * @param type      Type of context
 * @param obj_size  Size of context object
 *
 * @return          Crypto context
 */
struct rt_hwcrypto_ctx *rt_hwcrypto_ctx_create(struct rt_hwcrypto_device *device, hwcrypto_type type, rt_uint32_t obj_size)
{
    struct rt_hwcrypto_ctx *ctx;
    rt_err_t err;

    /* Parameter checking */
    if (device == RT_NULL || obj_size < sizeof(struct rt_hwcrypto_ctx))
    {
        return RT_NULL;
    }
    ctx = rt_malloc(obj_size);
    if (ctx == RT_NULL)
    {
        return ctx;
    }
    rt_memset(ctx, 0, obj_size);
    /* Init context */
    err = rt_hwcrypto_ctx_init(ctx, device, type);
    if (err != RT_EOK)
    {
        rt_free(ctx);
        ctx = RT_NULL;
    }
    return ctx;
}

/**
 * @brief           Destroy crypto context
 *
 * @param device    Crypto context
 */
void rt_hwcrypto_ctx_destroy(struct rt_hwcrypto_ctx *ctx)
{
    if (ctx == RT_NULL)
    {
        return;
    }
    /* Destroy hardware context */
    if (ctx->device->ops->destroy)
    {
        ctx->device->ops->destroy(ctx);
    }
    /* Free the resources */
    rt_free(ctx);
}

/**
 * @brief           Copy crypto context
 *
 * @param des       The destination context
 * @param src       The context to be copy
 *
 * @return          RT_EOK on success.
 */
rt_err_t rt_hwcrypto_ctx_cpy(struct rt_hwcrypto_ctx *des, const struct rt_hwcrypto_ctx *src)
{
    if (des == RT_NULL || src == RT_NULL)
    {
        return -RT_EINVAL;
    }

    /* The equipment is different or of different types and cannot be copied */
    if (des->device != src->device ||
        (des->type & HWCRYPTO_MAIN_TYPE_MASK) != (src->type & HWCRYPTO_MAIN_TYPE_MASK))
    {
        return -RT_EINVAL;
    }
    des->type = src->type;
    /* Calling Hardware Context Copy Function */
    return src->device->ops->copy(des, src);
}

/**
 * @brief           Get the default hardware crypto device
 *
 * @return          Hardware crypto device
 *
 */
struct rt_hwcrypto_device *rt_hwcrypto_dev_default(void)
{
    static struct rt_hwcrypto_device *hwcrypto_dev;

    /* If there is a default device, return the device */
    if (hwcrypto_dev)
    {
        return hwcrypto_dev;
    }
    /* Find by default device name */
    hwcrypto_dev = (struct rt_hwcrypto_device *)rt_device_find(RT_HWCRYPTO_DEFAULT_NAME);
    return hwcrypto_dev;
}

/**
 * @brief           Get the unique ID of the device
 *
 * @param device    Device object
 *
 * @return          Device unique ID
 */
rt_uint64_t rt_hwcrypto_id(struct rt_hwcrypto_device *device)
{
    if (device)
    {
        return device->id;
    }
    return 0;
}

#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops hwcrypto_ops =
{
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL
};
#endif

/**
 * @brief           Register hardware crypto device
 *
 * @param device    Hardware crypto device
 * @param name      Name of device
 *
 * @return          RT_EOK on success.
 */
rt_err_t rt_hwcrypto_register(struct rt_hwcrypto_device *device, const char *name)
{
    rt_err_t err;

    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(name != RT_NULL);
    RT_ASSERT(device->ops != RT_NULL);
    RT_ASSERT(device->ops->create != RT_NULL);
    RT_ASSERT(device->ops->destroy != RT_NULL);
    RT_ASSERT(device->ops->copy != RT_NULL);
    RT_ASSERT(device->ops->reset != RT_NULL);

    rt_memset(&device->parent, 0, sizeof(struct rt_device));
#ifdef RT_USING_DEVICE_OPS
    device->parent.ops = &hwcrypto_ops;
#else
    device->parent.init       = RT_NULL;
    device->parent.open       = RT_NULL;
    device->parent.close      = RT_NULL;
    device->parent.read       = RT_NULL;
    device->parent.write      = RT_NULL;
    device->parent.control    = RT_NULL;
#endif

    device->parent.user_data  = RT_NULL;
    device->parent.type = RT_Device_Class_Miscellaneous;

    /* Register device */
    err = rt_device_register(&device->parent, name, RT_DEVICE_FLAG_RDWR);

    return err;
}
