-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local NVME_MI_OPTIONS = require 'nvme.nvme_mi_protocol.nvme_mi_def'
local nvme_admin_command = require 'nvme.nvme_mi_protocol.nvme_mi_admin_command'
local log = require 'mc.logging'

local nvme_admin_command_process = {}

-- 判断该NVMe盘是否支持uuidlist
function nvme_admin_command_process.check_support_uuid_by_identify_ctrl(obj)
    local ret = nvme_admin_command.identify_ctrl(obj)
    if not ret then
        return false
    end
    local parse_ret = NVME_MI_OPTIONS.controller_data_structure_response:unpack(ret, true)
    if not parse_ret or not parse_ret.ctratt then
        return false
    end
    return (parse_ret.ctratt & NVME_MI_OPTIONS.UUID_LIST_SUPPORT) ~= 0
end

-- 判断指定opcode是否支持uuid
function nvme_admin_command_process.check_specified_opcode_support_uuid(obj, opcode)
    local ret = nvme_admin_command.get_support_effects_log(obj)
    if not ret then
        return false
    end
    local parse_ret = NVME_MI_OPTIONS.commands_supported_effects_log_response:unpack(ret, true)
    if not parse_ret then
        return false
    end
    local admin_command_support_bin = string.sub(parse_ret.acs_list, opcode * NVME_MI_OPTIONS.ACS_DATA_LENGTH + 1,
        opcode * NVME_MI_OPTIONS.ACS_DATA_LENGTH + 1 + NVME_MI_OPTIONS.ACS_DATA_LENGTH)
    local admin_command_support =
        NVME_MI_OPTIONS.commands_supported_effects_data_structure:unpack(admin_command_support_bin, true)
    if not admin_command_support or not admin_command_support.uuid_selection_supported then
        return false
    end
    return admin_command_support.uuid_selection_supported == 1
end

-- 获取指定uuid的uuid_index
function nvme_admin_command_process.get_specified_uuid_index(obj, uuid_ll, uuid_lh, uuid_hl, uuid_hh)
    local ret = nvme_admin_command.identify_uuid_list(obj)
    if not ret then
        return false
    end
    local parse_ret = NVME_MI_OPTIONS.uuid_list_response:unpack(ret, true)
    if not parse_ret or not parse_ret.uuid_list then
        return false
    end
    local uuid_entry = NVME_MI_OPTIONS.uuid_list_entry:unpack(parse_ret.uuid_list, true)
    if not uuid_entry then
        return false
    end
    local uuid_index = 1
    while uuid_index < NVME_MI_OPTIONS.UUID_ENTRY_NUM do
        if uuid_entry.uuid_ll == uuid_ll and uuid_entry.uuid_lh == uuid_lh and
            uuid_entry.uuid_hl == uuid_hl and uuid_entry.uuid_hh == uuid_hh then
            break;
        end
        uuid_entry = NVME_MI_OPTIONS.uuid_list_entry:unpack(uuid_entry.next_uuid_entry, true)
        if not uuid_entry then
            return false
        end
        uuid_index = uuid_index + 1
    end
    return uuid_index < NVME_MI_OPTIONS.UUID_ENTRY_NUM and uuid_index or false
end

-- 判断是否可以不填充uuid_index获取hw_defined smart log
function nvme_admin_command_process.check_support_hw_defined_without_uuid(obj)
    local ret = nvme_admin_command.get_hw_defined_smart_log(obj)
    return ret and true or false
end

-- 通过ADMIN COMMAND GET LOG PAGE(opcode:0x02)获取SMART/health raw data
function nvme_admin_command_process.get_smart_log_raw(obj)
    local smart_log = nvme_admin_command.get_log_page(obj, {nsid = NVME_MI_OPTIONS.NVME_NSID_ALL,
        log_id = NVME_MI_OPTIONS.LOG_PAGE_IDENTIFIERS.SMART_INFORMATION,
        data_len = NVME_MI_OPTIONS.DATA_LENGTH.SMART_LOG})
    if not smart_log then
        log:info('get Disk%s smart log failed', obj.nvme.Slot)
        obj.smart_log = nil
        return
    end

    local rsp = NVME_MI_OPTIONS.log_response:unpack(smart_log, true)
    if not rsp then
        log:info('get Disk%s smart log identifiers failed', obj.nvme.Slot)
        return
    end
    log:info('get Disk%s smart log identifiers successfully', obj.nvme.Slot)
    return rsp.data
end

-- 通过ADMIN COMMAND GET LOG PAGE(opcode:0xef)获取hw defiend smart log raw data
function nvme_admin_command_process.get_hw_defined_smart_log_raw(obj, uuid_index)
    local log_page = nvme_admin_command.get_log_page(obj, {nsid = NVME_MI_OPTIONS.NVME_NSID_ALL,
        log_id = NVME_MI_OPTIONS.LOG_PAGE_IDENTIFIERS.HW_DEFINED_SMART_LOG,
        data_len = NVME_MI_OPTIONS.DATA_LENGTH.HW_DEFINED_SMART_LOG, uuid_idx = uuid_index and uuid_index or 0})
    if not log_page then
        log:error('get Disk%s hw_defined smart log failed', obj.nvme.Slot)
        return
    end
    local parse_ret = NVME_MI_OPTIONS.hw_defined_smart_log_raw_response:unpack(log_page, true)
    if not parse_ret then
        log:error('get Disk%s hw_defined smart log failed', obj.nvme.Slot)
        return
    end
    return parse_ret.attr_list
end

return nvme_admin_command_process