/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 */
#include "ka_fs_pub.h"
#include "ka_memory_pub.h"
#include "ka_ioctl_pub.h"
#include "ka_kernel_def_pub.h"
#include "rmo_auto_init.h"
#include "pbl/pbl_task_ctx.h"

#include "pbl/pbl_davinci_api.h"
#include "rmo_fops.h"
#include "rmo_slab.h"

static int rmo_open(ka_inode_t *inode, ka_file_t *file)
{
    struct task_id_entity *task = NULL;

    task = (struct task_id_entity *)rmo_kzalloc(sizeof(struct task_id_entity), KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
    if (task == NULL) {
#ifndef EMU_ST
        rmo_err("Failed to kzalloc task_id_entity.\n");
        return -ENOMEM;
#endif
    }

    task->tgid = ka_task_get_current_tgid();
    task_get_start_time_by_tgid(ka_task_get_current_tgid(), &task->start_time);
    ka_fs_set_file_private_data(file, (void *)task);

    rmo_info("Open. (tgid=%d)\n", ka_task_get_current_tgid());
    return 0;
}

static int rmo_release(ka_inode_t *inode, ka_file_t *file)
{
    struct task_id_entity *task = (struct task_id_entity *)ka_fs_get_file_private_data(file);
    if (task != NULL) {
        module_feature_auto_uninit_task(0, task->tgid, (void*)&(task->start_time));
        rmo_info("Release. (tgid=%d)\n", task->tgid);
        rmo_kfree(task);
        ka_fs_set_file_private_data(file, NULL);
    }

    return 0;
}

static int (* rmo_ioctl_handler[RMO_MAX_CMD])(u32 cmd, unsigned long arg) = {NULL, };

void rmo_register_ioctl_cmd_func(int nr, int (*fn)(u32 cmd, unsigned long arg))
{
    rmo_ioctl_handler[nr] = fn;
}

static long rmo_ioctl(ka_file_t *file, u32 cmd, unsigned long arg)
{
    if (arg == 0) {
        return -EINVAL;
    }
    if (_KA_IOC_NR(cmd) >= RMO_MAX_CMD) {
        rmo_err("The command is invalid, which is out of range. (cmd=%u)\n", _KA_IOC_NR(cmd));
        return -EINVAL;
    }

    if (rmo_ioctl_handler[_KA_IOC_NR(cmd)] == NULL) {
        rmo_warn("The command is not support. (cmd=%u)\n", _KA_IOC_NR(cmd));
        return -EOPNOTSUPP;
    }

    return rmo_ioctl_handler[_KA_IOC_NR(cmd)](cmd, arg);
}

static int rmo_mremap(ka_vm_area_struct_t * area)
{
    return -ENOTSUPP;
}

static ka_vm_operations_struct_t rmo_vm_ops = {
    ka_vm_ops_init_mremap(rmo_mremap)
};

static int rmo_mmap(ka_file_t *file, ka_vm_area_struct_t *vma)
{
    ka_mm_set_vm_ops(vma, &rmo_vm_ops);
    ka_mm_set_vm_flags(vma, ka_mm_get_vm_flags(vma) | KA_VM_LOCKED |
                                KA_VM_DONTEXPAND | KA_VM_DONTDUMP | KA_VM_DONTCOPY | KA_VM_IO | KA_VM_PFNMAP);
    if (!(ka_mm_get_vm_flags(vma) & KA_VM_WRITE)) {
        ka_mm_vm_flags_clear(vma, KA_VM_MAYWRITE);
    }
    ka_mm_set_vm_private_data(vma, (void *)(uintptr_t)RMO_VMA_MAP_MAGIC_VERIFY);

    return 0;
}

static ka_file_operations_t rmo_fops = {
    .owner = KA_THIS_MODULE,
    .open = rmo_open,
    .release = rmo_release,
    .unlocked_ioctl = rmo_ioctl,
    .mmap = rmo_mmap,
};

int rmo_fops_init(void)
{
    int ret;

    ret = drv_davinci_register_sub_module(RMO_CHAR_DEV_NAME, &rmo_fops);
    if (ret != 0) {
        rmo_err("Module register fail. (ret=%d)\n", ret);
        return ret;
    }

    return 0;
}

void rmo_fops_uninit(void)
{
    (void)drv_ascend_unregister_sub_module(RMO_CHAR_DEV_NAME);
}