/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 */

#include "ka_system_pub.h"
#include "ka_driver_pub.h"
#include "ka_memory_pub.h"
#include "ka_kernel_def_pub.h"
#include "ka_compiler_pub.h"
#include "ka_list_pub.h"
#include "ka_barrier_pub.h"
// ubus header find in ubengine/ssapi/kernelspace
#include "ubcore_types.h"
#include "ubcore_uapi.h"
#include "ubcore_api.h"

#include "pbl/pbl_feature_loader.h"
#include "pbl/pbl_uda.h"
#include "ascend_ub_hotreset.h"
#include "ascend_kernel_hal.h"
#include "comm_kernel_interface.h"
#include "ascend_ub_common.h"
#include "ascend_ub_dev.h"
#include "ascend_ub_jetty.h"
#include "ascend_ub_load.h"
#include "ascend_ub_admin_msg.h"
#include "ascend_ub_non_trans_chan.h"
#include "ascend_ub_common_msg.h"
#include "ascend_ub_msg.h"
#include "ascend_ub_link_chan.h"
#include "ascend_ub_pair_dev_info.h"
#include "ascend_ub_rao.h"
#include "ascend_ub_urma_chan.h"
#include "ascend_ub_mem_decoder.h"
#include "ascend_ub_main.h"

struct ascend_ub_ctrl *g_ub_ctrl = NULL;
u32 g_add_davinci_flag = 0;

u32 get_global_add_davinci_flag(void)
{
    return g_add_davinci_flag;
}

struct ascend_ub_ctrl* get_global_ub_ctrl(void)
{
    return g_ub_ctrl;
}

int ubdrv_get_local_token(u32 dev_id, u32 *token_value)
{
    if (dev_id >= ASCEND_UB_DEV_MAX_NUM) {
        ubdrv_err("Check dev_id fail, when get token. (dev_id=%u)\n", dev_id);
        return -EINVAL;
    }

    ka_task_down_read(&g_ub_ctrl->asd_dev[dev_id].rw_sem);
    if (g_ub_ctrl->asd_dev[dev_id].token.token_valid != ASCEND_VALID) {
        ka_task_up_read(&g_ub_ctrl->asd_dev[dev_id].rw_sem);
        ubdrv_err("Token is invalid. (dev_id=%u)\n", dev_id);
        return -EAGAIN;
    }
    *token_value = g_ub_ctrl->asd_dev[dev_id].token.local_token;
    ka_task_up_read(&g_ub_ctrl->asd_dev[dev_id].rw_sem);
    return 0;
}

void ubdrv_set_local_token(u32 dev_id, u32 token_value, u32 token_valid)
{
    ka_task_down_write(&g_ub_ctrl->asd_dev[dev_id].rw_sem);
    g_ub_ctrl->asd_dev[dev_id].token.local_token = token_value;
    g_ub_ctrl->asd_dev[dev_id].token.token_valid = token_valid;
    ka_task_up_write(&g_ub_ctrl->asd_dev[dev_id].rw_sem);
}

int ubdrv_get_dev_id_info(u32 dev_id, struct devdrv_dev_id_info *id_info)
{
    if (dev_id >= ASCEND_UB_DEV_MAX_NUM) {
        ubdrv_err("Check dev_id fail, when get id_info. (dev_id=%u)\n", dev_id);
        return -EINVAL;
    }
    if (id_info == NULL) {
        ubdrv_err("Input parameter is NULL, when get id_info. (dev_id=%u)\n", dev_id);
        return -EINVAL;
    }
    ka_task_down_read(&g_ub_ctrl->asd_dev[dev_id].rw_sem);
    if (g_ub_ctrl->asd_dev[dev_id].id_info_valid != ASCEND_VALID) {
        ka_task_up_read(&g_ub_ctrl->asd_dev[dev_id].rw_sem);
        ubdrv_err("ID info is invalid. (dev_id=%u)\n", dev_id);
        return -EAGAIN;
    }
    id_info->device_id = g_ub_ctrl->asd_dev[dev_id].id_info.device_id;
    id_info->module_id = g_ub_ctrl->asd_dev[dev_id].id_info.module_id;
    id_info->module_vendor_id = g_ub_ctrl->asd_dev[dev_id].id_info.module_vendor_id;
    id_info->vendor_id = g_ub_ctrl->asd_dev[dev_id].id_info.vendor_id;
    ka_task_up_read(&g_ub_ctrl->asd_dev[dev_id].rw_sem);

    return 0;
}

void ubdrv_set_dev_id_info(u32 dev_id, struct ubdrv_id_info *id_info, u32 info_valid)
{
    ka_task_down_write(&g_ub_ctrl->asd_dev[dev_id].rw_sem);
    g_ub_ctrl->asd_dev[dev_id].id_info_valid = info_valid;
    if (id_info != NULL) {
        (void)memcpy_s(&g_ub_ctrl->asd_dev[dev_id].id_info, sizeof(struct ubdrv_id_info),
                id_info, sizeof(struct ubdrv_id_info));
    }
    ka_task_up_write(&g_ub_ctrl->asd_dev[dev_id].rw_sem);
}

struct ascend_ub_dev_status* ubdrv_get_dev_status_mng(u32 dev_id)
{
    return &g_ub_ctrl->dev_status[dev_id];
}

struct ascend_ub_link_res *ubdrv_get_link_res_by_devid(u32 dev_id)
{
    return &g_ub_ctrl->link_res[dev_id];
}

int ubdrv_get_device_status(u32 dev_id)
{
    if (dev_id >= ASCEND_UB_DEV_MAX_NUM) {
        ubdrv_err("Get device status fail, invalid dev_id. (dev_id=%u)\n", dev_id);
        return -EINVAL;
    }
    return g_ub_ctrl->dev_status[dev_id].device_status;
}

void ubdrv_set_device_status(u32 dev_id, u32 status)
{
    struct ascend_ub_dev_status *status_mng = ubdrv_get_dev_status_mng(dev_id);
    int i = 0;

    // ref_cnt is 0, when set offline status
    ka_task_down_write(&status_mng->rw_sem);
    if (status == UBDRV_DEVICE_OFFLINE) {
        for (i = 0; i < UBDRV_DEVICE_OFFLINE_WAIT_CNT; i++) {
            if (ka_base_atomic_read(&status_mng->ref_cnt) == 0) {
                break;
            }
            ka_system_usleep_range(1, 2);  // wait 1~2 us
        }
    }
    if (i == UBDRV_DEVICE_OFFLINE_WAIT_CNT) {
        ubdrv_warn("Wait device ref cnt timeout. (dev_id=%u; ref_cnt=%d)\n", dev_id, ka_base_atomic_read(&status_mng->ref_cnt));
    }
    status_mng->device_status = status;
    ka_task_up_write(&status_mng->rw_sem);
    return;
}

int ubdrv_add_device_status_ref(u32 dev_id)
{
    struct ascend_ub_dev_status *status_mng = NULL;
    int status;
    int ret = 0;

    if (dev_id >= ASCEND_UB_DEV_MAX_NUM) {
        ubdrv_err("Invalid dev_id. (dev_id=%u)\n", dev_id);
        return -EINVAL;
    }
    status_mng = ubdrv_get_dev_status_mng(dev_id);
    ka_task_down_read(&status_mng->rw_sem);
    status = status_mng->device_status;
    if ((status == UBDRV_DEVICE_BEGIN_ONLINE) || (status == UBDRV_DEVICE_ONLINE) ||
        (status == UBDRV_DEVICE_BEGIN_OFFLINE)) {
        ka_base_atomic_inc(&status_mng->ref_cnt);
    } else {
        ubdrv_warn("Add device status ref unsuccessful. (device_status=%d; dev_id=%u)\n", status, dev_id);
        ret = -ENODEV;
    }
    ka_task_up_read(&status_mng->rw_sem);
    return ret;
}

void ubdrv_sub_device_status_ref(u32 dev_id)
{
    struct ascend_ub_dev_status *status_mng = NULL;

    status_mng = ubdrv_get_dev_status_mng(dev_id);
    ka_base_atomic_sub(1, &status_mng->ref_cnt);
    return;
}

struct dev_eid_info *ubdrv_get_eid_info_by_devid(u32 devid)
{
    return &g_ub_ctrl->asd_dev[devid].eid_info;
}

struct ubcore_device *ubdrv_get_default_user_ctrl_urma_dev(void)
{
    return g_ub_ctrl->idev[USER_CTRL_DEFAULT_DEV_ID][USER_CTRL_DEFAULT_FE_IDX].ubc_dev;
}

enum ubdrv_eid_cmp_ret ubdrv_cmp_eid(union ubcore_eid *src_eid, union ubcore_eid *dst_eid)
{
    int i;

    for (i = 0; i < UBCORE_EID_SIZE; i++) {
        if (src_eid->raw[i] < dst_eid->raw[i]) {
            return UBDRV_CMP_SMALL;
        }
        if (src_eid->raw[i] > dst_eid->raw[i]) {
            return UBDRV_CMP_LARGE;
        }
    }
    return UBDRV_CMP_EQUAL;
}

STATIC bool ubdrv_check_is_bind(struct ub_idev *idev, u32 dev_id)
{
    u32 i;

    for (i = 0; i < ASCEND_UB_DEV_NUM_PER_FE; i++) {
        if (idev->dev_id[i] == dev_id) {
            return true;
        }
    }
    return false;
}

int ubdrv_davinci_bind_fe(struct ub_idev *idev, u32 dev_id)
{
    int cnt;
    u32 i;

    ka_task_down_write(&idev->rw_sem);
    if (idev->valid == ASCEND_UB_INVALID) {
        ubdrv_err("Urma dev is invalid. (idev_id=%u;ue_idx=%u)\n", idev->idev_id, idev->ue_idx);
        ka_task_up_write(&idev->rw_sem);
        return -EINVAL;
    }
    if (ubdrv_check_is_bind(idev, dev_id) == true) {
        ka_task_up_write(&idev->rw_sem);
        return 0;
    }
    for (i = 0; i < ASCEND_UB_DEV_NUM_PER_FE; i++) {
        if (idev->dev_id[i] == KA_U32_MAX) {
            idev->dev_id[i] = dev_id;
            ka_base_atomic_add(1, &idev->ref_cnt);
            ka_task_up_write(&idev->rw_sem);
            return 0;
        }
    }
    cnt = ka_base_atomic_read(&idev->ref_cnt);
    ubdrv_err("The urma device is bound to too many accounts. (idev_id=%u;ue_idx=%u;dev_id=%u;cnt=%d)\n",
        idev->idev_id, idev->ue_idx, dev_id, cnt);
    ka_task_up_write(&idev->rw_sem);
    return -EINVAL;
}

void ubdrv_davinci_unbind_fe(struct ub_idev *idev, u32 dev_id)
{
    u32 i;

    ka_task_down_write(&idev->rw_sem);
    for (i = 0; i < ASCEND_UB_DEV_NUM_PER_FE; i++) {
        if (idev->dev_id[i] == dev_id) {
            ka_base_atomic_sub(1, &idev->ref_cnt);
            idev->dev_id[i] = KA_U32_MAX;
            ka_task_up_write(&idev->rw_sem);
            return;
        }
    }
    ka_task_up_write(&idev->rw_sem);
    return;
}

STATIC int ubdrv_find_eid_index_by_eid(struct ub_idev *idev, struct ubcore_eid_info *eid, u32 *eid_idx)
{
    struct ubcore_eid_info *eid_info;
    u32 cnt, i;

    eid_info = ubcore_get_eid_list(idev->ubc_dev, &cnt);
    if (eid_info == NULL) {
        ubdrv_err("ubcore_get_eid_list failed.\n");
        return -EINVAL;
    }
    for (i = 0; i < cnt; i++) {
        if (ubdrv_cmp_eid(&eid->eid, &eid_info[i].eid) == UBDRV_CMP_EQUAL) {
            *eid_idx = eid_info[i].eid_index;
            ubcore_free_eid_list(eid_info);
            return 0;
        }
    }
    ubcore_free_eid_list(eid_info);
    return -EINVAL;
}

struct ub_idev *ubdrv_get_idev_by_eid_and_feidx(struct ubcore_eid_info *eid, u32 ue_idx)
{
    struct ub_idev *idev;
    u32 i, eid_idx;

    if (ue_idx >= ASCEND_UDMA_MAX_FE_NUM) {
        ubdrv_err("ue_idx is invalid. (ue_idx=%u)\n", ue_idx);
        return NULL;
    }
    for (i = 0; i < ASCEND_UDMA_DEV_MAX_NUM; i++) {
        idev = &g_ub_ctrl->idev[i][ue_idx];
        ka_task_down_read(&idev->rw_sem);
        if (idev->ubc_dev == NULL) {
            ka_task_up_read(&idev->rw_sem);
            continue;
        }
        if (ubdrv_find_eid_index_by_eid(idev, eid, &eid_idx) == 0) {
            ka_task_up_read(&idev->rw_sem);
            ubdrv_info("Idev match success. (idev_id=%u;ue_idx=%u;dev_name=%s)\n",
                idev->idev_id, ue_idx, idev->ubc_dev->dev_name);
            return idev;
        }
        ka_task_up_read(&idev->rw_sem);
    }
    return NULL;
}

struct ub_idev *ubdrv_get_idev_by_eid(struct ubcore_eid_info *eid)
{
    struct ub_idev *idev = NULL;
    u32 i = 0;

    for (i = 0; i < ASCEND_UDMA_MAX_FE_NUM; i++) {
        idev = ubdrv_get_idev_by_eid_and_feidx(eid, i);
        if (idev != NULL) {
            return idev;
        }
    }
    return NULL;
}

STATIC struct ub_idev *ubdrv_get_idev_by_ubc_dev(struct ubcore_device *ubc_dev)
{
    struct ub_idev *idev = NULL;
    u32 i;
    u32 j;

    for (i = 0; i < ASCEND_UDMA_DEV_MAX_NUM; i++) {
        for (j = 0; j < ASCEND_UDMA_MAX_FE_NUM; j++) {
            idev = &g_ub_ctrl->idev[i][j];
            ka_task_down_read(&idev->rw_sem);
            if ((idev != NULL) && (idev->ubc_dev == ubc_dev)) {
                ka_task_up_read(&idev->rw_sem);
                return idev;
            }
            ka_task_up_read(&idev->rw_sem);
        }
    }
    ubdrv_err("Idev match fail. (name=%s)\n", ubc_dev->dev_name);
    return NULL;
}

void ubdrv_set_startup_flag(u32 dev_id, enum ubdrv_dev_startup_flag_type startup_flag)
{
    g_ub_ctrl->asd_dev[dev_id].startup_flag = startup_flag;
}

enum ubdrv_dev_startup_flag_type ubdrv_get_startup_flag(u32 dev_id)
{
    return g_ub_ctrl->asd_dev[dev_id].startup_flag;
}

struct ascend_ub_msg_dev *ubdrv_get_msg_dev_by_devid(u32 dev_id)
{
    struct ascend_ub_msg_dev *msg_dev;

    if (dev_id >= ASCEND_UB_DEV_MAX_NUM) {
        ubdrv_err("Invalid dev_id, get msg_dev failed. (dev_id=%d)\n", dev_id);
        return NULL;
    }
    msg_dev = g_ub_ctrl->asd_dev[dev_id].msg_dev;
    return msg_dev;
}

struct ascend_dev *ubdrv_get_asd_dev_by_devid(u32 dev_id)
{
    if (dev_id >= ASCEND_UB_DEV_MAX_NUM) {
        ubdrv_err("Invalid dev_id, get ubus dev failed. (dev_id=%d)\n", dev_id);
        return NULL;
    }
    return &g_ub_ctrl->asd_dev[dev_id];
}

STATIC struct ascend_ub_msg_dev* ubdrv_msg_dev_init(u32 dev_id, u32 remote_id, struct ub_idev *idev)
{
    struct ascend_ub_dev_status *dev_status;
    struct ascend_ub_msg_dev *msg_dev;
    struct ascend_dev *asd_dev;
    int msg_dev_len = sizeof(struct ascend_ub_msg_dev);

    msg_dev = ubdrv_kzalloc(msg_dev_len, KA_GFP_KERNEL);
    if (msg_dev == NULL) {
        ubdrv_err("Alloc msg_dev mem failed. (dev_id=%u)\n", dev_id);
        return NULL;
    }
    msg_dev->dev_id = dev_id;
    msg_dev->remote_id = remote_id;
    asd_dev = &(g_ub_ctrl->asd_dev[dev_id]);
    msg_dev->idev = idev;
    msg_dev->ubc_dev = msg_dev->idev->ubc_dev;
    msg_dev->asd_dev = asd_dev;
    msg_dev->dev_valid = 1;
    msg_dev->chan_cnt = UBDRV_NON_TRANS_CHAN_CNT;
    ka_task_mutex_init(&msg_dev->mutex_lock);
    dev_status = &(g_ub_ctrl->dev_status[dev_id]);
    ka_base_atomic_set(&dev_status->ref_cnt, 0);
    return msg_dev;
}

STATIC void ubdrv_msg_dev_uninit(struct ascend_ub_msg_dev *msg_dev, enum ubdrv_dev_status final_state)
{
    struct ascend_ub_dev_status *dev_status;

    dev_status = &(g_ub_ctrl->dev_status[msg_dev->dev_id]);
    ka_base_atomic_set(&dev_status->ref_cnt, 0);
    dev_status->device_status = final_state;
    ka_task_mutex_destroy(&msg_dev->mutex_lock);
    ubdrv_davinci_unbind_fe(msg_dev->idev, msg_dev->dev_id);
    msg_dev->idev = NULL;
    msg_dev->ubc_dev = NULL;
    msg_dev->asd_dev = NULL;
    msg_dev->dev_valid = 0;
    ubdrv_kfree(msg_dev);
}

STATIC int ubdrv_admin_msg_chan_init(u32 dev_id, struct ascend_ub_msg_dev *msg_dev,
    struct jetty_exchange_data *data, struct ub_idev *idev)
{
    struct ascend_ub_admin_chan *msg_chan;
    struct ascend_ub_sync_jetty *admin_jetty;
    int ret = 0;

    admin_jetty = g_ub_ctrl->link_res[dev_id].admin_jetty;
    if (admin_jetty == NULL) {
        ubdrv_err("Admin jetty is null. (dev_id=%u)\n", dev_id);
        return -EINVAL;
    }
    ka_base_atomic_add(1, &(admin_jetty->user_cnt));
    msg_chan = &(msg_dev->admin_msg_chan);
    msg_chan->admin_jetty = admin_jetty;
    msg_chan->msg_dev = msg_dev;
    msg_chan->dev_id = msg_dev->dev_id;
    msg_chan->chan_stat.dev_id = dev_id;
    msg_chan->chan_stat.chan_id = 0;
    ka_task_mutex_init(&msg_chan->mutex_lock);

    ret = ubdrv_create_admin_msg_chan(dev_id, msg_dev);
    if (ret != 0) {
        goto destroy_mutex;
    }
    ret = ubdrv_admin_chan_import_jfr(msg_dev->idev, msg_chan, data, dev_id, UBDRV_ERR_LEVEL);
    if (ret != 0) {
        ubdrv_err("Admin chan import jetty failed. (ret=%d;dev_id=%u)\n", ret, dev_id);
        goto create_err;
    }
    ret = ubdrv_rearm_sync_jfc(admin_jetty);
    if (ret != 0) {
        ubdrv_err("Admin chan rearm jfc failed. (ret=%d;dev_id=%u)\n", ret, dev_id);
        goto rearm_err;
    }

    return 0;

rearm_err:
    ubdrv_admin_chan_unimport_jfr(msg_chan, dev_id);
create_err:
    ubdrv_del_admin_msg_chan(msg_dev, dev_id);
destroy_mutex:
    ka_task_mutex_destroy(&msg_chan->mutex_lock);
    msg_chan->msg_dev = NULL;
    msg_chan->admin_jetty = NULL;
    ka_base_atomic_sub(1, &(admin_jetty->user_cnt));
    return ret;
}

void ubdrv_admin_msg_chan_uninit(u32 dev_id, struct ascend_ub_msg_dev *msg_dev)
{
    struct ascend_ub_admin_chan *msg_chan;
    struct ascend_ub_sync_jetty *admin_jetty;

    msg_chan = &(msg_dev->admin_msg_chan);
    admin_jetty = msg_chan->admin_jetty;
    ubdrv_admin_chan_unimport_jfr(msg_chan, dev_id);
    ubdrv_del_admin_msg_chan(msg_dev, dev_id);
    ka_task_mutex_destroy(&msg_chan->mutex_lock);
    msg_chan->dev_id = 0;
    msg_chan->msg_dev = NULL;
    msg_chan->admin_jetty = NULL;
    ka_base_atomic_sub(1, &(admin_jetty->user_cnt));
}

struct ub_idev *ubdrv_get_idev(u32 idev_id, u32 ue_idx)
{
    if ((idev_id >= ASCEND_UDMA_DEV_MAX_NUM) || (ue_idx >= ASCEND_UDMA_MAX_FE_NUM)) {
        return NULL;
    }
    return &g_ub_ctrl->idev[idev_id][ue_idx];
}

static bool ubdrv_is_sub_ubdev(struct ub_idev *p_idev, struct ubcore_device *ubc_dev)
{
    ka_device_t *pdev = p_idev->ubc_dev->dma_dev;
    ka_device_t *dev = ubc_dev->dma_dev;
    if (pdev == NULL || dev == NULL) {
        return false;
    }

    return (dev->parent == pdev);
}

STATIC struct ub_idev* ubdrv_alloc_idev_id(struct ubcore_device *ubc_dev)
{
    u32 ue_idx = (u32)ubc_dev->attr.ue_idx;
    struct ubdrv_msg_chan_stat *stat;
    struct ub_idev *idev = NULL;
    u32 i;

    if (ue_idx >= ASCEND_UDMA_MAX_FE_NUM) {
        ubdrv_err("Invalid ue_idx. (idx=%d)", ue_idx);
        return NULL;
    }

    for (i = 0; i < ASCEND_UDMA_DEV_MAX_NUM; ++i) {
        idev = &g_ub_ctrl->idev[i][ue_idx];
        ka_task_down_write(&idev->rw_sem);
        if (idev->ubc_dev != NULL) {
            ka_task_up_write(&idev->rw_sem);
            continue;
        }
        if (ue_idx == 0) {
            goto alloc_succ;
        }
        ka_task_down_write(&g_ub_ctrl->idev[i][0].rw_sem);
        if ((g_ub_ctrl->idev[i][0].ubc_dev != NULL) &&
            ubdrv_is_sub_ubdev(&g_ub_ctrl->idev[i][0], ubc_dev)) {
            ka_task_up_write(&g_ub_ctrl->idev[i][0].rw_sem);
            goto alloc_succ;
        }
        ka_task_up_write(&g_ub_ctrl->idev[i][0].rw_sem);
        ka_task_up_write(&idev->rw_sem);
    }
    ubdrv_err("Fe num is over. (name=%s)", ubc_dev->dev_name);
    return NULL;

alloc_succ:
    g_ub_ctrl->idev_num += 1;
    idev = &g_ub_ctrl->idev[i][ue_idx];
    idev->ubc_dev = ubc_dev;
    stat = &idev->link_chan.chan_stat;
    stat->dev_id = idev->idev_id;
    stat->chan_id = idev->ue_idx;
    ka_task_up_write(&idev->rw_sem);
    ubdrv_info("alloc idev success, idev_id=%u, ue_idx=%u", idev->idev_id, idev->ue_idx);
    return idev;
}

STATIC void ubdrv_free_idev_id(u32 idev_id, u32 ue_idx)
{
    ka_task_down_write(&g_ub_ctrl->idev[idev_id][ue_idx].rw_sem);
    g_ub_ctrl->idev[idev_id][ue_idx].valid = ASCEND_UB_INVALID;
    g_ub_ctrl->idev[idev_id][ue_idx].ubc_dev = NULL;
    g_ub_ctrl->idev_num -= 1;
    ka_task_up_write(&g_ub_ctrl->idev[idev_id][ue_idx].rw_sem);
}

int ubdrv_ub_enable_funcs(u32 devid, u32 boot_mode)
{
    return -EOPNOTSUPP;
}

int ubdrv_ub_disable_funcs(u32 devid, u32 boot_mode)
{
    return -EOPNOTSUPP;
}

STATIC struct ubcore_client g_ascend_client = {
    .list_node = KA_LIST_HEAD_INIT(g_ascend_client.list_node),
    .client_name = "ascend_udma",
    .add = ubdrv_add_udma_device,
    .remove = ubdrv_remove_udma_device,
};

struct ubcore_client* get_global_ascend_client()
{
    return &g_ascend_client;
}

int devdrv_ub_get_connect_protocol(u32 dev_id)
{
    if (dev_id >= ASCEND_UB_DEV_MAX_NUM) {
        ubdrv_err("Invalid dev_id, get connect protocol failed. (dev_id=%u)\n", dev_id);
        return CONNECT_PROTOCOL_UNKNOWN;
    }
    return CONNECT_PROTOCOL_UB;
}

void devdrv_ub_set_device_boot_status(u32 dev_id, u32 status)
{
    g_ub_ctrl->asd_dev[dev_id].device_boot_status = status;
}

int devdrv_ub_get_device_boot_status(u32 devid, u32 *boot_status)
{
    if (boot_status == NULL) {
        ubdrv_err("Param boot_status is null, get boot status failed. (dev_id=%u)\n", devid);
        return -EINVAL;
    }

    if (ubdrv_is_valid_devid(devid) == false) {
        ubdrv_warn("Invalid devid, get boot status failed. (dev_id=%u)\n", devid);
        *boot_status = DSMI_BOOT_STATUS_UNINIT;
        return -ENXIO;
    }

    *boot_status = g_ub_ctrl->asd_dev[devid].device_boot_status;
    return 0;
}

int devdrv_ub_get_env_boot_type(u32 dev_id)
{
    return 0;
}

int devdrv_ub_get_pfvf_type_by_devid(u32 dev_id)
{
    if (dev_id < ASCEND_UB_PF_DEV_MAX_NUM){
        return DEVDRV_SRIOV_TYPE_PF;
    }
    return DEVDRV_SRIOV_TYPE_VF;
}

bool devdrv_ub_is_mdev_vm_boot_mode(u32 dev_id)
{
    return false;
}

bool devdrv_ub_is_sriov_support(u32 dev_id)
{
    return true;
}

/*
 * EID Selection Logic:
 * Device-side: Use the smallest value from `local_eid`.
 * Host-side: Use the smallest value from `remote_eid`.
 * Output Constraint: Only the selected EID (based on the above rules) is returned to the business logic.
*/
int ubdrv_get_ub_dev_info(u32 dev_id, struct devdrv_ub_dev_info *eid_query_info, int *num)
{
    struct dev_eid_info *eid_info = NULL;
    struct ub_idev *idev_tmp = NULL;
    u32 min_idx;
    int ret;

    if (dev_id >= ASCEND_UB_DEV_MAX_NUM) {
        ubdrv_err("Invalid dev_id, get msg_dev failed. (dev_id=%u)\n", dev_id);
        return -EINVAL;
    }

    if ((eid_query_info == NULL) || (num == NULL)) {
        ubdrv_err("Param is null, get ub dev info failed. (dev_id=%u)\n", dev_id);
        return -EINVAL;
    }

    eid_info = &g_ub_ctrl->asd_dev[dev_id].eid_info;
    if ((eid_info->num == 0) || (eid_info->min_idx >= eid_info->num)) {
        *num = 0;
        ubdrv_err("Invalid eid num, get eid num is 0. (dev_id=%u; num=%u; min_idx=%u)\n", dev_id,
            eid_info->num, eid_info->min_idx);
        return -EINVAL;
    }

    min_idx = eid_info->min_idx;
    // find fe according to local_eid
    idev_tmp = ubdrv_get_idev_by_eid(&eid_info->local_eid[min_idx]);
    if (idev_tmp == NULL) {
        ubdrv_err("Find fe according to local_eid fail. (dev_id=%u)\n", dev_id);
        return -EINVAL;
    }

    // find eid_cnt according idev and fe
    ret = ubdrv_find_eid_index_by_eid(idev_tmp, &eid_info->local_eid[min_idx], &eid_query_info->eid_index[0]);
    if (ret != 0) {
        ubdrv_err("Find eid_idx according eid and fe fail. (ret=%d; dev_id=%u; min_idx=%u; eid="EID_FMT")\n",
            ret, dev_id, min_idx, EID_ARGS(eid_info->local_eid[min_idx].eid));
        return -EINVAL;
    }

    eid_query_info->udma_dev[0] = idev_tmp->ubc_dev;
    *num = 1;

    return 0;
}

int ubdrv_process_add_pasid(u32 dev_id, u64 pasid)
{
    return -EOPNOTSUPP;
}

int ubdrv_process_del_pasid(u32 dev_id, u64 pasid)
{
    return -EOPNOTSUPP;
}

STATIC void ubdrv_print_udma_device_info(struct ubcore_device *ubc_dev)
{
    ubdrv_info("udma device info. (dev_name=%s;attr_fe_id=%d;fe_id=%d;virtual=%d;port_cnt=%d)\n",
        ubc_dev->dev_name, ubc_dev->attr.ue_idx, ubc_dev->cfg.ue_idx,
        ubc_dev->attr.virtualization, ubc_dev->attr.port_cnt);
    if (ubc_dev->dma_dev != NULL) {
        ubdrv_debug("ubc_dev->dma_dev=%pK, dma_dev->parent=%pK\n",
            ubc_dev->dma_dev, ubc_dev->dma_dev->parent);
    }

    ubdrv_info("udma eid info. (eid_cnt=%u)\n", ubc_dev->eid_table.eid_cnt);
}

int ubdrv_add_udma_device(struct ubcore_device *ubc_dev)
{
    struct ub_idev *idev;

    if (ubc_dev == NULL) {
        ubdrv_err("Param ubc_dev is null, add udma device fail.\n");
        return -EINVAL;
    }
    if ((ubc_dev->eid_table.eid_cnt > UBCORE_MAX_EID_CNT) || (ubc_dev->eid_table.eid_cnt == 0)) {
        ubdrv_err("Invalid eid_cnt. (eid_cnt=%u;dev_name=%s)\n", ubc_dev->eid_table.eid_cnt, ubc_dev->dev_name);
        return -EINVAL;
    }
    ubdrv_info("Start add udma device.\n");
    ubdrv_print_udma_device_info(ubc_dev);
    idev = ubdrv_alloc_idev_id(ubc_dev);
    if (idev == NULL) {
        ubdrv_err("Alloc idev id unsuccessful. (dev_name=%s;ue_idx=%d)\n",
            ubc_dev->dev_name, ubc_dev->attr.ue_idx);
        return -EINVAL;
    }

    ubcore_set_client_ctx_data(ubc_dev, &g_ascend_client, idev);
    if (ubdrv_get_ub_pcie_sel() == UBDRV_UB_SEL) {
        devdrv_set_communication_ops_status(DEVDRV_COMMNS_UB, DEVDRV_COMM_OPS_TYPE_ENABLE, idev->idev_id);
        ubdrv_pair_info_init_work(idev);
    }
    ubdrv_info("Add udma device success. (idev_id=%u;ue_idx=%u;udma_name=%s)\n",
        idev->idev_id, idev->ue_idx, idev->ubc_dev->dev_name);
    return 0;
}

STATIC void ubdrv_remove_device_resource(u32 dev_id, enum ubdrv_dev_status final_state)
{
    struct ascend_ub_dev_status *status_mng = NULL;

    status_mng = ubdrv_get_dev_status_mng(dev_id);
    ka_task_down_write(&status_mng->rw_sem);
    if (status_mng->device_status == UBDRV_DEVICE_ONLINE) {
        status_mng->device_status = UBDRV_DEVICE_DEAD;
        ka_task_up_write(&status_mng->rw_sem);
    } else {
        ka_task_up_write(&status_mng->rw_sem);
        return;
    }
     // remove davinci_dev
    if (dev_id < ASCEND_UB_PF_DEV_MAX_NUM) {
        ubdrv_remove_davinci_dev(dev_id, UDA_REAL);
    } else {
        ubdrv_remove_davinci_dev_proc(dev_id);
    }
    ubdrv_del_msg_device(dev_id, final_state);
    return;
}

STATIC void ubdrv_remove_davinci_bind_fe(struct ub_idev *idev)
{
    u32 i = 0, dev_id = 0, j = 0;

    ka_task_down_write(&idev->rw_sem);
    idev->valid = ASCEND_UB_INVALID;
    ka_task_up_write(&idev->rw_sem);

retry:
    for (i = 0; i < ASCEND_UB_DEV_NUM_PER_FE; i++) {
        if (idev->dev_id[i] != KA_U32_MAX) {
            dev_id = idev->dev_id[i];
            ubdrv_remove_device_resource(dev_id, UBDRV_DEVICE_FE_RESET);
        }
    }
    if ((ka_base_atomic_read(&idev->ref_cnt) == 0) || (j > UBDRV_DEVICE_REMOVE_WAIT_CNT)) {
        ubdrv_info("Remove davinci return. (ref_cnt=%d;cnt=%u)\n", ka_base_atomic_read(&idev->ref_cnt), j);
        return;
    }
    ka_system_usleep_range(200, 200);  // wait 200us
    j++;
    goto retry;
}

void ubdrv_remove_udma_device(struct ubcore_device *ubc_dev, void *client_ctx)
{
    struct ub_idev *idev;
    u32 idev_id;
    u32 ue_idx;

    if (ubc_dev == NULL) {
        ubdrv_err("Param ubc_dev is null, remove udma device fail.\n");
        return;
    }

    idev = (struct ub_idev *)ubcore_get_client_ctx_data(ubc_dev, &g_ascend_client);
    if (idev == NULL) {
        ubdrv_err("ubcore_get_client_ctx_data fail, when remove udma device.\n");
        return;
    }
    idev_id = idev->idev_id;
    ue_idx = idev->ue_idx;
    ubdrv_info("Start remove udma device. (idev_id=%u)\n", idev_id);

    if ((idev_id >= ASCEND_UDMA_DEV_MAX_NUM) || (ue_idx >= ASCEND_UDMA_MAX_FE_NUM)) {
        ubdrv_err("Invalid idev_id. (idev_id=%u;ue_idx=%u)\n", idev_id, ue_idx);
        return;
    }
    ubdrv_free_single_link_chan(idev);
    ubdrv_remove_davinci_bind_fe(idev);  // remove all davinci of bind the fe
    if (ubdrv_get_ub_pcie_sel() == UBDRV_UB_SEL) {
        devdrv_set_communication_ops_status(DEVDRV_COMMNS_UB, DEVDRV_COMM_OPS_TYPE_DISABLE, idev_id);
        ubdrv_pair_info_uninit_work(idev);
    }
    ubcore_set_client_ctx_data(ubc_dev, &g_ascend_client, NULL);
    ubdrv_free_idev_id(idev_id, ue_idx);
    ubdrv_info("Ascend remove udma device success. (idev_id=%u)\n", idev_id);
    return;
}

STATIC u32 ubdrv_get_chip_type(u32 dev_id)
{
#ifdef CFG_SOC_PLATFORM_CLOUD_V5
    return HISI_CLOUD_V5;
#else
    struct ascend_dev *asd_dev = NULL;

    asd_dev = ubdrv_get_asd_dev_by_devid(dev_id);
    if (asd_dev == NULL) {
        ubdrv_err("Get msg_dev failed. (dev_id=%u)\n", dev_id);
        return HISI_CLOUD_V4;
    }

    /* master slave mode */
    if (asd_dev->ub_dev != NULL) {
        if ((asd_dev->shr_para.chip_type == UBDRV_DEV_DEVICE_A5_1DIE)
            || (asd_dev->shr_para.chip_type == UBDRV_DEV_DEVICE_A5_2DIE)) {
            return HISI_CLOUD_V4;
        } else {
            ubdrv_err("Get chip_type failed. (dev_id=%u; chip_type=%u)\n", dev_id, asd_dev->shr_para.chip_type);
            return HISI_CLOUD_V4;
        }
    }
    return HISI_CLOUD_V4;
#endif
}

int ubdrv_add_davinci_dev(u32 dev_id, u32 dev_type)
{
    struct ascend_ub_msg_dev *msg_dev;
    struct uda_dev_type uda_type = {0};
    struct uda_dev_para uda_para = {0};
    u32 udevid = 0;
    int ret;

    msg_dev = ubdrv_get_msg_dev_by_devid(dev_id);
    if (msg_dev == NULL) {
        ubdrv_err("Get msg_dev failed. (dev_id=%u)\n", dev_id);
        return -EINVAL;
    }

    uda_dev_type_pack_proc(&uda_type, dev_type);
    ret = ub_check_pack_master_id_to_uda(msg_dev, &uda_para, dev_id);
    if (ret != 0){
        return -EINVAL;
    }
    uda_para.udevid = msg_dev->dev_id;  // host 0 1 2, device 0
    uda_para.remote_udevid = msg_dev->remote_id;  // host 0, device 0 1 2
    uda_para.chip_type = ubdrv_get_chip_type(dev_id);
    uda_para.dev = &msg_dev->ubc_dev->dev;
    uda_para.pf_flag = (dev_type == UDA_VIRTUAL) ? 0 : 1;
    ret = uda_add_dev(&uda_type, &uda_para, &udevid);
    if (ret != 0) {
        ubdrv_err("Add davinci_dev fail. (dev_id=%u; ret=%d)\n", dev_id, ret);
        return -EINVAL;
    }
    ubdrv_set_device_status(dev_id, UBDRV_DEVICE_ONLINE);
    ubdrv_info("Add davinci_dev success. (dev_id=%u; udevid=%u)\n", dev_id, udevid);
    return 0;
}

void ubdrv_remove_davinci_dev(u32 dev_id, u32 dev_type)
{
    struct ascend_ub_msg_dev *msg_dev;
    struct uda_dev_type uda_type = {0};
    int ret;

    msg_dev = ubdrv_get_msg_dev_by_devid(dev_id);
    if (msg_dev == NULL) {
        ubdrv_err("Get msg_dev failed. (dev_id=%u)\n", dev_id);
        return;
    }
    if (ubdrv_get_device_status(dev_id) != UBDRV_DEVICE_DEAD) {
        ubdrv_set_device_status(dev_id, UBDRV_DEVICE_BEGIN_OFFLINE);
    }
    uda_dev_type_pack_proc(&uda_type, dev_type);

    ret = uda_remove_dev(&uda_type, msg_dev->dev_id);
    if (ret != 0) {
        ubdrv_err("Remove davinci_dev fail. (dev_id=%u; ret=%d)\n", msg_dev->dev_id, ret);
        return;
    }
    ubdrv_info("Remove davinci_dev success. (dev_id=%u)\n", msg_dev->dev_id);
    return;
}

STATIC void ubdrv_chan_ctrl_init(struct ascend_ub_msg_dev *msg_dev)
{
    ubdrv_non_trans_msg_chan_init(msg_dev);
    ubdrv_rao_msg_chan_init(msg_dev);
    ubdrv_urma_chan_init(msg_dev);
    return;
}

STATIC void ubdrv_chan_ctrl_uninit(struct ascend_ub_msg_dev *msg_dev)
{
    ubdrv_urma_chan_uninit(msg_dev);
    ubdrv_rao_msg_chan_uninit(msg_dev);
    ubdrv_non_trans_msg_chan_uninit(msg_dev);
    return;
}

int ubdrv_add_msg_device(u32 dev_id, u32 remote_id, u32 idev_id, u32 ue_idx,
    struct jetty_exchange_data *data)
{
    struct ascend_ub_msg_dev *msg_dev = NULL;
    struct ub_idev *idev = ubdrv_get_idev(idev_id, ue_idx);
    int ret;

    if ((dev_id >= ASCEND_UB_DEV_MAX_NUM) || (idev == NULL) || (data == NULL)) {
        ubdrv_err("ubdrv_add_msg_device failed. (dev_id=%u;idev_id=%u;fe_id=%u)\n",
            dev_id, idev_id, ue_idx);
        return -EINVAL;
    }
    ubdrv_info("Start add msg device. (dev_id=%u;idev_id=%u;fe_id=%u)\n", dev_id, idev_id, ue_idx);
    msg_dev = ubdrv_msg_dev_init(dev_id, remote_id, idev);
    if (msg_dev == NULL) {
        ubdrv_err("Msg_dev init failed. (dev_id=%u)\n", dev_id);
        return -ENOMEM;
    }
    ka_task_down_write(&g_ub_ctrl->asd_dev[dev_id].rw_sem);
    g_ub_ctrl->asd_dev[dev_id].msg_dev = msg_dev;
    ka_task_up_write(&g_ub_ctrl->asd_dev[dev_id].rw_sem);
    ret = ubdrv_admin_msg_chan_init(dev_id, msg_dev, data, idev);
    if (ret != 0) {
        ubdrv_err("ubdrv_create_admin_msg_chan failed. (ret=%d;dev_id=%u)\n",
            ret, dev_id);
        goto msg_chan_uninit;
    }
    ubdrv_chan_ctrl_init(msg_dev);

    ret = ubdrv_add_msg_device_proc(msg_dev, dev_id);
    if (ret != 0){
        goto chan_ctrl_uninit;
    }
    ubdrv_set_startup_flag(dev_id, UBDRV_DEV_STARTUP_BOTTOM_HALF_OK);
    ubdrv_set_device_status(dev_id, UBDRV_DEVICE_BEGIN_ONLINE);
    ubdrv_info("Add msg device success. (dev_id=%u)\n", dev_id);
    g_add_davinci_flag = 1;
    return 0;

chan_ctrl_uninit:
    ubdrv_chan_ctrl_uninit(msg_dev);
    ubdrv_admin_msg_chan_uninit(dev_id, msg_dev);
msg_chan_uninit:
    g_ub_ctrl->asd_dev[dev_id].msg_dev = NULL;
    ubdrv_msg_dev_uninit(msg_dev, UBDRV_DEVICE_UNINIT);
    return ret;
}

/* Release all channel resources */
STATIC void ubdrv_exit_release_msg_chan(u32 dev_id)
{
    /* Host common free need set global chan is null, device is by unint call back set null */
    ubdrv_exit_release_msg_chan_proc(dev_id);
    ubdrv_free_all_non_trans_chan(dev_id);
    ubdrv_free_all_rao_chan(dev_id);
}

void ubdrv_del_msg_device(u32 dev_id, enum ubdrv_dev_status final_state)
{
    struct ascend_dev *asd_dev;
    struct ascend_ub_msg_dev *msg_dev;

    if (dev_id >= ASCEND_UB_DEV_MAX_NUM) {
        ubdrv_err("ubdrv_del_msg_device failed. (dev_id=%u)\n", dev_id);
        return;
    }
    ubdrv_info("Start del msg device. (dev_id=%u)\n", dev_id);
    ubdrv_set_startup_flag(dev_id, UBDRV_DEV_STARTUP_UNPROBED);
    asd_dev = &(g_ub_ctrl->asd_dev[dev_id]);
    ka_task_down_write(&asd_dev->rw_sem);
    msg_dev = asd_dev->msg_dev;
    if (msg_dev == NULL) {
        ka_task_up_write(&asd_dev->rw_sem);
        ubdrv_err("Msg_dev is null. (dev_id=%u)\n", dev_id);
        return;
    }
    ubdrv_exit_release_msg_chan(dev_id);
    if (ubdrv_get_device_status(dev_id) != UBDRV_DEVICE_DEAD) {
        ubdrv_set_device_status(dev_id, UBDRV_DEVICE_OFFLINE);
    }
    asd_dev->msg_dev = NULL;
    ubdrv_chan_ctrl_uninit(msg_dev);
    ubdrv_admin_msg_chan_uninit(dev_id, msg_dev);
    ubdrv_delete_admin_jetty(dev_id);
    ubdrv_msg_dev_uninit(msg_dev, final_state);
    ka_task_up_write(&asd_dev->rw_sem);
    ubdrv_info("Del msg device success. (dev_id=%u)\n", dev_id);
    return;
}

STATIC void ubdrv_asd_dev_init(void)
{
    struct ascend_ub_dev_status *dev_status;
    struct ascend_dev *asd_dev;
    u32 i;

    for (i = 0; i < ASCEND_UB_DEV_MAX_NUM; i++) {
        asd_dev = &(g_ub_ctrl->asd_dev[i]);
        asd_dev->dev_id = i;
        asd_dev->token.token_valid = ASCEND_INVALID;
        asd_dev->phy_flag = true;
        dev_status = &(g_ub_ctrl->dev_status[i]);
        dev_status->device_status = UBDRV_DEVICE_UNINIT;
        ka_base_atomic_set(&dev_status->ref_cnt, 0);
        ka_task_init_rwsem(&dev_status->rw_sem);
        ka_task_init_rwsem(&asd_dev->rw_sem);
    }
}

STATIC void ubdrv_asd_dev_uninit(void)
{
    struct ascend_ub_dev_status *dev_status;
    struct ascend_dev *asd_dev;
    int i;

    for (i = 0; i < ASCEND_UB_DEV_MAX_NUM; i++) {
        asd_dev = &(g_ub_ctrl->asd_dev[i]);
        dev_status = &(g_ub_ctrl->dev_status[i]);
        ka_base_atomic_set(&dev_status->ref_cnt, 0);
        dev_status->device_status = UBDRV_DEVICE_UNINIT;
        asd_dev->token.token_valid = ASCEND_INVALID;
        asd_dev->dev_id = 0;
        asd_dev->phy_flag = true;
    }
}

void ubdrv_print_exchange_data(struct ubdrv_jetty_exchange_data *data)
{
    ubdrv_info("(eid%d: "EID_FMT").\n",
            data->admin_jetty_info.eid.eid_index, EID_ARGS(data->admin_jetty_info.eid.eid));
    ubdrv_info("ue_idx = %u; jetty_id = %u\n", data->ue_idx, data->admin_jetty_info.id);
}

struct ub_idev* ubdrv_find_idev_by_udevid(u32 dev_id)
{
    struct devdrv_ub_dev_info eid_query_info = {0};
    struct ub_idev *idev = NULL;
    void *udma_dev = NULL;
    int num = 0;
    int ret = 0;

    ret = ubdrv_get_ub_dev_info(dev_id, &eid_query_info, &num);
    if (ret != 0) {
        return NULL;
    }

    udma_dev = (struct ubcore_device*)eid_query_info.udma_dev[0];
    if (udma_dev == NULL) {
        ubdrv_err("Check ubc dev is null. (dev_id=%u)\n", dev_id);
        return NULL;
    }
    idev = ubdrv_get_idev_by_ubc_dev((struct ubcore_device*)udma_dev);
    if (idev == NULL) {
        ubdrv_err("Find idev fail. (dev_id=%u)\n", dev_id);
        return NULL;
    }
    return idev;
}

int ubdrv_mia_dev_notifier_func(u32 udevid, enum uda_notified_action action)
{
    int ret = 0;

    if (action == UDA_INIT) {
        ret = ubdrv_fe_init_instance(udevid);
    } else if (action == UDA_UNINIT) {
        ret = ubdrv_fe_uninit_instance(udevid);
    }
    ubdrv_info("notifier action. (udevid=%u; action=%d; ret=%d)\n", udevid, action, ret);
    return ret;
}

void ubdrv_unregister_uda_notifier(void)
{
    struct uda_dev_type type;

    uda_davinci_local_real_entity_type_pack(&type);
    (void)uda_notifier_unregister(ASCEND_UB_REAL_NOTIFIER, &type);
    ubdrv_unregister_uda_notifier_proc(&type);
    (void)uda_notifier_unregister(ASCEND_UB_MIA_NOTIFIER, &type);
}

int ubdrv_init_ub_ctrl(void)
{
    u32 i, j, k;
    size_t len = sizeof(struct ascend_ub_ctrl);

    g_ub_ctrl = ubdrv_kzalloc(len, KA_GFP_KERNEL);
    if (g_ub_ctrl == NULL) {
        ubdrv_err("Failed to ubdrv_kzalloc ub_ctrl. (len=%zu)\n", len);
        return -ENOMEM;
    }
    g_ub_ctrl->idev_num = 0;
    for (i = 0; i < ASCEND_UDMA_DEV_MAX_NUM; i++) {
        for (j = 0; j < ASCEND_UDMA_MAX_FE_NUM; j++) {
            ka_task_init_rwsem(&g_ub_ctrl->idev[i][j].rw_sem);
            ka_base_atomic_set(&g_ub_ctrl->idev[i][j].ref_cnt, 0);
            g_ub_ctrl->idev[i][j].idev_id = i;
            g_ub_ctrl->idev[i][j].ue_idx = j;
            g_ub_ctrl->idev[i][j].valid = ASCEND_UB_INVALID;
            for (k = 0; k < ASCEND_UB_DEV_NUM_PER_FE; k++) {
                g_ub_ctrl->idev[i][j].dev_id[k] = KA_U32_MAX;
            }
        }
    }
    ka_task_mutex_init(&g_ub_ctrl->mutex_lock);
    ubdrv_asd_dev_init();
    return 0;
}

void ubdrv_uninit_ub_ctrl(void)
{
    ubdrv_asd_dev_uninit();
    ka_task_mutex_destroy(&g_ub_ctrl->mutex_lock);
    ubdrv_kfree(g_ub_ctrl);
    g_ub_ctrl = NULL;
    return;
}

STATIC int __ka_init ubdrv_module_init(void)
{
    struct devdrv_comm_ops *comm_ops = NULL;
    int ret;

    if (ubdrv_get_ub_pcie_sel() != UBDRV_UB_SEL) {
        return ubdrv_module_init_for_pcie();
    }

    comm_ops = get_global_ubdrv_ops();
    ret = devdrv_register_communication_ops(comm_ops);
    if (ret != 0) {
        ubdrv_err("Register ub ops failed. (ret=%d)\n", ret);
        return ret;
    }
    devdrv_set_communication_ops_status_proc(DEVDRV_COMMNS_UB, DEVDRV_COMM_OPS_TYPE_ENABLE, 0);
    if (ubdrv_init_ub_ctrl() != 0) {
        goto unregister_ops;
    }

    ubdrv_init_common_msg_ctrl_rwsem();
    ret = ubdrv_register_uda_notifier();
    if (ret != 0) {
        ubdrv_err("Register uda notifier failed. (ret=%d)\n", ret);
        goto unregister_uda_fail;
    }
#ifndef EMU_ST
    (void)module_feature_auto_init();
#endif
    ubdrv_link_init_work();
    ret = ubcore_register_client(&g_ascend_client);
    if (ret != 0) {
        ubdrv_err("Register urma client failed. (ret=%d)\n", ret);
        goto register_ub_client_fail;
    }
    ret = ubdrv_rao_client_ctrl_init();
    if (ret != 0) {
        ubdrv_err("RAO client ctrl init failed. (ret=%d)\n", ret);
        goto rao_ctrl_init_fail;
    }
    if (ubdrv_module_init_proc() != 0) {
        goto module_init_fail;
    }
    return 0;

module_init_fail:
    ubdrv_rao_client_ctrl_uninit();
rao_ctrl_init_fail:
    ubcore_unregister_client(&g_ascend_client);
register_ub_client_fail:
    ubdrv_link_uninit_work();
#ifndef EMU_ST
    module_feature_auto_uninit();
#endif
    ubdrv_unregister_uda_notifier();
unregister_uda_fail:
    ubdrv_uninit_ub_ctrl();
unregister_ops:
    devdrv_set_communication_ops_status_proc(DEVDRV_COMMNS_UB, DEVDRV_COMM_OPS_TYPE_DISABLE, 0);
    devdrv_unregister_communication_ops(comm_ops);
    return -EINVAL;
}

STATIC void __ka_exit ubdrv_module_exit(void)
{
    struct devdrv_comm_ops *comm_ops = NULL;
    u32 i;

    if (ubdrv_get_ub_pcie_sel() != UBDRV_UB_SEL) {
        ubdrv_module_uninit_for_pcie();
        return;
    }
    for (i = 0; i < ASCEND_UB_DEV_MAX_NUM; i++) {
        ubdrv_remove_device_resource(i, UBDRV_DEVICE_UNINIT);
    }

    ubdrv_module_exit_proc();
    ubdrv_rao_client_ctrl_uninit();
    ubcore_unregister_client(&g_ascend_client);
    ubdrv_link_uninit_work();
    ubdrv_put_all_pair_dev_info();
#ifndef EMU_ST
    module_feature_auto_uninit();
#endif
    ubdrv_unregister_uda_notifier();
    ubdrv_uninit_ub_ctrl();
    devdrv_set_communication_ops_status_proc(DEVDRV_COMMNS_UB, DEVDRV_COMM_OPS_TYPE_DISABLE, 0);
    comm_ops = get_global_ubdrv_ops();
    devdrv_unregister_communication_ops(comm_ops);
}

ka_module_init(ubdrv_module_init);
ka_module_exit(ubdrv_module_exit);
KA_MODULE_LICENSE("GPL");