-- 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 class = require 'mc.class'
local signal = require 'mc.signal'
local ctx = require 'mc.context'
local log = require 'mc.logging'
local utils = require 'mc.utils'
local skynet = require 'skynet'
local common_def = require 'common_def'
local nvme_utils = require 'nvme.utils'

local c_ssd_form_factor = class()

local MANUFACTURER_ARRAY <const> = {
    [common_def.NVME_VPD_VENDOR_ID_FOR_INTEL] = common_def.NVME_MANUFACTURER_INTEL,
    [common_def.NVME_VPD_VENDOR_ID_FOR_HUAWEI] = common_def.NVME_MANUFACTURER_HUAWEI,
    [common_def.NVME_VPD_VENDOR_ID_FOR_SAMSUNG] = common_def.NVME_MANUFACTURER_SAMSUNG,
    [common_def.NVME_VPD_VENDOR_ID_FOR_MEMBLAZE] = common_def.NVME_MANUFACTURER_MEMBLAZE,
    [common_def.NVME_VPD_VENDOR_ID_FOR_SHANNON] = common_def.NVME_MANUFACTURER_SHANNON,
    [common_def.NVME_VPD_VENDOR_ID_FOR_MICRON] = common_def.NVME_MANUFACTURER_MICRON,
    [common_def.NVME_VPD_VENDOR_ID_FOR_DERA] = common_def.NVME_MANUFACTURER_DERA
}

local PROTOCOL_PCIE <const> = 6
local MEDIA_SSD <const> = 1

-- 将nvme盘的速率转换为枚举值
local function nvme_max_speed2enum(nvme_max_speed_db)
    for k, v in pairs(common_def.CAPABLE_SPEED_GBPS) do
        if v == nvme_max_speed_db then
            return k
        end
    end
    return common_def.INVALID_U8
end

function c_ssd_form_factor:ctor(slot, chip)
    self.Slot = slot
    self.VPDChip = chip
    self.nvme_vpd_info = {
        SerialNumber = common_def.INVALID_STRING,
        Model = common_def.INVALID_STRING,
        ManufacturerId = common_def.INVALID_U32,
        Manufacturer = common_def.INVALID_STRING,
        CapacityMiB = common_def.INVALID_U32,
        CapableSpeedGbs = common_def.INVALID_U8,
        MediaType = MEDIA_SSD,
        Protocol = PROTOCOL_PCIE,
        NegotiatedSpeedGbs = common_def.INVALID_U8
    }
    self.manu_state = 0
end

function c_ssd_form_factor:init()
end

function c_ssd_form_factor:vpd_ssd_form_get_info(nvme_obj)
    if not self.VPDChip then
        log:error('SSD Form Factor %s have no VPDChip', self.Slot)
        return
    end

    self:update_serial_number()
    self:update_model()
    self:update_manufacture_id()
    self:update_manufacture()
    self:update_capable_speed_gbps()
    self:update_capacity()
    self.nvme_vpd_info.NegotiatedSpeedGbs = nvme_obj.NegotiatedSpeedGbs
    nvme_obj.SerialNumber = self.nvme_vpd_info.SerialNumber

    return self.nvme_vpd_info
end

function c_ssd_form_factor:read_vpd_chip(offset, length)
    local ok, ret = pcall(function()
        return self.VPDChip:Read(ctx.get_context_or_default(), offset, length)
    end)

    if not ok then
        log:info('SSD Form Factor %s VPDChip Read Failed', self.Slot)
        return
    end

    -- 十六进制字符串数组
    local arr = nvme_utils.string_split(utils.to_hex(ret), ' ', 16)
    local str_tab = {}
    for _, v in pairs(arr) do
        -- ASCII数字转字符
        if v ~= 0 then
            table.insert(str_tab, string.char(v))
        end
    end
    
    -- 过滤首尾空字符
    return table.concat(str_tab):gsub("^%s*(.-)%s*$", "%1")
end

function c_ssd_form_factor:read_num_from_chip(offset, length, default_val)
    local num = 0
    local ok, ret = pcall(function()
        return self.VPDChip:Read(ctx.get_context_or_default(), offset, length)
    end)

    if not ok then
        log:info('SSD Form Factor %s VPDChip Read Failed', self.Slot)
        return default_val
    end

    -- 十六进制字符串数组
    local arr = nvme_utils.string_split(utils.to_hex(ret), ' ', 16)
    for k, v in ipairs(arr) do
        -- 数字拼接
        num = num + (v << (8 * (k - 1)))
    end

    return num
end

-- 更新序列号
function c_ssd_form_factor:update_serial_number()
    if self.nvme_vpd_info.SerialNumber ~= common_def.INVALID_STRING then
        return
    end

    local ret = self:read_vpd_chip(common_def.VPD_SSD_FORM_SN_OFFSET,
        common_def.VPD_SSD_FORM_SN_LEN)
    if ret then
        self.nvme_vpd_info.SerialNumber = ret
    end
end

-- 更新序型号
function c_ssd_form_factor:update_model()
    if self.nvme_vpd_info.Model ~= common_def.INVALID_STRING then
        return
    end

    local ret = self:read_vpd_chip(common_def.VPD_SSD_FORM_MODEL_NUMBER_OFFSET,
        common_def.VPD_SSD_FORM_MODEL_NUMBER_LEN)
    if ret then
        self.nvme_vpd_info.Model = ret
    end
end

-- 更新厂商Id
function c_ssd_form_factor:update_manufacture_id()
    if self.nvme_vpd_info.ManufacturerId ~= common_def.INVALID_U32 then
        return
    end

    self.nvme_vpd_info.ManufacturerId = self:read_num_from_chip(
        common_def.VPD_SSD_FORM_VENDOR_ID_OFFSET,
        common_def.VPD_SSD_FORM_VENDOR_ID_LEN,
        common_def.INVALID_U32)
end

-- 更新厂商
function c_ssd_form_factor:update_manufacture()
    if self.nvme_vpd_info.Manufacturer ~= common_def.INVALID_STRING then
        return
    end

    for k, v in pairs(MANUFACTURER_ARRAY) do
        if k == self.nvme_vpd_info.ManufacturerId then
            self.nvme_vpd_info.Manufacturer = v
            return
        end
    end

    self.manu_state = common_def.NVME_VPD_STATE_ERROR
end

--  更新最大速率
function c_ssd_form_factor:update_capable_speed_gbps()
    if self.nvme_vpd_info.CapableSpeedGbs ~= common_def.INVALID_U8 then
        return
    end

    local link_speed = self:get_link_speed()
    local link_width = self:get_link_width()

    self.nvme_vpd_info.CapableSpeedGbs = nvme_max_speed2enum(link_speed * link_width)
end

local SPEED_TABLE <const> = {
    [common_def.PCI_EXPRESS_VERSION_GEN_ONE] = common_def.ORIGIN_MAX_SPEED_2P5G,
    [common_def.PCI_EXPRESS_VERSION_GEN_TWO] = common_def.ORIGIN_MAX_SPEED_5G,
    [common_def.PCI_EXPRESS_VERSION_GEN_THREE] = common_def.ORIGIN_MAX_SPEED_8G,
    [common_def.PCI_EXPRESS_VERSION_GEN_FOUR] = common_def.ORIGIN_MAX_SPEED_16G,
}

--  获取link speed信息
function c_ssd_form_factor:get_link_speed()
    local ret = self:read_num_from_chip(
        common_def.VPD_SSD_FORM_LINK_SPEED_OFFSET,
        common_def.VPD_SSD_FORM_LINK_SPEED_LEN,
        0
    )

    return SPEED_TABLE[ret] or common_def.ORIGIN_MAX_SPEED_DEFAULT
end

--  获取link width信息
function c_ssd_form_factor:get_link_width()
    local ret = self:read_num_from_chip(
        common_def.VPD_SSD_FORM_LINK_WIDTH_OFFSET,
        common_def.VPD_SSD_FORM_LINK_WIDTH_LEN,
        0
    )

    return ret
end

local SSD_FORM_CAPACITY_TABLE <const> = {
    ["SAMSUNG MZQLB960HAJR"] = common_def.SSD_FORM_CAPACITY_SAMSUNG_PM983_960,
    ["SAMSUNG MZQLB1T9HAJR"] = common_def.SSD_FORM_CAPACITY_SAMSUNG_PM983_1T9,
    ["SAMSUNG MZQLB3T8HALS"] = common_def.SSD_FORM_CAPACITY_SAMSUNG_PM983_3T8,
    ["SAMSUNG MZQLB7T6HMLA"] = common_def.SSD_FORM_CAPACITY_SAMSUNG_PM983_7T6,
    ["MEMBLAZE P6541RT0768Y00"] = common_def.SSD_FORM_CAPACITY_MEMBLAZE_7T6,
}

--  更新PM983容量信息,SSD Form Factor协议通过型号获取容量
function c_ssd_form_factor:update_capacity()
    if self.nvme_vpd_info.CapacityMiB ~= common_def.INVALID_U32 then
        return
    end

    local cap_bytes = SSD_FORM_CAPACITY_TABLE[self.nvme_vpd_info.Model]
    if cap_bytes then
        self.nvme_vpd_info.CapacityMiB = cap_bytes // (1024 * 1024)
    end
end

return c_ssd_form_factor