* Copyright (c) Huawei Technologies Co., Ltd. 2025-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.
*/
#ifdef CFG_SOC_PLATFORM_CLOUD
#include "trs_chan.h"
#endif
#include "pbl_uda.h"
#include "devdrv_functional_cqsq.h"
#include "debug_dma.h"
#include "securec.h"
#include "ascend_kernel_hal.h"
#include "ka_task_pub.h"
#include "ka_ioctl_pub.h"
#include "ka_base_pub.h"
#include "ka_driver_pub.h"
#include "ka_kernel_def_pub.h"
#include "ka_pci_pub.h"
#include "ka_dfx_pub.h"
#include "ka_common_pub.h"
#include "ka_fs_pub.h"
#include "ka_errno_pub.h"
#define DEBUG_SQCQ_DEPTH 4
#define DEBUG_SQ_BUF_LEN 64
#define DEBUG_CQ_BUF_LEN 64
#define DEBUG_NOTIFIER "debug"
#define DEBUG_DEFAULT_PID (-1)
#define DEBUG_TIMEOUT 10000
#define DEBUG_ENABLE 1
#define DEBUG_DISABLE 0
#define REQ_ENABLE_ID 0
#define REQ_DISABLE_ID 1
#define DEVICE_NUM_MAX 16
#define PARTITION_CQ_DST_OFFSET 8
#define CQ_QUEUE_DEPTH 10
#define HZ_TO_MS 1000
#define ERR_DEV_ID (-1)
#define ERR_QUEUE_FULL (-2)
#define ERR_WAIT_TIMEOUT (-3)
#define ERR_UNRECOGNIZED_CMD (-4)
#define ERR_INVALID_PARAM (-5)
#define ERR_SWITCH_CLOSE (-6)
#define TD_PRINT_ERR(fmt, ...) ka_dfx_printk(KERN_ERR "[ts_debug][ERROR]<%s:%d> " \
fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define TD_PRINT_INFO(fmt, ...) ka_dfx_printk(KERN_INFO "[ts_debug][INFO]<%s:%d> " \
fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
DECLARE_WAIT_QUEUE_HEAD(g_wq);
static u32 g_tsid = 0;
static int isUdaRegister = 0;
struct debug_cq_report {
u8 phase;
u8 reserved[3];
u16 sq_index;
u16 sq_head;
u32 cmd_type;
u32 return_val;
u32 msg_id;
u8 payload[44];
};
#ifndef CFG_SOC_PLATFORM_CLOUD
struct debug_cq_report_310P {
u32 cmd_type;
u32 return_val;
u32 msg_id;
u8 payload[44];
};
#endif
static struct debug_cq_report g_cq_queue[CQ_QUEUE_DEPTH];
static struct devdrv_func_sqcq_alloc_para_in __attribute__((unused)) para_in[DEVICE_NUM_MAX];
static struct devdrv_func_sqcq_alloc_para_out __attribute__((unused)) para_out[DEVICE_NUM_MAX];
static struct devdrv_func_sqcq_free_para __attribute__((unused)) para[DEVICE_NUM_MAX];
struct sqcq_chn_info {
int enable_debug;
int chn_id;
int cq_head;
int cq_tail;
int pid;
int debugger_pid;
int isChannelCreate;
ka_mutex_t mtx;
struct debug_cq_report cq_queue[CQ_QUEUE_DEPTH];
};
static struct sqcq_chn_info g_sqcq_chn_info_list[DEVICE_NUM_MAX];
static void cq_queue_info_init(void)
{
int i;
for (i = 0; i < DEVICE_NUM_MAX; ++i) {
struct sqcq_chn_info *info = &g_sqcq_chn_info_list[i];
info->enable_debug = 0;
info->cq_head = 0;
info->cq_tail = 0;
info->pid = DEBUG_DEFAULT_PID;
info->debugger_pid = DEBUG_DEFAULT_PID;
info->isChannelCreate = 0;
ka_task_mutex_init(&info->mtx);
}
isUdaRegister = 0;
}
struct ms_debug_info {
u32 dev_id;
int timeout;
u32 data_len;
int pid;
u8 data[64];
};
#define TS_DEBUG_MAGIC_WORD 'D'
#define CMD_SQ_SEND _KA_IOR(TS_DEBUG_MAGIC_WORD, 0, struct ms_debug_info*)
#define CMD_CQ_RECV _KA_IOWR(TS_DEBUG_MAGIC_WORD, 1, struct ms_debug_info*)
#define CMD_GM_COPY _KA_IOR(TS_DEBUG_MAGIC_WORD, 2, struct ms_debug_info*)
#define CMD_DEV_REGISTER _KA_IOR(TS_DEBUG_MAGIC_WORD, 3, struct ms_debug_info*)
#define CMD_QUIT _KA_IOR(TS_DEBUG_MAGIC_WORD, 4, struct ms_debug_info *)
#define SQ_SEND_INFO_PAYLOAD_SIZE 48
struct debug_send_info {
uint32_t req_id;
uint8_t is_return;
uint8_t reserved[3];
uint32_t msg_id;
uint32_t data_len;
uint8_t params[SQ_SEND_INFO_PAYLOAD_SIZE];
};
enum debug_error_code {
DEVICE_ALREADY_OCCUPIED = 0x10000,
DEVICE_ID_INVALID,
};
static bool debug_cqe_is_valid(void *cqe, u32 round)
{
struct debug_cq_report *report;
if (cqe == NULL) {
TD_PRINT_ERR("cqe is NULL\n");
return false;
}
report = (struct debug_cq_report *)cqe;
TD_PRINT_INFO("enter debug_cqe_is_valid phase=%u, round=%u ret=%d\n", report->phase, round,
(report->phase == ((round + 1) & 0x1)));
return (report->phase == ((round + 1) & 0x1));
}
static void debug_get_sq_head_in_cqe(void *cqe, u32 *sq_head)
{
struct debug_cq_report *report;
if ((cqe == NULL) || (sq_head == NULL)) {
TD_PRINT_ERR("cqe or sq_head is NULL\n");
return;
}
report = (struct debug_cq_report *)cqe;
TD_PRINT_INFO("enter debug_get_sq_head_in_cqe sq_head=%u cmd_type=%u return_val=%u\n", report->sq_head,
report->cmd_type, report->return_val);
*sq_head = report->sq_head;
}
static int debug_cq_0_recv(struct trs_id_inst *inst, u32 cqid, void *cqe)
{
struct debug_cq_report *cq_report;
struct debug_cq_report *cq_dst;
struct sqcq_chn_info *chn_info;
errno_t ret_s;
if ((inst == NULL) || (cqe == NULL)) {
TD_PRINT_ERR("inst or cqe is NULL\n");
return ERR_INVALID_PARAM;
}
TD_PRINT_INFO("enter debug_cq_0_recv\n");
if (inst->devid >= DEVICE_NUM_MAX) {
TD_PRINT_ERR("devid is out of range\n");
return ERR_DEV_ID;
}
chn_info = &g_sqcq_chn_info_list[inst->devid];
if (((chn_info->cq_head + 1) % CQ_QUEUE_DEPTH) == chn_info->cq_tail) {
TD_PRINT_ERR("g_cq_queue is full\n");
return ERR_QUEUE_FULL;
}
chn_info->cq_head = (chn_info->cq_head + 1) % CQ_QUEUE_DEPTH;
cq_report = (struct debug_cq_report *)cqe;
cq_dst = &(chn_info->cq_queue[chn_info->cq_head]);
ret_s = memcpy_s(cq_dst, sizeof(struct debug_cq_report), cq_report, sizeof(struct debug_cq_report));
if (ret_s != EOK) {
TD_PRINT_ERR("memcpy_s failed, ret = %d\n", ret_s);
return ret_s;
}
TD_PRINT_INFO("copy to cq_queue cq_dst->cmd_type=%u\n", cq_dst->cmd_type);
ka_task_wake_up_interruptible(&g_wq);
TD_PRINT_INFO("notify devid=%u cq_head=%d cq_tail=%d \n",
inst->devid, chn_info->cq_head, chn_info->cq_tail);
return CQ_RECV_FINISH;
}
#ifndef CFG_SOC_PLATFORM_CLOUD
static int debug_cq_0_recv_for_310P(u32 devid, u32 ts_id, uint8_t *cqe, void *sqe)
{
(void)ts_id;
struct debug_cq_report_310P *cq_report;
struct debug_cq_report *cq_dst;
struct sqcq_chn_info *chn_info;
errno_t ret_s;
if (cqe == NULL) {
TD_PRINT_ERR("cqe is NULL\n");
return ERR_INVALID_PARAM;
}
TD_PRINT_INFO("enter debug_cq_0_recv\n");
if (devid >= DEVICE_NUM_MAX) {
TD_PRINT_ERR("devid %u is out of range\n", devid);
return ERR_DEV_ID;
}
chn_info = &g_sqcq_chn_info_list[devid];
if (((chn_info->cq_head + 1) % CQ_QUEUE_DEPTH) == chn_info->cq_tail) {
TD_PRINT_ERR("g_cq_queue is full\n");
return ERR_QUEUE_FULL;
}
chn_info->cq_head = (chn_info->cq_head + 1) % CQ_QUEUE_DEPTH;
cq_report = (struct debug_cq_report_310P *)cqe;
cq_dst = &(chn_info->cq_queue[chn_info->cq_head]);
ret_s = memcpy_s(((u8 *)cq_dst) + PARTITION_CQ_DST_OFFSET, sizeof(struct debug_cq_report_310P), cq_report,
sizeof(struct debug_cq_report_310P));
if (ret_s != EOK) {
TD_PRINT_ERR("memcpy_s failed, ret = %d\n", ret_s);
return ret_s;
}
TD_PRINT_INFO("copy to cq_queue cq_dst->cmd_type=%u\n", cq_dst->cmd_type);
ka_task_wake_up_interruptible(&g_wq);
TD_PRINT_INFO("notify devid=%u, cq_head=%d cq_tail=%d \n",
devid, chn_info->cq_head, chn_info->cq_tail);
return CQ_RECV_FINISH;
}
#endif
static void set_chan_create_para(struct trs_chan_para *para)
{
para->flag = (0x1 << CHAN_FLAG_ALLOC_CQ_BIT);
para->types.type = CHAN_TYPE_MAINT;
para->types.sub_type = CHAN_SUB_TYPE_MAINT_DBG;
para->cq_para.cq_depth = DEBUG_SQCQ_DEPTH;
para->ops.cqe_is_valid = debug_cqe_is_valid;
para->ops.get_sq_head_in_cqe = debug_get_sq_head_in_cqe;
para->flag |= (0x1 << CHAN_FLAG_ALLOC_SQ_BIT);
para->flag |= (0x1 << CHAN_FLAG_AUTO_UPDATE_SQ_HEAD_BIT);
para->flag |= (0x1 << CHAN_FLAG_NOTICE_TS_BIT);
para->sq_para.sq_depth = DEBUG_SQCQ_DEPTH;
para->sq_para.sqe_size = DEBUG_SQ_BUF_LEN;
para->cq_para.cqe_size = DEBUG_SQ_BUF_LEN;
para->ops.cq_recv = debug_cq_0_recv;
para->ops.abnormal_proc = NULL;
}
static void save_debug_mode_enable_status(u32 phy_devid, u32 req_id)
{
if (phy_devid < DEVICE_NUM_MAX) {
if (req_id == REQ_ENABLE_ID) {
g_sqcq_chn_info_list[phy_devid].enable_debug = DEBUG_ENABLE;
} else if (req_id == REQ_DISABLE_ID) {
g_sqcq_chn_info_list[phy_devid].enable_debug = DEBUG_DISABLE;
}
}
}
static int send_sq(u32 phy_devid, struct ms_debug_info *info)
{
int ret, chn_id;
struct trs_id_inst id_inst;
struct trs_chan_send_para para;
struct debug_send_info *send_info;
if (phy_devid >= DEVICE_NUM_MAX) {
TD_PRINT_ERR("phy_devid %u is out of range\n", phy_devid);
return ERR_DEV_ID;
}
chn_id = g_sqcq_chn_info_list[phy_devid].chn_id;
id_inst.devid = phy_devid;
id_inst.tsid = g_tsid;
send_info = (struct debug_send_info *)info->data;
para.sqe = (u8 *)send_info;
para.sqe_num = 1;
para.timeout = info->timeout;
TD_PRINT_INFO("send_sq devid=%u, tsid=%u, req_id=%u, is_return=%u, msg_id=%u data_len=%u\n", id_inst.devid,
id_inst.tsid, send_info->req_id, send_info->is_return, send_info->msg_id, send_info->data_len);
ret = hal_kernel_trs_chan_send(&id_inst, chn_id, ¶);
TD_PRINT_INFO("hal_kernel_trs_chan_send chnid=%d ret=%d\n", chn_id, ret);
if ((ret == 0) && ((send_info->req_id == REQ_ENABLE_ID) || (send_info->req_id == REQ_DISABLE_ID))) {
save_debug_mode_enable_status(phy_devid, send_info->req_id);
}
return ret;
}
#ifndef CFG_SOC_PLATFORM_CLOUD
static int send_sq_for_310P(u32 phy_devid, struct ms_debug_info *info)
{
int ret;
struct debug_send_info *sqe;
if (phy_devid >= DEVICE_NUM_MAX) {
TD_PRINT_ERR("phy_devid %u is out of range\n", phy_devid);
return ERR_DEV_ID;
}
if (info == NULL) {
TD_PRINT_ERR("ms_debug_info is NULL\n");
return ERR_INVALID_PARAM;
}
sqe = (struct debug_send_info *)info->data;
TD_PRINT_INFO("send_sq devid=%u, tsid=%u, req_id=%u, is_return=%u, msg_id=%u data_len=%u\n", phy_devid, g_tsid,
sqe->req_id, sqe->is_return, sqe->msg_id, sqe->data_len);
ret = devdrv_functional_sq_send(phy_devid, g_tsid, para_out[phy_devid].sq_id, (void *)sqe, DEBUG_SQ_BUF_LEN);
TD_PRINT_INFO("devdrv_functional_sq_send chnid=%d ret=%d\n", para_out[phy_devid].sq_id, ret);
if ((ret == 0) && ((sqe->req_id == REQ_ENABLE_ID) || (sqe->req_id == REQ_DISABLE_ID))) {
save_debug_mode_enable_status(phy_devid, sqe->req_id);
}
return ret;
}
#endif
static int copy_cq_out(u32 devid, struct debug_cq_report *recv_info)
{
struct debug_cq_report *cq_src;
struct sqcq_chn_info *chn_info;
errno_t ret_s;
TD_PRINT_INFO("enter copy_cq_out\n");
if (devid >= DEVICE_NUM_MAX) {
TD_PRINT_ERR("phy_devid %u is out of range\n", devid);
return ERR_DEV_ID;
}
chn_info = &g_sqcq_chn_info_list[devid];
if (chn_info->cq_tail == chn_info->cq_head) {
TD_PRINT_ERR("cq_tail == cq_head, no new cq msg received phy_devid=%u\n", devid);
return ERR_QUEUE_FULL;
}
chn_info->cq_tail = (chn_info->cq_tail + 1) % CQ_QUEUE_DEPTH;
cq_src = &(g_sqcq_chn_info_list[devid].cq_queue[chn_info->cq_tail]);
TD_PRINT_INFO("before memcpy cq_src->cmd_type=%u devid=%u, cq_tail=%d\n", cq_src->cmd_type, devid,
chn_info->cq_tail);
ret_s = memcpy_s(recv_info, sizeof(struct debug_cq_report), cq_src, sizeof(struct debug_cq_report));
if (ret_s != EOK) {
TD_PRINT_ERR("memcpy_s failed, ret = %d\n", ret_s);
return ret_s;
}
TD_PRINT_INFO("cq already copied out cq_head=%d, cq_tail=%d recv_info.cmd_type=%u\n", chn_info->cq_head,
chn_info->cq_tail, recv_info->cmd_type);
return 0;
}
static int sqcq_chn_create(u32 devid, u32 tsid)
{
struct trs_id_inst id_inst = {
.devid = devid,
.tsid = tsid
};
struct trs_chan_para para = { 0 };
int ret;
TD_PRINT_INFO("enter sqcq_chn_create devid=%u, tsid=%u\n", devid, tsid);
if (devid >= DEVICE_NUM_MAX) {
TD_PRINT_ERR("invalid devid=%u\n", devid);
return ERR_DEV_ID;
}
set_chan_create_para(¶);
ret = hal_kernel_trs_chan_create(&id_inst, ¶, &g_sqcq_chn_info_list[devid].chn_id);
if (ret != 0) {
TD_PRINT_ERR("create chan 0 fail. (devid=%u; tsid=%u)\n", devid, tsid);
return ret;
}
g_sqcq_chn_info_list[devid].isChannelCreate = 1;
TD_PRINT_INFO("sqcq_chn_create done chnid=%d\n", g_sqcq_chn_info_list[devid].chn_id);
return ret;
}
#ifndef CFG_SOC_PLATFORM_CLOUD
static int sqcq_chn_create_for_310P(u32 devid, u32 tsid)
{
int ret;
TD_PRINT_INFO("enter sqcq_chn_create devid=%u, tsid=%u\n", devid, tsid);
if (devid >= DEVICE_NUM_MAX) {
TD_PRINT_ERR("invalid devid=%u\n", devid);
return ERR_DEV_ID;
}
para_in[devid].type = DEBUG_CQSQ_CREATE;
para_in[devid].sqe_size = DEBUG_SQ_BUF_LEN;
para_in[devid].cqe_size = DEBUG_CQ_BUF_LEN;
para_in[devid].callback = (void (*)(u32, u32, const u8 *, u8 *))debug_cq_0_recv_for_310P;
ret = devdrv_create_functional_sqcq(devid, tsid, ¶_in[devid], ¶_out[devid]);
if (ret != 0) {
TD_PRINT_ERR("create chan 0 fail. (devid=%u; tsid=%u)\n", devid, tsid);
return ret;
}
g_sqcq_chn_info_list[devid].isChannelCreate = 1;
g_sqcq_chn_info_list[devid].chn_id = para_out[devid].sq_id;
TD_PRINT_INFO("sqcq_chn_create done chnid=%u\n", para_out[devid].sq_id);
return ret;
}
#endif
static int chan_destroy(void)
{
int ret = 0;
#ifndef AOS_LLVM_BUILD
struct uda_dev_type type;
uda_davinci_near_real_entity_type_pack(&type);
ret = uda_notifier_unregister(DEBUG_NOTIFIER, &type);
if (ret != 0) {
TD_PRINT_ERR("uda_notifier_unregister failed,ret = %d\n", ret);
return ret;
}
TD_PRINT_INFO("uda_notifier_unregister done\n");
isUdaRegister = 0;
#else
ret = debug_notifier_func(0, UDA_UNINIT);
#endif
return ret;
}
static void sqcq_chn_destroy(u32 devid, u32 tsid)
{
struct trs_id_inst id_inst = {
.devid = devid,
.tsid = tsid
};
TD_PRINT_INFO("enter sqcq_chn_destroy devid=%u, tsid=%u\n", devid, tsid);
if (devid >= DEVICE_NUM_MAX) {
TD_PRINT_ERR("invalid devid=%u\n", devid);
return;
}
if (g_sqcq_chn_info_list[devid].isChannelCreate == 1) {
hal_kernel_trs_chan_destroy(&id_inst, g_sqcq_chn_info_list[devid].chn_id);
}
g_sqcq_chn_info_list[devid].isChannelCreate = 0;
TD_PRINT_INFO("sqcq_chn_destroy done chnid=%d\n", g_sqcq_chn_info_list[devid].chn_id);
}
#ifndef CFG_SOC_PLATFORM_CLOUD
static void sqcq_chn_destroy_for_310P(u32 devid, u32 tsid)
{
para[devid].type = DEBUG_CQSQ_RELEASE;
para[devid].sq_id = para_out[devid].sq_id;
para[devid].cq_id = para_out[devid].cq_id;
if (devid >= DEVICE_NUM_MAX) {
TD_PRINT_ERR("invalid devid=%u\n", devid);
return;
}
TD_PRINT_INFO("enter sqcq_chn_destroy devid=%u, tsid=%u\n", devid, tsid);
if (g_sqcq_chn_info_list[devid].isChannelCreate == 1) {
devdrv_destroy_functional_sqcq(devid, tsid, ¶[devid]);
}
g_sqcq_chn_info_list[devid].isChannelCreate = 0;
TD_PRINT_INFO("sqcq_chn_destroy done sq_id=%u, cq_id=%u\n", para[devid].sq_id, para[devid].cq_id);
}
#endif
static int debug_each_phy_device_init(u32 device_id)
{
#ifndef CFG_SOC_PLATFORM_CLOUD
return sqcq_chn_create_for_310P(device_id, g_tsid);
#else
return sqcq_chn_create(device_id, g_tsid);
#endif
}
static void debug_each_phy_device_uninit(u32 device_id)
{
#ifndef CFG_SOC_PLATFORM_CLOUD
sqcq_chn_destroy_for_310P(device_id, g_tsid);
#else
sqcq_chn_destroy(device_id, g_tsid);
#endif
}
static int debug_notifier_func(u32 udevid, enum uda_notified_action action)
{
int ret = 0;
TD_PRINT_INFO("enter debug_notifier_func, udevid=%u, action=%d\n", udevid, (int)action);
if (udevid >= DEVICE_NUM_MAX) {
TD_PRINT_ERR("Invalid para. (udevid=%u)\n", udevid);
return -EINVAL;
}
if (action == UDA_INIT) {
ret = debug_each_phy_device_init(udevid);
} else if (action == UDA_UNINIT) {
debug_each_phy_device_uninit(udevid);
}
TD_PRINT_INFO("debug_notifier_func done\n");
return ret;
}
static void send_disable_debug_mode_cmd(u32 phy_devid, int chn_id)
{
int ret;
errno_t ret_s;
struct ms_debug_info info;
struct debug_send_info send_info;
TD_PRINT_INFO("detect app exit while debug mode enabled. send disable cmd now\n");
info.pid = ka_task_get_current_pid();
info.dev_id = phy_devid;
info.timeout = DEBUG_TIMEOUT;
send_info.req_id = REQ_DISABLE_ID;
send_info.is_return = 0;
send_info.msg_id = 0;
ret_s = memcpy_s(&info.data[0], sizeof(info.data), (void *)&send_info, sizeof(struct debug_send_info));
if (ret_s != EOK) {
TD_PRINT_ERR("memcpy_s failed, ret = %d\n", ret_s);
return;
}
#ifndef CFG_SOC_PLATFORM_CLOUD
ret = send_sq_for_310P(phy_devid, &info);
#else
ret = send_sq(phy_devid, &info);
#endif
if (ret == 0) {
TD_PRINT_INFO("send disable cmd for phy_devid %u succeed\n", phy_devid);
}
}
static int wait_for_cq_arrive(u32 phy_devid, int timeout)
{
int ret;
struct sqcq_chn_info *chn_info;
if (phy_devid >= DEVICE_NUM_MAX) {
TD_PRINT_ERR("invalid phy_devid=%u\n", phy_devid);
return ERR_DEV_ID;
}
chn_info = &g_sqcq_chn_info_list[phy_devid];
ret = ka_task_wait_event_interruptible_timeout(g_wq, (chn_info->cq_tail != chn_info->cq_head),
timeout * KA_HZ / HZ_TO_MS);
TD_PRINT_INFO("wake up phy_devid=%u ret=%d cq_head=%d cq_tail=%d\n", phy_devid, ret, chn_info->cq_head,
chn_info->cq_tail);
return ret;
}
static int chan_create(void)
{
int ret = 0;
#ifndef AOS_LLVM_BUILD
struct uda_dev_type type;
uda_davinci_near_real_entity_type_pack(&type);
TD_PRINT_INFO("uda_davinci_near_real_entity_type_pack done\n");
ret = uda_notifier_register(DEBUG_NOTIFIER, &type, UDA_PRI3, debug_notifier_func);
if (ret != 0) {
TD_PRINT_ERR("uda_notifier_register failed ret=%d\n", ret);
return ret;
}
TD_PRINT_INFO("uda_notifier_register done ret=%d\n", ret);
isUdaRegister = 1;
#else
TD_PRINT_INFO("skip uda_notifier_register\n");
ret = debug_notifier_func(0, UDA_INIT);
#endif
return ret;
}
static int register_device_id(u32 phy_devid, u32 ts_id, int pid)
{
ka_struct_pid_t *pid_struct = NULL;
if (phy_devid >= DEVICE_NUM_MAX) {
TD_PRINT_ERR("invalid phy_devid=%u\n", phy_devid);
return ERR_DEV_ID;
}
if (g_sqcq_chn_info_list[phy_devid].debugger_pid == DEBUG_DEFAULT_PID) {
g_sqcq_chn_info_list[phy_devid].debugger_pid = pid;
TD_PRINT_INFO("devid %u is free. now register for pid %d\n", phy_devid, pid);
return 0;
}
pid_struct = ka_task_find_get_pid(g_sqcq_chn_info_list[phy_devid].debugger_pid);
if (pid_struct == NULL) {
TD_PRINT_INFO("Device %d is free. pid=%d is not found.\n", phy_devid,
g_sqcq_chn_info_list[phy_devid].debugger_pid);
if (g_sqcq_chn_info_list[phy_devid].enable_debug == 1) {
send_disable_debug_mode_cmd(phy_devid, g_sqcq_chn_info_list[phy_devid].chn_id);
g_sqcq_chn_info_list[phy_devid].enable_debug = 0;
chan_destroy();
}
g_sqcq_chn_info_list[phy_devid].cq_head = 0;
g_sqcq_chn_info_list[phy_devid].cq_tail = 0;
g_sqcq_chn_info_list[phy_devid].pid = DEBUG_DEFAULT_PID;
TD_PRINT_INFO("phy_dev_id=%u released\n", phy_devid);
g_sqcq_chn_info_list[phy_devid].debugger_pid = pid;
chan_create();
TD_PRINT_INFO("devid %u is free. now register for pid %d\n", phy_devid, pid);
return 0;
}
TD_PRINT_ERR("devid %u has been occupied by debugger pid %d,current pid %d\n", phy_devid,
g_sqcq_chn_info_list[phy_devid].debugger_pid, pid);
return DEVICE_ALREADY_OCCUPIED;
}
static int __attribute__((unused)) check_process(u32 phy_devid, int pid)
{
if (phy_devid >= DEVICE_NUM_MAX) {
TD_PRINT_ERR("invalid phy_devid=%u\n", phy_devid);
return ERR_DEV_ID;
}
if (g_sqcq_chn_info_list[phy_devid].debugger_pid != pid) {
TD_PRINT_ERR("devid %u has been occupied by pid %d\n", phy_devid, g_sqcq_chn_info_list[phy_devid].debugger_pid);
return DEVICE_ALREADY_OCCUPIED;
}
return 0;
}
static int debug_drv_open(ka_inode_t *a, ka_file_t *fd)
{
TD_PRINT_INFO("misc file open pid=%d\n", ka_task_get_current_pid());
return 0;
}
static int debug_drv_release(ka_inode_t *a, ka_file_t *fd)
{
int i;
int ret;
TD_PRINT_INFO("misc file release pid=%d\n", ka_task_get_current_pid());
for (i = 0; i < DEVICE_NUM_MAX; ++i) {
TD_PRINT_INFO("debugger_pid = %d\n", g_sqcq_chn_info_list[i].debugger_pid);
if (g_sqcq_chn_info_list[i].debugger_pid == ka_task_get_current_pid()) {
if (g_sqcq_chn_info_list[i].enable_debug == 1) {
send_disable_debug_mode_cmd(i, g_sqcq_chn_info_list[i].chn_id);
g_sqcq_chn_info_list[i].enable_debug = 0;
}
if (g_sqcq_chn_info_list[i].isChannelCreate == 1 || isUdaRegister == 1) {
ret = chan_destroy();
}
g_sqcq_chn_info_list[i].cq_head = 0;
g_sqcq_chn_info_list[i].cq_tail = 0;
g_sqcq_chn_info_list[i].pid = DEBUG_DEFAULT_PID;
g_sqcq_chn_info_list[i].debugger_pid = DEBUG_DEFAULT_PID;
TD_PRINT_INFO("phy_dev_id=%d released\n", i);
break;
}
}
return ret;
}
static int parse_ioctl_cmd(u32 phy_devid, struct ms_debug_info *info, unsigned int cmd, unsigned long *arg)
{
int ret;
switch (cmd) {
case CMD_SQ_SEND:
TD_PRINT_INFO("CMD_SQ_SEND\n");
#ifndef CFG_SOC_PLATFORM_CLOUD
ret = send_sq_for_310P(phy_devid, info);
#else
ret = send_sq(phy_devid, info);
#endif
if (ret != 0) {
TD_PRINT_ERR("CMD_SQ_SEND send_sq failed. ret=%d\n", ret);
}
break;
case CMD_CQ_RECV:
TD_PRINT_INFO("CMD_CQ_RECV\n");
ret = wait_for_cq_arrive(phy_devid, info->timeout);
if (ret <= 0) {
TD_PRINT_INFO("wait cq timed out or error\n");
ret = ERR_WAIT_TIMEOUT;
break;
}
copy_cq_out(phy_devid, (struct debug_cq_report *)&(info->data));
ret = ka_base_copy_to_user((struct ms_debug_info *)(*arg), info, sizeof(struct ms_debug_info));
if (ret != 0) {
TD_PRINT_ERR("CMD_CQ_RECV ka_base_copy_to_user failed. ret=%d\n", ret);
}
break;
case CMD_GM_COPY:
TD_PRINT_INFO("CMD_GM_COPY\n");
ret = dma_copy_sync(info->dev_id, phy_devid, g_tsid, info->pid, (struct dma_param *)&(info->data));
break;
case CMD_DEV_REGISTER:
ret = 0;
break;
default:
TD_PRINT_INFO("unrecognized cmd=%d\n", cmd);
ret = ERR_UNRECOGNIZED_CMD;
break;
}
return ret;
}
static int get_switch_status(void)
{
ka_file_t *debug_switch;
ssize_t bytes_read;
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)
mm_segment_t oldfs __attribute__((unused));
#endif
loff_t pos = 0;
int switch_status = -1;
char readBuffer[50] = { 0 };
int ret;
debug_switch = ka_fs_filp_open("/proc/debug_switch", O_RDONLY, 0);
if (KA_IS_ERR(debug_switch)) {
TD_PRINT_ERR("/proc/debug_switch doesn't exist,please check if debug_switch has been loaded\n");
return -ENOENT;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
oldfs = get_fs();
set_fs(KERNEL_DS);
bytes_read = ka_fs_vfs_read(debug_switch, readBuffer, sizeof(readBuffer) - 1, &pos);
#else
bytes_read = ka_fs_kernel_read(debug_switch, readBuffer, sizeof(readBuffer) - 1, &pos);
#endif
if (bytes_read < 0) {
TD_PRINT_ERR("Failed to read /proc/debug_switch\n");
ka_fs_filp_close(debug_switch, NULL);
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
set_fs(oldfs);
#endif
return -EIO;
}
readBuffer[bytes_read] = '\0';
TD_PRINT_INFO("%s", readBuffer);
ret = sscanf_s(readBuffer, "debug_switch_status = %d", &switch_status);
if (ret != 1) {
TD_PRINT_INFO("sscanf error, sccanf ret = %d\n", ret);
switch_status = -1;
}
if (switch_status != 0 && switch_status != 1) {
TD_PRINT_ERR("get debug_switch_status error\n");
switch_status = -1;
}
ka_fs_filp_close(debug_switch, NULL);
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
set_fs(oldfs);
#endif
return switch_status;
}
static int check_dev_registered(u32 phy_devid)
{
int ret = 0;
if (g_sqcq_chn_info_list[phy_devid].debugger_pid == DEBUG_DEFAULT_PID) {
TD_PRINT_INFO("CMD_DEV_REGISTER\n");
ret = register_device_id(phy_devid, g_tsid, current->tgid);
} else if (g_sqcq_chn_info_list[phy_devid].debugger_pid != current->tgid) {
TD_PRINT_INFO("CMD_DEV_REGISTER\n");
ret = register_device_id(phy_devid, g_tsid, current->tgid);
} else {
TD_PRINT_INFO("current pid %d equal to debugger_pid %d\n", current->tgid,
g_sqcq_chn_info_list[phy_devid].debugger_pid);
}
return ret;
}
static int check_and_create_chan(unsigned int cmd, u32 phy_devid)
{
int ret = 0;
if (g_sqcq_chn_info_list[phy_devid].isChannelCreate == 0) {
int status = get_switch_status();
if (status != 1) {
TD_PRINT_ERR("debug_switch_status = %d, channel create is forbidden\n", status);
return ERR_SWITCH_CLOSE;
} else {
ret = chan_create();
}
}
return ret;
}
static long debug_drv_ioctl(struct file *fd, unsigned int cmd, unsigned long arg)
{
int ret = 0;
u32 phy_devid, vfid;
struct ms_debug_info info;
if ((fd == NULL) || ((struct ms_debug_info *)arg == NULL)) {
TD_PRINT_ERR("fd or arg is NULL\n");
return ERR_INVALID_PARAM;
}
if (_IOC_TYPE(cmd) != TS_DEBUG_MAGIC_WORD) {
TD_PRINT_ERR("invalid cmd pattern.\n");
return ERR_INVALID_PARAM;
}
ret = ka_base_copy_from_user(&info, (struct ms_debug_info *)arg, sizeof(struct ms_debug_info));
if (ret != 0) {
TD_PRINT_ERR("ka_base_copy_from_user failed. ret=%d\n", ret);
return ret;
}
if (info.dev_id >= DEVICE_NUM_MAX) {
TD_PRINT_ERR("ms_debug_info.devid=%u is out of range\n", info.dev_id);
return ERR_DEV_ID;
}
if (info.timeout < 0 || info.pid < 0) {
TD_PRINT_ERR("Parameter out of range (must be >= 0), ms_debug_info:timeout=%d, pid=%d.\n",
info.timeout, info.pid);
return ERR_INVALID_PARAM;
}
TD_PRINT_INFO("ms_debug_info: dev_id=%u timeout=%d pid=%d\n", info.dev_id, info.timeout, info.pid);
ret = uda_devid_to_phy_devid(info.dev_id, &phy_devid, &vfid);
if (ret != 0) {
TD_PRINT_ERR("uda_devid_to_phy_devid failed logic_devid=%u\n", info.dev_id);
return ret;
}
if (phy_devid >= DEVICE_NUM_MAX) {
TD_PRINT_ERR("phy_devid=%u is out of range\n", phy_devid);
return ERR_DEV_ID;
}
mutex_lock(&(g_sqcq_chn_info_list[0].mtx));
ret = check_dev_registered(phy_devid);
mutex_unlock(&(g_sqcq_chn_info_list[0].mtx));
if (ret != 0) {
return ret;
}
ret = check_and_create_chan(cmd, phy_devid);
if (ret != 0) {
return ret;
}
ka_task_mutex_lock((ka_mutex_t *)&(g_sqcq_chn_info_list[phy_devid].mtx));
ret = parse_ioctl_cmd(phy_devid, &info, cmd, &arg);
ka_task_mutex_unlock((ka_mutex_t *)&(g_sqcq_chn_info_list[phy_devid].mtx));
return ret;
}
static ka_file_operations_t g_debug_drv_fops = {
.owner = KA_THIS_MODULE,
.open = debug_drv_open,
.release = debug_drv_release,
.unlocked_ioctl = debug_drv_ioctl
};
static ka_miscdevice_t g_debug_drv_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "drv_debug",
.fops = &g_debug_drv_fops,
.mode = 0666,
};
static int ts_drv_module_init(void)
{
int ret;
ret = ka_driver_misc_register(&g_debug_drv_dev);
if (ret < 0) {
TD_PRINT_ERR("ka_driver_misc_register error\n");
return ret;
}
TD_PRINT_INFO("ka_driver_misc_register succeed\n");
cq_queue_info_init();
return 0;
}
static void ts_drv_module_exit(void)
{
int i;
errno_t ret_s;
ka_driver_misc_deregister(&g_debug_drv_dev);
TD_PRINT_INFO("ka_driver_misc_deregister!\n");
for (i = 0; i < DEVICE_NUM_MAX; ++i) {
if (g_sqcq_chn_info_list[i].enable_debug == 1) {
send_disable_debug_mode_cmd(i, g_sqcq_chn_info_list[i].chn_id);
g_sqcq_chn_info_list[i].enable_debug = 0;
}
g_sqcq_chn_info_list[i].cq_head = 0;
g_sqcq_chn_info_list[i].cq_tail = 0;
g_sqcq_chn_info_list[i].pid = DEBUG_DEFAULT_PID;
g_sqcq_chn_info_list[i].debugger_pid = DEBUG_DEFAULT_PID;
}
ret_s = memset_s(&g_cq_queue[0], sizeof(struct debug_cq_report) * CQ_QUEUE_DEPTH, 0,
sizeof(struct debug_cq_report) * CQ_QUEUE_DEPTH);
if (ret_s != EOK) {
TD_PRINT_ERR("memset_s failed, ret = %d\n", ret_s);
return;
}
}
#define PCI_VENDOR_ID_HUAWEI 0x19e5
static const struct pci_device_id g_ts_debug_tbl[] = {
{KA_PCI_VDEVICE(HUAWEI, 0xd802), 0},
{KA_PCI_VDEVICE(HUAWEI, 0xd803), 0},
{KA_PCI_VDEVICE(HUAWEI, 0xd500), 0},
{}
};
KA_MODULE_DEVICE_TABLE(pci, g_ts_debug_tbl);
ka_module_init(ts_drv_module_init);
ka_module_exit(ts_drv_module_exit);
KA_MODULE_DESCRIPTION("ts debug driver");
KA_MODULE_LICENSE("GPL");
KA_MODULE_AUTHOR("Huawei Tech. Co., Ltd.");