* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* 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 FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdbool.h>
#include "securec.h"
#include "mmpa_api.h"
#include "davinci_interface.h"
#include "dmc_user_interface.h"
#include "urd_cmd.h"
#include "urd_user_inner.h"
#include "pbl/pbl_urd_user.h"
#define URD_INVALID_PID_OR_FD (-1)
#define FdIsValid(fd) ((fd) >= 0)
#define PROC_MOUDULE_FILE_NAME "/proc/modules"
#define DEV_MODULE_INIT_INFO_LEN 1024
#define MAX_FOPEN_RETRY_TIMES 5
static mmProcess g_urd_fd = (mmProcess)URD_INVALID_PID_OR_FD;
static mmMutex_t g_urd_fd_mutex = PTHREAD_MUTEX_INITIALIZER;
static pid_t g_urd_tgid = (pid_t)URD_INVALID_PID_OR_FD;
static int g_env_virt = false;
#ifdef STATIC_SKIP
#define STATIC
#else
#define STATIC static
#endif
#ifdef CFG_FEATURE_MODULE_CHECK
STATIC int urd_check_module_init(const char *module_name)
{
char *buff = NULL;
FILE *fp = NULL;
size_t name_len;
int retry_time = -1;
if (module_name == NULL) {
urd_err("Para is NULL.\n");
return -1;
}
name_len = strnlen(module_name, DEV_MODULE_INIT_INFO_LEN);
if (name_len >= DEV_MODULE_INIT_INFO_LEN) {
urd_err("Length out range. (length=%d)\n", name_len);
return -1;
}
buff = (char *)malloc(DEV_MODULE_INIT_INFO_LEN);
if (buff == NULL) {
urd_err("Malloc memory error. (size=%d)\n", DEV_MODULE_INIT_INFO_LEN);
return -1;
}
do {
fp = fopen(PROC_MOUDULE_FILE_NAME, "r");
retry_time++;
} while (fp == NULL && retry_time < MAX_FOPEN_RETRY_TIMES);
if (fp == NULL) {
urd_err("Fopen error. (file=\"%s\"; errno:%d, retry_time=%d)\n",
PROC_MOUDULE_FILE_NAME, errno, retry_time);
(void)free(buff);
buff = NULL;
return -1;
}
while (fgets(buff, DEV_MODULE_INIT_INFO_LEN, fp) != NULL) {
if (strstr(buff, module_name) != NULL) {
(void)free(buff);
(void)fclose(fp);
buff = NULL;
fp = NULL;
return 0;
}
}
(void)free(buff);
(void)fclose(fp);
buff = NULL;
fp = NULL;
return -1;
}
#endif
STATIC int urd_ioctl_open(int fd)
{
struct davinci_intf_open_arg arg = {0};
int ret;
if (!FdIsValid(fd)) {
return DRV_ERROR_INVALID_VALUE;
}
#ifdef CFG_FEATURE_MODULE_CHECK
if (urd_check_module_init("asdrv_vdms") == 0) {
g_env_virt = true;
return 0;
}
#endif
ret = strcpy_s(arg.module_name, DAVINIC_MODULE_NAME_MAX, DAVINCI_INTF_MODULE_URD);
if (ret != 0) {
urd_err("Strcpy_s failed. (ret=%d)\n", ret);
return ret;
}
ret = ioctl(fd, DAVINCI_INTF_IOCTL_OPEN, &arg);
if (ret != 0) {
urd_err("Call devdrv_device ioctl open failed. (ret=%d; errno=%d)\n", ret, errno);
return ret;
}
return 0;
}
STATIC void urd_ioctl_close(int fd)
{
struct davinci_intf_close_arg arg = {0};
int ret;
if ((!FdIsValid(fd)) || (g_env_virt == true)) {
return;
}
ret = strcpy_s(arg.module_name, DAVINIC_MODULE_NAME_MAX, DAVINCI_INTF_MODULE_URD);
if (ret != 0) {
return;
}
ret = ioctl(fd, DAVINCI_INTF_IOCTL_CLOSE, &arg);
if (ret != 0) {
return;
}
return;
}
STATIC mmProcess urd_open_intf(void)
{
mmProcess fd = URD_INVALID_PID_OR_FD;
int err = 0;
int ret = 0;
if (FdIsValid(g_urd_fd) && (g_urd_tgid == getpid())) {
return g_urd_fd;
}
(void)mmMutexLock(&g_urd_fd_mutex);
if (FdIsValid(g_urd_fd)) {
if (g_urd_tgid != getpid()) {
g_urd_fd = (mmProcess)URD_INVALID_PID_OR_FD;
} else {
fd = g_urd_fd;
goto out;
}
}
fd = mmOpen2(davinci_intf_get_dev_path(), M_RDWR|O_CLOEXEC, M_IRUSR);
err = (__errno_location() != NULL ? errno : 0);
if (!FdIsValid(fd)) {
urd_err("Open failed. (dev_name=%s; fd=%d; g_urd_fd=%d; errno=%d)\n", davinci_intf_get_dev_path(), fd, g_urd_fd, err);
fd = (mmProcess)URD_INVALID_PID_OR_FD;
goto out;
}
ret = urd_ioctl_open(fd);
if (ret != 0) {
urd_ioctl_close(fd);
(void)mmClose(fd);
urd_err("Urd ioctl open failed. (ret=%d; fd=%d)\n", ret, fd);
fd = (mmProcess)URD_INVALID_PID_OR_FD;
goto out;
}
g_urd_fd = fd;
g_urd_tgid = getpid();
out:
(void)mmMutexUnLock(&g_urd_fd_mutex);
return fd;
}
STATIC void urd_close_intf(void)
{
(void)mmMutexLock(&g_urd_fd_mutex);
if (FdIsValid(g_urd_fd)) {
if (g_urd_tgid == getpid()) {
urd_ioctl_close(g_urd_fd);
(void)mmClose(g_urd_fd);
}
g_urd_fd = (mmProcess)URD_INVALID_PID_OR_FD;
g_urd_tgid = (pid_t)URD_INVALID_PID_OR_FD;
}
(void)mmMutexUnLock(&g_urd_fd_mutex);
}
STATIC void __attribute__((constructor)) urd_user_init(void)
{
mmProcess fd = URD_INVALID_PID_OR_FD;
if (access(davinci_intf_get_dev_path(), R_OK | W_OK) != 0) {
return;
}
fd = urd_open_intf();
if (!FdIsValid(fd)) {
urd_close_intf();
urd_err("Open dms device failed. (fd=%d)\n", fd);
}
}
STATIC void __attribute__((destructor)) urd_user_uninit(void)
{
urd_close_intf();
}
STATIC inline int is_valid_user_errno(int err)
{
return ((err >=0) && (err <= DRV_ERROR_POWER_OP_FAIL)) || (err == DRV_ERROR_NOT_SUPPORT);
}
|kernel return | ioctl return | errno |
|ret=[, -4096] | ret | |
|ret=[-4095, -1] | -1 | |ret| |
|ret=[0, ] | ret | |
convert ioctl return/errno to userspace errno.
support kernel return kernel errno(negative) or userspace errno.
*/
STATIC drvError_t urd_ioctl_errno_convert(int ret, int errno_param)
{
if (ret == -1) {
return errno_to_user_errno(errno_param);
}
if (is_valid_user_errno(ret)) {
return ret;
}
return DRV_ERROR_IOCRL_FAIL;
}
STATIC drvError_t urd_ioctl(unsigned long cmd, struct urd_ioctl_arg *ioarg)
{
mmProcess fd;
drvError_t ret;
if (ioarg == NULL) {
urd_err("Ioctl arg is null\n");
return DRV_ERROR_PARA_ERROR;
}
fd = urd_open_intf();
if (!FdIsValid(fd)) {
urd_err("Open device failed. (fd=%d)\n", fd);
return DRV_ERROR_OPEN_FAILED;
}
#ifndef CFG_FEATURE_SRIOV
if (g_env_virt == true) {
return DRV_ERROR_NOT_SUPPORT;
}
#endif
ret = ioctl(fd, cmd, ioarg);
if (ret == 0) {
return 0;
}
ret = urd_ioctl_errno_convert(ret, errno);
share_log_read_err(HAL_MODULE_TYPE_DEV_MANAGER);
return ret;
}
int urd_dev_usr_cmd(uint32_t devid, struct urd_cmd *cmd, struct urd_cmd_para *cmd_para)
{
struct urd_ioctl_arg ioarg = {0};
int ret;
if ((cmd == NULL) || (cmd_para == NULL)) {
urd_err("Invalid cmd_para or cmd is NULL. (devid=%u, cmd=%s, cmd_para=%s)\n",
devid, (cmd == NULL) ? "NULL" : "OK", (cmd_para == NULL) ? "NULL" : "OK");
return DRV_ERROR_PARA_ERROR;
}
ioarg.devid = devid;
ret = memcpy_s(&(ioarg.cmd), sizeof(ioarg.cmd), cmd, sizeof(*cmd));
if (ret != 0) {
urd_err("Memcpy fail. (ret=%d; devid=%u)\n", ret, devid);
return DRV_ERROR_INNER_ERR;
}
ret = memcpy_s(&(ioarg.cmd_para), sizeof(ioarg.cmd_para), cmd_para, sizeof(*cmd_para));
if (ret != 0) {
urd_err("Memcpy fail. (ret=%d; devid=%u)\n", ret, devid);
return DRV_ERROR_INNER_ERR;
}
ret = urd_ioctl(URD_IOCTL_CMD, &ioarg);
if (ret != 0) {
urd_ex_err(ret, "Ioctl fail. (ret=%d; devid=%u; main_cmd=0x%x; sub_cmd=0x%x)\n",
ret, devid, cmd->main_cmd, cmd->sub_cmd);
return ret;
}
return 0;
}
int urd_usr_cmd(struct urd_cmd *cmd, struct urd_cmd_para *cmd_para)
{
return urd_dev_usr_cmd(0, cmd, cmd_para);
}
#ifndef DMS_UT
drvError_t urdCloseRestoreHandler(uint32_t devid, halDevCloseIn *in)
{
UNUSED(devid);
UNUSED(in);
urd_close_intf();
mmProcess fd = URD_INVALID_PID_OR_FD;
if (access(davinci_intf_get_dev_path(), R_OK | W_OK) != 0) {
return DRV_ERROR_NO_DEVICE;
}
fd = urd_open_intf();
if (!FdIsValid(fd)) {
urd_close_intf();
urd_err("Open urd failed. (fd=%d)\n", fd);
return DRV_ERROR_OPEN_FAILED;
}
return DRV_ERROR_NONE;
}
#endif