/*

 * 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.

 */

#ifndef QUEUE_UT

#include <securec.h>

#include "queue_module.h"

#include "queue_channel.h"

#include "queue_fops.h"

#include "queue_msg.h"

#include "hdc_kernel_interface.h"

#include "queue_context.h"

#include "pbl/pbl_uda.h"



STATIC ka_device_t *queue_devices[MAX_DEVICE] = {NULL};

STATIC ka_rw_semaphore_t queue_dev_sem[MAX_DEVICE];



ka_device_t *queue_get_device(u32 dev_id)

{

    ka_task_down_read(&queue_dev_sem[dev_id]);

    if (queue_devices[dev_id] == NULL) {

        ka_task_up_read(&queue_dev_sem[dev_id]);

        return NULL;

    }

    return queue_devices[dev_id];

}



void queue_put_device(u32 dev_id)

{

    if (queue_devices[dev_id] != NULL) {

        ka_task_up_read(&queue_dev_sem[dev_id]);

    }

}



STATIC void queue_init_instance(u32 devid, ka_device_t *dev)

{

    ka_task_down_write(&queue_dev_sem[devid]);

    queue_devices[devid] = dev;

    ka_task_up_write(&queue_dev_sem[devid]);



    queue_info("notifier init action success. (devid=%u).\n", devid);

    return;

}



STATIC void queue_uninit_instance(u32 devid)

{

    ka_task_down_write(&queue_dev_sem[devid]);

    queue_devices[devid] = NULL;

    ka_task_up_write(&queue_dev_sem[devid]);



    queue_info("notifier uninit action success. (devid=%u).\n", devid);

    return;

}



#define QUEUE_HOST_NOTIFIER "queue_host"

static int queue_host_notifier_func(u32 udevid, enum uda_notified_action action)

{

    if (udevid >= MAX_DEVICE) {

        queue_err("Invalid para. (udevid=%u)\n", udevid);

        return -EINVAL;

    }



    if (action == UDA_INIT) {

        queue_init_instance(udevid, uda_get_device(udevid));

    } else if (action == UDA_UNINIT) {

        queue_uninit_instance(udevid);

    }



    return 0;

}



int queue_drv_msg_chan_init(void)

{

    struct uda_dev_type type;

    u32 devid;



    for (devid = 0; devid < MAX_DEVICE; devid++) {

        ka_task_init_rwsem(&queue_dev_sem[devid]);

    }

    uda_davinci_near_real_entity_type_pack(&type);

    return uda_notifier_register(QUEUE_HOST_NOTIFIER, &type, UDA_PRI2, queue_host_notifier_func);

}



void queue_drv_msg_chan_uninit(void)

{

    struct uda_dev_type type;

    uda_davinci_near_real_entity_type_pack(&type);

    (void)uda_notifier_unregister(QUEUE_HOST_NOTIFIER, &type);

}



STATIC int queue_data_in(int devid, int vfid, int local_pid, struct hdcdrv_data_info data)

{

    struct queue_reply_complete_msg *reply_msg = (struct queue_reply_complete_msg *)(uintptr_t)data.src_addr;

    struct queue_qid_status *qid_status = NULL;

    struct queue_context *ctx = NULL;

    int ret;



    if (reply_msg == NULL) {

        queue_err("head is NULL, invalid src_addr.\n");

        return HDCDRV_RX_FINISH;

    }



    if (data.len != sizeof(struct queue_reply_complete_msg)) {

        queue_err("invalid in_data_len(%u).\n", data.len);

        return HDCDRV_RX_FINISH;

    }



    ctx = queue_context_get(reply_msg->dma_info.hostpid);

    if (ctx == NULL) {

        queue_err("Pid is not exit. (pid=%d)\n", reply_msg->dma_info.hostpid);

        return HDCDRV_RX_FINISH;

    }



    qid_status = queue_get_qid_status(ctx, reply_msg->qid);

    if (qid_status != NULL) {

        queue_set_qid_status_timestamp(qid_status, HOST_HDC_RECV);

        queue_set_qid_status_dma_node_num(qid_status, reply_msg->dma_node_num);

        (void)memcpy_s(&qid_status->time_record[DEV_QUEUE_STATUS_RECORE_START], REPLY_MSG_TIME_RECORD_SIZE,

            reply_msg->time_record, REPLY_MSG_TIME_RECORD_SIZE);

    }



    ret = queue_wakeup_enqueue(ctx, reply_msg->dma_info.que_chan_addr);

    queue_set_qid_status_timestamp(qid_status, HOST_WAKE_UP);



    queue_context_put(ctx);

    if (ret != 0) {

        queue_err("queue_free_dma_node failed. (ret=%d, serial_num=%llu; que_chan_addr=0x%pK).\n",

            ret, reply_msg->dma_info.serial_num, (void *)(uintptr_t)reply_msg->dma_info.que_chan_addr);

    }



    return HDCDRV_RX_FINISH;

}



struct hdcdrv_session_notify g_queue_notify = {

    .data_in_notify = queue_data_in,

};



void queue_register_hdc_cb_func(void)

{

    queue_info("queue_register_hdc_cb_func succ.\n");

    hdcdrv_session_notify_register(QUEUE_HDC_SERVICE_TYPE, &g_queue_notify);

}



void queue_unregister_hdc_cb_func(void)

{

    queue_info("queue_unregister_hdc_cb_func succ.\n");

    hdcdrv_session_notify_unregister(QUEUE_HDC_SERVICE_TYPE);

}



#else

void queue_drv_msg_chan_uninit(void)

{

    return;

}

#endif