/*
 * 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 "comm_kernel_interface.h"
#include "virtmnghost_unit.h"
#include "virtmng_public_def.h"
#include "virtmnghost_sysfs.h"

STATIC struct vmngh_pci_dev *vmngh_sysfs_get_valid_pdev(ka_device_t *dev)
{
    struct vmngh_pci_dev *vmngh_pdev = NULL;
    u32 dev_id;

    dev_id = (u32)devdrv_get_dev_id_by_pdev(ka_pci_to_pci_dev(dev));
    if (dev_id >= ASCEND_PDEV_MAX_NUM) {
        vmng_err("Device id is invalid. (dev_id=%u)\n", dev_id);
        return NULL;
    }
    vmngh_pdev = vmngh_get_pdev_from_unit(dev_id);
    if (vmngh_pdev == NULL) {
        vmng_err("vmngh_pdev is invalid. (dev_id=%u)\n", dev_id);
        return NULL;
    }
    if (vmngh_pdev->status != VMNG_STARTUP_PROBED) {
        vmng_err("pdev_status is invalid. (dev_id=%u; status=%u)\n", dev_id, vmngh_pdev->status);
        return NULL;
    }
    return vmngh_pdev;
}

STATIC ssize_t vmngh_sysfs_mdev_info(ka_device_t *dev, ka_device_attribute_t *attr, char *buf)
{
    struct vmngh_pci_dev *vmngh_pdev = NULL;
    struct vmngh_vd_dev *vd_dev = NULL;
    ssize_t offset = 0;
    ka_uuid_le_t uuid;
    u32 dev_id;
    u32 fid;
    int ret;

    (void)attr;
    vmngh_pdev = vmngh_sysfs_get_valid_pdev(dev);
    if (vmngh_pdev == NULL) {
        vmng_err("Get sysfs pdev error.\n");
        return offset;
    }
    dev_id = vmngh_pdev->dev_id;
    ret = snprintf_s(buf, KA_MM_PAGE_SIZE, KA_MM_PAGE_SIZE - 1, "dev %u all mdev info.\n", dev_id);
    if (ret >= 0) {
        offset += ret;
    }

    for (fid = 0; fid < VMNG_VDEV_MAX_PER_PDEV; fid++) {
        vd_dev = vmngh_pdev->vd_dev[fid];
        if ((vd_dev != NULL) && (vd_dev->init_status != VMNG_STARTUP_UNPROBED)) {
            uuid = vd_dev->vascend_uuid;

            ret = snprintf_s(buf + offset, KA_MM_PAGE_SIZE - offset, KA_MM_PAGE_SIZE - offset - 1,
                "[chip] 19e5:0x%04x %02x:%02x.%u id %2u: "
                "[vd] uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x fid %2u dtype "
                "d%u: ",
                vmngh_pdev->ep_devic_id, ka_pci_get_bus_number(vmngh_pdev->pdev),
                KA_PCI_SLOT(ka_pci_get_devfn(vmngh_pdev->pdev)), KA_PCI_FUNC(ka_pci_get_devfn(vmngh_pdev->pdev)),
                dev_id, uuid.b[3], uuid.b[2], uuid.b[1], uuid.b[0], uuid.b[5], uuid.b[4], uuid.b[7], uuid.b[6],
                uuid.b[8], uuid.b[9], uuid.b[10], uuid.b[11], uuid.b[12], uuid.b[13], uuid.b[14], uuid.b[15],
                vd_dev->fid, vd_dev->dtype);
            if (ret != -1) {
                offset += ret;
            }

            if (vd_dev->init_status == VMNG_STARTUP_BOTTOM_HALF_OK) {
                ret = snprintf_s(buf + offset, KA_MM_PAGE_SIZE - offset, KA_MM_PAGE_SIZE - offset - 1,
                    "[vm] pid %6u vmid %2u [agent]: %04x %02x:%02x.%u id %2u.\n", vd_dev->vm_pid, vd_dev->vm_id,
                    vd_dev->agent_device, (vd_dev->agent_bdf >> VMNG_VDEV_AGENT_BDF_SHIFT_8),
                    KA_PCI_SLOT(vd_dev->agent_bdf & 0xff), KA_PCI_FUNC(vd_dev->agent_bdf & 0xff), vd_dev->agent_dev_id);
            } else {
                ret = snprintf_s(buf + offset, KA_MM_PAGE_SIZE - offset, KA_MM_PAGE_SIZE - offset - 1, "[agent] not build.\n");
            }
            if (ret != -1) {
                offset += ret;
            }
        }
    }
    return offset;
}

static KA_DRIVER_DEVICE_ATTR(mdev_info, KA_S_IRUSR | KA_S_IRGRP, vmngh_sysfs_mdev_info, NULL);

#ifdef CFG_SOC_PLATFORM_CLOUD_V2
STATIC ssize_t vmngh_sysfs_vm_full_vf_enable_show(ka_device_t *dev, ka_device_attribute_t *attr, char *buf)
{
    struct vmngh_pci_dev *vmngh_pdev = NULL;
    ssize_t offset = 0;
    u32 dev_id;
    int ret;

    vmngh_pdev = vmngh_sysfs_get_valid_pdev(dev);
    if (vmngh_pdev == NULL) {
        vmng_err("Get sysfs pdev error.\n");
        return offset;
    }
    dev_id = vmngh_pdev->dev_id;

    ret = snprintf_s(buf, KA_MM_PAGE_SIZE, KA_MM_PAGE_SIZE - 1, "dev %u vm_full_spec_enable is %u.\n",
        dev_id, vmngh_pdev->vm_full_spec_enable);
    if (ret >= 0) {
        offset += ret;
    }

    return offset;
}

STATIC ssize_t vmngh_sysfs_vm_full_vf_enable_store(ka_device_t *dev, ka_device_attribute_t *attr,
    const char *buf, size_t count)
{
    struct vmngh_pci_dev *vmngh_pdev = NULL;
    int input;
    int ret;
#ifndef DRV_UT
    vmngh_pdev = vmngh_sysfs_get_valid_pdev(dev);
    if (vmngh_pdev == NULL) {
        vmng_err("Get sysfs pdev error. (uid=%u)\n", ka_task_get_current_cred_uid());
        return -1;
    }
    ret = ka_base_kstrtoint(buf, 0, &input);
    if (ret != 0) {
        vmng_err("ka_base_kstrtoint failed. (devid=%u; ret=%d; uid=%u)\n",
            vmngh_pdev->dev_id, ret, ka_task_get_current_cred_uid());
        return -1;
    }

    ka_task_mutex_lock(&vmngh_pdev->vpdev_mutex);
    if (input == 0) {
        vmngh_pdev->vm_full_spec_enable = 0;
        vmng_event("Disable full spec support, all the vf template can select. (devid=%u; uid=%u)\n",
            vmngh_pdev->dev_id, ka_task_get_current_cred_uid());
    } else if (input == 1) {
        if (vmngh_pdev->vdev_ref == 0) {
            vmngh_pdev->vm_full_spec_enable = 1;
            vmng_event("Enable full spec support, only full spec vf can select. (devid=%u; uid=%u)\n",
                vmngh_pdev->dev_id, ka_task_get_current_cred_uid());
        } else {
            vmng_err("Mdevs are may be alive, please remove all mdevs.(devid=%u; uid=%u)\n",
                vmngh_pdev->dev_id, ka_task_get_current_cred_uid());
            ka_task_mutex_unlock(&vmngh_pdev->vpdev_mutex);
            return -1;
        }
    } else {
        vmng_err("Invalid input vf_vnic enable. (devid=%u; input=%d; uid=%u)\n",
            vmngh_pdev->dev_id, input, ka_task_get_current_cred_uid());
    }
    ka_task_mutex_unlock(&vmngh_pdev->vpdev_mutex);
#endif

    return count;
}
static KA_DRIVER_DEVICE_ATTR(vm_full_vf_enable, (KA_S_IWUSR | KA_S_IRUSR | KA_S_IWGRP | KA_S_IRGRP),
    vmngh_sysfs_vm_full_vf_enable_show, vmngh_sysfs_vm_full_vf_enable_store);
#endif

static ka_attribute_t *g_vmngh_sysfs_attrs[] = {
    ka_fs_get_dev_attr(dev_attr_mdev_info)
#ifdef CFG_SOC_PLATFORM_CLOUD_V2
    ka_fs_get_dev_attr(dev_attr_vm_full_vf_enable)
#endif
    NULL,
};

static const ka_attribute_group_t g_vmngh_sysfs_group = {
    ka_fs_init_ag_attrs(g_vmngh_sysfs_attrs)
};

STATIC bool vmng_get_sysfs_creat_group_capbility(ka_pci_dev_t *pcidev, int dev_id)
{
    int devnum_by_pdev;

    devnum_by_pdev = devdrv_get_davinci_dev_num_by_pdev(pcidev);
    if (devnum_by_pdev == 0) {
        vmng_err("Davinci device num is zero. (dev_id=%u)\n", dev_id);
        return false;
    }

    if (dev_id % devnum_by_pdev == 0) {
        return true;
    }

    return false;
}

int vmngh_sysfs_init(u32 dev_id, ka_pci_dev_t *pcidev)
{
    int ret;

    if (devdrv_get_connect_protocol(dev_id) != CONNECT_PROTOCOL_PCIE) {
        return 0;
    }

    if (vmng_get_sysfs_creat_group_capbility(pcidev, (int)dev_id)) {
        ret = ka_sysfs_create_group(&pcidev->dev.kobj, &g_vmngh_sysfs_group);
        if (ret != 0) {
            vmng_err("Create group failed. (dev_id=%u; ret=%d)\n", dev_id, ret);
            return ret;
        }
    }
    return 0;
}

void vmngh_sysfs_exit(u32 dev_id, ka_pci_dev_t *pcidev)
{
    if (devdrv_get_connect_protocol(dev_id) != CONNECT_PROTOCOL_PCIE) {
        return;
    }

    if (vmng_get_sysfs_creat_group_capbility(pcidev, (int)dev_id)) {
        ka_sysfs_remove_group(&pcidev->dev.kobj, &g_vmngh_sysfs_group);
    }
}