/*
 * Copyright (C) 2026 Intel Corporation
 *
 * SPDX-License-Identifier: MIT
 *
 */

#include "level_zero/sysman/source/shared/linux/nl_api/sysman_drm_nl_api.h"

#include "shared/source/helpers/debug_helpers.h"

#include "level_zero/sysman/source/shared/linux/zes_os_sysman_driver_imp.h"

#include <errno.h>
#include <linux/netlink.h>

namespace L0 {
namespace Sysman {

extern "C" {

static int globalDrmHandleMsg(struct nl_msg *msg, void *arg) {
    DrmNlApi *pDrmNlApi = reinterpret_cast<DrmNlApi *>(arg);
    return pDrmNlApi->handleMsg(msg);
}

static int globalDrmHandleAck(struct nl_msg *msg, void *arg) {
    DrmNlApi *pDrmNlApi = reinterpret_cast<DrmNlApi *>(arg);
    return pDrmNlApi->handleAck(msg);
}

static int globalDrmHandleEventMsg(struct nl_msg *msg, void *arg) {
    DrmNlApi *pDrmNlApi = reinterpret_cast<DrmNlApi *>(arg);
    return pDrmNlApi->handleEventMsg(msg);
}

static int drmNlOperationReadAll(struct nl_cache_ops *ops, struct genl_cmd *cmd, struct genl_info *info, void *arg) {
    DrmNlApi *drmNlApi = reinterpret_cast<DrmNlApi *>(arg);
    drmNlApi->getErrorListRsp(ops, cmd, info);
    return NL_OK;
}

static int drmNlOperationListNodes(struct nl_cache_ops *ops, struct genl_cmd *cmd, struct genl_info *info, void *arg) {
    DrmNlApi *drmNlApi = reinterpret_cast<DrmNlApi *>(arg);
    drmNlApi->listNodesRsp(ops, cmd, info);
    return NL_OK;
}

static int drmNlOperationGetThreshold(struct nl_cache_ops *ops, struct genl_cmd *cmd, struct genl_info *info, void *arg) {
    DrmNlApi *drmNlApi = reinterpret_cast<DrmNlApi *>(arg);
    drmNlApi->getErrorThresholdRsp(ops, cmd, info);
    return NL_OK;
}
}

DrmNlApi::DrmNlApi() {
    pNlApi.reset(new NlApi);
    UNRECOVERABLE_IF(pNlApi.get() == nullptr);
    setupNlOperations();
    currentOperation = std::make_unique<Operation>(0, nullptr);
}

DrmNlApi::~DrmNlApi() {
    if (eventSubscribed) {
        cleanupEventSocket();
    }
    if (nullptr != nlSock) {
        pNlApi->nlSocketFree(nlSock);
        nlSock = nullptr;
    }
}

ze_result_t DrmNlApi::getErrorCounter(const uint32_t &nodeId,
                                      const uint32_t &errorId,
                                      DrmErrorCounter &errorCounter) {
    return issueRequestReadOne(DRM_RAS_CMD_GET_ERROR_COUNTER, nodeId, errorId, reinterpret_cast<void *>(&errorCounter));
}

ze_result_t DrmNlApi::getErrorsList(const uint32_t &nodeId,
                                    std::vector<DrmErrorCounter> &errorList) {
    return issueRequestQueryErrors(DRM_RAS_CMD_GET_ERROR_COUNTER, nodeId, reinterpret_cast<void *>(&errorList));
}

ze_result_t DrmNlApi::setErrorThreshold(const uint32_t &nodeId,
                                        const uint32_t &errorId,
                                        const uint32_t &threshold) {
    return issueRequestSetThreshold(nodeId, errorId, threshold);
}

ze_result_t DrmNlApi::getErrorThreshold(const uint32_t &nodeId,
                                        const uint32_t &errorId,
                                        DrmErrorThreshold &threshold) {
    return issueRequestGetThreshold(nodeId, errorId, reinterpret_cast<void *>(&threshold));
}

ze_result_t DrmNlApi::clearErrorCounter(const uint32_t &nodeId, const uint32_t &errorId) {
    return issueRequestClearErrorCounter(nodeId, errorId);
}

int DrmNlApi::handleMsg(struct nl_msg *msg) {
    return pNlApi->genlHandleMsg(msg, reinterpret_cast<void *>(this));
}

int DrmNlApi::handleAck(struct nl_msg *msg) {
    auto isAckOnlyCmd = [](uint16_t cmdOp) {
        return cmdOp == DRM_RAS_CMD_CLEAR_ERROR_COUNTER || cmdOp == DRM_RAS_CMD_SET_ERROR_THRESHOLD;
    };

    struct nlmsghdr *nlh = pNlApi->nlmsgHdr(msg);
    if (!nlh) {
        if (currentOperation && isAckOnlyCmd(currentOperation->cmdOp)) {
            currentOperation->result = ZE_RESULT_ERROR_UNKNOWN;
            currentOperation->done = true;
        }
        return NL_STOP;
    }

    if (nlh->nlmsg_type == NLMSG_ERROR) {
        if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
            if (currentOperation && isAckOnlyCmd(currentOperation->cmdOp)) {
                currentOperation->result = ZE_RESULT_ERROR_UNKNOWN;
                currentOperation->done = true;
            }
            return NL_STOP;
        }
        auto *err = static_cast<struct nlmsgerr *>(NLMSG_DATA(nlh));
        if (currentOperation && isAckOnlyCmd(currentOperation->cmdOp)) {
            if (err->error == 0) {
                currentOperation->result = ZE_RESULT_SUCCESS;
            } else {
                switch (-err->error) {
                case EPERM:
                case EACCES:
                    currentOperation->result = ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS;
                    break;
                case ENOENT:
                    currentOperation->result = ZE_RESULT_ERROR_INVALID_ARGUMENT;
                    break;
                case ENODEV:
                    currentOperation->result = ZE_RESULT_ERROR_DEVICE_LOST;
                    break;
                case EINVAL:
                    currentOperation->result = ZE_RESULT_ERROR_INVALID_ARGUMENT;
                    break;
                case EOPNOTSUPP:
                case ENOSYS:
                    currentOperation->result = ZE_RESULT_ERROR_UNSUPPORTED_FEATURE;
                    break;
                case EBUSY:
                    currentOperation->result = ZE_RESULT_NOT_READY;
                    break;
                default:
                    currentOperation->result = ZE_RESULT_ERROR_UNKNOWN;
                    break;
                }
            }
            currentOperation->done = true;
        }
    }
    return NL_STOP;
}

ze_result_t DrmNlApi::listNodes(std::vector<DrmRasNode> &nodeList) {
    return issueRequestListNodes(reinterpret_cast<void *>(&nodeList));
}

ze_result_t DrmNlApi::issueRequestListNodes(void *pOutput) {
    ze_result_t result = initConnection();
    if (ZE_RESULT_SUCCESS != result) {
        return result;
    }

    struct nl_msg *msg;
    result = allocMsg(DRM_RAS_CMD_LIST_NODES, true, msg);
    if (ZE_RESULT_SUCCESS == result) {
        result = performTransaction(DRM_RAS_CMD_LIST_NODES, msg, pOutput, false);
    }

    cleanupConnection();
    return result;
}

ze_result_t DrmNlApi::listNodesRsp(struct nl_cache_ops *ops,
                                   struct genl_cmd *cmd,
                                   struct genl_info *info) {
    std::vector<DrmRasNode> *nodeList = reinterpret_cast<std::vector<DrmRasNode> *>(currentOperation->pOutput);
    UNRECOVERABLE_IF(nodeList == nullptr);

    const struct nlmsghdr *nlh = info->nlh;
    auto nla = pNlApi->nlmsgAttrdata(nlh, GENL_HDRLEN);
    auto rem = pNlApi->nlmsgAttrlen(nlh, GENL_HDRLEN);

    DrmRasNode currentNode = {};
    while (pNlApi->nlaOk(nla, rem)) {
        switch (pNlApi->nlaType(nla)) {
        case DRM_RAS_A_NODE_ATTRS_NODE_ID:
            currentNode.nodeId = pNlApi->nlaGetU32(nla);
            break;
        case DRM_RAS_A_NODE_ATTRS_DEVICE_NAME:
            currentNode.deviceName = pNlApi->nlaGetString(nla);
            break;
        case DRM_RAS_A_NODE_ATTRS_NODE_NAME:
            currentNode.nodeName = pNlApi->nlaGetString(nla);
            break;
        case DRM_RAS_A_NODE_ATTRS_NODE_TYPE:
            currentNode.nodeType = pNlApi->nlaGetU32(nla);
            nodeList->push_back(currentNode);
            currentNode = {};
            break;
        }
        nla = pNlApi->nlaNext(nla, &rem);
    }

    currentOperation->done = true;
    return ZE_RESULT_SUCCESS;
}

ze_result_t DrmNlApi::getErrorListRsp(struct nl_cache_ops *ops, struct genl_cmd *cmd, struct genl_info *info) {
    if (currentOperation->parseSingleErrorCounter) {
        return getSingleErrorCounterRsp(ops, cmd, info);
    }

    std::vector<DrmErrorCounter> *errorList =
        reinterpret_cast<std::vector<DrmErrorCounter> *>(currentOperation->pOutput);
    UNRECOVERABLE_IF(errorList == nullptr);

    const struct nlmsghdr *nlh = info->nlh;
    auto nla = pNlApi->nlmsgAttrdata(nlh, GENL_HDRLEN);
    auto rem = pNlApi->nlmsgAttrlen(nlh, GENL_HDRLEN);

    // FLAT structure - one message = one error counter
    DrmErrorCounter currentError = {};
    while (pNlApi->nlaOk(nla, rem)) {
        switch (pNlApi->nlaType(nla)) {
        case DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID:
            currentError.nodeId = pNlApi->nlaGetU32(nla);
            break;
        case DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID:
            currentError.errorId = pNlApi->nlaGetU32(nla);
            break;
        case DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_NAME:
            currentError.errorName = pNlApi->nlaGetString(nla);
            break;
        case DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_VALUE:
            currentError.errorValue = pNlApi->nlaGetU32(nla);
            // When we get VALUE, error counter is complete
            errorList->push_back(currentError);
            currentError = {};
            break;
        }
        nla = pNlApi->nlaNext(nla, &rem);
    }

    currentOperation->done = true;
    return ZE_RESULT_SUCCESS;
}

ze_result_t DrmNlApi::getSingleErrorCounterRsp(struct nl_cache_ops *ops, struct genl_cmd *cmd, struct genl_info *info) {
    DrmErrorCounter *errorCounter = reinterpret_cast<DrmErrorCounter *>(currentOperation->pOutput);
    UNRECOVERABLE_IF(errorCounter == nullptr);

    if (info->attrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID]) {
        errorCounter->nodeId = pNlApi->nlaGetU32(
            info->attrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID]);
    }
    if (info->attrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID]) {
        errorCounter->errorId = pNlApi->nlaGetU32(
            info->attrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID]);
    }
    if (info->attrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_NAME]) {
        errorCounter->errorName = pNlApi->nlaGetString(
            info->attrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_NAME]);
    }
    if (info->attrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_VALUE]) {
        errorCounter->errorValue = pNlApi->nlaGetU32(
            info->attrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_VALUE]);
    }

    currentOperation->done = true;
    return ZE_RESULT_SUCCESS;
}

ze_result_t DrmNlApi::getErrorThresholdRsp(struct nl_cache_ops *ops, struct genl_cmd *cmd, struct genl_info *info) {
    DrmErrorThreshold *threshold = reinterpret_cast<DrmErrorThreshold *>(currentOperation->pOutput);
    UNRECOVERABLE_IF(threshold == nullptr);

    if (info->attrs[DRM_RAS_A_ERROR_THRESHOLD_ATTRS_ERROR_ID]) {
        threshold->errorId = pNlApi->nlaGetU32(
            info->attrs[DRM_RAS_A_ERROR_THRESHOLD_ATTRS_ERROR_ID]);
    }
    if (info->attrs[DRM_RAS_A_ERROR_THRESHOLD_ATTRS_ERROR_NAME]) {
        threshold->errorName = pNlApi->nlaGetString(
            info->attrs[DRM_RAS_A_ERROR_THRESHOLD_ATTRS_ERROR_NAME]);
    }
    if (info->attrs[DRM_RAS_A_ERROR_THRESHOLD_ATTRS_ERROR_THRESHOLD]) {
        threshold->threshold = pNlApi->nlaGetU32(
            info->attrs[DRM_RAS_A_ERROR_THRESHOLD_ATTRS_ERROR_THRESHOLD]);
    }

    currentOperation->done = true;
    return ZE_RESULT_SUCCESS;
}

ze_result_t DrmNlApi::initSocketBase(struct nl_sock *&sock, int &resolvedFamilyId) {
    sock = pNlApi->nlSocketAlloc();
    if (nullptr == sock) {
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    int retval = pNlApi->genlConnect(sock);
    if (retval < 0) {
        pNlApi->nlSocketFree(sock);
        sock = nullptr;
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    resolvedFamilyId = pNlApi->genlCtrlResolve(sock, DRM_RAS_FAMILY_NAME);
    if (resolvedFamilyId < 0) {
        pNlApi->nlSocketFree(sock);
        sock = nullptr;
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    return ZE_RESULT_SUCCESS;
}

ze_result_t DrmNlApi::initConnection() {

    if (!isInitDone) {
        if (!pNlApi->loadEntryPoints()) {
            return ZE_RESULT_ERROR_DEPENDENCY_UNAVAILABLE;
        }
        isInitDone = true;
    }

    int resolvedFamilyId = 0;
    ze_result_t result = initSocketBase(nlSock, resolvedFamilyId);
    if (ZE_RESULT_SUCCESS != result) {
        return result;
    }

    int retval = pNlApi->genlRegisterFamily(&ops);
    if (retval != 0) {
        freeNlSocket(nlSock);
        if (-NLE_EXIST == retval) {
            return ZE_RESULT_NOT_READY;
        }
        return ZE_RESULT_ERROR_UNKNOWN;
    }
    isDrmRasFamilyRegistered = true;

    retval = pNlApi->genlOpsResolve(nlSock, &ops);
    if (retval < 0) {
        cleanupConnection();
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    familyId = resolvedFamilyId;

    retval = pNlApi->nlSocketModifyCb(nlSock, NL_CB_VALID, NL_CB_CUSTOM,
                                      globalDrmHandleMsg, reinterpret_cast<void *>(this));
    if (retval < 0) {
        cleanupConnection();
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    retval = pNlApi->nlSocketModifyCb(nlSock, NL_CB_ACK, NL_CB_CUSTOM,
                                      globalDrmHandleAck, reinterpret_cast<void *>(this));
    if (retval < 0) {
        cleanupConnection();
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    return ZE_RESULT_SUCCESS;
}

void DrmNlApi::freeNlSocket(struct nl_sock *&sock) {
    pNlApi->nlSocketFree(sock);
    sock = nullptr;
}

void DrmNlApi::cleanupConnection() {
    freeNlSocket(nlSock);

    pNlApi->genlUnregisterFamily(&ops);
    isDrmRasFamilyRegistered = false;

    familyId = 0;
    eventGroupId = -1;
    eventSubscribed = false;
}

ze_result_t DrmNlApi::allocMsg(const uint16_t &cmdOp, const bool useDumpFlags, struct nl_msg *&msg) {
    msg = pNlApi->nlmsgAlloc();
    if (nullptr == msg) {
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    int flags = NLM_F_REQUEST | NLM_F_ACK;
    if (useDumpFlags) {
        flags |= NLM_F_ROOT | NLM_F_MATCH;
    }

    if (nullptr == pNlApi->genlmsgPut(msg, NL_AUTO_PORT, NL_AUTO_SEQ, familyId, 0, flags, cmdOp, 1)) {
        pNlApi->nlmsgFree(msg);
        msg = nullptr;
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    return ZE_RESULT_SUCCESS;
}

ze_result_t DrmNlApi::performTransaction(const uint16_t &cmdOp, struct nl_msg *msg, void *pOutput, const bool parseSingleErrorCounter) {
    UNRECOVERABLE_IF(currentOperation == nullptr);
    currentOperation->cmdOp = cmdOp;
    currentOperation->pOutput = pOutput;
    currentOperation->parseSingleErrorCounter = parseSingleErrorCounter;
    currentOperation->done = false;
    currentOperation->result = ZE_RESULT_SUCCESS;

    if (pNlApi->nlSendAuto(nlSock, msg) < 0) {
        pNlApi->nlmsgFree(msg);
        return ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS;
    }
    pNlApi->nlmsgFree(msg);

    while (!currentOperation->done) {
        int res = pNlApi->nlRecvmsgsDefault(nlSock);
        if (res < 0) {
            currentOperation->result = (res == -NLE_PERM) ? ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS : ZE_RESULT_ERROR_UNKNOWN;
            currentOperation->done = true;
        }
    }

    return currentOperation->result;
}

ze_result_t DrmNlApi::issueRequestReadOne(const uint16_t cmdOp, const uint32_t &nodeId, const uint32_t &errorId, void *pOutput) {
    ze_result_t result = initConnection();
    if (ZE_RESULT_SUCCESS != result) {
        return result;
    }

    struct nl_msg *msg;
    result = allocMsg(cmdOp, false, msg);
    if (ZE_RESULT_SUCCESS == result) {
        pNlApi->nlaPutU32(msg, DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID, nodeId);
        pNlApi->nlaPutU32(msg, DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID, errorId);
        result = performTransaction(cmdOp, msg, pOutput, true);
    }

    cleanupConnection();
    return result;
}

ze_result_t DrmNlApi::issueRequestQueryErrors(const uint16_t &cmdOp, const uint32_t &nodeId, void *pOutput) {
    ze_result_t result = initConnection();
    if (ZE_RESULT_SUCCESS != result) {
        return result;
    }

    struct nl_msg *msg;
    result = allocMsg(cmdOp, true, msg);
    if (ZE_RESULT_SUCCESS == result) {
        pNlApi->nlaPutU32(msg, DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID, nodeId);
        result = performTransaction(cmdOp, msg, pOutput, false);
    }

    cleanupConnection();
    return result;
}

ze_result_t DrmNlApi::issueRequestSetThreshold(const uint32_t &nodeId, const uint32_t &errorId, const uint32_t &threshold) {
    ze_result_t result = initConnection();
    if (ZE_RESULT_SUCCESS != result) {
        return result;
    }

    struct nl_msg *msg;
    result = allocMsg(DRM_RAS_CMD_SET_ERROR_THRESHOLD, false, msg);
    if (ZE_RESULT_SUCCESS == result) {
        pNlApi->nlaPutU32(msg, DRM_RAS_A_ERROR_THRESHOLD_ATTRS_NODE_ID, nodeId);
        pNlApi->nlaPutU32(msg, DRM_RAS_A_ERROR_THRESHOLD_ATTRS_ERROR_ID, errorId);
        pNlApi->nlaPutU32(msg, DRM_RAS_A_ERROR_THRESHOLD_ATTRS_ERROR_THRESHOLD, threshold);
        result = performTransaction(DRM_RAS_CMD_SET_ERROR_THRESHOLD, msg, nullptr, false);
    }

    cleanupConnection();
    return result;
}

ze_result_t DrmNlApi::issueRequestGetThreshold(const uint32_t &nodeId, const uint32_t &errorId, void *pOutput) {
    ze_result_t result = initConnection();
    if (ZE_RESULT_SUCCESS != result) {
        return result;
    }

    struct nl_msg *msg;
    result = allocMsg(DRM_RAS_CMD_GET_ERROR_THRESHOLD, false, msg);
    if (ZE_RESULT_SUCCESS == result) {
        pNlApi->nlaPutU32(msg, DRM_RAS_A_ERROR_THRESHOLD_ATTRS_NODE_ID, nodeId);
        pNlApi->nlaPutU32(msg, DRM_RAS_A_ERROR_THRESHOLD_ATTRS_ERROR_ID, errorId);
        result = performTransaction(DRM_RAS_CMD_GET_ERROR_THRESHOLD, msg, pOutput, false);
    }

    cleanupConnection();
    return result;
}

ze_result_t DrmNlApi::issueRequestClearErrorCounter(const uint32_t &nodeId, const uint32_t &errorId) {
    ze_result_t result = initConnection();
    if (ZE_RESULT_SUCCESS != result) {
        return result;
    }

    struct nl_msg *msg;
    result = allocMsg(DRM_RAS_CMD_CLEAR_ERROR_COUNTER, false, msg);
    if (ZE_RESULT_SUCCESS == result) {
        pNlApi->nlaPutU32(msg, DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID, nodeId);
        pNlApi->nlaPutU32(msg, DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID, errorId);
        result = performTransaction(DRM_RAS_CMD_CLEAR_ERROR_COUNTER, msg, nullptr, false);
    }

    cleanupConnection();
    return result;
}

void DrmNlApi::setupNlOperations() {
    // Setup node attribute policy
    memset(nodePolicy, 0, sizeof(nla_policy) * (DRM_RAS_A_NODE_ATTRS_MAX + 1));
    nodePolicy[DRM_RAS_A_NODE_ATTRS_NODE_ID].type = NLA_U32;
    nodePolicy[DRM_RAS_A_NODE_ATTRS_DEVICE_NAME].type = NLA_NUL_STRING;
    nodePolicy[DRM_RAS_A_NODE_ATTRS_NODE_NAME].type = NLA_NUL_STRING;
    nodePolicy[DRM_RAS_A_NODE_ATTRS_NODE_TYPE].type = NLA_U32;

    // Setup error counter attribute policy
    memset(errorPolicy, 0, sizeof(nla_policy) * (DRM_RAS_A_ERROR_COUNTER_ATTRS_MAX + 1));
    errorPolicy[DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID].type = NLA_U32;
    errorPolicy[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID].type = NLA_U32;
    errorPolicy[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_NAME].type = NLA_NUL_STRING;
    errorPolicy[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_VALUE].type = NLA_U32;

    // Setup threshold attribute policy
    memset(thresholdPolicy, 0, sizeof(nla_policy) * (DRM_RAS_A_ERROR_THRESHOLD_ATTRS_MAX + 1));
    thresholdPolicy[DRM_RAS_A_ERROR_THRESHOLD_ATTRS_NODE_ID].type = NLA_U32;
    thresholdPolicy[DRM_RAS_A_ERROR_THRESHOLD_ATTRS_ERROR_ID].type = NLA_U32;
    thresholdPolicy[DRM_RAS_A_ERROR_THRESHOLD_ATTRS_ERROR_NAME].type = NLA_NUL_STRING;
    thresholdPolicy[DRM_RAS_A_ERROR_THRESHOLD_ATTRS_ERROR_THRESHOLD].type = NLA_U32;

    // Setup commands
    memset(newCmds, 0, sizeof(genl_cmd) * NEO_DRM_RAS_CMD_MAX);

    newCmds[0].c_id = DRM_RAS_CMD_LIST_NODES;
    newCmds[0].c_name = const_cast<char *>("LIST_NODES");
    newCmds[0].c_maxattr = DRM_RAS_A_NODE_ATTRS_MAX;
    newCmds[0].c_attr_policy = nodePolicy;
    newCmds[0].c_msg_parser = &drmNlOperationListNodes;

    newCmds[1].c_id = DRM_RAS_CMD_GET_ERROR_COUNTER;
    newCmds[1].c_name = const_cast<char *>("GET_ERROR_COUNTER");
    newCmds[1].c_maxattr = DRM_RAS_A_ERROR_COUNTER_ATTRS_MAX;
    newCmds[1].c_attr_policy = errorPolicy;
    newCmds[1].c_msg_parser = &drmNlOperationReadAll;

    newCmds[2].c_id = DRM_RAS_CMD_CLEAR_ERROR_COUNTER;
    newCmds[2].c_name = const_cast<char *>("CLEAR_ERROR_COUNTER");
    newCmds[2].c_maxattr = DRM_RAS_A_ERROR_COUNTER_ATTRS_MAX;
    newCmds[2].c_attr_policy = errorPolicy;
    newCmds[2].c_msg_parser = nullptr; // ACK-only command, no data parser needed

    newCmds[3].c_id = DRM_RAS_CMD_SET_ERROR_THRESHOLD;
    newCmds[3].c_name = const_cast<char *>("SET_ERROR_THRESHOLD");
    newCmds[3].c_maxattr = DRM_RAS_A_ERROR_THRESHOLD_ATTRS_MAX;
    newCmds[3].c_attr_policy = thresholdPolicy;
    newCmds[3].c_msg_parser = nullptr; // SET has no reply payload; ACK is handled by NL_CB_ACK

    newCmds[4].c_id = DRM_RAS_CMD_GET_ERROR_THRESHOLD;
    newCmds[4].c_name = const_cast<char *>("GET_ERROR_THRESHOLD");
    newCmds[4].c_maxattr = DRM_RAS_A_ERROR_THRESHOLD_ATTRS_MAX;
    newCmds[4].c_attr_policy = thresholdPolicy;
    newCmds[4].c_msg_parser = &drmNlOperationGetThreshold;

    newCmds[5].c_id = DRM_RAS_CMD_ERROR_EVENT;
    newCmds[5].c_name = const_cast<char *>("ERROR_EVENT");
    newCmds[5].c_maxattr = DRM_RAS_A_ERROR_COUNTER_ATTRS_MAX;
    newCmds[5].c_attr_policy = errorPolicy;
    newCmds[5].c_msg_parser = nullptr; // Events parsed in handleEventMsg

    // Use fixed family name
    ops.o_name = const_cast<char *>(DRM_RAS_FAMILY_NAME); // "drm-ras"
    ops.o_hdrsize = 0U;
    ops.o_cmds = newCmds;
    ops.o_ncmds = NEO_DRM_RAS_CMD_MAX;
}

ze_result_t DrmNlApi::subscribeToEvents() {
    if (!isInitDone) {
        if (!pNlApi->loadEntryPoints()) {
            return ZE_RESULT_ERROR_DEPENDENCY_UNAVAILABLE;
        }
        isInitDone = true;
    }

    return initEventSocket();
}

ze_result_t DrmNlApi::unsubscribeFromEvents() {
    if (!eventSubscribed) {
        return ZE_RESULT_SUCCESS;
    }

    cleanupEventSocket();
    return ZE_RESULT_SUCCESS;
}

ze_result_t DrmNlApi::pollEvent(DrmRasEvent &event) {
    if (!eventSubscribed) {
        return ZE_RESULT_ERROR_UNINITIALIZED;
    }

    eventReady = false;

    int res = pNlApi->nlRecvmsgsDefault(nlEventSock);
    if (res < 0) {
        return (res == -NLE_PERM) ? ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS : ZE_RESULT_ERROR_UNKNOWN;
    }

    // eventReady is set to true by handleEventMsg when a RAS multicast event
    // is received and parsed successfully during nlRecvmsgsDefault above.
    if (eventReady) {
        event = pendingEvent;
        return ZE_RESULT_SUCCESS;
    }

    // This is returned when nlRecvmsgsDefault succeeds but no RAS event was parsed
    // e.g. a non-event netlink message (ACK, NOOP) was received on the event socket.
    return ZE_RESULT_NOT_READY;
}

int DrmNlApi::getEventSocketFd() const {
    if (!eventSubscribed || nlEventSock == nullptr) {
        return -1;
    }
    return pNlApi->nlSocketGetFd(nlEventSock);
}

int DrmNlApi::handleEventMsg(struct nl_msg *msg) {
    struct nlmsghdr *nlh = pNlApi->nlmsgHdr(msg);
    if (!nlh) {
        return NL_STOP;
    }

    parseEventMessage(msg, pendingEvent);
    eventReady = true;

    return NL_OK;
}

ze_result_t DrmNlApi::initEventSocket() {
    int famId = 0;
    ze_result_t result = initSocketBase(nlEventSock, famId);
    if (ZE_RESULT_SUCCESS != result) {
        return result;
    }

    int retval = pNlApi->nlSocketSetNonblocking(nlEventSock);
    if (retval < 0) {
        cleanupEventSocket();
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    eventGroupId = pNlApi->genlCtrlResolveGrp(nlEventSock, DRM_RAS_FAMILY_NAME, "error-notify");
    if (eventGroupId < 0) {
        cleanupEventSocket();
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    retval = pNlApi->nlSocketAddMembership(nlEventSock, eventGroupId);
    if (retval < 0) {
        cleanupEventSocket();
        if (retval == -NLE_PERM) {
            return ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS;
        }
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    retval = pNlApi->nlSocketModifyCb(nlEventSock, NL_CB_VALID, NL_CB_CUSTOM,
                                      globalDrmHandleEventMsg, reinterpret_cast<void *>(this));
    if (retval < 0) {
        cleanupEventSocket();
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    // Disable sequence number checking for multicast events
    // Multicast messages don't have matching sequence numbers with requests
    pNlApi->nlSocketDisableSeqCheck(nlEventSock);

    eventSubscribed = true;
    return ZE_RESULT_SUCCESS;
}

void DrmNlApi::cleanupEventSocket() {
    if (nlEventSock != nullptr) {
        if (eventGroupId >= 0) {
            pNlApi->nlSocketDropMembership(nlEventSock, eventGroupId);
        }
        freeNlSocket(nlEventSock);
    }
    eventGroupId = -1;
    eventSubscribed = false;
    eventReady = false;
}

ze_result_t DrmNlApi::parseEventMessage(struct nl_msg *msg, DrmRasEvent &event) {
    struct nlmsghdr *nlh = pNlApi->nlmsgHdr(msg);
    if (!nlh) {
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    auto nla = pNlApi->nlmsgAttrdata(nlh, GENL_HDRLEN);
    auto rem = pNlApi->nlmsgAttrlen(nlh, GENL_HDRLEN);

    event = {};
    while (pNlApi->nlaOk(nla, rem)) {
        switch (pNlApi->nlaType(nla)) {
        case DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID:
            event.nodeId = pNlApi->nlaGetU32(nla);
            break;
        case DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID:
            event.errorId = pNlApi->nlaGetU32(nla);
            break;
        }
        nla = pNlApi->nlaNext(nla, &rem);
    }

    return ZE_RESULT_SUCCESS;
}

DrmNlApi *LinuxSysmanDriverImp::createDrmNlApi() {
    return new DrmNlApi();
}

void LinuxSysmanDriverImp::destroyDrmNlApi(DrmNlApi *pDrmNl) {
    if (pDrmNl != nullptr && pDrmNl->isSubscribedToEvents()) {
        pDrmNl->unsubscribeFromEvents();
    }
    delete pDrmNl;
}

} // namespace Sysman
} // namespace L0
