/*

 * 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 "fms/fms_dtm.h"

#include "fms_define.h"

#include "kernel_version_adapt.h"

#include "ascend_hal_error.h"

#include "ka_list_pub.h"

#include "ka_memory_pub.h"

#include "ka_kernel_def_pub.h"

#include "ka_task_pub.h"

#include "ka_dfx_pub.h"



#define print_sysfs (void)ka_dfx_printk



int dms_check_node_type(int node_type)

{

    if (DMS_EVENT_OBJ_TYPE(node_type) == DMS_EVENT_OBJ_KERNEL) {

        if (((node_type & 0xFF) >= (int)DMS_DEV_TYPE_SOC) && ((node_type & 0xFF) < (int)DMS_DEV_TYPE_MAX)) {

            return DRV_ERROR_NONE;

        }

    }



    if (DMS_EVENT_OBJ_TYPE(node_type) == DMS_EVENT_OBJ_USER) {

        if ((node_type >= (int)HAL_DMS_DEV_TYPE_BASE_SERVCIE) && (node_type < (int)HAL_DMS_DEV_TYPE_MAX)) {

            return DRV_ERROR_NONE;

        }

    }



    return DRV_ERROR_PARA_ERROR;

}

KA_EXPORT_SYMBOL(dms_check_node_type);



void dev_node_release(int owner_pid)

{

    int i;

    struct dms_node *node = NULL;

    struct dms_node *next = NULL;

    ka_list_head_t tmp_node;

    struct dms_dev_ctrl_block *dev_cb = NULL;



    KA_INIT_LIST_HEAD(&tmp_node);

    for (i = 0; i < ASCEND_DEV_MAX_NUM; i++) {

        dev_cb = dms_get_dev_cb(i);

        if (dev_cb == NULL) {

            continue;

        }

        ka_task_mutex_lock(&dev_cb->node_lock);

        if (ka_list_empty_careful(&dev_cb->dev_node_list) != 0) {

            ka_task_mutex_unlock(&dev_cb->node_lock);

            continue;

        }

        ka_list_for_each_entry_safe(node, next, &dev_cb->dev_node_list, list) {

            if ((node->pid == owner_pid) && (DMS_EVENT_OBJ_TYPE(node->node_type) == DMS_EVENT_OBJ_USER)) {

                dms_info("release dev_node. (devid=%d; node_type=0x%x; owner_pid=%d)\n", i, node->node_type, owner_pid);

                ka_list_del(&node->list);

                ka_list_add(&node->list, &tmp_node);

            }

        }

        ka_task_mutex_unlock(&dev_cb->node_lock);

    }



    ka_list_for_each_entry_safe(node, next, &tmp_node, list) {

        ka_list_del(&node->list);

        if ((node->ops != NULL) && (node->ops->uninit != NULL)) {

            /* Release all nodes of all devices under the PID. Only need to do it once. */

            node->ops->uninit(node);

            break;

        }

    }

    return;

}

KA_EXPORT_SYMBOL(dev_node_release);



#ifdef CFG_FEATURE_DEVICE_STATE_TABLE

static void dms_state_table_item_set(struct dms_node *node, struct dms_node *val)

{

    uint32_t i;

    struct dms_state_table *table = dms_get_state_table();



    if (node->owner_devid != 0) {

        return;

    }

    for(i = 0; i < table->num; i++) {

        if ((table->item[i].node_type == node->node_type) && (table->item[i].node_id == node->node_id)) {

            table->item[i].node = val;

            break;

        }

    }

}



static void dms_state_table_enable(struct dms_node *node)

{

    dms_state_table_item_set(node, node);

}



static void dms_state_table_disable(struct dms_node *node)

{

    dms_state_table_item_set(node, NULL);

}

#endif



static int _add_dev_node_to_list(struct dms_node *node)

{

    struct dms_dev_ctrl_block *dev_cb = NULL;

    dev_cb = dms_get_dev_cb(node->owner_devid);

    if (dev_cb == NULL) {

        return -ENODEV;

    }

    ka_task_mutex_lock(&dev_cb->node_lock);

    ka_list_add(&node->list, &dev_cb->dev_node_list);

#ifdef CFG_FEATURE_DEVICE_STATE_TABLE

    dms_state_table_enable(node);

#endif

    ka_task_mutex_unlock(&dev_cb->node_lock);

    return 0;

}

static int _remove_dev_node_from_list(struct dms_node *node)

{

    struct dms_dev_ctrl_block *dev_cb = NULL;

    dev_cb = dms_get_dev_cb(node->owner_devid);

    if (dev_cb == NULL) {

        return -ENODEV;

    }

    ka_task_mutex_lock(&dev_cb->node_lock);

    ka_list_del(&node->list);

#ifdef CFG_FEATURE_DEVICE_STATE_TABLE

    dms_state_table_disable(node);

#endif

    ka_task_mutex_unlock(&dev_cb->node_lock);

    return 0;

}

static int _devnode_check_para(struct dms_node *node)

{

    if (node == NULL) {

        dms_err("node is null\n");

        return DRV_ERROR_INVALID_VALUE;

    }



    if (node->ops == NULL) {

        dms_err("ops is null. (node_type=%d; node_id=%d; devid=%d)\n",

            node->node_type, node->node_id, node->owner_devid);

        return DRV_ERROR_INVALID_VALUE;

    }



    if (node->ops->init == NULL) {

        dms_err("ops->init is null. (node_type=%d; node_id=%d; devid=%d)\n",

            node->node_type, node->node_id, node->owner_devid);

        return DRV_ERROR_INVALID_VALUE;

    }



    if (node->ops->uninit == NULL) {

        dms_err("ops->uninit is null. (node_type=%d; node_id=%d; devid=%d)\n",

            node->node_type, node->node_id, node->owner_devid);

        return DRV_ERROR_INVALID_VALUE;

    }



    if (dms_check_node_type(node->node_type) != (int)DRV_ERROR_NONE) {

        dms_err("node_type is error. (node_type=0x%x; node_id=%d; devid=%d)\n",

            node->node_type, node->node_id, node->owner_devid);

        return DRV_ERROR_INVALID_VALUE;

    }



    if (node->owner_devid > ASCEND_DEV_MAX_NUM) {

        dms_err("owner_devid is error. (node_type=%d; node_id=%d; devid=%d)\n",

            node->node_type, node->node_id, node->owner_devid);

        return DRV_ERROR_INVALID_VALUE;

    }

    return 0;

}

int dms_register_dev_node(struct dms_node *node)

{

    int ret;

    ret = _devnode_check_para(node);

    if (ret != 0) {

        return ret;

    }

    /* call device node init function */

    if ((node->ops != NULL) && (node->ops->init != NULL)) {

        ret = node->ops->init(node);

        if (ret != 0) {

            return ret;

        }

    }

    ret = _add_dev_node_to_list(node);

    if (ret != 0) {

        if ((node->ops != NULL) && (node->ops->uninit != NULL)) {

            node->ops->uninit(node);

        }

    }

    return ret;

}

EXPORT_SYMBOL_ADAPT(dms_register_dev_node);



int dms_unregister_dev_node(struct dms_node *node)

{

    int ret;



    ret = _devnode_check_para(node);

    if (ret != 0) {

        return ret;

    }



    ret = _remove_dev_node_from_list(node);

    if (ret != 0) {

        return ret;

    }



    if ((node->ops != NULL) && (node->ops->uninit != NULL)) {

        node->ops->uninit(node);

    }

    return 0;

}

EXPORT_SYMBOL_ADAPT(dms_unregister_dev_node);



struct dms_node *dms_get_devnode_cb_nolock(u32 dev_id, int node_type, int node_id)

{

    struct dms_dev_ctrl_block *dev_cb = NULL;

    struct dms_node *node = NULL;

    struct dms_node *next = NULL;

    dev_cb = dms_get_dev_cb(dev_id);

    if (dev_cb == NULL) {

        return NULL;

    }



    if (ka_list_empty_careful(&dev_cb->dev_node_list) != 0) {

        return NULL;

    }

    ka_list_for_each_entry_safe(node, next, &dev_cb->dev_node_list, list)

    {

        if ((node->node_id == node_id) && (node->node_type == node_type)) {

            return node;

        }

    }



    return NULL;

}



struct dms_node *dms_get_devnode_cb(u32 dev_id, int node_type, int node_id)

{

    struct dms_dev_ctrl_block *dev_cb = NULL;

    struct dms_node *node = NULL;

    struct dms_node *next = NULL;

    dev_cb = dms_get_dev_cb(dev_id);

    if (dev_cb == NULL) {

        return NULL;

    }

    ka_task_mutex_lock(&dev_cb->node_lock);

    if (ka_list_empty_careful(&dev_cb->dev_node_list) != 0) {

        ka_task_mutex_unlock(&dev_cb->node_lock);

        return NULL;

    }

    ka_list_for_each_entry_safe(node, next, &dev_cb->dev_node_list, list)

    {

        if ((node->node_id == node_id) && (node->node_type == node_type)) {

            ka_task_mutex_unlock(&dev_cb->node_lock);

            return node;

        }

    }



    ka_task_mutex_unlock(&dev_cb->node_lock);

    return NULL;

}

KA_EXPORT_SYMBOL(dms_get_devnode_cb);



int dms_update_devnode_cb(u32 dev_id, int node_type, int node_id,

    int (*update_dms_node)(struct dms_node *node))

{

    int ret;

    struct dms_node *node;

    struct dms_dev_ctrl_block *dev_cb = NULL;

    dev_cb = dms_get_dev_cb(dev_id);

    if (dev_cb == NULL) {

        return -EINVAL;

    }

    ka_task_mutex_lock(&dev_cb->node_lock);

    node = dms_get_devnode_cb_nolock(dev_id, node_type, node_id);

    if (node == NULL) {

        dms_err("node is unregister, node_type=%d, node_id=%d\n", node_type, node_id);

        ka_task_mutex_unlock(&dev_cb->node_lock);

        return -ENODEV;

    }



    if (update_dms_node == NULL) {

        dms_err("update_dms_node is NULL, node_type=%d, node_id=%d\n", node_type, node_id);

        ka_task_mutex_unlock(&dev_cb->node_lock);

        return -EINVAL;

    }



    ret = update_dms_node(node);

    if (ret != 0) {

        ka_task_mutex_unlock(&dev_cb->node_lock);

        dms_err("update_dms_node failed, node_type=%d, node_id=%d\n", node_type, node_id);

        return ret;

    }

    ka_task_mutex_unlock(&dev_cb->node_lock);

    return DRV_ERROR_NONE;

}

KA_EXPORT_SYMBOL(dms_update_devnode_cb);



int dms_traverse_devnode_get_capacity(u32 dev_id, struct dsmi_dtm_node_s node_info[],

    unsigned int size, unsigned int *out_size)

{

    struct dms_dev_ctrl_block *dev_cb = NULL;

    struct dms_node *node = NULL;

    struct dms_node *next = NULL;

    int ret;

    const unsigned long long support_power_mask = (0x1 << 1);

    unsigned long long capacity;



    if (node_info == NULL || out_size == NULL) {

        dms_err("node_info or out_size is NULL\n");

        return -EINVAL;

    }

    *out_size = 0;



    dev_cb = dms_get_dev_cb(dev_id);

    if (dev_cb == NULL) {

        return -EINVAL;

    }

    ka_task_mutex_lock(&dev_cb->node_lock);

    if (ka_list_empty_careful(&dev_cb->dev_node_list) != 0) {

        ka_task_mutex_unlock(&dev_cb->node_lock);

        return -EINVAL;

    }

    ka_list_for_each_entry_safe(node, next, &dev_cb->dev_node_list, list)

    {

        if ((node->ops != NULL) && (node->ops->get_capacity != NULL)) {

            ret = node->ops->get_capacity(node, &capacity);

            if (ret != (int)DRV_ERROR_NONE) {

                dms_err("get_capacity failed, node_type=%d, node_id=%d\n", node->node_type, node->node_id);

                ka_task_mutex_unlock(&dev_cb->node_lock);

                return ret;

            }



            capacity &= support_power_mask;

            if (capacity == support_power_mask) {

                node_info[*out_size].node_type = node->node_type;

                node_info[*out_size].node_id = node->node_id;

                (*out_size)++;

                capacity = 0;

            }



            if (*out_size >= size) {

                dms_warn("dms_traverse_devnode_get_capacity out_size=%u, size=%d\n", *out_size, size);

                ka_task_mutex_unlock(&dev_cb->node_lock);

                return DRV_ERROR_NONE;

            }

        }

    }



    ka_task_mutex_unlock(&dev_cb->node_lock);

    return DRV_ERROR_NONE;

}

KA_EXPORT_SYMBOL(dms_traverse_devnode_get_capacity);



int dms_devnode_get_state(u32 dev_id, int node_type, int node_id, u32 *state)

{

    struct dms_node *node = NULL;

    node = dms_get_devnode_cb(dev_id, node_type, node_id);

    if (node == NULL) {

        return -ENODEV;

    }

    if ((node->ops == NULL) || (node->ops->get_state == NULL)) {

        return -EOPNOTSUPP;

    }

    return node->ops->get_state(node, state);

}



int dms_devnode_get_capacity(u32 dev_id, int node_type, int node_id,

    unsigned long long *cap)

{

    struct dms_node *node = NULL;

    node = dms_get_devnode_cb(dev_id, node_type, node_id);

    if (node == NULL) {

        return -ENODEV;

    }

    if ((node->ops == NULL) || (node->ops->get_capacity == NULL)) {

        return -EOPNOTSUPP;

    }

    return node->ops->get_capacity(node, cap);

}



int dms_devnode_enable_device(u32 dev_id, int node_type, int node_id)

{

    struct dms_node *node = NULL;

    int ret;

    struct dms_dev_ctrl_block *dev_cb = NULL;

    dms_event("dms_devnode_enable_device, node_type=%d, node_id=%d\n", node_type, node_id);

    dev_cb = dms_get_dev_cb(dev_id);

    if (dev_cb == NULL) {

        return -EINVAL;

    }

    ka_task_mutex_lock(&dev_cb->node_lock);

    node = dms_get_devnode_cb_nolock(dev_id, node_type, node_id);

    if (node == NULL) {

        dms_err("node is unregister, node_type=%d, node_id=%d\n", node_type, node_id);

        ka_task_mutex_unlock(&dev_cb->node_lock);

        return -ENODEV;

    }

    if (node->ops == NULL) {

        dms_err("node->ops is NULL, node_type=%d, node_id=%d\n", node_type, node_id);

        ka_task_mutex_unlock(&dev_cb->node_lock);

        return -EINVAL;

    }



    if (node->ops->enable_device == NULL) {

        dms_err("enable_device is NULL, node_type=%d, node_id=%d\n", node_type, node_id);

        ka_task_mutex_unlock(&dev_cb->node_lock);

        return -EINVAL;

    }

    ret = node->ops->enable_device(node);

    if (ret != 0) {

        dms_err("enable_device failed, node_type=%d, node_id=%d\n", node_type, node_id);

        ka_task_mutex_unlock(&dev_cb->node_lock);

        return ret;

    }



    ka_task_mutex_unlock(&dev_cb->node_lock);

    return ret;

}

KA_EXPORT_SYMBOL(dms_devnode_enable_device);



int dms_devnode_disable_device(u32 dev_id, int node_type, int node_id)

{

    struct dms_node *node = NULL;

    int ret;

    struct dms_dev_ctrl_block *dev_cb = NULL;

    dms_event("dms_devnode_disable_device, node_type=%d, node_id=%d\n", node_type, node_id);

    dev_cb = dms_get_dev_cb(dev_id);

    if (dev_cb == NULL) {

        return -EINVAL;

    }

    ka_task_mutex_lock(&dev_cb->node_lock);

    node = dms_get_devnode_cb_nolock(dev_id, node_type, node_id);

    if (node == NULL) {

        dms_err("node is unregister, node_type=%d, node_id=%d\n", node_type, node_id);

        ka_task_mutex_unlock(&dev_cb->node_lock);

        return -ENODEV;

    }



    if (node->ops == NULL) {

        dms_err("node->ops is NULL, node_type=%d, node_id=%d\n", node_type, node_id);

        ka_task_mutex_unlock(&dev_cb->node_lock);

        return -EINVAL;

    }



    if (node->ops->disable_device == NULL) {

        dms_err("disable_device is NULL, node_type=%d, node_id=%d\n", node_type, node_id);

        ka_task_mutex_unlock(&dev_cb->node_lock);

        return -EINVAL;

    }

    ret = node->ops->disable_device(node);

    if (ret != 0) {

        ka_task_mutex_unlock(&dev_cb->node_lock);

        dms_err("disable_device failed, node_type=%d, node_id=%d\n", node_type, node_id);

        return ret;

    }



    ka_task_mutex_unlock(&dev_cb->node_lock);

    return ret;

}

KA_EXPORT_SYMBOL(dms_devnode_disable_device);



int dms_devnode_get_power_info(u32 dev_id, int node_type, int node_id, void *buf, unsigned int size)

{

    struct dms_node *node = NULL;

    struct dms_dev_ctrl_block *dev_cb = NULL;

    int ret;



    if (buf == NULL) {

        dms_err("buf is NULL\n");

        return -EINVAL;

    }

    dev_cb = dms_get_dev_cb(dev_id);

    if (dev_cb == NULL) {

        return -EINVAL;

    }

    ka_task_mutex_lock(&dev_cb->node_lock);

    node = dms_get_devnode_cb_nolock(dev_id, node_type, node_id);

    if (node == NULL) {

        dms_err("node is unregister, node_type=%d, node_id=%d\n", node_type, node_id);

        ka_task_mutex_unlock(&dev_cb->node_lock);

        return -ENODEV;

    }



    if (node->ops == NULL) {

        dms_err("node->ops is NULL, node_type=%d, node_id=%d\n", node_type, node_id);

        ka_task_mutex_unlock(&dev_cb->node_lock);

        return -EINVAL;

    }



    if (node->ops->get_power_info == NULL) {

        dms_err("get_power_info is NULL, node_type=%d, node_id=%d\n", node_type, node_id);

        ka_task_mutex_unlock(&dev_cb->node_lock);

        return -EINVAL;

    }

    ret = node->ops->get_power_info(node, buf, size);

    if (ret != 0) {

        ka_task_mutex_unlock(&dev_cb->node_lock);

        dms_err("get_power_info failed, node_type=%d, node_id=%d\n", node_type, node_id);

        return ret;

    }



    ka_task_mutex_unlock(&dev_cb->node_lock);

    return ret;

}

KA_EXPORT_SYMBOL(dms_devnode_get_power_info);



int dms_devnode_set_power_info(u32 dev_id, int node_type, int node_id, void *buf, unsigned int size)

{

    struct dms_node *node = NULL;

    int ret;

    struct dms_dev_ctrl_block *dev_cb = NULL;

    dms_event("dms_devnode_set_power_info, node_type=%d, node_id=%d\n", node_type, node_id);

    if (buf == NULL) {

        dms_err("buf is NULL\n");

        return -EINVAL;

    }

    dev_cb = dms_get_dev_cb(dev_id);

    if (dev_cb == NULL) {

        return -EINVAL;

    }

    ka_task_mutex_lock(&dev_cb->node_lock);

    node = dms_get_devnode_cb_nolock(dev_id, node_type, node_id);

    if (node == NULL) {

        ka_task_mutex_unlock(&dev_cb->node_lock);

        dms_err("node is unregister, node_type=%d, node_id=%d\n", node_type, node_id);

        return -ENODEV;

    }



    if (node->ops == NULL) {

        ka_task_mutex_unlock(&dev_cb->node_lock);

        dms_err("node->ops is NULL, node_type=%d, node_id=%d\n", node_type, node_id);

        return -EINVAL;

    }



    if (node->ops->set_power_info == NULL) {

        ka_task_mutex_unlock(&dev_cb->node_lock);

        dms_err("set_power_info is NULL, node_type=%d, node_id=%d\n", node_type, node_id);

        return -EINVAL;

    }

    ret = node->ops->set_power_info(node, buf, size);

    if (ret != 0) {

        ka_task_mutex_unlock(&dev_cb->node_lock);

        dms_err("set_power_info failed, node_type=%d, node_id=%d\n", node_type, node_id);

        return ret;

    }



    ka_task_mutex_unlock(&dev_cb->node_lock);

    return ret;

}

KA_EXPORT_SYMBOL(dms_devnode_set_power_info);



int dms_devnode_set_power_state(u32 dev_id, int node_type, int node_id,

    DSMI_POWER_STATE state)

{

    struct dms_node *node = NULL;

    node = dms_get_devnode_cb(dev_id, node_type, node_id);

    if (node == NULL) {

        return -ENODEV;

    }

    if ((node->ops == NULL) || (node->ops->set_power_state == NULL)) {

        return -EOPNOTSUPP;

    }

    return node->ops->set_power_state(node, state);

}



int dms_devnode_fault_diag(u32 dev_id, int node_type, int node_id, int *state)

{

    struct dms_node *node = NULL;

    node = dms_get_devnode_cb(dev_id, node_type, node_id);

    if (node == NULL) {

        return -ENODEV;

    }

    if ((node->ops == NULL) || (node->ops->fault_diag == NULL)) {

        return -EOPNOTSUPP;

    }

    return node->ops->fault_diag(node, state);

}



ssize_t dms_devnode_print_node_list(char *buf)

{

    struct dms_dev_ctrl_block *dev_cb = NULL;

    struct dms_node *node = NULL;

    struct dms_node *next = NULL;

    int i;

    ssize_t buf_ret = 0;

    ssize_t offset = 0;

    offset = snprintf_s(buf, KA_MM_PAGE_SIZE, KA_MM_PAGE_SIZE - 1UL,

        "dev_id\ttype\tnode_id\tname\tcap\tperm\tstate\towner\n");

    print_sysfs("==================================\n");

    print_sysfs("dev_id\ttype\tnode_id\tname\tcap\tperm\tstate\towner\n");

    if (offset < 0) {

        return 0;

    }

    buf_ret += offset;

    for (i = 0; i < ASCEND_DEV_MAX_NUM; i++) {

        dev_cb = dms_get_dev_cb(i);

        if (dev_cb == NULL) {

            continue;

        }

        ka_task_mutex_lock(&dev_cb->node_lock);

        if (ka_list_empty_careful(&dev_cb->dev_node_list) != 0) {

            ka_task_mutex_unlock(&dev_cb->node_lock);

            continue;

        }

        ka_list_for_each_entry_safe(node, next, &dev_cb->dev_node_list, list)

        {

            print_sysfs("%d\t%d\t%d\t%s\t%llx\t%x\t%d\t%s\n", i, node->node_type, node->node_id, node->node_name,

                node->capacity, node->permission, node->state,

                (node->owner_device == NULL) ? "null" : node->owner_device->node_name);

            offset = snprintf_s(buf + buf_ret, KA_MM_PAGE_SIZE - buf_ret, KA_MM_PAGE_SIZE - 1 - buf_ret,

                "%d\t%d\t%d\t%s\t%llx\t%x\t%d\t%s\n", i, node->node_type, node->node_id, node->node_name,

                node->capacity, node->permission, node->state,

                (node->owner_device == NULL) ? "null" : node->owner_device->node_name);

            if (offset >= 0) {

                buf_ret += offset;

            }

        }

        ka_task_mutex_unlock(&dev_cb->node_lock);

    }



    return buf_ret;

}

KA_EXPORT_SYMBOL(dms_devnode_print_node_list);