* 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 <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <stdbool.h>
#include "securec.h"
#include "davinci_interface.h"
#include "pbl_uda_user.h"
#include "svm_user_adapt.h"
#include "svm_log.h"
#include "svm_pub.h"
#include "svm_ioctl_ex.h"
struct svm_dev_intf {
int valid;
int fd;
int pid;
};
static struct svm_dev_intf svm_dev_fd[SVM_MAX_DEV_NUM];
static pthread_mutex_t svm_fd_mutex = PTHREAD_MUTEX_INITIALIZER;
#ifndef L2_EMU_ST
static void svm_set_dev_fd(u32 devid, int fd)
{
svm_dev_fd[devid].fd = fd;
svm_dev_fd[devid].pid = svm_getpid();
svm_dev_fd[devid].valid = (fd == -1) ? 0 : 1;
}
static int svm_get_dev_fd(u32 devid)
{
if (svm_dev_fd[devid].valid == 0) {
return -1;
}
if (svm_dev_fd[devid].pid != svm_getpid()) {
return -1;
}
return svm_dev_fd[devid].fd;
}
#endif
static int svm_char_dev_open(u32 udevid, int *fd)
{
struct davinci_intf_open_arg arg;
int tmp_errno, ret, cnt = 0;
bool retry = false;
arg.device_id = (int)udevid;
ret = strcpy_s(arg.module_name, DAVINIC_MODULE_NAME_MAX, SVM_CHAR_DEV_NAME);
if (ret < 0) {
svm_err("Strcpy_s failed. (udevid=%u; ret=%d)\n", udevid, ret);
return DRV_ERROR_INNER_ERR;
}
*fd = svm_file_open(davinci_intf_get_dev_path(), O_RDWR | O_CLOEXEC);
if (*fd < 0) {
tmp_errno = errno;
svm_err("Open device failed. (udevid=%u; fd=%d; errno=%d, error=%s)\n", udevid, *fd, tmp_errno, strerror(tmp_errno));
return errno_to_user_errno(tmp_errno);
}
As a result, the new process fails to be added to the hash table. */
do {
ret = svm_user_ioctl(*fd, DAVINCI_INTF_IOCTL_OPEN, &arg);
cnt++;
retry = ((ret != 0) && (errno == EBUSY) && (cnt < 1000));
if (retry) {
usleep(100000);
}
} while (retry);
if (ret != 0) {
tmp_errno = errno;
svm_err("Ioctl failed. (udevid=%u; fd=%d; errno=%d, error=%s)\n", udevid, *fd, tmp_errno, strerror(tmp_errno));
svm_file_close(*fd);
return errno_to_user_errno(tmp_errno);
}
return 0;
}
static int svm_char_dev_close(u32 udevid, int fd)
{
struct davinci_intf_close_arg arg;
int ret, tmp_errno;
arg.device_id = (int)udevid;
ret = strcpy_s(arg.module_name, DAVINIC_MODULE_NAME_MAX, SVM_CHAR_DEV_NAME);
if (ret < 0) {
svm_err("Strcpy_s failed. (udevid=%u; ret=%d)\n", udevid, ret);
return DRV_ERROR_INNER_ERR;
}
ret = svm_user_ioctl(fd, DAVINCI_INTF_IOCTL_CLOSE, &arg);
if (ret != 0) {
tmp_errno = errno;
svm_err("Ioctl warn. (udevid=%u; fd=%d; errno=%d, error=%s)\n", udevid, fd, tmp_errno, strerror(tmp_errno));
return errno_to_user_errno(tmp_errno);
}
svm_file_close(fd);
return 0;
}
#define SVM_IOCTL_DEV_HANDLE_MAX_NUM 20
static int (* svm_ioctl_dev_init_post_handle[SVM_IOCTL_DEV_HANDLE_MAX_NUM])(u32 devid) = {NULL, };
static int (* svm_ioctl_dev_uninit_pre_handle[SVM_IOCTL_DEV_HANDLE_MAX_NUM])(u32 devid) = {NULL, };
int svm_register_ioctl_dev_init_post_handle(int (*fn)(u32 devid))
{
int i;
for (i = 0; i < SVM_IOCTL_DEV_HANDLE_MAX_NUM; i++) {
if (svm_ioctl_dev_init_post_handle[i] == NULL) {
svm_ioctl_dev_init_post_handle[i] = fn;
return DRV_ERROR_NONE;
}
}
return DRV_ERROR_INNER_ERR;
}
int svm_register_ioctl_dev_uninit_pre_handle(int (*fn)(u32 devid))
{
int i;
for (i = 0; i < SVM_IOCTL_DEV_HANDLE_MAX_NUM; i++) {
if (svm_ioctl_dev_uninit_pre_handle[i] == NULL) {
svm_ioctl_dev_uninit_pre_handle[i] = fn;
return DRV_ERROR_NONE;
}
}
return DRV_ERROR_INNER_ERR;
}
static int svm_call_ioctl_dev_init_post_handle(u32 devid)
{
int ret, i;
for (i = 0; i < SVM_IOCTL_DEV_HANDLE_MAX_NUM; i++) {
if (svm_ioctl_dev_init_post_handle[i] != NULL) {
ret = svm_ioctl_dev_init_post_handle[i](devid);
if (ret != DRV_ERROR_NONE) {
svm_err("Post handle failed. (i=%d; ret=%d; devid=%u)\n", i, ret, devid);
return ret;
}
}
}
return DRV_ERROR_NONE;
}
static int svm_call_ioctl_dev_uninit_pre_handle(u32 devid)
{
int ret, i;
for (i = SVM_IOCTL_DEV_HANDLE_MAX_NUM - 1; i >= 0; i--) {
if (svm_ioctl_dev_uninit_pre_handle[i] != NULL) {
ret = svm_ioctl_dev_uninit_pre_handle[i](devid);
if (ret != DRV_ERROR_NONE) {
svm_err("Pre handle failed. (i=%d; ret=%d; devid=%u)\n", i, ret, devid);
return ret;
}
}
}
return DRV_ERROR_NONE;
}
int svm_ioctl_dev_init(u32 devid)
{
u32 udevid;
int fd, ret;
if (devid >= SVM_MAX_DEV_NUM) {
svm_err("Invalid para. (devid=%u)\n", devid);
return DRV_ERROR_INVALID_VALUE;
}
ret = uda_get_udevid_by_devid_ex(devid, &udevid);
if (ret != DRV_ERROR_NONE) {
svm_err("Get udevid failed. (devid=%u; ret=%d)\n", devid, ret);
return DRV_ERROR_PARA_ERROR;
}
(void)pthread_mutex_lock(&svm_fd_mutex);
fd = svm_get_dev_fd(devid);
if (fd >= 0) {
(void)pthread_mutex_unlock(&svm_fd_mutex);
return DRV_ERROR_REPEATED_INIT;
}
ret = svm_char_dev_open(udevid, &fd);
share_log_read_run_info(HAL_MODULE_TYPE_DEVMM);
if (ret != DRV_ERROR_NONE) {
(void)pthread_mutex_unlock(&svm_fd_mutex);
svm_err("Open failed. (devid=%u; ret=%d)\n", devid, ret);
return ret;
}
svm_set_dev_fd(devid, fd);
ret = svm_call_ioctl_dev_init_post_handle(devid);
if (ret != DRV_ERROR_NONE) {
svm_err("Post handle failed. (ret=%d; devid=%u)\n", ret, devid);
svm_set_dev_fd(devid, -1);
(void)svm_char_dev_close(udevid, fd);
(void)pthread_mutex_unlock(&svm_fd_mutex);
return ret;
}
(void)pthread_mutex_unlock(&svm_fd_mutex);
svm_debug("Open fd. (pid=%d; devid=%u)\n", svm_getpid(), devid);
return 0;
}
int svm_ioctl_dev_uninit(u32 devid)
{
u32 udevid;
int fd, ret;
if (devid >= SVM_MAX_DEV_NUM) {
svm_err("Invalid para. (devid=%u)\n", devid);
return DRV_ERROR_INVALID_VALUE;
}
ret = uda_get_udevid_by_devid_ex(devid, &udevid);
if (ret != DRV_ERROR_NONE) {
svm_err("Get udevid failed. (devid=%u; ret=%d)\n", devid, ret);
return ret;
}
(void)pthread_mutex_lock(&svm_fd_mutex);
fd = svm_get_dev_fd(devid);
if (fd < 0) {
(void)pthread_mutex_unlock(&svm_fd_mutex);
svm_err("Repeated close. (devid=%u)\n", devid);
return DRV_ERROR_NOT_EXIST;
}
ret = svm_call_ioctl_dev_uninit_pre_handle(devid);
if (ret != DRV_ERROR_NONE) {
(void)pthread_mutex_unlock(&svm_fd_mutex);
return ret;
}
ret = svm_char_dev_close(udevid, fd);
share_log_read_run_info(HAL_MODULE_TYPE_DEVMM);
if (ret != DRV_ERROR_NONE) {
(void)pthread_mutex_unlock(&svm_fd_mutex);
return ret;
}
svm_set_dev_fd(devid, -1);
(void)pthread_mutex_unlock(&svm_fd_mutex);
svm_debug("Close fd. (pid=%d; devid=%u)\n", svm_getpid(), devid);
return 0;
}
int svm_cmd_ioctl(u32 devid, u32 cmd, void *para)
{
int fd, ret, tmp_errno;
if (devid >= SVM_MAX_DEV_NUM) {
svm_err("Invalid para. (devid=%u)\n", devid);
return DRV_ERROR_INVALID_VALUE;
}
fd = svm_get_dev_fd(devid);
if (fd < 0) {
svm_err("No valid fd. (devid=%u)\n", devid);
return DRV_ERROR_NOT_EXIST;
}
do {
ret = svm_user_ioctl(fd, (unsigned int)cmd, para);
tmp_errno = errno;
if ((ret == -1) && (tmp_errno == EINTR)) {
svm_run_info("Ioctl retry. (devid=%u; cmd=%u)\n", devid, cmd);
}
} while ((ret == -1) && (tmp_errno == EINTR));
return (ret == 0) ? 0 : errno_to_user_errno(tmp_errno);
}