* 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 "devdrv_ctrl.h"
#include "devdrv_dma.h"
#include "devdrv_msg.h"
#include "devdrv_common_msg.h"
#include "devdrv_msg_def.h"
#include "devdrv_util.h"
int (*global_common_fun[DEVDRV_COMMON_MSG_TYPE_MAX])(u32 devid, void *data, u32 in_data_len, u32 out_data_len,
u32 *real_out_len);
ka_mutex_t g_common_mutex[DEVDRV_COMMON_MSG_TYPE_MAX];
ka_mutex_t *devdrv_get_common_msg_mutex(void)
{
return g_common_mutex;
}
STATIC int rx_msg_common_msg_process(void *msg_chan, void *data, u32 in_data_len, u32 out_data_len,
u32 *real_out_len)
{
struct devdrv_non_trans_msg_desc *msg_desc = NULL;
struct devdrv_msg_chan *chan = devdrv_find_msg_chan(msg_chan);
int ret;
u32 cost_time;
if (chan == NULL) {
if (devdrv_is_dev_hot_reset() != true) {
devdrv_err("Input parameter msg_chan is null.\n");
}
return -EINVAL;
}
if (data == NULL) {
devdrv_err("Input parameter data is null. (dev_id=%u)\n", chan->msg_dev->pci_ctrl->dev_id);
return -EINVAL;
}
if (real_out_len == NULL) {
devdrv_err("Input parameter real_out_len is null. (dev_id=%u)\n", chan->msg_dev->pci_ctrl->dev_id);
return -EINVAL;
}
msg_desc = ka_container_of(data, struct devdrv_non_trans_msg_desc, data);
if (msg_desc->msg_type >= (u32)DEVDRV_COMMON_MSG_TYPE_MAX) {
devdrv_err("Msg type is not support yet. (dev_id=%u;msg_type=%u)\n", chan->msg_dev->pci_ctrl->dev_id,
msg_desc->msg_type);
return -EOPNOTSUPP;
}
ka_task_mutex_lock(&g_common_mutex[msg_desc->msg_type]);
if ((chan->msg_dev->common_msg.common_fun[msg_desc->msg_type] == NULL) &&
(global_common_fun[msg_desc->msg_type] == NULL)) {
ka_task_mutex_unlock(&g_common_mutex[msg_desc->msg_type]);
devdrv_warn("Rx common callback func is null. (dev_id=%u; common_type=%d)\n", chan->msg_dev->pci_ctrl->dev_id,
msg_desc->msg_type);
return -EUNATCH;
}
cost_time = ka_system_jiffies_to_msecs(ka_jiffies - chan->stamp);
if (cost_time > chan->msg_dev->common_msg.com_msg_stat[msg_desc->msg_type].rx_work_max_time) {
chan->msg_dev->common_msg.com_msg_stat[msg_desc->msg_type].rx_work_max_time = cost_time;
}
if (cost_time > DEVDRV_COMMON_WORK_RESQ_TIME) {
chan->msg_dev->common_msg.com_msg_stat[msg_desc->msg_type].rx_work_delay_cnt++;
devdrv_info("(dev_id=%u; msg_type=%d; cost_time=%ums)\n",
chan->msg_dev->pci_ctrl->dev_id, msg_desc->msg_type, cost_time);
}
chan->msg_dev->common_msg.com_msg_stat[msg_desc->msg_type].rx_total_cnt++;
if (chan->msg_dev->common_msg.common_fun[msg_desc->msg_type] != NULL) {
ret = chan->msg_dev->common_msg.common_fun[msg_desc->msg_type](devdrv_get_devid_by_dev(chan->msg_dev), data,
in_data_len, out_data_len, real_out_len);
} else {
ret = global_common_fun[msg_desc->msg_type](devdrv_get_devid_by_dev(chan->msg_dev), data, in_data_len,
out_data_len, real_out_len);
}
ka_task_mutex_unlock(&g_common_mutex[msg_desc->msg_type]);
if (ret == 0) {
chan->msg_dev->common_msg.com_msg_stat[msg_desc->msg_type].rx_success_cnt++;
}
return ret;
}
int devdrv_pci_common_msg_send(u32 index_id, void *data, u32 in_data_len, u32 out_data_len, u32 *real_out_len,
enum devdrv_common_msg_type msg_type)
{
struct devdrv_msg_chan *msg_chan = NULL;
struct devdrv_common_msg_stat *com_msg_stat = NULL;
struct devdrv_pci_ctrl *pci_ctrl = NULL;
int msg_type_tmp;
int ret;
msg_type_tmp = (int)msg_type;
if (index_id >= MAX_DEV_CNT) {
devdrv_err("Invalid index_id. (index_id=%u)\n", index_id);
return -EINVAL;
}
if (data == NULL) {
devdrv_err("Input parameter is invalid. (index_id=%u)\n", index_id);
return -EINVAL;
}
if (real_out_len == NULL) {
devdrv_err("Input parameter is invalid. (index_id=%u)\n", index_id);
return -EINVAL;
}
if ((msg_type_tmp < DEVDRV_COMMON_MSG_PCIVNIC) || (msg_type_tmp >= DEVDRV_COMMON_MSG_TYPE_MAX)) {
devdrv_err("Msg type is not support yet. (index_id=%u;msg_type=%d)\n", index_id, msg_type_tmp);
return -EOPNOTSUPP;
}
pci_ctrl = devdrv_pci_ctrl_get(index_id);
if (pci_ctrl == NULL) {
if (devdrv_is_dev_hot_reset() == true) {
devdrv_warn_limit("Get pci_ctrl unsuccessful. (index_id=%u)\n", index_id);
} else {
devdrv_err_limit("Get pci_ctrl failed. (index_id=%u)\n", index_id);
}
return -EINVAL;
}
if (pci_ctrl->msg_dev == NULL) {
devdrv_pci_ctrl_put(pci_ctrl);
devdrv_err("pcie msg dev is invalid. (index_id=%u)\n", index_id);
return -EINVAL;
}
msg_chan = pci_ctrl->msg_dev->common_msg.msg_chan;
if ((msg_chan == NULL) || (msg_chan->status == DEVDRV_DISABLE)) {
devdrv_pci_ctrl_put(pci_ctrl);
devdrv_err("msg chan is invalid. (index_id=%u)\n", index_id);
return -EINVAL;
}
com_msg_stat = &(msg_chan->msg_dev->common_msg.com_msg_stat[msg_type]);
com_msg_stat->tx_total_cnt++;
ret = devdrv_sync_non_trans_msg_send(msg_chan, data, in_data_len, out_data_len, real_out_len, msg_type);
if (ret == 0) {
com_msg_stat->tx_success_cnt++;
} else if (ret == -EINVAL) {
com_msg_stat->tx_einval_err++;
} else if (ret == -ENODEV) {
com_msg_stat->tx_enodev_err++;
} else if (ret == -ENOSYS) {
com_msg_stat->tx_enosys_err++;
} else if (ret == -ETIMEDOUT) {
com_msg_stat->tx_etimedout_err++;
} else {
com_msg_stat->tx_default_err++;
}
devdrv_pci_ctrl_put(pci_ctrl);
return ret;
}
int devdrv_pci_register_common_msg_client(const struct devdrv_common_msg_client *msg_client)
{
struct devdrv_ctrl *ctrl = NULL;
struct devdrv_pci_ctrl *pci_ctrl = NULL;
u32 i;
if (msg_client == NULL) {
devdrv_err("Input parameter is invalid.\n");
return -EINVAL;
}
if (msg_client->type >= DEVDRV_COMMON_MSG_TYPE_MAX) {
devdrv_err("Msg client type is not support yet. (msg_client_type=%d)\n", (int)msg_client->type);
return -EOPNOTSUPP;
}
ka_task_mutex_lock(&g_common_mutex[msg_client->type]);
global_common_fun[msg_client->type] = msg_client->common_msg_recv;
ka_task_mutex_unlock(&g_common_mutex[msg_client->type]);
for (i = 0; i < MAX_DEV_CNT; i++) {
ctrl = devdrv_get_devctrl_by_id(i);
if (ctrl == NULL) {
continue;
}
if (ctrl->startup_flg != DEVDRV_DEV_STARTUP_BOTTOM_HALF_OK) {
continue;
}
pci_ctrl = (struct devdrv_pci_ctrl *)ctrl->priv;
if (pci_ctrl == NULL) {
continue;
}
if (pci_ctrl->msg_dev == NULL) {
devdrv_info("msg_dev is NULL.\n");
continue;
}
ka_task_mutex_lock(&g_common_mutex[msg_client->type]);
pci_ctrl->msg_dev->common_msg.common_fun[msg_client->type] = msg_client->common_msg_recv;
ka_task_mutex_unlock(&g_common_mutex[msg_client->type]);
if (msg_client->init_notify != NULL) {
msg_client->init_notify(pci_ctrl->dev_id, 0);
}
}
return 0;
}
int devdrv_pci_unregister_common_msg_client(u32 index_id, const struct devdrv_common_msg_client *msg_client)
{
struct devdrv_pci_ctrl *pci_ctrl = NULL;
struct devdrv_ctrl *ctrl = NULL;
if (index_id >= MAX_DEV_CNT) {
devdrv_err("Input parameter is invalid. (index_id=%u)\n", index_id);
return -EINVAL;
}
if (msg_client == NULL) {
devdrv_err("Input parameter is invalid. (index_id=%u)\n", index_id);
return -EINVAL;
}
if (msg_client->type >= DEVDRV_COMMON_MSG_TYPE_MAX) {
devdrv_err("Msg client type is not support yet. (index_id=%u;msg_client_type=%d)\n", index_id,
(int)msg_client->type);
return -EOPNOTSUPP;
}
ctrl = devdrv_get_bottom_half_devctrl_by_id(index_id);
if (ctrl == NULL) {
devdrv_info("Device is offline. (index_id=%u; msg_client_type=%d)\n", index_id, (int)msg_client->type);
return -EINVAL;
}
pci_ctrl = ctrl->priv;
ka_task_mutex_lock(&g_common_mutex[msg_client->type]);
pci_ctrl->msg_dev->common_msg.common_fun[msg_client->type] = NULL;
global_common_fun[msg_client->type] = NULL;
ka_task_mutex_unlock(&g_common_mutex[msg_client->type]);
devdrv_debug("Unregister common msg_client success. (index_id=%u; msg_client_type=%d)\n", index_id,
msg_client->type);
return 0;
}
STATIC struct devdrv_non_trans_msg_chan_info g_common_msg_chan_info = {
.msg_type = devdrv_msg_client_common,
.flag = DEVDRV_MSG_SYNC,
.level = DEVDRV_MSG_CHAN_LEVEL_LOW,
.s_desc_size = DEVDRV_NON_TRANS_MSG_DEFAULT_DESC_SIZE,
.c_desc_size = DEVDRV_NON_TRANS_MSG_DEFAULT_DESC_SIZE,
.rx_msg_process = rx_msg_common_msg_process,
};
int devdrv_alloc_common_msg_queue(struct devdrv_pci_ctrl *pci_ctrl)
{
void *msg_chan = NULL;
msg_chan = devdrv_pcimsg_alloc_non_trans_queue_inner(pci_ctrl->dev_id, &g_common_msg_chan_info);
if (msg_chan == NULL) {
devdrv_err("Alloc common msg_queue failed, msg_chan is null. (dev_id=%u)\n", pci_ctrl->dev_id);
return -EINVAL;
}
pci_ctrl->msg_dev->common_msg.msg_chan = (struct devdrv_msg_chan *)msg_chan;
return 0;
}
void devdrv_free_common_msg_queue(struct devdrv_pci_ctrl *pci_ctrl)
{
int ret;
if ((pci_ctrl->msg_dev == NULL) || (pci_ctrl->msg_dev->common_msg.msg_chan == NULL)) {
devdrv_info("Input parameter is invalid. (dev_id=%u)\n", pci_ctrl->dev_id);
return;
}
ret = devdrv_pcimsg_free_non_trans_queue_inner((void *)(pci_ctrl->msg_dev->common_msg.msg_chan));
if (ret != 0) {
devdrv_info("No need to free common msg_queue. (dev_id=%u; ret=%d)\n", pci_ctrl->dev_id, ret);
}
devdrv_info("Free common msg_queue success. (dev_id=%u)\n", pci_ctrl->dev_id);
pci_ctrl->msg_dev->common_msg.msg_chan = NULL;
return;
}