/*
 * 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 "vfio_ops.h"
#include "dvt.h"
#include "dvt_sysfs.h"

STATIC struct hw_vdavinci_type *get_vdavinci_type(struct device *dev,
                                                  const char *name)
{
    struct hw_vdavinci_type *type = NULL;
    struct vdavinci_priv *priv = NULL;

    if (dev == NULL || name == NULL) {
        return NULL;
    }
    priv = kdev_to_davinci(dev);
    if (priv == NULL || priv->dvt == NULL) {
        return NULL;
    }
    type = g_hw_vdavinci_ops.dvt_find_vdavinci_type(priv->dvt, name);

    return type;
}

unsigned int available_instances_ops(struct device *dev, const char *name)
{
    struct hw_vdavinci_type *type = NULL;

    type = get_vdavinci_type(dev, name);
    if (type == NULL) {
        return 0;
    }

    return type->avail_instance;
}

ssize_t description_ops(struct device *dev, const char *name, char *buf)
{
    struct hw_vdavinci_type *type = NULL;

    if (buf == NULL) {
        return 0;
    }
    type = get_vdavinci_type(dev, name);
    if (type == NULL) {
        return 0;
    }

    return snprintf_s(buf, PAGE_SIZE, PAGE_SIZE - 1,
        "type name: %s\naicore num: %u\nmemory size: %luGB\naicpu num: %u\nvpc num: %u\n"\
        "jpegd num: %u\njpege num: %u\nvenc num: %u\nvdec num: %u\nbar2 size: %uKB\nbar4 size: %luKB\n",
        type->template_name, type->aicore_num, type->mem_size, type->aicpu_num, type->vpc_num,
        type->jpegd_num, type->jpege_num, type->venc_num, type->vdec_num,
        BYTES_TO_KB(type->bar2_size), BYTES_TO_KB(type->bar4_size));
}

ssize_t device_api_ops(char *buf)
{
    int ret;

    if (buf == NULL) {
        return 0;
    }
    ret = snprintf_s(buf, PAGE_SIZE, PAGE_SIZE - 1, "%s\n",
                     VFIO_DEVICE_API_PCI_STRING);
    return ret == -1 ? 0 : ret;
}

ssize_t vfg_id_store_ops(struct device *dev, const char *name,
                         const char *buf, size_t count)
{
    unsigned int vfg_id;
    struct hw_vdavinci_type *type = NULL;

    if (buf == NULL) {
        return 0;
    }
    type = get_vdavinci_type(dev, name);
    if (type == NULL) {
        return -EINVAL;
    }

    if (kstrtouint(buf, 0, &vfg_id) < 0 || vfg_id >= VDAVINCI_VFG_MAX) {
        vascend_err(dev, "Invalid vfg_id %u\n", vfg_id);
        return -EINVAL;
    }

    type->vfg_id = vfg_id;

    return count;
}

STATIC ssize_t
vdavinci_id_show(struct device *dev, struct device_attribute *attr,
                 char *buf)
{
    struct mdev_device *mdev = get_mdev_device(dev);
    int ret;

    if (mdev != NULL) {
        struct hw_vdavinci *vdavinci = (struct hw_vdavinci *)
            get_mdev_drvdata(dev);
        ret = snprintf_s(buf, PAGE_SIZE, PAGE_SIZE - 1, "%u\n", vdavinci->id);
        return ret == -1 ? 0 : ret;
    }

    ret = snprintf_s(buf, PAGE_SIZE, PAGE_SIZE - 1, "\n");
    return ret == -1 ? 0 : ret;
}

STATIC ssize_t
hw_id_show(struct device *dev, struct device_attribute *attr,
           char *buf)
{
    struct mdev_device *mdev = get_mdev_device(dev);
    int ret;

    if (mdev != NULL) {
        struct hw_vdavinci *vdavinci = (struct hw_vdavinci *)
            get_mdev_drvdata(dev);
        ret = snprintf_s(buf, PAGE_SIZE, PAGE_SIZE - 1,
            "Device [%x:%x]\n", vdavinci->dvt->vendor,
            vdavinci->dvt->device);
        return ret == -1 ? 0 : ret;
    }

    return 0;
}

STATIC ssize_t
vfg_id_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct mdev_device *mdev = get_mdev_device(dev);
    struct hw_vdavinci *vdavinci = NULL;
    int ret;
    if (mdev == NULL) {
        return -ENOENT;
    }
    vdavinci = (struct hw_vdavinci *)get_mdev_drvdata(dev);
    ret = snprintf_s(buf, PAGE_SIZE, PAGE_SIZE - 1, "%u\n", vdavinci->vfg_id);

    return ret == -1 ? 0 : ret;
}

STATIC ssize_t
vf_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct mdev_device *mdev = get_mdev_device(dev);
    struct hw_vdavinci *vdavinci = NULL;
    struct pci_dev *pdev = NULL;
    struct hw_dvt *dvt;
    int ret;
    if (mdev == NULL) {
        return -ENOENT;
    }

    vdavinci = (struct hw_vdavinci *)get_mdev_drvdata(dev);
    dvt = vdavinci->dvt;
    if (dvt->is_sriov_enabled && dvt->sriov.vf_num > 0) {
        pdev = vdavinci->vf.pdev;
        ret = snprintf_s(buf, PAGE_SIZE, PAGE_SIZE - 1,
            "%02x:%02x.%u\n", pdev->bus->number, PCI_SLOT(pdev->devfn),
            PCI_FUNC(pdev->devfn));
        return ret == -1 ? 0 : ret;
    }
    ret = snprintf_s(buf, PAGE_SIZE, PAGE_SIZE - 1, "\n");
    return ret == -1 ? 0 : ret;
}
static DEVICE_ATTR_RO(vfg_id);
static DEVICE_ATTR_RO(vdavinci_id);
static DEVICE_ATTR_RO(hw_id);
static DEVICE_ATTR_RO(vf);

static struct attribute *hw_vdavinci_attrs[] = {
    &dev_attr_vdavinci_id.attr,
    &dev_attr_hw_id.attr,
    &dev_attr_vfg_id.attr,
    &dev_attr_vf.attr,
    NULL
};

static struct attribute_group hw_vdavinci_group = {
    .name = "hw_vdavinci",
    .attrs = hw_vdavinci_attrs,
};

const struct attribute_group *hw_vdavinci_groups[] = {
    &hw_vdavinci_group,
    NULL,
};

const struct attribute_group **get_hw_vdavinci_groups(void)
{
    return hw_vdavinci_groups;
}