/*****************************************************************************
 *
 *   SPDX-License-Identifier: BSD-3-Clause
 *   Copyright(c) 2007-2026 Intel Corporation
 * 
 *   These contents may have been developed with support from one or more
 *   Intel-operated generative artificial intelligence solutions.
 *
 *****************************************************************************/
#include <string.h>
#include <stdint.h>
#include <dirent.h>
#include <linux/vfio.h>
#include <libgen.h>
#include <sys/stat.h>

#include "adf_kernel_types.h"

#include "cpa.h"
#include "icp_accel_devices.h"
#include "icp_platform.h"
#include "qat_mgr.h"
#include "qat_log.h"

#define DEVVFIO_DIR "/sys/bus/pci/drivers"
#define DEVICE_NAME_LENGTH 5

static int filter_pf_in_use(const struct dirent *entry)
{
    unsigned node, bus, dev, func;

    /* Deduce the entry is a PF if the name has 4 parts in format n:b:d.f */
    if (sscanf(entry->d_name, "%x:%x:%x.%x", &node, &bus, &dev, &func) != 4)
        return 0;

    return 1;
}

static int is_qat_dev_present(char *dev_name)
{
    int i = 0;
    int status = -1;
    struct dirent **namelist = NULL;
    int32_t number_of_dev = 0;

    number_of_dev = scandir(DEVVFIO_DIR, &namelist, NULL, alphasort);

    if (number_of_dev < 0)
    {
        return status;
    }

    for (i = 0; i < number_of_dev; i++)
    {
        if (strncmp(namelist[i]->d_name, dev_name, DEVICE_NAME_LENGTH) == 0)
        {
            status = 0;
            break;
        }
    }

    for (i = 0; i < number_of_dev; i++)
    {
        free(namelist[i]);
    }
    free(namelist);

    return status;
}

Cpa32S adf_vfio_init_pfs_info(icp_accel_pf_info_t *pf_info, size_t pf_info_len)
{
    unsigned domain, bus, dev, func;
    struct dirent **namelist = NULL;
    int32_t i, j, num_pfs_per_type = 0, total_num_pfs = 0;
    int status = CPA_STATUS_FAIL;
    char dev_path[QATMGR_MAX_STRLEN] = { '\0' };
    char *device_name = NULL;

    ICP_CHECK_FOR_NULL_PARAM(pf_info);
    for (j = 0; j < device_names_size; j++)
    {
        device_name = device_names[j];
        status = is_qat_dev_present(device_name);
        if (status == CPA_STATUS_SUCCESS)
        {
            snprintf(
                dev_path, sizeof(dev_path), "%s/%s", DEVVFIO_DIR, device_name);
            num_pfs_per_type =
                scandir(dev_path, &namelist, &filter_pf_in_use, alphasort);
            /* This shows scandir dir failed. */
            if (num_pfs_per_type < 0)
            {
                ADF_ERROR("Failed to scan directory %s\n", DEVVFIO_DIR);
                return CPA_STATUS_FAIL;
            }
            /* Check before adding to prevent overflow */
            if (num_pfs_per_type > (int32_t)pf_info_len - total_num_pfs)
            {
                ADF_ERROR("Remaining pf info array length %d is too small"
                          " for %d PFs\n",
                          (int32_t)pf_info_len - total_num_pfs,
                          num_pfs_per_type);
                for (i = 0; i < num_pfs_per_type; i++)
                {
                    free(namelist[i]);
                }
                free(namelist);
                return CPA_STATUS_INVALID_PARAM;
            }
            for (i = 0; i < num_pfs_per_type; i++)
            {
                int pf_info_idx = total_num_pfs + i;
                sscanf(namelist[i]->d_name,
                       "%x:%x:%x.%x",
                       &domain,
                       &bus,
                       &dev,
                       &func);
                pf_info[pf_info_idx].pkg_id = pf_info_idx;
                pf_info[pf_info_idx].domain = domain;
                pf_info[pf_info_idx].bdf =
                    ((0xFF & bus) << 8) + ((0x1F & dev) << 3) + (0x07 & func);
                strncpy(pf_info[pf_info_idx].device_gen,
                        device_name,
                        ADF_DEVICE_TYPE_LENGTH);
                free(namelist[i]);
            }
            total_num_pfs += num_pfs_per_type;
            free(namelist);
        }
    }
    return total_num_pfs;
}
