-- 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 sml_core = require 'sml.core'

-- 这个文件会被安装到 hwproxy 的插件目录，最好不要再依赖 stroge 的其他库，
-- 所以这些宏都拷贝了一份
local INVALID_U8<const> = 0xff
local INVALID_NUM2STR<const> = '255.0'

local VENDER_LSI<const> = 0
local VENDER_PMC<const> = 1
local VENDER_HUAWEI<const> = 2

local PMC_3152_8I_SMART_RAID<const> = 64
local PMC_2100_8I_SMART_HBA<const> = 65
local HI1880_SP186_M_16i<const> = 96
local HI1880_SP186_M_8i<const> = 113

local OOB_TYPE_OVER_I2C<const> = 0
local OOB_TYPE_OVER_PCIE<const> = 1

local function BIT(x)
    return 1 << x
end

local LD_PROP_SETTING_NAME<const> = BIT(0)
local LD_PROP_SETTING_READ_POLICY<const> = BIT(1)
local LD_PROP_SETTING_WRITE_POLICY<const> = BIT(2)
local LD_PROP_SETTING_IO_POLICY<const> = BIT(3)
local LD_PROP_SETTING_ACCESS_POLICY<const> = BIT(4)
local LD_PROP_SETTING_DISK_CACHE_POLICY<const> = BIT(5)
local LD_PROP_SETTING_BGI_ENABLE<const> = BIT(6)
local LD_PROP_SETTING_ACCELERATOR<const> = BIT(7)
local LD_PROP_SETTING_CAPACITY_SIZE<const> = BIT(8)
local LD_PROP_SETTING_STRIP_SIZE<const> = BIT(9)
local LD_OPERATION_SET_PROPERTIES<const> = 2
local LD_OPERATION_START_FGI<const> = 4
local LD_OPERATION_CANCEL_FGI<const> = 5
local MAX_PERCENT_NUM<const> = 10000

-- 检查RAID卡的厂商类型
local function test_controller_vendor(type_id, vendor_id)
    if vendor_id == VENDER_LSI then
        if type_id < PMC_3152_8I_SMART_RAID then
            return true
        end
    elseif vendor_id == VENDER_PMC then
        if type_id == PMC_3152_8I_SMART_RAID or type_id == PMC_2100_8I_SMART_HBA then
            return true
        end
    elseif vendor_id == VENDER_HUAWEI then
        if type_id >= HI1880_SP186_M_16i and type_id <= HI1880_SP186_M_8i then
            return true
        end
    end
    return false
end

local function get_oob_interface_type(type_id)
    if test_controller_vendor(type_id, VENDER_HUAWEI) or test_controller_vendor(type_id, VENDER_PMC) then
        return OOB_TYPE_OVER_PCIE
    elseif test_controller_vendor(type_id, VENDER_LSI) then
        return OOB_TYPE_OVER_I2C
    else
        return INVALID_U8
    end
end

local ctrl_props = {
    'last_update_timestamp', 'properties', 'operations', 'ctrl_name', 'ctrl_sn', 'fw_version',
    'nvdata_version', 'hardware_revision', 'ld_present_conut', 'ld_degraded_count',
    'ld_offline_count', 'pd_present_count', 'pddisk_present_count', 'pddisk_prefail_count',
    'pddisk_fail_count', 'memory_size', 'memory_ecc_count', 'memory_ecc_bucket_size',
    'memory_ce_count', 'memory_uce_count', 'max_pd_raid0', 'max_pd_raid1', 'max_pd_raid5',
    'max_pd_raid6', 'max_pd_raid10', 'max_pd_raid50', 'max_pd_raid60', 'max_lds_per_array',
    'max_lds', 'allow_mix_ssd_hdd', 'allow_ssd_mix', 'raid0_supported', 'raid1_supported',
    'raid5_supported', 'raid6_supported', 'raid10_supported', 'raid50_supported',
    'raid60_supported', 'raid1adm_supported', 'raid10adm_supported', 'raid1triple_supported',
    'raid10triple_supported', 'ssc_raid0_unsupported', 'ssc_raid1_supported', 'ssc_raid5_supported',
    'min_pd_raid0', 'min_pd_raid1', 'min_pd_raid5', 'min_pd_raid6', 'min_pd_raid10',
    'min_pd_raid50', 'min_pd_raid60', 'ctrl_temp', 'mode', 'spare_activation_mode',
    'pcie_link_width', 'nobattery_write_cache', 'read_cache_percent', 'write_cache_policy',
    'ctrl_warnig_info_reported', 'cache_pinned', 'maint_pd_fail_history', 'device_interface',
    'min_strip', 'max_strip', 'consis_check_enabled', 'consis_check_period', 'consis_check_rate',
    'consis_check_repair', 'consis_check_status', 'consis_check_totalvd',
    'consis_check_completedvd', 'consis_check_delay', 'work_mode', 'device_interface'
}

local pd_props = {
    'identify_status', 'device_id', 'sequence_num', 'health', 'fde_capable', 'fw_state',
    'power_state', 'scsi_dev_type', 'device_speed', 'link_speed', 'encl_device_id', 'slot_num',
    'media_type', 'interface_type', 'temperature', 'boot_priority', 'sas_addr1', 'sas_addr2',
    'serial_num', 'model', 'firmware_version', 'manufacturer', 'vendor_id', 'coerced_size',
    'media_err_count', 'other_err_count', 'prefail_count', 'hot_spare', 'remnant_media_wearout',
    'spareblock', 'rotation_speed', 'proginfo', 'form_factor', 'dev_not_supported', 'fw_state_raw',
    'is_foreign', 'coerced_size_blk', 'block_size', 'power_on_hours', 'halflife', 'last_update_timestamp',
    'bootable', 'last_prefail_event_seq_num', 'hw_defined_estimated_lifespan', 'vendor_estimated_lifespan',
    'writeamp', 'hw_defined_write_amp', 'vendor_write_amp'
}

local ld_props = {
    'last_update_timestamp', 'ld_warnig_info_reported', 'target_id', 'name', 'drive_state',
    'raid_level', 'default_read_policy', 'default_write_policy', 'default_cache_policy',
    'current_read_policy', 'current_write_policy', 'current_cache_policy', 'access_policy',
    'disk_cache_policy', 'init_state', 'consistent_check', 'span_depth', 'num_drive_per_span',
    'bgi_enabled', 'bootable', 'is_sscd', 'is_epd', 'strip_size', 'accelerator', 'size',
    'max_resizeable_size', 'boot_priority', 'cache_line_size', 'ref_array', 'progress_info',
    'sscd_ld_list', 'spare_pd_ids', 'spare_pd_slots', 'spare_pd_enclosure_ids', 'ld_ref_array',
    'current_fgi_state', 'fgi', 'spare_pd_slots', 'spare_pd_enclosure_ids'
}

local array_props = {
    'last_update_timestamp', 'used_space', 'total_free_space', 'free_blocks_count',
    'free_blocks_space', 'ld_count', 'ld_ids', 'pd_count', 'pd_ids', 'pd_slots', 'pd_enclosures'
}

local battery_props = {
    'last_update_timestamp', 'bbu_warnig_info_reported', 'present', 'temperature', 'type',
    'pack_missing', 'voltage_low', 'temperature_high', 'replacepack', 'learn_cycle_failed',
    'learn_cycle_timeout', 'predictive_failure', 'remaining_capacity_low', 'no_space', 'failed'
}

local pd_sas_smart_props = {
    'strip_temperature', 'glist_len', 'plist_len', 'manufacture_data', 'blocks_sent',
    'blocks_received', 'minutes_left'
}

local pd_slow_disk_props = {
    'cmd_timeout_times', 'unexpected_sense_times', 'reset_times', 'power_on_times'
}

local ctrl_exp_sas_phy_err_props = {
    'i_controller_index', 'o_expander_count', 'phy_count', 'phy_id', 'invalid_dword_count',
    'loss_dword_sync_count', 'phy_reset_problem_count', 'running_disparity_error_count'
}

local diagnose_link_phy_err_props = {
    'i_controller_index', 'i_pd_device_id', 'i_encl_device_id', 'i_pd_error', 'pd_short_dst_error',
    'pd_passthru_cmd_fail', 'pd_crc_error', 'pd_smart_log_error', 'encl_encl_link_error',
    'ctrl_pd_link_error', 'encl_pd_link_error', 'ctrl_encl_link_error', 'encl_comm_error',
    'sense_error', 'o_fault_dword', 'io_buf'
}

local ctrl_sas_phy_err_count_props = {
    'phy_count', 'invalid_dword_count', 'loss_dword_sync_count', 'phy_reset_problem_count',
    'running_disparity_error_count'
}

local function update_info(class_name, ...)
    local info = sml_core[class_name].new()
    info:reset_zero()
    info:update(...)
    return info
end

local function update_info_spec(class_name, ...)
    local info = sml_core[class_name].new()
    info:reset_zero()
    info:update_spec(...)
    return info
end

local function update_pd_info(...)
    return update_info('SML_PD_BASIC_INFO_S', ...)
end

local function update_pd_sas_smart_info(...)
    return update_info('SML_PD_SAS_SMART_INFO', ...)
end

local function update_pd_slow_disk_info(...)
    return update_info('SML_PD_SLOW_DATA_S', ...)
end

local function update_pd_sas_smart_info_spec(...)
    return update_info_spec('SML_PD_SAS_SMART_INFO', ...)
end

local function update_ld_info(...)
    return update_info('SML_LD_BASIC_INFO_S', ...)
end

local function update_array_info(...)
    return update_info('SML_ARRAY_INFO_S', ...)
end

local function update_battery_info(...)
    return update_info('SML_BBU_STATUS_S', ...)
end

local function diagnose_link_phy_error_info(...)
    return update_info('SML_PD_FAULT_ANALYSIS', ...)
end

local function update_ctrl_exp_sas_phy_err_info(...)
    return update_info('SML_CTRL_EXP_SAS_PHY_INFO_S', ...)
end

local function update_ctrl_sas_phy_err_count_info(...)
    return update_info('SML_SASPHY_INFO_S', ...)
end

local function update_ctrl_info(...)
    return update_info('SML_CTRL_BASIC_INFO_S', ...)
end

local function update_ctrl_sas_phy_err_info(...)
    return update_info('SML_CTRL_SAS_PHY_ERR_S', ...)
end

local function get_result(props, cb)
    local result = {}
    for _, prop in ipairs(props) do
        result[prop] = cb(prop)
    end
    return result
end

local CMD = {}

function CMD.register_controller(ctrl_index, type_id, operation, data)
    local ctrl = sml_core.register_ctrl_oob.new()
    ctrl.i_controller_index = ctrl_index
    ctrl.i_oob_operate = operation
    if operation == sml_core.SML_ADD_CTRL then
        ctrl.i_controller_typeid = type_id
        -- 动态加载sml库
        ctrl:register_sml_adapter_function()
        if get_oob_interface_type(type_id) == sml_core.OOB_TYPE_OVER_PCIE then
            ctrl.mctp_eid = data.Eid
            ctrl.mctp_phy_addr = data.Phyaddr
        end
    elseif operation == sml_core.SML_REGISTER_CTRL then
        if get_oob_interface_type(type_id) ~= INVALID_U8 then
            ctrl.i_oob_interface_type = get_oob_interface_type(type_id)
        else
            error('Invalid TypeId')
        end
    end
    return ctrl:register_controller()
end

function CMD.unregister_controller(ctrl_index, type_id, operation, data)
    local ctrl = sml_core.register_ctrl_oob.new()
    ctrl.i_controller_index = ctrl_index
    ctrl.i_oob_operate = operation
    if operation == sml_core.SML_DEL_CTRL then
        ctrl.i_controller_typeid = type_id
    elseif operation == sml_core.SML_UNREGISTER_CTRL then
        if get_oob_interface_type(type_id) ~= INVALID_U8 then
            ctrl.i_oob_interface_type = get_oob_interface_type(type_id)
        else
            error('Invalid TypeId')
        end
    end
    return ctrl:unregister_controller()
end

function CMD.pd_operation(ctrl_index, device_id, operation, data)
    return sml_core.pd_operation(ctrl_index, device_id, operation, data)
end

function CMD.pd_log_get_drive_log(ctrl_index, protocol, device_id, u8, u16, string)
    return sml_core.pd_log_get_drive_log(ctrl_index, protocol, device_id, u8, u16, string)
end

function CMD.pd_get_io_diagnose_info(ctrl_index, protocol, device_id, u8, u32, string)
    return sml_core.pd_get_io_diagnose_info(ctrl_index, protocol, device_id, u8, u32, string)
end

function CMD.pd_get_read_io_latency_info(ctrl_index, device_id, media_type, manufacture)
    return sml_core.pd_get_read_io_latency_info(ctrl_index, device_id, media_type, manufacture)
end

function CMD.pd_get_write_io_latency_info(ctrl_index, device_id, media_type, manufacture)
    return sml_core.pd_get_write_io_latency_info(ctrl_index, device_id, media_type, manufacture)
end

function CMD.pd_log_write_subhealthy_info(ctrl_index, estimated_remaining_lifespan, u8, u64, string)
    return sml_core.pd_log_write_subhealthy_info(estimated_remaining_lifespan, u8, u64, string)
end

function CMD.pd_get_smart_info_str(ctrl_index, device_id, hdd_intf_type, string)
    return sml_core.pd_get_smart_info_str(ctrl_index, device_id, hdd_intf_type, string)
end

function CMD.pd_diag_by_smart_log(ctrl_index, device_id)
    return sml_core.pd_diag_by_smart_log(ctrl_index, device_id)
end

function CMD.pd_diag_by_ext_error_log(ctrl_index, device_id)
    return sml_core.pd_diag_by_ext_error_log(ctrl_index, device_id)
end

function CMD.pd_diag_by_ext_self_test_log(ctrl_index, device_id, in_progress)
    return sml_core.pd_diag_by_ext_self_test_log(ctrl_index, device_id, in_progress)
end

function CMD.pd_diag_get_sata_self_test_status(ctrl_index, device_id)
    return sml_core.pd_diag_get_sata_self_test_status(ctrl_index, device_id)
end

function CMD.pd_diag_encl_comm_error(ctrl_index, pd_device_id, encl_device_id)
    return sml_core.pd_diag_encl_comm_error(ctrl_index, pd_device_id, encl_device_id)
end

function CMD.pd_diag_sense_error(ctrl_index, pd_device_id)
    return sml_core.pd_diag_sense_error(ctrl_index, pd_device_id)
end

function CMD.dump_ctrl_single_log(ctrl_index, src_dir, log_type, log_name)
    return sml_core.dump_ctrl_single_log(ctrl_index, src_dir, log_type, log_name)
end

function CMD.parse_controller_bin_log(ctrl_index, src_dir, dest_dir)
    return sml_core.parse_controller_bin_log(ctrl_index, src_dir, dest_dir)
end

function CMD.trans_drive_data(ctrl_index, json_raw_data)
    return sml_core.trans_drive_data(ctrl_index, json_raw_data)
end

function CMD.ctrl_operation(ctrl_index, operation, data)
    return sml_core.ctrl_operation(ctrl_index, operation, data)
end

function CMD.get_ctrl_pd_list(ctrl_index, force_update)
    return sml_core.get_ctrl_pd_list(ctrl_index, force_update)
end

function CMD.get_ctrl_sas_phy_err(ctrl_index, force_update)
    return sml_core.get_ctrl_sas_phy_err(ctrl_index, force_update)
end

function CMD.get_ctrl_ld_list(ctrl_index)
    return sml_core.get_ctrl_ld_list(ctrl_index)
end

function CMD.get_ctrl_array_list(ctrl_index)
    return sml_core.get_ctrl_array_list(ctrl_index)
end

function CMD.get_ctrl_init_state(ctrl_index)
    return sml_core.get_ctrl_init_state(ctrl_index)
end

function CMD.clear_all_controller_info(ctrl_index)
    return sml_core.clear_all_controller_info(ctrl_index)
end

function CMD.get_ctrl_sas_addr(raid_index)
    return sml_core.get_ctrl_sas_addr(raid_index)
end

local function update_write_amp_info(prop, info)
    if prop == 'writeamp' then
        local writeamp = info.writeamp
        return {
            update_support_flag = writeamp.update_support_flag
        }
    end
    if prop == 'hw_defined_write_amp' then
        local hw_defined_write_amp = info.hw_defined_write_amp
        return {
            valid_flag = hw_defined_write_amp.valid_flag,
            nand_write_l = hw_defined_write_amp.nand_write_l,
            nand_write_h = hw_defined_write_amp.nand_write_h,
            host_write_l = hw_defined_write_amp.host_write_l,
            host_write_h = hw_defined_write_amp.host_write_h,
        }
    end
    if prop == 'vendor_write_amp' then
        local vendor_write_amp = info.vendor_write_amp
        return {
            valid_flag = vendor_write_amp.valid_flag,
            nand_write = vendor_write_amp.nand_write,
            host_write = vendor_write_amp.host_write
        }
    end
end

local function to_percent_string(number)
    if number > MAX_PERCENT_NUM then
        return INVALID_NUM2STR
    end
    return string.format('%.2f', number / 100)
end

function CMD.get_pd_info(ctrl_index, pd_device_id)
    local info = update_pd_info(ctrl_index, pd_device_id)
    return get_result(pd_props, function(prop)
        if prop == 'proginfo' then
            local proginfo = info.proginfo
            return {
                rebuild_state = proginfo.rebuild_state,
                rebuild_progress = proginfo.rebuild_progress,
                patrol_state = proginfo.patrol_state,
                patrol_progress = proginfo.patrol_progress
            }
        end
        if prop == 'spareblock' then
            local spareblock = info.spareblock
            return {
                slc_value = spareblock.slc_value,
                tlc_value = spareblock.tlc_value
            }
        end

        if prop == 'hw_defined_estimated_lifespan' then
            local hw_defined_estimated_lifespan = info.hw_defined_estimated_lifespan
            return {
                slc_pe_cycle = hw_defined_estimated_lifespan.slc_pe_cycle,
                tlc_pe_cycle = hw_defined_estimated_lifespan.tlc_pe_cycle,
                slc_avg_ec = hw_defined_estimated_lifespan.slc_avg_ec,
                tlc_avg_ec = hw_defined_estimated_lifespan.tlc_avg_ec,
                slc_poh = hw_defined_estimated_lifespan.slc_poh,
                tlc_poh = hw_defined_estimated_lifespan.tlc_poh,
                hw_valid_flag = hw_defined_estimated_lifespan.hw_valid_flag,
                slc_used_lifespan = to_percent_string(hw_defined_estimated_lifespan.slc_used_lifespan),
                tlc_used_lifespan = to_percent_string(hw_defined_estimated_lifespan.tlc_used_lifespan),
            }
        end

        if prop == 'vendor_estimated_lifespan' then
            local vendor_estimated_lifespan = info.vendor_estimated_lifespan
            return {
                remn_wearout = vendor_estimated_lifespan.remn_wearout,
                power_on_hours = vendor_estimated_lifespan.power_on_hours,
                vendor_valid_flag = vendor_estimated_lifespan.vendor_valid_flag,
            }
        end
        local write_amp_info = update_write_amp_info(prop, info)
        if write_amp_info then
            return write_amp_info
        end
        return info[prop]
    end)
end

function CMD.get_pd_sas_smart_info(ctrl_index, pd_device_id)
    local info = update_pd_sas_smart_info(ctrl_index, pd_device_id)
    return get_result(pd_sas_smart_props, function(prop)
        return info[prop]
    end)
end

function CMD.get_pd_slow_disk_info(ctrl_index, pd_device_id)
    local info = update_pd_slow_disk_info(ctrl_index, pd_device_id)
    return get_result(pd_slow_disk_props, function(prop)
        return info[prop]
    end)
end

function CMD.get_pd_sas_smart_info_spec(ctrl_index, pd_device_id, filter)
    local info = update_pd_sas_smart_info_spec(ctrl_index, pd_device_id, filter)
    return get_result(pd_sas_smart_props, function(prop)
        return info[prop]
    end)
end

function CMD.get_ld_info(ctrl_index, ld_target_id)
    local info = update_ld_info(ctrl_index, ld_target_id)
    return get_result(ld_props, function(prop)
        if prop == 'progress_info' then
            local progress_info = info.progress_info
            return {
                rebuild_state = progress_info.rebuild_state,
                rebuild_progress = progress_info.rebuild_progress
            }
        end
        return info[prop]
    end)
end

function CMD.get_array_info(ctrl_index, array_target_id)
    local info = update_array_info(ctrl_index, array_target_id)
    return get_result(array_props, function(prop)
        return info[prop]
    end)
end

function CMD.get_battery_info(ctrl_index)
    local info = update_battery_info(ctrl_index)
    return get_result(battery_props, function(prop)
        return info[prop]
    end)
end

function CMD.get_ctrl_sas_phy_err_count(ctrl_index, force_update)
    local info = update_ctrl_sas_phy_err_count_info(ctrl_index, force_update)
    return get_result(ctrl_sas_phy_err_count_props, function(prop)
        return info[prop]
    end)
end

function CMD.pd_diag_link_phy_error(ctrl_index, pd_device_id, encl_device_id, pd_error,
    serial_threshold, recent_threshold)
    local info = diagnose_link_phy_error_info(ctrl_index, pd_device_id, encl_device_id, pd_error,
        serial_threshold, recent_threshold)
    return get_result(diagnose_link_phy_err_props, function(prop)
        return info[prop]
    end)
end

function CMD.get_ctrl_exp_sas_phy_err_info(ctrl_index)
    local info = update_ctrl_exp_sas_phy_err_info(ctrl_index)
    return get_result(ctrl_exp_sas_phy_err_props, function(prop)
        return info[prop]
    end)
end

function CMD.get_ctrl_info(ctrl_index)
    local info = update_ctrl_info(ctrl_index)
    return get_result(ctrl_props, function(prop)
        return info[prop]
    end)
end

function CMD.get_ctrl_sas_phy_err_info(ctrl_index, force_update)
    local info = update_ctrl_sas_phy_err_info(ctrl_index, force_update)
    return get_result(ctrl_props, function(prop)
        return info[prop]
    end)
end

function CMD.get_ctrl_boot_devices(...)
    return sml_core.get_ctrl_boot_devices(...)
end

function CMD.get_ctrl_faultcode(...)
    return sml_core.get_ctrl_faultcode(...)
end

function CMD.set_ld_boot_priority(ctrl_index, volume_id, bootpriority)
    local target = sml_core['SML_LD_TARGET_S'].new()
    target:reset_zero()
    target.i_controller_index = ctrl_index
    target.i_target_id = volume_id
    return target:set_bootable(bootpriority)
end

function CMD.set_ld_readpolicy(ctrl_index, volume_id, readpolicy)
    local set_prop = sml_core['SML_LD_SET_PROPERTIES_S'].new()
    set_prop:reset_zero()
    set_prop.read_policy = readpolicy
    set_prop.setting_options = set_prop.setting_options | LD_PROP_SETTING_READ_POLICY
    return set_prop:set(ctrl_index, volume_id, LD_OPERATION_SET_PROPERTIES)
end

function CMD.set_ld_writepolicy(ctrl_index, volume_id, writepolicy)
    local set_prop = sml_core['SML_LD_SET_PROPERTIES_S'].new()
    set_prop:reset_zero()
    set_prop.write_policy = writepolicy
    set_prop.setting_options = set_prop.setting_options | LD_PROP_SETTING_WRITE_POLICY
    return set_prop:set(ctrl_index, volume_id, LD_OPERATION_SET_PROPERTIES)
end

function CMD.set_ld_name(ctrl_index, volume_id, name)
    local set_prop = sml_core['SML_LD_SET_PROPERTIES_S'].new()
    set_prop:reset_zero()
    set_prop.ld_name = name
    set_prop.setting_options = set_prop.setting_options | LD_PROP_SETTING_NAME
    return set_prop:set(ctrl_index, volume_id, LD_OPERATION_SET_PROPERTIES)
end

function CMD.set_ld_io_policy(ctrl_index, volume_id, io_policy)
    local set_prop = sml_core['SML_LD_SET_PROPERTIES_S'].new()
    set_prop:reset_zero()
    set_prop.io_policy = io_policy
    set_prop.setting_options = set_prop.setting_options | LD_PROP_SETTING_IO_POLICY
    return set_prop:set(ctrl_index, volume_id, LD_OPERATION_SET_PROPERTIES)
end

function CMD.set_ld_bgi_enable(ctrl_index, volume_id, bgi_enable)
    local set_prop = sml_core['SML_LD_SET_PROPERTIES_S'].new()
    set_prop:reset_zero()
    set_prop.bgi_enable = bgi_enable
    set_prop.setting_options = set_prop.setting_options | LD_PROP_SETTING_BGI_ENABLE
    return set_prop:set(ctrl_index, volume_id, LD_OPERATION_SET_PROPERTIES)
end

function CMD.set_ld_access_policy(ctrl_index, volume_id, access_policy)
    local set_prop = sml_core['SML_LD_SET_PROPERTIES_S'].new()
    set_prop:reset_zero()
    set_prop.access_policy = access_policy
    set_prop.setting_options = set_prop.setting_options | LD_PROP_SETTING_ACCESS_POLICY
    return set_prop:set(ctrl_index, volume_id, LD_OPERATION_SET_PROPERTIES)
end

function CMD.set_ld_disk_cache_policy(ctrl_index, volume_id, disk_cache_policy)
    local set_prop = sml_core['SML_LD_SET_PROPERTIES_S'].new()
    set_prop:reset_zero()
    set_prop.disk_cache_policy = disk_cache_policy
    set_prop.setting_options = set_prop.setting_options | LD_PROP_SETTING_DISK_CACHE_POLICY
    return set_prop:set(ctrl_index, volume_id, LD_OPERATION_SET_PROPERTIES)
end

function CMD.set_ld_accelerator(ctrl_index, volume_id, accelerator)
    local set_prop = sml_core['SML_LD_SET_PROPERTIES_S'].new()
    set_prop:reset_zero()
    set_prop.accelerator = accelerator
    set_prop.setting_options = set_prop.setting_options | LD_PROP_SETTING_ACCELERATOR
    return set_prop:set(ctrl_index, volume_id, LD_OPERATION_SET_PROPERTIES)
end

function CMD.set_ld_capacity_size(ctrl_index, volume_id, capacity_size)
    local set_prop = sml_core['SML_LD_SET_PROPERTIES_S'].new()
    set_prop:reset_zero()
    set_prop.capacity = capacity_size
    set_prop.setting_options = set_prop.setting_options | LD_PROP_SETTING_CAPACITY_SIZE
    return set_prop:set(ctrl_index, volume_id, LD_OPERATION_SET_PROPERTIES)
end

function CMD.set_ld_strip_size(ctrl_index, volume_id, strip_size)
    local set_prop = sml_core['SML_LD_SET_PROPERTIES_S'].new()
    set_prop:reset_zero()
    set_prop.strip_size = strip_size
    set_prop.setting_options = set_prop.setting_options | LD_PROP_SETTING_STRIP_SIZE
    return set_prop:set(ctrl_index, volume_id, LD_OPERATION_SET_PROPERTIES)
end

function CMD.set_ld_cachecade(ctrl_index, volume_id, associate)
    local set_prop = sml_core['SML_LD_SET_CACHECADE_ASSOCIATION_S'].new()
    set_prop:reset_zero()
    return set_prop:set_ld_cachecade(ctrl_index, volume_id, associate)
end

function CMD.start_ld_fgi(ctrl_index, volume_id, init_type)
    local set_prop = sml_core['SML_LD_SET_PROPERTIES_S'].new()
    set_prop:reset_zero()
    return set_prop:start_ld_fgi(ctrl_index, volume_id, LD_OPERATION_START_FGI, init_type)
end

function CMD.cancel_ld_fgi(ctrl_index, volume_id)
    local set_prop = sml_core['SML_LD_SET_PROPERTIES_S'].new()
    set_prop:reset_zero()
    return set_prop:set(ctrl_index, volume_id, LD_OPERATION_CANCEL_FGI)
end

function CMD.add_ld_on_exist_array(...)
    local add_ld = sml_core['SML_RAID_ON_EXISTED_ARRAY_PARAM_S'].new()
    add_ld:reset_zero()
    return add_ld:add_ld_on_exist_array(...), add_ld:get_ld_id()
end

function CMD.create_ld_on_new_array(...)
    local create_ld = sml_core['SML_RAID_ON_NEW_ARRAY_PARAM_S'].new()
    create_ld:reset_zero()
    return create_ld:create_ld_on_new_array(...), create_ld:get_ld_id()
end

function CMD.create_ld_as_cachecade(...)
    local create_chche_ld = sml_core['SML_RAID_CACHECADE_PARAM_S'].new()
    create_chche_ld:reset_zero()
    return create_chche_ld:create_ld_as_cachecade(...)
end

function CMD.delete_volume(controller_id, volume_id)
    local target = sml_core['SML_LD_TARGET_S'].new()
    target:reset_zero()
    target.i_controller_index = controller_id
    target.i_target_id = volume_id
    return target:delete_volume()
end

function CMD.get_ld_sscd_caching_enable(controller_id, volume_id)
    local target = sml_core['SML_LD_SSCD_CACHING_ENABLE_S'].new()
    target:reset_zero()
    target.i_controller_index = controller_id
    target.i_target_id = volume_id
    target:update()
    return target.o_sscd_caching_enable
end

function CMD.get_sscd_associated_ld_list(controller_id, volume_id)
    local target = sml_core['SML_SSCD_ASSOCIATED_LD_LIST_S'].new()
    target:reset_zero()
    target.i_controller_index = controller_id
    target.i_sscd_id = volume_id
    target:update()
    return target:get_list()
end

function CMD.get_ld_associated_sscd_list(controller_id, volume_id)
    local target = sml_core['SML_LD_ASSOCIATED_SSCD_LIST_S'].new()
    target:reset_zero()
    target.i_controller_index = controller_id
    target.i_ld_id = volume_id
    target:update()
    return target:get_list()
end

return CMD
