data: publish complete Calculet NPU research archive

This commit is contained in:
2026-08-02 14:57:11 +08:00
parent b5c4fbffab
commit 98e6cadcb6
12400 changed files with 6166642 additions and 0 deletions
@@ -0,0 +1,13 @@
Package: cal-pcie
Version: 0.9.0-1
Architecture: amd64
Maintainer: Junwei Qin <qinjunwei@calculet.tech>
Installed-Size: 212
Section: kernel
Priority: optional
Homepage: http://192.168.20.33:1000/niuzhenyu/cal-pcie.git
Description: Kernel driver module for PCIe performance
This package provides the Linux kernel module for Calculet
PCIe hardware interfaces.It implements the essential IOCTL
character device framework to allow direct register communication
from user-space.
@@ -0,0 +1,18 @@
c589326957579b94e55a9e11adc4098c usr/share/doc/cal-pcie/changelog.Debian.gz
a7f9bb8aebf66a03d992267809cff877 usr/share/doc/cal-pcie/copyright
788fdcca7726bfe0a2883e0f291b380c usr/src/cal-pcie-0.9.0/Makefile
4e755f9719aa171e82dd8d1909c29480 usr/src/cal-pcie-0.9.0/board_mgr.c
24b662df4710a9dc482b15e5d5a1f501 usr/src/cal-pcie-0.9.0/board_mgr.h
f720659b9f4260f2a226d1f39dcf5aac usr/src/cal-pcie-0.9.0/calculet_pci.mod.c
daf9a5d6a4faad743f65577363930f53 usr/src/cal-pcie-0.9.0/calculet_pci_core.c
e7185b5821ebbcc04d851f2763d17c21 usr/src/cal-pcie-0.9.0/calculet_pci_core.h
1c5ea12ea5ef46cdc127032587e8b315 usr/src/cal-pcie-0.9.0/dkms.conf
6692d02c13baed72ac24306c7428e288 usr/src/cal-pcie-0.9.0/dma.c
e68fc8b3641aad7096bec1240df2b67f usr/src/cal-pcie-0.9.0/dma.h
4cb1973a1b2da31db0816d6b9da6a53c usr/src/cal-pcie-0.9.0/fops.c
6798c3ca8909724756f565bd29a2ddcd usr/src/cal-pcie-0.9.0/fops.h
7bfe47920e7947dc2c4931a3ecad7b1b usr/src/cal-pcie-0.9.0/ioctl.h
3ed33ff44b0ee698ace348f2c746f603 usr/src/cal-pcie-0.9.0/proc.c
5c253d5cea8c5874dfbcae442b5d2799 usr/src/cal-pcie-0.9.0/proc.h
3b20c1cd73163609e6b96abbaf58e682 usr/src/cal-pcie-0.9.0/utils.c
2bc24094f0d939c8cbb4950650d7f8a7 usr/src/cal-pcie-0.9.0/utils.h
@@ -0,0 +1,107 @@
#!/bin/bash
# postinst - 安装后加载内核模块
set -e
# 当脚本由 dpkg 触发时,$DPKG_MAIN_TSN_PACKAGE 通常可用
# 如果为空,则回退到通过文件名解析
PACKAGE_NAME="${DPKG_MAIN_TSN_PACKAGE}"
if [ -z "$PACKAGE_NAME" ]; then
SCRIPT_NAME=$(basename "$0")
PACKAGE_NAME=$(echo "$SCRIPT_NAME" | sed 's/\.postinst$//' | sed 's/\.prerm$//')
fi
# 从 dpkg 数据库获取版本
if [ -n "$PACKAGE_NAME" ]; then
PACKAGE_VERSION=$(dpkg-query -W -f='${Version}' "$PACKAGE_NAME" 2>/dev/null)
# 去掉 Debian 修订号(-1, -2, -ubuntu1 等)
PACKAGE_VERSION=$(echo "$PACKAGE_VERSION" | sed 's/-.*$//')
fi
DKMS_SRC_DIR="/usr/src/${PACKAGE_NAME}-${PACKAGE_VERSION}"
# 从预期的源码释放路径去解析驱动名称
MAKEFILE_PATH="${DKMS_SRC_DIR}/Makefile"
MODULE_NAME=""
case "$1" in
configure)
echo "正在配置 ${PACKAGE_NAME} 软件包..."
# 创建 DKMS 源码目录
echo "创建 DKMS 源码目录: ${DKMS_SRC_DIR}"
mkdir -p "${DKMS_SRC_DIR}"
# 复制源码文件到 DKMS 目录
SOURCE_DIR="/usr/share/${PACKAGE_NAME}/source"
if [ -d "${SOURCE_DIR}" ]; then
echo "从 ${SOURCE_DIR} 复制源码到 DKMS 目录..."
cp -rf "${SOURCE_DIR}"/* "${DKMS_SRC_DIR}/"
fi
# 确保 dkms.conf 存在
if [ ! -f "${DKMS_SRC_DIR}/dkms.conf" ]; then
echo "错误: DKMS 配置文件不存在: ${DKMS_SRC_DIR}/dkms.conf" >&2
exit 1
fi
# 动态解析驱动模块名称
if [ -f "${MAKEFILE_PATH}" ]; then
MODULE_NAME=$(sed -n -E 's/^DRIVER_NAME[[:space:]]*:?=[[:space:]]*([^[:space:]]+).*/\1/p' "${MAKEFILE_PATH}" | head -1 | sed 's/\.ko$//')
fi
# 如果 Makefile 里没解析出来,降级使用包名作为模块名
if [ -z "$MODULE_NAME" ]; then
MODULE_NAME="${PACKAGE_NAME}"
fi
echo "解析到的驱动模块名称: ${MODULE_NAME}"
# 设置正确的权限
# chmod -R 644 "${DKMS_SRC_DIR}"
# 添加到 DKMS
echo "添加 ${PACKAGE_NAME} 到 DKMS..."
if ! dkms status -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" | grep -q "added"; then
dkms add -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}"
fi
# 编译模块
echo "编译内核模块..."
dkms build -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}"
# 安装模块
echo "安装内核模块..."
dkms install -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}"
# 更新模块依赖
echo "更新模块依赖..."
depmod -a
# 加载模块
echo "加载内核模块: ${MODULE_NAME}"
if modprobe "${MODULE_NAME}" 2>/dev/null; then
echo "模块 ${MODULE_NAME} 加载成功"
else
echo "错误: 模块 ${MODULE_NAME} 加载失败" >&2
exit 1
fi
# 设置模块开机自动加载
if [ ! -f "/etc/modules-load.d/${PACKAGE_NAME}.conf" ]; then
echo "配置模块开机自动加载..."
echo "${MODULE_NAME}" > "/etc/modules-load.d/${PACKAGE_NAME}.conf"
fi
echo "${PACKAGE_NAME} 安装完成"
;;
abort-upgrade|abort-remove|abort-deconfigure)
echo "安装过程被中断,不做配置"
;;
*)
echo "postinst 被调用时参数: $1" >&2
;;
esac
exit 0
@@ -0,0 +1,102 @@
#!/bin/bash
# prerm - 卸载前卸载内核模块
set -e
# 当脚本由 dpkg 触发时,$DPKG_MAIN_TSN_PACKAGE 通常可用
PACKAGE_NAME="${DPKG_MAIN_TSN_PACKAGE}"
if [ -z "$PACKAGE_NAME" ]; then
SCRIPT_NAME=$(basename "$0")
PACKAGE_NAME=$(echo "$SCRIPT_NAME" | sed 's/\.postinst$//' | sed 's/\.prerm$//')
fi
# 从 dpkg 数据库获取版本
if [ -n "$PACKAGE_NAME" ]; then
PACKAGE_VERSION=$(dpkg-query -W -f='${Version}' "$PACKAGE_NAME" 2>/dev/null)
# 去掉 Debian 修订号(-1, -2, -ubuntu1 等)
PACKAGE_VERSION=$(echo "$PACKAGE_VERSION" | sed 's/-.*$//')
fi
DKMS_SRC_DIR="/usr/src/${PACKAGE_NAME}-${PACKAGE_VERSION}"
MAKEFILE_ABS_PATH="${DKMS_SRC_DIR}/Makefile"
MODULE_NAME=""
# 【核心修复】:使用绝对路径去解析真正的 Makefile 提取驱动名
if [ -f "${MAKEFILE_ABS_PATH}" ]; then
MODULE_NAME=$(sed -n -E 's/^DRIVER_NAME[[:space:]]*:?=[[:space:]]*([^[:space:]]+).*/\1/p' "${MAKEFILE_ABS_PATH}" | head -1 | sed 's/\.ko$//')
fi
# 如果从 Makefile 没解析出来,降级使用包名作为模块名
if [ -z "$MODULE_NAME" ]; then
MODULE_NAME="${PACKAGE_NAME}"
fi
case "$1" in
remove)
echo "正在卸载 ${PACKAGE_NAME}..."
# 1. 卸载内核模块
echo "检查内核模块状态: ${MODULE_NAME}"
if lsmod | grep -q "^${MODULE_NAME}"; then
echo "正在从当前内核中卸载模块 ${MODULE_NAME}..."
if rmmod "${MODULE_NAME}" 2>/dev/null; then
echo "模块 ${MODULE_NAME} 卸载成功"
else
echo "警告: 模块 ${MODULE_NAME} 卸载失败,可能有其他进程正在使用它" >&2
fi
else
echo "模块 ${MODULE_NAME} 当前未加载"
fi
# 2. 从 DKMS 移除模块 (修复:扩大 grep 范围以兼容 added/installed 各种状态)
if dkms status | grep -q "${PACKAGE_NAME}/${PACKAGE_VERSION}"; then
echo "从 DKMS 移除 ${PACKAGE_NAME} ${PACKAGE_VERSION}..."
dkms remove -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" --all 2>/dev/null || true
fi
echo "正在强行清理 /lib/modules/ 下的所有 ${MODULE_NAME} 残留文件..."
find /lib/modules/ -name "${MODULE_NAME}.ko*" -delete 2>/dev/null || true
# 3. 更新模块依赖
echo "更新模块依赖..."
depmod -a
# 4. 删除开机自动加载配置
if [ -f "/etc/modules-load.d/${PACKAGE_NAME}.conf" ]; then
echo "删除开机自动加载配置..."
rm -f "/etc/modules-load.d/${PACKAGE_NAME}.conf"
fi
echo "${PACKAGE_NAME} 卸载完成"
;;
upgrade)
echo "软件包升级中,清理旧版本..."
# 1. 升级时卸载旧版本的内核运行模块
if lsmod | grep -q "^${MODULE_NAME}"; then
echo "正在卸载旧版本内核运行模块..."
rmmod "${MODULE_NAME}" 2>/dev/null || true
fi
# 2. 从 DKMS 移除旧版本
if dkms status | grep -q "${PACKAGE_NAME}/${PACKAGE_VERSION}"; then
echo "从 DKMS 移除旧版本模块记录..."
dkms remove -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" --all || true
fi
# 3. 清理旧的内核模块文件 (原逻辑保留,提供兜底支持)
rm -f /lib/modules/*/kernel/drivers/misc/${MODULE_NAME}.ko 2>/dev/null || true
depmod -a
;;
failed-upgrade)
echo "升级失败,保留原有配置"
;;
*)
echo "prerm 被调用时参数: $1" >&2
;;
esac
exit 0
@@ -0,0 +1,22 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: cal-pcie
Source: http://192.168.20.33:1000/niuzhenyu/cal-pcie.git
Files: debian/*
Copyright: 2026 Junwei Qin <qinjunwei@calculet.tech>
License: GPL-2+
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
.
This package 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.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>
.
On Debian systems, the complete text of the GNU General
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
@@ -0,0 +1,334 @@
# SPDX-License-Identifier: GPL-2.0
# quiet command
ifeq ($(Q),)
Q=@
endif
# If KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq ($(KERNELRELEASE),)
# 主要的构建目标
obj-m := calculet_pci.o
# 源文件列表 - 包含所有优化后的模块
calculet_pci-objs := calculet_pci_core.o board_mgr.o proc.o dma.o fops.o utils.o
# 基本编译标志
ccflags-y += -DCALCULET_DRIVER_OPTIMIZED
ccflags-y += -Wall -Wextra -Wno-unused-parameter
ccflags-y += -DCALCULET_VERSION_MAJOR=1 -DCALCULET_VERSION_MINOR=1
# 日志级别控制
# LOG_LEVEL: 0=EMERG, 1=ALERT, 2=CRIT, 3=ERROR, 4=WARN, 5=NOTICE, 6=INFO, 7=DEBUG
ifndef LOG_LEVEL
ifeq ($(DEBUG), 1)
LOG_LEVEL = 7 # DEBUG级别
else
LOG_LEVEL = 6 # INFO级别
endif
endif
ccflags-y += -DCALCULET_LOG_LEVEL=$(LOG_LEVEL)
# 调试模式设置
ifeq ($(DEBUG), 1)
ccflags-y += -DDEBUG -g -O0
ccflags-y += -DCALCULET_DEBUG_MODE
ccflags-y += -DCALCULET_LOG_LEVEL=7
else
ccflags-y += -O2
ccflags-y += -DNDEBUG
endif
# 性能分析模式
ifeq ($(PERF), 1)
ccflags-y += -DCALCULET_PERF_ANALYSIS
endif
# 模拟器模式
ifeq ($(EMULATOR), 1)
ccflags-y += -DCALCULET_EMULATOR
endif
# 启用多线程优化
ccflags-y += -DCALCULET_MULTITHREAD_OPTIMIZED
# 严格模式(更多编译检查)
ifeq ($(STRICT), 1)
ccflags-y += -Werror -Wno-error=unused-function -Wno-error=unused-variable
endif
# 内存调试
ifeq ($(MEM_DEBUG), 1)
ccflags-y += -DCALCULET_MEM_DEBUG
endif
else
# Otherwise we were called directly from the command
# line; invoke the kernel build system.
BUILD_DIR = build
# default is system arch
ifndef ARCH
ARCH=$(shell uname -m)
endif
# set output folder ( e.g. release | debug )
# Default is release folder
TARGET_DIR:=$(BUILD_DIR)/release/$(ARCH)
DRIVER_NAME=calculet_pci.ko
DRIVER_NAME_NO_EXT=calculet_pci
# 版本号提取 - 使用脚本避免shell语法问题
DRIVER_VERSION=$(shell ./extract_version.sh 2>/dev/null || echo "1.0.0")
# 日志级别控制
ifndef LOG_LEVEL
ifeq ($(DEBUG), 1)
LOG_LEVEL = 7 # DEBUG级别
else
LOG_LEVEL = 6 # INFO级别
endif
endif
ifeq ($(DEBUG), 1)
GDB_FLAG=CONFIG_DEBUG_INFO=y CONFIG_FRAME_POINTER=y
TARGET_DIR:=$(BUILD_DIR)/debug/$(ARCH)
endif
# 构建参数设置
USER_FLAGS=
# Internal use only. Compile driver in emulator mode.
ifeq ($(EMULATOR), 1)
USER_FLAGS+=EMULATOR=1
endif
# 性能分析模式
ifeq ($(PERF), 1)
USER_FLAGS+= PERF=1
endif
# 严格模式
ifeq ($(STRICT), 1)
USER_FLAGS+= STRICT=1
endif
# 内存调试模式
ifeq ($(MEM_DEBUG), 1)
USER_FLAGS+= MEM_DEBUG=1
endif
# 日志级别传递
USER_FLAGS+= LOG_LEVEL=$(LOG_LEVEL)
ifndef kernelver
kernelver=$(shell uname -r)
endif
MODULES := /lib/modules/${kernelver}/
KERNEL_DIR ?= $(MODULES)/build
DEPMOD ?= depmod
PWD := $(shell pwd)
default: all
help:
$(Q)echo "******************************************************************************"
$(Q)echo "* Optimized PCIe Driver *"
$(Q)echo "* usage: make [options] [target] *"
$(Q)echo "* *"
$(Q)echo "* options: *"
$(Q)echo "* DEBUG=1 : Activate debug mode with full debugging support *"
$(Q)echo "* LOG_LEVEL=n : Set log level (0=EMERG to 7=DEBUG, default: 6 for *"
$(Q)echo "* release, 7 for debug) *"
$(Q)echo "* STRICT=1 : Enable strict compilation with -Werror *"
$(Q)echo "* PERF=1 : Enable performance analysis features *"
$(Q)echo "* MEM_DEBUG=1 : Enable memory debugging and leak detection *"
$(Q)echo "* EMULATOR=1 : Compile driver in emulator mode *"
$(Q)echo "* Q= : Activate makefile verbose mode *"
$(Q)echo "* *"
$(Q)echo "* target: *"
$(Q)echo "* all Generate the ko file in $(BUILD_DIR)/[release|debug]/$(ARCH) *"
$(Q)echo "* clean Delete generated files and $(BUILD_DIR) directory *"
$(Q)echo "* install Install driver and setup auto boot *"
$(Q)echo "* install_dkms Install driver using DKMS *"
$(Q)echo "* uninstall Uninstall driver *"
$(Q)echo "* check Run static analysis and code checks *"
$(Q)echo "* test Run basic driver tests (if available) *"
$(Q)echo "* debug Build debug version (same as DEBUG=1 all) *"
$(Q)echo "* release Build release version (optimized) *"
$(Q)echo "* help Display this help *"
$(Q)echo "* *"
$(Q)echo "* Examples: *"
$(Q)echo "* make # Build release version *"
$(Q)echo "* make DEBUG=1 # Build debug version *"
$(Q)echo "* make LOG_LEVEL=4 # Build with WARN log level *"
$(Q)echo "* make PERF=1 STRICT=1 # Build with performance analysis *"
$(Q)echo "******************************************************************************"
all: $(TARGET_DIR)
$(Q)echo "Building optimized PCIe driver (LOG_LEVEL=$(LOG_LEVEL))..."
$(Q)$(MAKE) -C $(KERNEL_DIR) M=$(PWD) $(GDB_FLAG) $(USER_FLAGS) modules
$(Q)cp $(DRIVER_NAME) $(TARGET_DIR)
$(Q)echo "Build completed. Driver located at: $(TARGET_DIR)/$(DRIVER_NAME)"
debug:
$(Q)echo "Building debug version..."
$(Q)$(MAKE) DEBUG=1 all
release:
$(Q)echo "Building release version..."
$(Q)$(MAKE) DEBUG=0 all
$(TARGET_DIR):
$(Q)mkdir -p $@
clean:
$(Q)echo "Cleaning build artifacts..."
$(Q)$(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean
$(Q)rm -rf $(BUILD_DIR)
$(Q)rm -f *.o.ur-safe
$(Q)echo "Clean completed."
install:
$(Q)echo "Installing driver..."
$(Q)$(MAKE) -C $(KERNEL_DIR) M=$(PWD) INSTALL_MOD_DIR=kernel/drivers/misc modules_install
$(Q)$(DEPMOD) -a
$(Q)echo "Driver installed successfully."
uninstall: uninstall_all_dkms
ifneq ($(wildcard $(MODULES)),)
$(Q)echo "Uninstalling driver..."
$(Q)rm -f $(MODULES)kernel/drivers/misc/$(DRIVER_NAME)
$(Q)$(DEPMOD) -a
$(Q)echo "Driver uninstalled."
endif
install_dkms: uninstall
ifneq ($(shell id -u),0)
@echo "make install_dkms should run as root"
exit 1
endif
ifeq ($(strip $(shell which dkms)),)
@echo "make install_dkms requires dkms to be installed"
exit 1
endif
$(Q)echo "Building DKMS package..."
# build DKMS
$(Q)mkdir -p /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)
$(Q)cp -r . /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)/
$(Q)echo 'PACKAGE_NAME="$(DRIVER_NAME_NO_EXT)"' > /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)/dkms.conf
$(Q)echo 'PACKAGE_VERSION="$(DRIVER_VERSION)"' >> /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)/dkms.conf
$(Q)echo 'BUILT_MODULE_NAME[0]="$(DRIVER_NAME_NO_EXT)"' >> /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)/dkms.conf
$(Q)echo 'DEST_MODULE_LOCATION[0]="/kernel/drivers/misc"' >> /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)/dkms.conf
$(Q)echo 'MAKE[0]="make all"' >> /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)/dkms.conf
$(Q)echo 'CLEAN="make clean"' >> /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)/dkms.conf
$(Q)echo 'AUTOINSTALL="yes"' >> /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)/dkms.conf
$(Q)dkms add -m $(DRIVER_NAME_NO_EXT) -v $(DRIVER_VERSION)
$(Q)dkms build -m $(DRIVER_NAME_NO_EXT) -v $(DRIVER_VERSION) || (cat /var/lib/dkms/$(DRIVER_NAME_NO_EXT)/$(DRIVER_VERSION)/build/make.log; exit 1)
# install DKMS
$(Q)dkms install -m $(DRIVER_NAME_NO_EXT) -v $(DRIVER_VERSION) --force
$(Q)echo "DKMS installation completed."
uninstall_all_dkms:
ifneq ($(shell id -u),0)
@echo "make uninstall_all_dkms should run as root"
exit 1
endif
# Uninstall driver from dkms, if dkms is installed
# If the driver wasn't installed with dkms, the following commands won't do anything
ifneq ($(strip $(shell which dkms)),)
-$(Q)dkms remove $(DRIVER_NAME_NO_EXT)/$(DRIVER_VERSION) --all 2>/dev/null || true
-$(Q)rm -rf /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION) 2>/dev/null || true
$(Q)echo "DKMS uninstallation completed."
endif
# 代码检查目标
check:
$(Q)echo "Running code checks..."
$(Q)if command -v sparse >/dev/null 2>&1; then \
echo "Running sparse analysis..."; \
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) C=1 modules; \
else \
echo "Sparse not found, skipping static analysis"; \
fi
$(Q)if command -v checkpatch.pl >/dev/null 2>&1; then \
echo "Running checkpatch analysis..."; \
find . -name "*.c" -o -name "*.h" | xargs -I {} checkpatch.pl --no-tree --file {}; \
else \
echo "checkpatch.pl not found, skipping patch style check"; \
fi
$(Q)echo "Code checks completed."
# 测试目标
test: all
$(Q)echo "Running basic driver tests..."
$(Q)if [ -f tests/run_tests.sh ]; then \
cd tests && ./run_tests.sh; \
else \
echo "No tests available. Please implement tests/run_tests.sh"; \
fi
# 性能分析目标
perf:
$(Q)echo "Building driver with performance analysis..."
$(Q)$(MAKE) PERF=1 all
# 文档生成
docs:
$(Q)echo "Generating documentation..."
$(Q)if command -v doxygen >/dev/null 2>&1; then \
doxygen Doxyfile 2>/dev/null || echo "Doxyfile not found"; \
else \
echo "Doxygen not found, skipping documentation generation"; \
fi
# 打包目标
package: all
$(Q)echo "Creating driver package..."
$(Q)mkdir -p package/calculet-driver-$(DRIVER_VERSION)
$(Q)cp -r . package/calculet-driver-$(DRIVER_VERSION)/
$(Q)cd package && tar czf calculet-driver-$(DRIVER_VERSION).tar.gz calculet-driver-$(DRIVER_VERSION)/
$(Q)echo "Package created: package/calculet-driver-$(DRIVER_VERSION).tar.gz"
# 显示编译信息
info:
$(Q)echo "Driver Information:"
$(Q)echo " Name: $(DRIVER_NAME)"
$(Q)echo " Version: $(DRIVER_VERSION)"
$(Q)echo " Architecture: $(ARCH)"
$(Q)echo " Kernel: $(kernelver)"
$(Q)echo " Build Dir: $(TARGET_DIR)"
$(Q)echo " Log Level: $(LOG_LEVEL)"
$(Q)echo " Debug Mode: $(if $(DEBUG),Enabled,Disabled)"
$(Q)echo " Emulator: $(if $(EMULATOR),Enabled,Disabled)"
$(Q)echo " Performance: $(if $(PERF),Enabled,Disabled)"
$(Q)echo " Strict Mode: $(if $(STRICT),Enabled,Disabled)"
$(Q)echo " Memory Debug: $(if $(MEM_DEBUG),Enabled,Disabled)"
# 日志级别说明
log-help:
$(Q)echo "Log Level Settings:"
$(Q)echo " 0 = EMERG - Emergency: system is unusable"
$(Q)echo " 1 = ALERT - Alert: action must be taken immediately"
$(Q)echo " 2 = CRIT - Critical: critical conditions"
$(Q)echo " 3 = ERROR - Error: error conditions"
$(Q)echo " 4 = WARN - Warning: warning conditions"
$(Q)echo " 5 = NOTICE - Notice: normal but significant condition"
$(Q)echo " 6 = INFO - Info: informational messages (default for release)"
$(Q)echo " 7 = DEBUG - Debug: debug-level messages (default for debug)"
$(Q)echo ""
$(Q)echo "Examples:"
$(Q)echo " make LOG_LEVEL=3 # Only show errors and above"
$(Q)echo " make LOG_LEVEL=7 # Show all messages"
$(Q)echo " make DEBUG=1 # Debug build with LOG_LEVEL=7"
endif
.PHONY: help all clean install uninstall install_dkms uninstall_all_dkms check test perf docs package info debug release log-help
@@ -0,0 +1,381 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#include "board_mgr.h"
#include "proc.h"
#include "utils.h"
#include <linux/idr.h>
static struct calculet_board_manager g_board_mgr = {
.board_list = LIST_HEAD_INIT(g_board_mgr.board_list),
.board_mutex = __MUTEX_INITIALIZER(g_board_mgr.board_mutex),
.board_count = ATOMIC_INIT(0),
.next_board_index = 0,
.index_lock = __SPIN_LOCK_UNLOCKED(g_board_mgr.index_lock),
.initialized = false,
};
// 板卡引用计数管理
void calculet_board_get(struct calculet_pcie_board *board)
{
if (board) {
kref_get(&board->ref_count);
}
}
void calculet_board_put(struct calculet_pcie_board *board)
{
if (board) {
kref_put(&board->ref_count, calculet_board_release);
}
}
void calculet_board_release(struct kref *ref)
{
struct calculet_pcie_board *board = container_of(ref, struct calculet_pcie_board, ref_count);
calculet_log(CALCULET_LOG_DEBUG, "Releasing board %d resources", board->board_index);
// 释放IDR索引
spin_lock(&g_board_mgr.index_lock);
idr_remove(&g_board_mgr.board_idr, board->board_index);
spin_unlock(&g_board_mgr.index_lock);
kfree(board);
}
// 板卡状态管理(简化,去掉不必要的锁)
void calculet_board_set_state(struct calculet_pcie_board *board, enum calculet_board_state state)
{
if (board && board->state != state) {
calculet_log(CALCULET_LOG_DEBUG, "Board %d state change: %d -> %d",
board->board_index, board->state, state);
WRITE_ONCE(board->state, state);
}
}
enum calculet_board_state calculet_board_get_state(struct calculet_pcie_board *board)
{
return board ? READ_ONCE(board->state) : BOARD_STATE_UNKNOWN;
}
bool calculet_board_is_operational(struct calculet_pcie_board *board)
{
if (!board) return false;
enum calculet_board_state state = calculet_board_get_state(board);
return (state == BOARD_STATE_ACTIVE) && board->pcie_resources.resources_valid;
}
int calculet_board_mgr_init(void)
{
if (g_board_mgr.initialized) {
return 0;
}
INIT_LIST_HEAD(&g_board_mgr.board_list);
mutex_init(&g_board_mgr.board_mutex);
atomic_set(&g_board_mgr.board_count, 0);
g_board_mgr.next_board_index = 0;
spin_lock_init(&g_board_mgr.index_lock);
// 初始化IDR
idr_init(&g_board_mgr.board_idr);
// 初始化全局复位控制
atomic_set(&g_board_mgr.reset_ctrl.boards_resetting, 0);
atomic_set(&g_board_mgr.reset_ctrl.boards_total, 0);
mutex_init(&g_board_mgr.reset_ctrl.reset_mutex);
init_waitqueue_head(&g_board_mgr.reset_ctrl.reset_wq);
g_board_mgr.initialized = true;
calculet_log(CALCULET_LOG_DEBUG, "Board manager initialized");
return 0;
}
void calculet_board_mgr_cleanup(void)
{
struct calculet_pcie_board *board, *tmp;
if (!g_board_mgr.initialized) {
return;
}
mutex_lock(&g_board_mgr.board_mutex);
list_for_each_entry_safe(board, tmp, &g_board_mgr.board_list, board_list) {
list_del(&board->board_list);
calculet_board_set_state(board, BOARD_STATE_REMOVING);
calculet_board_put(board);
}
mutex_unlock(&g_board_mgr.board_mutex);
// 销毁IDR
idr_destroy(&g_board_mgr.board_idr);
g_board_mgr.initialized = false;
calculet_log(CALCULET_LOG_DEBUG, "Board manager cleaned up");
}
struct calculet_pcie_board* calculet_board_mgr_get_board(u32 index)
{
struct calculet_pcie_board *board = NULL;
if (!g_board_mgr.initialized) {
return NULL;
}
rcu_read_lock();
board = idr_find(&g_board_mgr.board_idr, index);
if (board && calculet_board_is_operational(board)) {
calculet_board_get(board);
} else {
board = NULL;
}
rcu_read_unlock();
return board;
}
void calculet_board_mgr_put_board(struct calculet_pcie_board *board)
{
calculet_board_put(board);
}
int calculet_board_mgr_add_board(struct calculet_pcie_board *board)
{
int ret = 0;
int board_id;
if (!g_board_mgr.initialized || !board) {
return -EINVAL;
}
calculet_board_set_state(board, BOARD_STATE_ACTIVE);
mutex_lock(&g_board_mgr.board_mutex);
// 使用IDR分配板卡索引
spin_lock(&g_board_mgr.index_lock);
board_id = idr_alloc(&g_board_mgr.board_idr, board, 0, MAX_CALCULET_BOARDS, GFP_ATOMIC);
spin_unlock(&g_board_mgr.index_lock);
if (board_id < 0) {
ret = board_id;
goto out;
}
board->board_index = board_id;
// 初始化板卡结构
kref_init(&board->ref_count);
// 初始化统计信息
memset(&board->stats, 0, sizeof(board->stats));
board->stats.uptime_start = ktime_get();
atomic64_set(&board->stats.dma_transfers_h2c, 0);
atomic64_set(&board->stats.dma_transfers_c2h, 0);
atomic64_set(&board->stats.dma_bytes_h2c, 0);
atomic64_set(&board->stats.dma_bytes_c2h, 0);
atomic64_set(&board->stats.interrupts_count, 0);
atomic64_set(&board->stats.errors_count, 0);
// 初始化进程监测
INIT_LIST_HEAD(&board->process_list);
mutex_init(&board->process_list_mutex);
atomic_set(&board->process_count, 0);
// 初始化软复位控制
atomic_set(&board->reset_in_progress, 0);
// 初始化资源
board->pcie_resources.resources_valid = true;
// 添加到列表
list_add_tail(&board->board_list, &g_board_mgr.board_list);
atomic_inc(&g_board_mgr.board_count);
atomic_inc(&g_board_mgr.reset_ctrl.boards_total);
// 创建proc条目
snprintf(board->proc_name, sizeof(board->proc_name), "device%d", board->board_index);
ret = calculet_proc_add_device(board);
if (ret < 0) {
list_del(&board->board_list);
atomic_dec(&g_board_mgr.board_count);
atomic_dec(&g_board_mgr.reset_ctrl.boards_total);
spin_lock(&g_board_mgr.index_lock);
idr_remove(&g_board_mgr.board_idr, board->board_index);
spin_unlock(&g_board_mgr.index_lock);
goto out;
}
calculet_log(CALCULET_LOG_DEBUG, "Board %d added successfully", board->board_index);
out:
mutex_unlock(&g_board_mgr.board_mutex);
return ret;
}
void calculet_board_mgr_remove_board(struct calculet_pcie_board *board)
{
if (!board) {
return;
}
calculet_board_set_state(board, BOARD_STATE_REMOVING);
mutex_lock(&g_board_mgr.board_mutex);
// 清理进程监测
calculet_proc_cleanup_processes(board);
// 移除proc条目
calculet_proc_remove_device(board);
// 从列表移除
list_del(&board->board_list);
atomic_dec(&g_board_mgr.board_count);
atomic_dec(&g_board_mgr.reset_ctrl.boards_total);
// 标记资源无效
board->pcie_resources.resources_valid = false;
mutex_unlock(&g_board_mgr.board_mutex);
// 释放引用(这将触发最终清理)
calculet_board_put(board);
calculet_log(CALCULET_LOG_INFO, "Board removed successfully");
}
int calculet_board_mgr_get_all_boards(struct calculet_pcie_board **boards, int max_boards)
{
struct calculet_pcie_board *board;
int count = 0;
if (!g_board_mgr.initialized || !boards) {
return 0;
}
mutex_lock(&g_board_mgr.board_mutex);
list_for_each_entry(board, &g_board_mgr.board_list, board_list) {
if (count >= max_boards) {
break;
}
if (calculet_board_is_operational(board)) {
boards[count] = board;
calculet_board_get(board);
count++;
}
}
mutex_unlock(&g_board_mgr.board_mutex);
return count;
}
// 板卡健康检查
int calculet_board_health_check(struct calculet_pcie_board *board)
{
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
// 检查PCIe设备状态
if (!board->pDev || pci_channel_offline(board->pDev)) {
calculet_log(CALCULET_LOG_ERROR, "Board %d PCIe device is offline", board->board_index);
return -EIO;
}
// 检查资源有效性
if (!board->pcie_resources.resources_valid) {
calculet_log(CALCULET_LOG_ERROR, "Board %d resources are invalid", board->board_index);
return -EINVAL;
}
// 检查错误率
u64 total_ops = atomic64_read(&board->stats.dma_transfers_h2c) +
atomic64_read(&board->stats.dma_transfers_c2h);
u64 error_rate = total_ops > 0 ?
(atomic64_read(&board->stats.errors_count) * 100) / total_ops : 0;
if (error_rate > 5) { // 错误率超过5%
calculet_log(CALCULET_LOG_WARN, "Board %d high error rate: %llu%%",
board->board_index, error_rate);
return -EFAULT;
}
return 0;
}
// 全局软复位功能
int calculet_global_soft_reset_prepare(struct calculet_pcie_board *board)
{
if (!board) {
return -EINVAL;
}
// 检查是否已经在复位中
if (atomic_cmpxchg(&board->reset_in_progress, 0, 1) != 0) {
calculet_log(CALCULET_LOG_WARN, "Reset already in progress for board %d", board->board_index);
return -EBUSY;
}
mutex_lock(&g_board_mgr.reset_ctrl.reset_mutex);
// 增加正在复位的板卡计数
atomic_inc(&g_board_mgr.reset_ctrl.boards_resetting);
calculet_log(CALCULET_LOG_DEBUG, "Board %d prepared for reset (%d/%d boards resetting)",
board->board_index,
atomic_read(&g_board_mgr.reset_ctrl.boards_resetting),
atomic_read(&g_board_mgr.reset_ctrl.boards_total));
mutex_unlock(&g_board_mgr.reset_ctrl.reset_mutex);
return 0;
}
void calculet_global_soft_reset_complete(struct calculet_pcie_board *board)
{
if (!board) {
return;
}
mutex_lock(&g_board_mgr.reset_ctrl.reset_mutex);
// 减少正在复位的板卡计数
if (atomic_read(&g_board_mgr.reset_ctrl.boards_resetting) > 0) {
atomic_dec(&g_board_mgr.reset_ctrl.boards_resetting);
}
// 清除板卡复位标志
atomic_set(&board->reset_in_progress, 0);
calculet_log(CALCULET_LOG_DEBUG, "Board %d reset completed (%d/%d boards still resetting)",
board->board_index,
atomic_read(&g_board_mgr.reset_ctrl.boards_resetting),
atomic_read(&g_board_mgr.reset_ctrl.boards_total));
// 唤醒等待的线程
wake_up_all(&g_board_mgr.reset_ctrl.reset_wq);
mutex_unlock(&g_board_mgr.reset_ctrl.reset_mutex);
}
bool calculet_global_soft_reset_can_rescan(void)
{
int resetting = atomic_read(&g_board_mgr.reset_ctrl.boards_resetting);
int total = atomic_read(&g_board_mgr.reset_ctrl.boards_total);
// 只有当所有板卡都完成复位或者没有板卡时才能进行rescan
bool can_rescan = (resetting == 0) || (total == 0);
calculet_log(CALCULET_LOG_DEBUG, "Reset status: %d/%d boards resetting, can_rescan=%s",
resetting, total, can_rescan ? "yes" : "no");
return can_rescan;
}
@@ -0,0 +1,169 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#ifndef _CALCULET_BOARD_MGR_H_
#define _CALCULET_BOARD_MGR_H_
#include "ioctl.h"
#include "dma.h"
#include <linux/pci.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/atomic.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/kref.h>
#include <linux/idr.h>
#define MAX_CALCULET_BOARDS 16
struct calculet_dma_controller;
// 板卡状态枚举
enum calculet_board_state {
BOARD_STATE_UNKNOWN = 0,
BOARD_STATE_ACTIVE,
BOARD_STATE_REMOVING
};
// 资源结构体
struct calculet_resource {
u64 phy_address;
resource_size_t address;
size_t size;
struct mutex dma_global_lock; // 只保留DMA全局锁
};
// PCIe资源集合
struct calculet_pcie_resources {
struct calculet_resource dma_registers; // BAR0
struct calculet_resource rt_registers; // BAR2
struct calculet_resource smi_registers; // BAR4
bool resources_valid; // 资源有效性标志
};
// 简化的板卡统计信息
struct calculet_board_stats {
atomic64_t dma_transfers_h2c;
atomic64_t dma_transfers_c2h;
atomic64_t dma_bytes_h2c;
atomic64_t dma_bytes_c2h;
atomic64_t interrupts_count;
atomic64_t errors_count;
ktime_t uptime_start;
};
// 全局软复位控制
struct calculet_global_reset_ctrl {
atomic_t boards_resetting; // 正在复位的板卡数量
atomic_t boards_total; // 总板卡数量
struct mutex reset_mutex; // 复位互斥锁
wait_queue_head_t reset_wq; // 复位等待队列
};
// PCIe板卡结构体(简化)
struct calculet_pcie_board {
struct list_head board_list;
struct pci_dev *pDev;
u32 board_index;
struct kref ref_count;
enum calculet_board_state state; // 板卡状态
struct calculet_pcie_resources pcie_resources;
struct calculet_dma_controller cdma;
bool interrupts_enabled;
// 简化的统计信息
struct calculet_board_stats stats;
// 进程监测相关字段
struct list_head process_list; // 进程列表头
struct mutex process_list_mutex; // 保护进程列表的互斥锁
atomic_t process_count; // 进程计数
// proc相关
struct proc_dir_entry *proc_entry;
char proc_name[32];
// 软复位相关
atomic_t reset_in_progress; // 复位进行中标志
};
// 全局板卡管理器
struct calculet_board_manager {
struct list_head board_list;
struct mutex board_mutex;
atomic_t board_count;
u32 next_board_index;
spinlock_t index_lock;
struct idr board_idr;
bool initialized;
struct calculet_global_reset_ctrl reset_ctrl; // 全局复位控制
};
// 板卡引用计数管理
void calculet_board_get(struct calculet_pcie_board *board);
void calculet_board_put(struct calculet_pcie_board *board);
void calculet_board_release(struct kref *ref);
// 板卡状态管理(简化)
void calculet_board_set_state(struct calculet_pcie_board *board, enum calculet_board_state state);
enum calculet_board_state calculet_board_get_state(struct calculet_pcie_board *board);
bool calculet_board_is_operational(struct calculet_pcie_board *board);
// 函数声明
int calculet_board_mgr_init(void);
void calculet_board_mgr_cleanup(void);
struct calculet_pcie_board* calculet_board_mgr_get_board(u32 index);
void calculet_board_mgr_put_board(struct calculet_pcie_board *board);
int calculet_board_mgr_add_board(struct calculet_pcie_board *board);
void calculet_board_mgr_remove_board(struct calculet_pcie_board *board);
// 统计信息更新(原子版本,无锁)
static inline void calculet_board_stats_update_dma_h2c(struct calculet_pcie_board *board, size_t bytes)
{
if (likely(board && calculet_board_is_operational(board))) {
atomic64_inc(&board->stats.dma_transfers_h2c);
atomic64_add(bytes, &board->stats.dma_bytes_h2c);
}
}
static inline void calculet_board_stats_update_dma_c2h(struct calculet_pcie_board *board, size_t bytes)
{
if (likely(board && calculet_board_is_operational(board))) {
atomic64_inc(&board->stats.dma_transfers_c2h);
atomic64_add(bytes, &board->stats.dma_bytes_c2h);
}
}
static inline void calculet_board_stats_update_interrupt(struct calculet_pcie_board *board)
{
if (likely(board)) {
atomic64_inc(&board->stats.interrupts_count);
}
}
static inline void calculet_board_stats_update_error(struct calculet_pcie_board *board)
{
if (likely(board)) {
atomic64_inc(&board->stats.errors_count);
}
}
// 获取所有板卡列表(用于proc显示)
int calculet_board_mgr_get_all_boards(struct calculet_pcie_board **boards, int max_boards);
// 板卡健康检查和错误处理
int calculet_board_health_check(struct calculet_pcie_board *board);
// 全局软复位功能
int calculet_global_soft_reset_prepare(struct calculet_pcie_board *board);
void calculet_global_soft_reset_complete(struct calculet_pcie_board *board);
bool calculet_global_soft_reset_can_rescan(void);
#endif /* _CALCULET_BOARD_MGR_H_ */
@@ -0,0 +1,195 @@
#include <linux/module.h>
#define INCLUDE_VERMAGIC
#include <linux/build-salt.h>
#include <linux/elfnote-lto.h>
#include <linux/export-internal.h>
#include <linux/vermagic.h>
#include <linux/compiler.h>
#ifdef CONFIG_UNWINDER_ORC
#include <asm/orc_header.h>
ORC_HEADER;
#endif
BUILD_SALT;
BUILD_LTO_INFO;
MODULE_INFO(vermagic, VERMAGIC_STRING);
MODULE_INFO(name, KBUILD_MODNAME);
__visible struct module __this_module
__section(".gnu.linkonce.this_module") = {
.name = KBUILD_MODNAME,
.init = init_module,
#ifdef CONFIG_MODULE_UNLOAD
.exit = cleanup_module,
#endif
.arch = MODULE_ARCH_INIT,
};
#ifdef CONFIG_MITIGATION_RETPOLINE
MODULE_INFO(retpoline, "Y");
#endif
KSYMTAB_DATA(calculet_log_level, "", "");
KSYMTAB_FUNC(calculet_global_stats_init, "", "");
KSYMTAB_FUNC(calculet_global_stats_update, "", "");
KSYMTAB_FUNC(calculet_global_stats_get, "", "");
KSYMTAB_FUNC(calculet_global_stats_cleanup, "", "");
KSYMTAB_FUNC(calculet_random_delay_us, "", "");
KSYMTAB_FUNC(calculet_strdup, "", "");
KSYMTAB_FUNC(calculet_snprintf_safe, "", "");
KSYMTAB_FUNC(calculet_ffs, "", "");
KSYMTAB_FUNC(calculet_fls, "", "");
KSYMTAB_FUNC(calculet_popcount, "", "");
KSYMTAB_FUNC(calculet_get_cycles, "", "");
KSYMTAB_FUNC(calculet_get_monotonic_time, "", "");
KSYMTAB_FUNC(calculet_get_real_time, "", "");
KSYMTAB_FUNC(calculet_crc32, "", "");
KSYMTAB_FUNC(calculet_utils_init, "", "");
KSYMTAB_FUNC(calculet_utils_cleanup, "", "");
SYMBOL_CRC(calculet_log_level, 0xa6c4c065, "");
SYMBOL_CRC(calculet_global_stats_init, 0x3c49c21b, "");
SYMBOL_CRC(calculet_global_stats_update, 0x2703dbf9, "");
SYMBOL_CRC(calculet_global_stats_get, 0x8eaef0c2, "");
SYMBOL_CRC(calculet_global_stats_cleanup, 0xab13d9fc, "");
SYMBOL_CRC(calculet_random_delay_us, 0x41de3c4b, "");
SYMBOL_CRC(calculet_strdup, 0x5ee22703, "");
SYMBOL_CRC(calculet_snprintf_safe, 0x12fec1fb, "");
SYMBOL_CRC(calculet_ffs, 0xb1fb6ba9, "");
SYMBOL_CRC(calculet_fls, 0x70cd63a2, "");
SYMBOL_CRC(calculet_popcount, 0x2171fdb4, "");
SYMBOL_CRC(calculet_get_cycles, 0x2ddb2922, "");
SYMBOL_CRC(calculet_get_monotonic_time, 0xfd0d90b9, "");
SYMBOL_CRC(calculet_get_real_time, 0xfc4423cc, "");
SYMBOL_CRC(calculet_crc32, 0x62005d8a, "");
SYMBOL_CRC(calculet_utils_init, 0xbecbf10a, "");
SYMBOL_CRC(calculet_utils_cleanup, 0xa17436e2, "");
static const struct modversion_info ____versions[]
__used __section("__versions") = {
{ 0xc1514a3b, "free_irq" },
{ 0x22dde395, "pcie_capability_read_word" },
{ 0xa78af5f3, "ioread32" },
{ 0x88db9f48, "__check_object_size" },
{ 0x8134414f, "dma_ops" },
{ 0x9e7d6bd0, "__udelay" },
{ 0x20978fb9, "idr_find" },
{ 0x5a6efd55, "param_ops_uint" },
{ 0x13c49cc2, "_copy_from_user" },
{ 0x381657e7, "devm_kmalloc" },
{ 0x67620c6, "pci_enable_device" },
{ 0x8d522714, "__rcu_read_lock" },
{ 0x4a453f53, "iowrite32" },
{ 0xe1688e24, "proc_create" },
{ 0x94a18028, "pci_iomap" },
{ 0x920e8334, "pci_alloc_irq_vectors" },
{ 0x656e4a6e, "snprintf" },
{ 0xa6257a2f, "complete" },
{ 0x608741b5, "__init_swait_queue_head" },
{ 0x92540fbf, "finish_wait" },
{ 0xeea0e0d, "class_destroy" },
{ 0x355f4c49, "__pci_register_driver" },
{ 0x7d628444, "memcpy_fromio" },
{ 0xb52801ce, "pci_disable_msi" },
{ 0xd4defbf9, "pci_request_regions" },
{ 0x69acdf38, "memcpy" },
{ 0x88e5f076, "remap_pfn_range" },
{ 0x37a0cba, "kfree" },
{ 0x7ab1f0cb, "pcpu_hot" },
{ 0x7369f212, "seq_lseek" },
{ 0xe964de4b, "proc_create_data" },
{ 0xc3055d20, "usleep_range_state" },
{ 0x8c26d495, "prepare_to_wait_event" },
{ 0xb3f7646e, "kthread_should_stop" },
{ 0xe2964344, "__wake_up" },
{ 0x10ed95a8, "pci_irq_vector" },
{ 0x148653, "vsnprintf" },
{ 0xddbeeecc, "pci_lock_rescan_remove" },
{ 0xba8fbd64, "_raw_spin_lock" },
{ 0xe0872c22, "pci_unregister_driver" },
{ 0xbdfb6dbb, "__fentry__" },
{ 0xcfabba12, "wake_up_process" },
{ 0x4b517ad, "pci_read_config_dword" },
{ 0x122c3a7e, "_printk" },
{ 0x8ddd8aad, "schedule_timeout" },
{ 0x1000e51, "schedule" },
{ 0xf0fdf6cb, "__stack_chk_fail" },
{ 0x296695f, "refcount_warn_saturate" },
{ 0x87a21cb3, "__ubsan_handle_out_of_bounds" },
{ 0x4323050a, "pci_find_capability" },
{ 0x7665a95b, "idr_remove" },
{ 0x9f46ced8, "__sw_hweight64" },
{ 0xfe487975, "init_wait_entry" },
{ 0x98378a1d, "cc_mkdec" },
{ 0xb8f11603, "idr_alloc" },
{ 0x92d5838e, "request_threaded_irq" },
{ 0x2469810f, "__rcu_read_unlock" },
{ 0xa4bf0f83, "class_create" },
{ 0x4c03a563, "random_kmalloc_seed" },
{ 0x69dd3b5b, "crc32_le" },
{ 0x4dfa8d4b, "mutex_lock" },
{ 0x7475b515, "dma_alloc_attrs" },
{ 0x2e5f3dce, "pci_read_config_word" },
{ 0x9166fada, "strncpy" },
{ 0x689f3974, "kthread_stop" },
{ 0xcefb0c9f, "__mutex_init" },
{ 0x89940875, "mutex_lock_interruptible" },
{ 0x71cf81bb, "pci_iounmap" },
{ 0x8e17b3ae, "idr_destroy" },
{ 0xa4eda9c3, "proc_mkdir" },
{ 0x2c015e41, "from_kuid" },
{ 0x6794def6, "pci_set_master" },
{ 0x25974000, "wait_for_completion" },
{ 0x5b8239ca, "__x86_return_thunk" },
{ 0x6b10bee1, "_copy_to_user" },
{ 0xd9a5ea54, "__init_waitqueue_head" },
{ 0xac3c422b, "proc_remove" },
{ 0x42b507fc, "dma_set_coherent_mask" },
{ 0x3817aecb, "kthread_create_on_node" },
{ 0xc1d9b323, "seq_read" },
{ 0x3c3ff9fd, "sprintf" },
{ 0xc688b77e, "device_create_with_groups" },
{ 0xa9962270, "dma_free_attrs" },
{ 0x3213f038, "mutex_unlock" },
{ 0x5790e7a0, "pci_unlock_rescan_remove" },
{ 0x2e1c1b75, "param_ops_bool" },
{ 0x8bf35ae8, "pci_release_regions" },
{ 0x6423a399, "init_user_ns" },
{ 0xb329595b, "__register_chrdev" },
{ 0xe9b868f, "device_destroy" },
{ 0xb43f9365, "ktime_get" },
{ 0xc00e2b80, "seq_printf" },
{ 0xd36dc10c, "get_random_u32" },
{ 0x2e4b8e3f, "pci_disable_device" },
{ 0xe16cab4a, "boot_cpu_data" },
{ 0x8e66928c, "single_release" },
{ 0x935c45ad, "dma_set_mask" },
{ 0x904c0891, "pci_stop_and_remove_bus_device" },
{ 0xbf55f104, "kmalloc_trace" },
{ 0x46cf10eb, "cachemode2protval" },
{ 0x54b1fac6, "__ubsan_handle_load_invalid_value" },
{ 0x754d539c, "strlen" },
{ 0xa7e3af6c, "param_ops_int" },
{ 0xbdbea117, "_dev_printk" },
{ 0x2e0257b3, "pci_rescan_bus" },
{ 0x46b0be46, "single_open" },
{ 0xb5b54b34, "_raw_spin_unlock" },
{ 0x4503bd8e, "pci_free_irq_vectors" },
{ 0xf9a482f9, "msleep" },
{ 0xc4f0da12, "ktime_get_with_offset" },
{ 0xeb233a45, "__kmalloc" },
{ 0xe2c17b5d, "__SCT__might_resched" },
{ 0x1004e946, "kmalloc_caches" },
{ 0x6bc3fbc0, "__unregister_chrdev" },
{ 0x73776b79, "module_layout" },
};
MODULE_INFO(depends, "");
MODULE_ALIAS("pci:v00005678d00001234sv*sd*bc*sc*i*");
MODULE_ALIAS("pci:v000020F5d*sv*sd*bc*sc*i*");
MODULE_ALIAS("pci:v000016C3d0000ABCDsv*sd*bc*sc*i*");
MODULE_INFO(srcversion, "E0486E16735A427A5167C20");
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#ifndef _CALCULET_PCI_H_
#define _CALCULET_PCI_H_
#include "board_mgr.h"
#include "dma.h"
#include "ioctl.h"
#include "utils.h"
#include <linux/pci.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#define DRIVER_NAME "calculet"
#define DEVICE_NODE_NAME "calculet"
#define PCI_VENDOR_ID_CALCULET 0x1234
#define PCI_DEVICE_ID_CALCULET 0x5678
#define CALCULET_PCIE_DMA_REGS_BAR (0)
#define CALCULET_PCIE_RT_REGS_BAR (2)
#define CALCULET_PCIE_SMI_REGS_BAR (4)
// DMA中断写入地址寄存器定义
#define DMA_WRITE_DONE_IMWR_ADDRESS_L (0x60)
#define DMA_WRITE_DONE_IMWR_ADDRESS_H (0x64)
#define DMA_WRITE_ABORT_IMWR_ADDRESS_L (0x68)
#define DMA_WRITE_ABORT_IMWR_ADDRESS_H (0x6C)
#define DMA_WRITE_IMWR_DATA (0x70)
#define DMA_READ_DONE_IMWR_ADDRESS_L (0xCC)
#define DMA_READ_DONE_IMWR_ADDRESS_H (0xD0)
#define DMA_READ_ABORT_IMWR_ADDRESS_L (0xD4)
#define DMA_READ_ABORT_IMWR_ADDRESS_H (0xD8)
#define DMA_READ_IMWR_DATA (0xdc)
//
#define CALCULET_OS_STATUS_REG 0x223810C
#define CALCULET_RT_STATUS_REG 0x2238250
// 函数声明
int calculet_enable_interrupts(struct calculet_pcie_board *board);
void calculet_disable_interrupts(struct calculet_pcie_board *board);
int calculet_activate_board(struct calculet_pcie_board *board);
// 资源管理函数
u64 calculet_resource_read64(struct calculet_resource *resource, size_t offset);
u32 calculet_resource_read32(struct calculet_resource *resource, size_t offset);
void calculet_resource_write64(struct calculet_resource *resource, size_t offset, u64 value);
void calculet_resource_write32(struct calculet_resource *resource, size_t offset, u32 value);
void calculet_read_strings(char* buf, struct calculet_resource *resource, size_t offset, u64 count);
int calculet_bar64_transfer(struct calculet_resource *resource, struct calculet_bar64_transfer_params *transfer);
int calculet_bar32_transfer(struct calculet_resource *resource, struct calculet_bar32_transfer_params *transfer);
// PCIe功能函数
int calculet_pcie_bar64_transfer(struct calculet_pcie_resources *resources, struct calculet_bar64_transfer_params *transfer);
int calculet_pcie_bar32_transfer(struct calculet_pcie_resources *resources, struct calculet_bar32_transfer_params *transfer);
void calculet_pcie_enable_interrupts(struct pci_dev *pDev, struct calculet_pcie_resources *resources);
void calculet_pcie_enable_poll(struct pci_dev *pDev, struct calculet_pcie_resources *resources, struct calculet_dma_poll *poll);
void calculet_pcie_disable_interrupts(struct pci_dev *pDev, struct calculet_pcie_resources* resources);
// 板卡管理
int pcie_resources_init(struct pci_dev *pdev, struct calculet_pcie_resources *resources);
void pcie_resources_release(struct pci_dev *pdev, struct calculet_pcie_resources *resources);
#endif /* _CALCULET_PCI_H_ */
@@ -0,0 +1,78 @@
#!/bin/bash
# 1. 获取当前 dkms.conf 的绝对路径
DKMS_CONF_PATH="${BASH_SOURCE[0]}"
# 2. 获取真实的物理目录路径
DKMS_SRC_DIR="$(cd "$(dirname "$(readlink -f "${DKMS_CONF_PATH}")")" && pwd)"
# 3. 提取目录名
CURRENT_DIR=$(basename "${DKMS_SRC_DIR}")
# 4. 解析PACKAGE_NAME和PACKAGE_VERSION
PACKAGE_NAME=$(echo "${CURRENT_DIR}" | sed 's/-[0-9][0-9.]*$//')
PACKAGE_VERSION=$(echo "${CURRENT_DIR}" | sed 's/^.*-\([0-9][0-9.]*\)$/\1/')
if [ "$PACKAGE_NAME" = "$CURRENT_DIR" ] || [ -z "$PACKAGE_VERSION" ]; then
echo "错误: 目录名 '$CURRENT_DIR' 不符合 '<包名>-<版本号>' 格式" >&2
exit 1
fi
echo "提取结果: PACKAGE_NAME=$PACKAGE_NAME, PACKAGE_VERSION=$PACKAGE_VERSION" >&2
# 5. Makefile 路径校验
MAKEFILE_ABS_PATH="${DKMS_SRC_DIR}/Makefile"
if [ ! -f "${MAKEFILE_ABS_PATH}" ]; then
echo "错误: 在目录 ${DKMS_SRC_DIR} 下找不到 Makefile" >&2
exit 1
fi
# 6. 从 Makefile 中提取或推断驱动名称
DRIVER_NAME=$(grep -E '^[[:space:]]*DRIVER_NAME[[:space:]]*[:?]?=' "${MAKEFILE_ABS_PATH}" | head -1 | sed -E 's/^[^=]*=[[:space:]]*//' | sed 's/[[:space:]]*$//' | tr -d '"' | tr -d "'")
# 去掉 .ko 或 .o 后缀
DRIVER_NAME=$(echo "$DRIVER_NAME" | sed 's/\.ko$//' | sed 's/\.o$//')
if [ -z "$DRIVER_NAME" ] || [ "$DRIVER_NAME" = "Makefile" ]; then
echo "警告: Makefile 中未找到 DRIVER_NAME 定义,尝试从 obj-m 提取..." >&2
# 降级抓取 obj-m
DRIVER_NAME=$(grep -E '^obj-m[[:space:]]*[\+:]?=' "${MAKEFILE_ABS_PATH}" | head -1 | sed -E 's/^obj-m[[:space:]]*[\+:]?=[[:space:]]*//' | sed 's/[[:space:]]*$//' | tr -d '"' | tr -d "'")
# 去掉所有 .o 和 .ko
DRIVER_NAME=$(echo "$DRIVER_NAME" | sed 's/\.ko$//' | sed 's/\.o$//')
if [ -n "$DRIVER_NAME" ]; then
echo "从 obj-m 推断驱动名称: $DRIVER_NAME" >&2
else
echo "错误: 无法从 Makefile 中确定驱动名称" >&2
exit 1
fi
else
echo "从 Makefile 提取驱动名称: $DRIVER_NAME" >&2
fi
# 7. DKMS 标准配置参数定义
PACKAGE_NAME="${PACKAGE_NAME}"
PACKAGE_VERSION="${PACKAGE_VERSION}"
BUILT_MODULE_NAME[0]="${DRIVER_NAME}"
BUILT_MODULE_LOCATION[0]="."
DEST_MODULE_LOCATION[0]="/kernel/drivers/misc"
# 编译与清理命令定义
MAKE[0]="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build"
CLEAN="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build clean"
AUTOINSTALL="yes"
# # 输出最终配置信息
# echo "========================================" >&2
# echo "DKMS 最终配置:" >&2
# echo " PACKAGE_NAME: $PACKAGE_NAME" >&2
# echo " PACKAGE_VERSION: $PACKAGE_VERSION" >&2
# echo " BUILT_MODULE_NAME: ${BUILT_MODULE_NAME[0]}" >&2
# echo " BUILT_MODULE_LOCATION: ${BUILT_MODULE_LOCATION[0]}" >&2
# echo " DEST_MODULE_LOCATION: ${DEST_MODULE_LOCATION[0]}" >&2
# echo "========================================" >&2
@@ -0,0 +1,966 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#include "dma.h"
#include "board_mgr.h"
#include "utils.h"
#include <linux/sched.h>
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <asm/cacheflush.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
#include <linux/dma-map-ops.h>
#else
#include <linux/dma-mapping.h>
#endif
static int calculet_set_dma_mask(struct device *dev)
{
int err = -EINVAL;
/* Check and configure DMA length */
if (!(err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)))) {
calculet_dev_log(CALCULET_LOG_INFO, dev, "Enabled 64 bit dma");
} else if (!(err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)))) {
calculet_dev_log(CALCULET_LOG_DEBUG, dev, "Enabled 48 bit dma");
} else if (!(err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)))) {
calculet_dev_log(CALCULET_LOG_DEBUG, dev, "Enabled 40 bit dma");
} else if (!(err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)))) {
calculet_dev_log(CALCULET_LOG_DEBUG, dev, "Enabled 36 bit dma");
} else if (!(err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)))) {
calculet_dev_log(CALCULET_LOG_INFO, dev, "Enabled 32 bit dma");
} else {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "Error enabling dma %d", err);
return err;
}
return 0;
}
// DMA通道状态管理函数(简化,减少不必要的锁)
void calculet_dma_channel_set_state(struct calculet_dma_channel *channel,
enum calculet_dma_channel_state state)
{
if (channel && channel->state != state) {
calculet_log(CALCULET_LOG_DEBUG, "DMA channel %d state change: %d -> %d",
channel->channel_index, channel->state, state);
WRITE_ONCE(channel->state, state);
channel->last_used = ktime_get();
}
}
enum calculet_dma_channel_state calculet_dma_channel_get_state(struct calculet_dma_channel *channel)
{
return channel ? READ_ONCE(channel->state) : DMA_CHANNEL_ERROR;
}
bool calculet_dma_channel_is_available(struct calculet_dma_channel *channel)
{
if (!channel) return false;
enum calculet_dma_channel_state state = calculet_dma_channel_get_state(channel);
return (state == DMA_CHANNEL_IDLE);
}
// 通道分配和释放(简化,使用原子操作)
int calculet_dma_allocate_channel(struct calculet_dma_controller *controller,
enum calculet_transfer_direction direction,
int *channel_id)
{
unsigned long *bitmap;
struct calculet_dma_channel *channels;
int max_channels;
int id;
if (!controller || !channel_id) {
return -EINVAL;
}
if (direction == TRANSFER_H2C_DIRECTION) {
bitmap = &controller->read_channels_bitmap;
channels = controller->dma_rdch_s;
max_channels = CALCULET_READ_CHANNELS;
} else {
bitmap = &controller->write_channels_bitmap;
channels = controller->dma_wrch_s;
max_channels = CALCULET_WRITE_CHANNELS;
}
// 查找可用通道(无锁版本)
do {
id = find_first_zero_bit(bitmap, max_channels);
if (id >= max_channels) {
return -EBUSY;
}
// 检查通道状态
if (!calculet_dma_channel_is_available(&channels[id])) {
continue;
}
} while (test_and_set_bit(id, bitmap));
calculet_dma_channel_set_state(&channels[id], DMA_CHANNEL_BUSY);
*channel_id = id;
calculet_log(CALCULET_LOG_DEBUG, "Allocated %s channel %d",
direction == TRANSFER_H2C_DIRECTION ? "H2C" : "C2H", id);
return 0;
}
void calculet_dma_release_channel(struct calculet_dma_controller *controller,
enum calculet_transfer_direction direction,
int channel_id)
{
unsigned long *bitmap;
struct calculet_dma_channel *channels;
int max_channels;
if (!controller) {
return;
}
if (direction == TRANSFER_H2C_DIRECTION) {
bitmap = &controller->read_channels_bitmap;
channels = controller->dma_rdch_s;
max_channels = CALCULET_READ_CHANNELS;
} else {
bitmap = &controller->write_channels_bitmap;
channels = controller->dma_wrch_s;
max_channels = CALCULET_WRITE_CHANNELS;
}
if (channel_id >= 0 && channel_id < max_channels) {
clear_bit(channel_id, bitmap);
calculet_dma_channel_set_state(&channels[channel_id], DMA_CHANNEL_IDLE);
calculet_log(CALCULET_LOG_DEBUG, "Released %s channel %d",
direction == TRANSFER_H2C_DIRECTION ? "H2C" : "C2H", channel_id);
}
}
// 传输上下文管理(简化)
struct calculet_dma_transfer_context* calculet_dma_create_transfer_context(pid_t pid)
{
struct calculet_dma_transfer_context *ctx;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
return NULL;
}
init_completion(&ctx->completion);
ctx->result = 0;
ctx->start_time = ktime_get();
ctx->requester_pid = pid;
atomic_set(&ctx->ref_count, 1);
return ctx;
}
void calculet_dma_destroy_transfer_context(struct calculet_dma_transfer_context *ctx)
{
if (ctx) {
kfree(ctx);
}
}
void calculet_dma_transfer_context_get(struct calculet_dma_transfer_context *ctx)
{
if (ctx) {
atomic_inc(&ctx->ref_count);
}
}
void calculet_dma_transfer_context_put(struct calculet_dma_transfer_context *ctx)
{
if (ctx && atomic_dec_and_test(&ctx->ref_count)) {
calculet_dma_destroy_transfer_context(ctx);
}
}
int calculet_dma_controller_init(struct calculet_dma_controller *controller, struct device *dev, bool poll_en)
{
int i;
int err = 0;
struct calculet_dma_poll *poll;
if (!dev) {
calculet_log(CALCULET_LOG_ERROR, "Device pointer is NULL");
return -EINVAL;
}
controller->dev = dev;
controller->controller_enabled = false;
// 初始化控制器级别的锁和状态
mutex_init(&controller->controller_mutex);
mutex_init(&controller->allocation_mutex);
atomic_set(&controller->active_transfers, 0);
// 初始化通道分配位图
controller->read_channels_bitmap = 0;
controller->write_channels_bitmap = 0;
/* Check and configure DMA length */
err = calculet_set_dma_mask(dev);
if (0 > err) {
return err;
}
if (get_dma_ops(controller->dev)) {
calculet_dev_log(CALCULET_LOG_DEBUG, controller->dev, "Using specialized dma_ops=%ps", get_dma_ops(controller->dev));
}
else {
calculet_dev_log(CALCULET_LOG_WARN, dev, "No specialized dma_ops found");
}
// 初始化读通道(简化初始化)
for(i = 0; i < CALCULET_READ_CHANNELS; i++) {
struct calculet_dma_channel *channel = &controller->dma_rdch_s[i];
memset(channel, 0, sizeof(*channel));
channel->channel_index = i;
channel->state = DMA_CHANNEL_IDLE;
spin_lock_init(&channel->state_lock);
mutex_init(&channel->buffer_mutex);
mutex_init(&channel->transfer_mutex);
atomic_set(&channel->buffer_users, 0);
atomic_set(&channel->transfer_finish, 0);
atomic64_set(&channel->transfer_count, 0);
atomic64_set(&channel->transfer_bytes, 0);
init_waitqueue_head(&channel->channel_wq);
}
// 初始化写通道
for(i = 0; i < CALCULET_WRITE_CHANNELS; i++) {
struct calculet_dma_channel *channel = &controller->dma_wrch_s[i];
memset(channel, 0, sizeof(*channel));
channel->channel_index = i;
channel->state = DMA_CHANNEL_IDLE;
spin_lock_init(&channel->state_lock);
mutex_init(&channel->buffer_mutex);
mutex_init(&channel->transfer_mutex);
atomic_set(&channel->buffer_users, 0);
atomic_set(&channel->transfer_finish, 0);
atomic64_set(&channel->transfer_count, 0);
atomic64_set(&channel->transfer_bytes, 0);
init_waitqueue_head(&channel->channel_wq);
}
// 初始化轮询模式
if(poll_en) {
/* 分配 DMA Kthread 结构体内存 */
if(!controller->dma_kthread) {
controller->dma_kthread = devm_kzalloc(dev, sizeof(struct calculet_dma_kthread), GFP_KERNEL);
if (!controller->dma_kthread) {
return -ENOMEM;
}
}
/* 分配 DMA Poll 结构体内存 */
if (!controller->dma_kthread->dma_poll) {
controller->dma_kthread->dma_poll = devm_kzalloc(dev, sizeof(struct calculet_dma_poll), GFP_KERNEL);
if (!controller->dma_kthread->dma_poll) {
return -ENOMEM;
}
}
poll = controller->dma_kthread->dma_poll;
poll->status_region = dma_alloc_coherent(dev, sizeof(uint32_t), &poll->status_region_phys, GFP_KERNEL);
if (!poll->status_region) {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to allocate coherent memory for status region");
return -ENOMEM;
}
spin_lock_init(&poll->poll_lock);
atomic_set(&poll->poll_active, 0);
for (i = 0; i < CALCULET_TOTAL_CHANNELS; i++) {
poll->channel_data[i] = i + 100;
}
// 初始化内核线程
atomic_set(&controller->dma_kthread->should_stop, 0);
init_completion(&controller->dma_kthread->thread_completion);
}
controller->controller_enabled = true;
calculet_log(CALCULET_LOG_DEBUG, "DMA controller initialized (poll_mode: %s)",
poll_en ? "enabled" : "disabled");
return 0;
}
void calculet_dma_controller_deinit(struct calculet_dma_controller *controller, bool poll_en)
{
int i;
struct calculet_dma_poll *poll;
if (!controller) {
return;
}
controller->controller_enabled = false;
// 等待所有活动传输完成
while (atomic_read(&controller->active_transfers) > 0) {
msleep(10);
}
// 清理读通道
for(i = 0; i < CALCULET_READ_CHANNELS; i++) {
struct calculet_dma_channel *channel = &controller->dma_rdch_s[i];
// 设置通道为禁用状态
calculet_dma_channel_set_state(channel, DMA_CHANNEL_DISABLED);
// 释放缓冲区
mutex_lock(&channel->buffer_mutex);
if (channel->kernel_buf) {
dma_free_coherent(controller->dev, channel->dma_len,
channel->kernel_buf, channel->dma_handle);
channel->kernel_buf = NULL;
channel->dma_len = 0;
}
mutex_unlock(&channel->buffer_mutex);
// 清理传输上下文
if (channel->current_transfer) {
calculet_dma_transfer_context_put(channel->current_transfer);
channel->current_transfer = NULL;
}
}
// 清理写通道
for(i = 0; i < CALCULET_WRITE_CHANNELS; i++) {
struct calculet_dma_channel *channel = &controller->dma_wrch_s[i];
// 设置通道为禁用状态
calculet_dma_channel_set_state(channel, DMA_CHANNEL_DISABLED);
// 释放缓冲区
mutex_lock(&channel->buffer_mutex);
if (channel->kernel_buf) {
dma_free_coherent(controller->dev, channel->dma_len,
channel->kernel_buf, channel->dma_handle);
channel->kernel_buf = NULL;
channel->dma_len = 0;
}
mutex_unlock(&channel->buffer_mutex);
// 清理传输上下文
if (channel->current_transfer) {
calculet_dma_transfer_context_put(channel->current_transfer);
channel->current_transfer = NULL;
}
}
// 清理轮询相关资源
if(poll_en && controller->dma_kthread) {
poll = controller->dma_kthread->dma_poll;
if (poll) {
for (i = 0; i < CALCULET_TOTAL_CHANNELS; i++) {
poll->channel_data[i] = 0;
}
if (poll->status_region) {
dma_free_coherent(controller->dev, sizeof(uint32_t),
poll->status_region, poll->status_region_phys);
poll->status_region = NULL;
}
}
}
calculet_log(CALCULET_LOG_DEBUG, "DMA controller deinitialized");
}
// DMA参数验证
int calculet_dma_validate_transfer_params(struct calculet_dma_transfer_channels_params *params)
{
if (!params) {
return -EINVAL;
}
// 检查传输方向
if (params->dma_transfer_direction != TRANSFER_H2C_DIRECTION &&
params->dma_transfer_direction != TRANSFER_C2H_DIRECTION) {
return -EINVAL;
}
// 检查通道ID
if (params->dma_transfer_direction == TRANSFER_H2C_DIRECTION) {
if (params->dma_channel_id < 0 || params->dma_channel_id >= CALCULET_READ_CHANNELS) {
return -EINVAL;
}
} else {
if (params->dma_channel_id < 0 || params->dma_channel_id >= CALCULET_WRITE_CHANNELS) {
return -EINVAL;
}
}
// 检查传输大小
if (params->dma_channel_size == 0 || params->dma_channel_size > (32 * 1024 * 1024)) {
return -EINVAL;
}
// 检查地址对齐
if (params->device_address & 0x3) {
calculet_log(CALCULET_LOG_WARN, "Device address 0x%llx is not 4-byte aligned",
params->device_address);
}
return 0;
}
// 安全的DMA传输函数
long calculet_dma_safe_transfer(struct calculet_dma_controller *controller,
struct calculet_dma_transfer_channels_params *params,
struct calculet_resource *resource)
{
struct calculet_dma_channel *channel;
struct calculet_dma_transfer_context *ctx;
long ret;
int ch_id;
if (!controller || !params || !resource) {
return -EINVAL;
}
// 验证传输参数
ret = calculet_dma_validate_transfer_params(params);
if (ret) {
return ret;
}
// 检查控制器状态
if (!controller->controller_enabled) {
return -ENODEV;
}
ch_id = params->dma_channel_id;
// 获取通道
if (params->dma_transfer_direction == TRANSFER_H2C_DIRECTION) {
if (ch_id >= CALCULET_READ_CHANNELS) {
return -EINVAL;
}
channel = &controller->dma_rdch_s[ch_id];
} else {
if (ch_id >= CALCULET_WRITE_CHANNELS) {
return -EINVAL;
}
channel = &controller->dma_wrch_s[ch_id];
}
// 检查通道状态
if (!calculet_dma_channel_is_available(channel)) {
return -EBUSY;
}
// 创建传输上下文
ctx = calculet_dma_create_transfer_context(current->pid);
if (!ctx) {
return -ENOMEM;
}
// 设置通道参数
channel->dma_channel_size = params->dma_channel_size;
channel->buffer = params->host_address;
channel->dst_address = params->device_address;
channel->current_transfer = ctx;
// 增加活动传输计数
atomic_inc(&controller->active_transfers);
// 执行传输
if (params->dma_transfer_direction == TRANSFER_H2C_DIRECTION) {
ret = calculet_dma_h2c_transfer(controller->dev, channel, resource);
} else {
ret = calculet_dma_c2h_transfer(controller->dev, channel, resource);
}
// 清理
channel->current_transfer = NULL;
calculet_dma_transfer_context_put(ctx);
atomic_dec(&controller->active_transfers);
return ret;
}
static long calculet_block_dma_transfer_ioctl(struct calculet_resource *resource, struct calculet_dma_controller *controller, unsigned long arg)
{
struct calculet_dma_transfer_channels_params input;
long ret = -1;
if (copy_from_user(&input, (void *)arg, sizeof(input))) {
calculet_dev_log(CALCULET_LOG_ERROR, controller->dev, "copy_from_user fail");
return -EFAULT;
}
// 使用安全的传输函数
ret = calculet_dma_safe_transfer(controller, &input, resource);
return ret;
}
int calculet_dma_poll_ioctl(struct calculet_dma_controller *controller)
{
struct calculet_dma_poll *poll;
int region_data = 0;
int i;
if (!controller || !controller->dma_kthread || !controller->dma_kthread->dma_poll) {
return -EINVAL;
}
poll = controller->dma_kthread->dma_poll;
spin_lock(&poll->poll_lock);
region_data = *poll->status_region;
if(region_data != 0) {
for(i = 0; i < CALCULET_TOTAL_CHANNELS; i++)
{
if(poll->channel_data[i] == region_data) {
if(i < CALCULET_TOTAL_CHANNELS/2) {
atomic_set(&controller->dma_rdch_s[i].transfer_finish, 1);
wake_up_interruptible(&controller->dma_rdch_s[i].channel_wq);
break;
}
else {
atomic_set(&controller->dma_wrch_s[i - CALCULET_READ_CHANNELS].transfer_finish, 1);
wake_up_interruptible(&controller->dma_wrch_s[i - CALCULET_READ_CHANNELS].channel_wq);
break;
}
}
}
*poll->status_region = 0;
}
spin_unlock(&poll->poll_lock);
return region_data;
}
long calculet_dma_ioctl(struct calculet_resource *resource, struct calculet_dma_controller *controller, unsigned int cmd, unsigned long arg)
{
if (!resource || !controller) {
return -EINVAL;
}
switch(cmd) {
case CALCULET_BLOCK_DMA_TRANSFER:
return calculet_block_dma_transfer_ioctl(resource, controller, arg);
case CALCULET_INTERRUPT_POLL:
return calculet_dma_poll_ioctl(controller);
default:
calculet_dev_log(CALCULET_LOG_ERROR, controller->dev, "Invalid DMA ioctl code 0x%x (nr: %d)", cmd, _IOC_NR(cmd));
return -ENOTTY;
}
}
//block dma api
long calculet_dma_h2c_transfer(struct device *dev, struct calculet_dma_channel *channel, struct calculet_resource *resource)
{
int ch = channel->channel_index;
unsigned long timeout = msecs_to_jiffies(100000); // 设置超时时间为100秒
struct calculet_pcie_board *board = dev_get_drvdata(dev);
long ret = 0;
int transfer_result;
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
CALCULET_PERF_START(h2c_transfer);
// 获取传输互斥锁
if (mutex_lock_interruptible(&channel->transfer_mutex)) {
return -ERESTARTSYS;
}
// 缓冲区管理
mutex_lock(&channel->buffer_mutex);
if(channel->dma_channel_size <= DMA_CHANNEL_MAX_SIZE) {
if(channel->kernel_buf == NULL) {
channel->kernel_buf = dma_alloc_coherent(dev, DMA_CHANNEL_MAX_SIZE, &channel->dma_handle, GFP_KERNEL);
if (!channel->kernel_buf) {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to allocate DMA buffer");
ret = -ENOMEM;
goto error_unlock_buffer;
}
}
} else {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "H2C transfer size %ld is too large", channel->dma_channel_size);
ret = -EINVAL;
goto error_unlock_buffer;
}
// if(channel->dma_len != channel->dma_channel_size) {
// if(channel->kernel_buf == NULL) {
// channel->kernel_buf = dma_alloc_coherent(dev, channel->dma_channel_size, &channel->dma_handle, GFP_KERNEL);
// if (!channel->kernel_buf) {
// calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to allocate DMA buffer");
// ret = -ENOMEM;
// goto error_unlock_buffer;
// }
// } else {
// dma_free_coherent(dev, channel->dma_len, channel->kernel_buf, channel->dma_handle);
// channel->kernel_buf = dma_alloc_coherent(dev, channel->dma_channel_size, &channel->dma_handle, GFP_KERNEL);
// if (!channel->kernel_buf) {
// calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to allocate DMA buffer");
// ret = -ENOMEM;
// goto error_unlock_buffer;
// }
// }
// channel->dma_len = channel->dma_channel_size;
// }
atomic_inc(&channel->buffer_users);
mutex_unlock(&channel->buffer_mutex);
// 从用户空间拷贝数据到DMA一致性内存
if (copy_from_user(channel->kernel_buf, (void __user *)channel->buffer, channel->dma_channel_size)) {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to copy_from_user");
ret = -EFAULT;
goto error_cleanup;
}
// 配置DMA寄存器
iowrite32(0x10, (u8*)resource->address + DMA_RDCH_CONTROL1_OFF(ch));
iowrite32(PCI_DMA_L(channel->dma_channel_size), (u8*)resource->address + DMA_RDCH_SIZE_OFF(ch));
iowrite32(PCI_DMA_L(channel->dma_handle), (u8*)resource->address + DMA_RDCH_SAR_LOW_OFF(ch));
iowrite32(PCI_DMA_H(channel->dma_handle), (u8*)resource->address + DMA_RDCH_SAR_HIGH_OFF(ch));
iowrite32(PCI_DMA_L(channel->dst_address), (u8*)resource->address + DMA_RDCH_DAR_LOW_OFF(ch));
iowrite32(PCI_DMA_H(channel->dst_address), (u8*)resource->address + DMA_RDCH_DAR_HIGH_OFF(ch));
// 重置完成标志
atomic_set(&channel->transfer_finish, 0);
// 使用资源结构体中的锁
mutex_lock(&resource->dma_global_lock);
iowrite32(ch, (u8*)resource->address + DMA_READ_DOORBELL_OFF);
mutex_unlock(&resource->dma_global_lock);
// 等待传输完成或超时
transfer_result = wait_event_interruptible_timeout(channel->channel_wq,
atomic_read(&channel->transfer_finish) != 0,
timeout);
if (transfer_result > 0) {
int finish_status = atomic_read(&channel->transfer_finish);
if (finish_status == 1) {
// 传输成功
calculet_board_stats_update_dma_h2c(board, channel->dma_channel_size);
calculet_dma_update_channel_stats(channel, channel->dma_channel_size, true);
ret = 0;
} else {
// 传输错误
calculet_dev_log(CALCULET_LOG_ERROR, dev, "H2C transfer failed with status %d", finish_status);
calculet_board_stats_update_error(board);
calculet_dma_update_channel_stats(channel, 0, false);
ret = finish_status;
}
} else if (transfer_result == 0) {
// 超时
calculet_dev_log(CALCULET_LOG_ERROR, dev, "H2C transfer timed out");
calculet_board_stats_update_error(board);
calculet_dma_update_channel_stats(channel, 0, false);
ret = -ETIMEDOUT;
} else {
// 被信号中断
calculet_dev_log(CALCULET_LOG_ERROR, dev, "H2C transfer interrupted");
ret = -ERESTARTSYS;
}
CALCULET_PERF_END(h2c_transfer, "channel %d, size %ld, result %ld",
ch, channel->dma_channel_size, ret);
calculet_dev_log(CALCULET_LOG_DEBUG, dev, "H2C transfer: channel %d, src 0x%llx, dst 0x%llx, size %ld, result %ld",
ch, channel->dma_handle, channel->dst_address, channel->dma_channel_size, ret);
error_cleanup:
mutex_lock(&channel->buffer_mutex);
if (atomic_dec_and_test(&channel->buffer_users)) {
// 如果发生错误,释放缓冲区
if (ret != 0 && channel->kernel_buf) {
dma_free_coherent(dev, channel->dma_channel_size, channel->kernel_buf, channel->dma_handle);
channel->kernel_buf = NULL;
channel->dma_len = 0;
}
}
error_unlock_buffer:
mutex_unlock(&channel->buffer_mutex);
mutex_unlock(&channel->transfer_mutex);
return ret;
}
long calculet_dma_c2h_transfer(struct device *dev, struct calculet_dma_channel *channel, struct calculet_resource *resource)
{
int ch = channel->channel_index;
unsigned long timeout = msecs_to_jiffies(100000); // 设置超时时间为100秒
struct calculet_pcie_board *board = dev_get_drvdata(dev);
long ret = 0;
int transfer_result;
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
CALCULET_PERF_START(c2h_transfer);
// 获取传输互斥锁
if (mutex_lock_interruptible(&channel->transfer_mutex)) {
return -ERESTARTSYS;
}
// 缓冲区管理
mutex_lock(&channel->buffer_mutex);
if(channel->dma_channel_size <= DMA_CHANNEL_MAX_SIZE) {
if(channel->kernel_buf == NULL) {
channel->kernel_buf = dma_alloc_coherent(dev, DMA_CHANNEL_MAX_SIZE, &channel->dma_handle, GFP_KERNEL);
if (!channel->kernel_buf) {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to allocate DMA buffer");
ret = -ENOMEM;
goto error_unlock_buffer;
}
}
} else {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "C2H transfer size %ld is too large", channel->dma_channel_size);
ret = -EINVAL;
goto error_unlock_buffer;
}
// if(channel->dma_len != channel->dma_channel_size) {
// if(channel->kernel_buf == NULL) {
// channel->kernel_buf = dma_alloc_coherent(dev, channel->dma_channel_size, &channel->dma_handle, GFP_KERNEL);
// if (!channel->kernel_buf) {
// calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to allocate DMA buffer");
// ret = -ENOMEM;
// goto error_unlock_buffer;
// }
// } else {
// dma_free_coherent(dev, channel->dma_len, channel->kernel_buf, channel->dma_handle);
// channel->kernel_buf = dma_alloc_coherent(dev, channel->dma_channel_size, &channel->dma_handle, GFP_KERNEL);
// if (!channel->kernel_buf) {
// calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to allocate DMA buffer");
// ret = -ENOMEM;
// goto error_unlock_buffer;
// }
// }
// channel->dma_len = channel->dma_channel_size;
// }
atomic_inc(&channel->buffer_users);
mutex_unlock(&channel->buffer_mutex);
// 配置DMA寄存器
iowrite32(0x10, (u8*)resource->address + DMA_WRCH_CONTROL1_OFF(ch));
iowrite32(PCI_DMA_L(channel->dma_channel_size), (u8*)resource->address + DMA_WRCH_SIZE_OFF(ch));
iowrite32(PCI_DMA_L(channel->dst_address), (u8*)resource->address + DMA_WRCH_SAR_LOW_OFF(ch));
iowrite32(PCI_DMA_H(channel->dst_address), (u8*)resource->address + DMA_WRCH_SAR_HIGH_OFF(ch));
iowrite32(PCI_DMA_L(channel->dma_handle), (u8*)resource->address + DMA_WRCH_DAR_LOW_OFF(ch));
iowrite32(PCI_DMA_H(channel->dma_handle), (u8*)resource->address + DMA_WRCH_DAR_HIGH_OFF(ch));
// 重置完成标志
atomic_set(&channel->transfer_finish, 0);
// 使用资源结构体中的锁
mutex_lock(&resource->dma_global_lock);
iowrite32(ch, (u8*)resource->address + DMA_WRITE_DOORBELL_OFF);
mutex_unlock(&resource->dma_global_lock);
// 等待传输完成或超时
transfer_result = wait_event_interruptible_timeout(channel->channel_wq,
atomic_read(&channel->transfer_finish) != 0,
timeout);
if (transfer_result > 0) {
int finish_status = atomic_read(&channel->transfer_finish);
if (finish_status == 1) {
// 传输成功,将数据从DMA一致性内存复制回用户空间
if (copy_to_user((void __user *)channel->buffer, channel->kernel_buf, channel->dma_channel_size)) {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to copy_to_user");
calculet_board_stats_update_error(board);
calculet_dma_update_channel_stats(channel, 0, false);
ret = -EFAULT;
} else {
calculet_board_stats_update_dma_c2h(board, channel->dma_channel_size);
calculet_dma_update_channel_stats(channel, channel->dma_channel_size, true);
ret = 0;
}
} else {
// 传输错误
calculet_dev_log(CALCULET_LOG_ERROR, dev, "C2H transfer failed with status %d", finish_status);
calculet_board_stats_update_error(board);
calculet_dma_update_channel_stats(channel, 0, false);
ret = finish_status;
}
} else if (transfer_result == 0) {
// 超时
calculet_dev_log(CALCULET_LOG_ERROR, dev, "C2H transfer timed out");
calculet_board_stats_update_error(board);
calculet_dma_update_channel_stats(channel, 0, false);
ret = -ETIMEDOUT;
} else {
// 被信号中断
calculet_dev_log(CALCULET_LOG_ERROR, dev, "C2H transfer interrupted");
ret = -ERESTARTSYS;
}
CALCULET_PERF_END(c2h_transfer, "channel %d, size %ld, result %ld",
ch, channel->dma_channel_size, ret);
calculet_dev_log(CALCULET_LOG_DEBUG, dev, "C2H transfer: channel %d, src 0x%llx, dst 0x%llx, size %ld, result %ld",
ch, channel->dst_address, channel->dma_handle, channel->dma_channel_size, ret);
mutex_lock(&channel->buffer_mutex);
if (atomic_dec_and_test(&channel->buffer_users)) {
// 如果发生错误,释放缓冲区
if (ret != 0 && channel->kernel_buf) {
dma_free_coherent(dev, channel->dma_channel_size, channel->kernel_buf, channel->dma_handle);
channel->kernel_buf = NULL;
channel->dma_len = 0;
}
}
error_unlock_buffer:
mutex_unlock(&channel->buffer_mutex);
mutex_unlock(&channel->transfer_mutex);
return ret;
}
irqreturn_t calculet_irqhandler(int irq, void *dev_id)
{
irqreturn_t return_value = IRQ_HANDLED;
struct calculet_dma_channel *channel = (struct calculet_dma_channel *)dev_id;
struct calculet_pcie_board *board;
if (!channel) {
return IRQ_NONE;
}
calculet_log(CALCULET_LOG_DEBUG, "IRQ %d for channel %d (vector %d)",
irq, channel->channel_index, channel->vector_index);
// 设置传输完成标志
atomic_set(&channel->transfer_finish, 1);
// 唤醒等待的线程
wake_up_interruptible(&channel->channel_wq);
// 更新中断统计(简化版本)
board = container_of(channel, struct calculet_pcie_board, cdma.dma_rdch_s[channel->channel_index]);
if (!board) {
board = container_of(channel, struct calculet_pcie_board, cdma.dma_wrch_s[channel->channel_index]);
}
if (board) {
calculet_board_stats_update_interrupt(board);
}
return return_value;
}
int calculet_poll_handler(void *data)
{
struct calculet_dma_controller *controller = (struct calculet_dma_controller *)data;
struct calculet_dma_poll *poll = controller->dma_kthread->dma_poll;
int region_data = 0;
int i;
atomic_set(&poll->poll_active, 1);
while(!kthread_should_stop() && !atomic_read(&controller->dma_kthread->should_stop))
{
spin_lock(&poll->poll_lock);
region_data = *poll->status_region;
if(region_data != 0) {
for(i = 0; i < CALCULET_TOTAL_CHANNELS; i++)
{
if(poll->channel_data[i] == region_data) {
if(i < CALCULET_TOTAL_CHANNELS/2) {
atomic_set(&controller->dma_rdch_s[i].transfer_finish, 1);
wake_up_interruptible(&controller->dma_rdch_s[i].channel_wq);
break;
}
else {
atomic_set(&controller->dma_wrch_s[i - CALCULET_READ_CHANNELS].transfer_finish, 1);
wake_up_interruptible(&controller->dma_wrch_s[i - CALCULET_READ_CHANNELS].channel_wq);
break;
}
}
}
*poll->status_region = 0;
}
spin_unlock(&poll->poll_lock);
schedule();
}
atomic_set(&poll->poll_active, 0);
complete(&controller->dma_kthread->thread_completion);
calculet_log(CALCULET_LOG_DEBUG, "Poll handler thread exiting");
return 0;
}
// 统计信息更新(简化版本)
void calculet_dma_update_channel_stats(struct calculet_dma_channel *channel,
size_t bytes, bool success)
{
if (!channel) {
return;
}
atomic64_inc(&channel->transfer_count);
if (success) {
atomic64_add(bytes, &channel->transfer_bytes);
}
channel->last_used = ktime_get();
}
// 控制器状态转储
void calculet_dma_dump_controller_state(struct calculet_dma_controller *controller)
{
int i;
if (!controller) {
return;
}
calculet_log(CALCULET_LOG_DEBUG, "DMA Controller State Dump");
calculet_log(CALCULET_LOG_DEBUG, " Enabled: %s", controller->controller_enabled ? "YES" : "NO");
calculet_log(CALCULET_LOG_DEBUG, " Active Transfers: %d", atomic_read(&controller->active_transfers));
calculet_log(CALCULET_LOG_DEBUG, " Read Channels Bitmap: 0x%lx", controller->read_channels_bitmap);
calculet_log(CALCULET_LOG_DEBUG, " Write Channels Bitmap: 0x%lx", controller->write_channels_bitmap);
// 转储读通道状态
calculet_log(CALCULET_LOG_DEBUG, " Read Channels:");
for (i = 0; i < CALCULET_READ_CHANNELS; i++) {
struct calculet_dma_channel *ch = &controller->dma_rdch_s[i];
calculet_log(CALCULET_LOG_DEBUG, " CH%d: state=%d, transfers=%lld, bytes=%lld",
i, calculet_dma_channel_get_state(ch),
atomic64_read(&ch->transfer_count),
atomic64_read(&ch->transfer_bytes));
}
// 转储写通道状态
calculet_log(CALCULET_LOG_DEBUG, " Write Channels:");
for (i = 0; i < CALCULET_WRITE_CHANNELS; i++) {
struct calculet_dma_channel *ch = &controller->dma_wrch_s[i];
calculet_log(CALCULET_LOG_DEBUG, " CH%d: state=%d, transfers=%lld, bytes=%lld",
i, calculet_dma_channel_get_state(ch),
atomic64_read(&ch->transfer_count),
atomic64_read(&ch->transfer_bytes));
}
}
@@ -0,0 +1,220 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#ifndef _CALCULET_DMA_H_
#define _CALCULET_DMA_H_
#include "ioctl.h"
#include <linux/dma-mapping.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/scatterlist.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ktime.h>
#include <linux/atomic.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#define DESC_MAGIC 0x0UL
#define DESC_MAX_COUNT (1024)
#define DMA_TRANSFER_MAX_DESC (2048)
#define DMA_CHANNEL_MAX_SIZE (8 * 1024 * 1024)
/* 获取64位地址的高32位和低32位 */
#define PCI_DMA_H(addr) ((addr >> 16) >> 16)
#define PCI_DMA_L(addr) (addr & 0xffffffffUL)
// DMA寄存器偏移定义
#define DMA_CHANNEL_BASE_OFF(i) ((i) * 2 * 0x100)
#define DMA_WRITE_ENGINE_EN_OFF 0xc
#define DMA_WRITE_DOORBELL_OFF 0x10
#define DMA_WRCH_CONTROL1_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x200)
#define DMA_WRCH_SIZE_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x208)
#define DMA_WRCH_SAR_LOW_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x20c)
#define DMA_WRCH_SAR_HIGH_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x210)
#define DMA_WRCH_DAR_LOW_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x214)
#define DMA_WRCH_DAR_HIGH_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x218)
#define DMA_READ_ENGINE_EN_OFF 0x2c
#define DMA_READ_DOORBELL_OFF 0x30
#define DMA_RDCH_CONTROL1_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x300)
#define DMA_RDCH_SIZE_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x308)
#define DMA_RDCH_SAR_LOW_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x30c)
#define DMA_RDCH_SAR_HIGH_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x310)
#define DMA_RDCH_DAR_LOW_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x314)
#define DMA_RDCH_DAR_HIGH_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x318)
// 前向声明
struct calculet_resource;
// DMA通道状态枚举
enum calculet_dma_channel_state {
DMA_CHANNEL_IDLE = 0,
DMA_CHANNEL_BUSY,
DMA_CHANNEL_COMPLETING,
DMA_CHANNEL_ERROR,
DMA_CHANNEL_DISABLED
};
// DMA IO回调结构体
struct calculet_dma_io_cb {
void __user *buf;
size_t len;
unsigned int pages_nr;
struct sg_table sgt;
struct page **pages;
};
// DMA传输上下文
struct calculet_dma_transfer_context {
struct completion completion;
int result;
ktime_t start_time;
ktime_t end_time;
pid_t requester_pid;
atomic_t ref_count;
};
// DMA通道结构体
struct calculet_dma_channel {
u8 channel_index;
u8 vector_index;
int msi_irq_line;
u32 irq_bitmask;
// 改进的状态管理
enum calculet_dma_channel_state state;
atomic_t transfer_finish;
spinlock_t state_lock;
resource_size_t dma_regs;
size_t dma_channel_size;
uint8_t *buffer;
uint64_t dst_address;
wait_queue_head_t channel_wq;
struct calculet_dma_io_cb *desc_io_cb;
// 改进的缓冲区管理
void *kernel_buf;
size_t dma_len;
dma_addr_t dma_handle;
struct mutex buffer_mutex;
atomic_t buffer_users;
// 传输统计和监控
atomic64_t transfer_count;
atomic64_t transfer_bytes;
ktime_t last_used;
// 当前传输上下文
struct calculet_dma_transfer_context *current_transfer;
struct mutex transfer_mutex;
};
// DMA轮询结构体
struct calculet_dma_poll {
dma_addr_t status_region_phys;
uint32_t *status_region;
uint32_t channel_status[CALCULET_TOTAL_CHANNELS];
uint32_t channel_data[CALCULET_TOTAL_CHANNELS];
spinlock_t poll_lock;
atomic_t poll_active;
};
// DMA内核线程结构体
struct calculet_dma_kthread {
struct calculet_dma_poll *dma_poll;
struct task_struct *poll_thread;
u32 poll_interval_ms;
char thread_name[24];
atomic_t should_stop;
struct completion thread_completion;
};
// DMA控制器结构体
struct calculet_dma_controller {
struct device *dev;
struct calculet_dma_channel dma_rdch_s[CALCULET_READ_CHANNELS];
struct calculet_dma_channel dma_wrch_s[CALCULET_WRITE_CHANNELS];
struct calculet_dma_channel msi_req[CALCULET_TOTAL_CHANNELS];
// struct calculet_dma_channel soft_reset;
struct calculet_dma_kthread *dma_kthread;
// 控制器级别的锁和状态
struct mutex controller_mutex;
atomic_t active_transfers;
bool controller_enabled;
// 通道分配管理
unsigned long read_channels_bitmap; // 读通道分配位图
unsigned long write_channels_bitmap; // 写通道分配位图
struct mutex allocation_mutex;
};
// 通道分配和释放
int calculet_dma_allocate_channel(struct calculet_dma_controller *controller,
enum calculet_transfer_direction direction,
int *channel_id);
void calculet_dma_release_channel(struct calculet_dma_controller *controller,
enum calculet_transfer_direction direction,
int channel_id);
// DMA通道状态管理
void calculet_dma_channel_set_state(struct calculet_dma_channel *channel,
enum calculet_dma_channel_state state);
enum calculet_dma_channel_state calculet_dma_channel_get_state(struct calculet_dma_channel *channel);
bool calculet_dma_channel_is_available(struct calculet_dma_channel *channel);
// 传输上下文管理
struct calculet_dma_transfer_context* calculet_dma_create_transfer_context(pid_t pid);
void calculet_dma_destroy_transfer_context(struct calculet_dma_transfer_context *ctx);
void calculet_dma_transfer_context_get(struct calculet_dma_transfer_context *ctx);
void calculet_dma_transfer_context_put(struct calculet_dma_transfer_context *ctx);
// 函数声明
int calculet_dma_controller_init(struct calculet_dma_controller *controller, struct device *dev, bool poll_en);
void calculet_dma_controller_deinit(struct calculet_dma_controller *controller, bool poll_en);
long calculet_dma_ioctl(struct calculet_resource *resource, struct calculet_dma_controller *controller, unsigned int cmd, unsigned long arg);
int calculet_dma_poll_ioctl(struct calculet_dma_controller *controller);
// DMA传输函数(改进版本)
long calculet_dma_h2c_transfer(struct device *dev, struct calculet_dma_channel *channel, struct calculet_resource *resource);
long calculet_dma_c2h_transfer(struct device *dev, struct calculet_dma_channel *channel, struct calculet_resource *resource);
// 新增:安全的DMA传输函数
long calculet_dma_safe_transfer(struct calculet_dma_controller *controller,
struct calculet_dma_transfer_channels_params *params,
struct calculet_resource *resource);
// 超时处理
void calculet_dma_timeout_handler(struct timer_list *timer);
int calculet_dma_set_timeout(struct calculet_dma_channel *channel, unsigned int timeout_ms);
// 中断处理函数
irqreturn_t calculet_irqhandler(int irq, void* dev_id);
int calculet_poll_handler(void *data);
// 错误恢复
void calculet_dma_error_recovery_work(struct work_struct *work);
int calculet_dma_channel_recovery(struct calculet_dma_channel *channel);
// 统计和监控
void calculet_dma_update_channel_stats(struct calculet_dma_channel *channel,
size_t bytes, bool success);
void calculet_dma_dump_channel_stats(struct calculet_dma_controller *controller);
// 调试和诊断
int calculet_dma_validate_transfer_params(struct calculet_dma_transfer_channels_params *params);
void calculet_dma_dump_controller_state(struct calculet_dma_controller *controller);
#endif /* _CALCULET_DMA_H_ */
@@ -0,0 +1,645 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#include <linux/version.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/pagemap.h>
#include <linux/uaccess.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/kref.h>
#include <linux/crc32.h>
#include <linux/hash.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/signal.h>
#endif
#include "fops.h"
#include "proc.h"
#include "calculet_pci_core.h"
#define DRIVER_NAME "calculet"
#define CALCULET_SOFT_RESET_REG 0x2238108
// 简化的文件上下文
struct calculet_file_private {
struct calculet_pcie_board *board;
pid_t owner_pid;
bool is_valid;
struct kref ref_count;
};
// 自定义混淆函数
static u32 calculet_hash_mix(u32 input, u32 salt)
{
input ^= salt;
input = ((input >> 16) ^ input) * 0x45d9f3b;
input = ((input >> 16) ^ input) * 0x45d9f3b;
input = (input >> 16) ^ input;
return input;
}
void calculet_generate_simple_uuid(const char *serial_number, char *uuid_str)
{
u32 seeds[4];
u8 uuid_bytes[16];
u32 base_hash;
int i;
// 计算基础哈希
base_hash = crc32(0, serial_number, strlen(serial_number));
// 生成4个种子值
seeds[0] = calculet_hash_mix(base_hash, 0x12345678);
seeds[1] = calculet_hash_mix(base_hash, 0x9ABCDEF0);
seeds[2] = calculet_hash_mix(base_hash, 0xFEDCBA98);
seeds[3] = calculet_hash_mix(base_hash, 0x76543210);
// 转换为字节
for (i = 0; i < 4; i++) {
uuid_bytes[i*4] = (seeds[i] >> 24) & 0xFF;
uuid_bytes[i*4+1] = (seeds[i] >> 16) & 0xFF;
uuid_bytes[i*4+2] = (seeds[i] >> 8) & 0xFF;
uuid_bytes[i*4+3] = seeds[i] & 0xFF;
}
// 设置UUID版本和变体位
uuid_bytes[6] = (uuid_bytes[6] & 0x0F) | 0x40; // 版本4
uuid_bytes[8] = (uuid_bytes[8] & 0x3F) | 0x80; // 变体位
// 格式化为UUID字符串
snprintf(uuid_str, 37, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
uuid_bytes[0], uuid_bytes[1], uuid_bytes[2], uuid_bytes[3],
uuid_bytes[4], uuid_bytes[5], uuid_bytes[6], uuid_bytes[7],
uuid_bytes[8], uuid_bytes[9], uuid_bytes[10], uuid_bytes[11],
uuid_bytes[12], uuid_bytes[13], uuid_bytes[14], uuid_bytes[15]);
}
// 文件上下文释放函数
static void calculet_file_private_release(struct kref *ref)
{
struct calculet_file_private *priv = container_of(ref, struct calculet_file_private, ref_count);
if (priv->board) {
calculet_board_mgr_put_board(priv->board);
}
kfree(priv);
}
static void calculet_file_private_get(struct calculet_file_private *priv)
{
if (priv) {
kref_get(&priv->ref_count);
}
}
static void calculet_file_private_put(struct calculet_file_private *priv)
{
if (priv) {
kref_put(&priv->ref_count, calculet_file_private_release);
}
}
int calculet_pcie_fops_open(struct inode *inode, struct file *filp)
{
u32 major = MAJOR(inode->i_rdev);
u32 minor = MINOR(inode->i_rdev);
struct calculet_pcie_board *board;
struct calculet_file_private *priv;
int err = 0;
calculet_log(CALCULET_LOG_DEBUG, "Device open: PID %d, major %d, minor %d",
current->pid, major, minor);
// 分配文件私有数据
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
return -ENOMEM;
}
// 初始化文件私有数据
kref_init(&priv->ref_count);
priv->owner_pid = current->pid;
priv->is_valid = true;
// 获取板卡引用
board = calculet_board_mgr_get_board(minor);
if (!board) {
calculet_log(CALCULET_LOG_ERROR, "PCIe board not found for /dev/calculet%d", minor);
err = -ENODEV;
goto error_free_priv;
}
// 检查板卡状态
if (!calculet_board_is_operational(board)) {
calculet_log(CALCULET_LOG_ERROR, "Board %d is not operational", minor);
err = -ENODEV;
goto error_put_board;
}
priv->board = board;
// 添加进程监测
err = calculet_proc_add_process(board, current);
if (err) {
calculet_log(CALCULET_LOG_WARN, "Failed to add process monitoring for PID %d: %d",
current->pid, err);
}
filp->private_data = priv;
calculet_board_log(CALCULET_LOG_DEBUG, board, "Device opened by process %s (PID: %d)",
current->comm, current->pid);
return 0;
error_put_board:
calculet_board_mgr_put_board(board);
error_free_priv:
kfree(priv);
return err;
}
int calculet_pcie_fops_release(struct inode *inode, struct file *filp)
{
struct calculet_file_private *priv = (struct calculet_file_private *)filp->private_data;
if (!priv) {
return 0;
}
// 标记为无效,防止新的操作
priv->is_valid = false;
if (priv->board) {
// 更新进程状态为已关闭
calculet_proc_remove_process(priv->board, priv->owner_pid);
calculet_board_log(CALCULET_LOG_DEBUG, priv->board,
"Device closed by process PID %d", priv->owner_pid);
}
// 释放文件私有数据
calculet_file_private_put(priv);
filp->private_data = NULL;
return 0;
}
static int calculet_msi_ioctl(struct device *dev, struct calculet_dma_controller *controller, unsigned long arg)
{
struct calculet_msi_req *req_num = kmalloc(sizeof(*req_num), GFP_KERNEL);
unsigned long timeout = msecs_to_jiffies(30000); // 30秒超时
struct calculet_dma_channel *channel;
if (!dev || !controller) {
return -EINVAL;
}
if (copy_from_user(req_num, (void __user*)arg, sizeof(*req_num))) {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "copy_from_user fail");
kfree(req_num);
return -EFAULT;
}
channel = &controller->msi_req[req_num->msi_req];
if (wait_event_interruptible_timeout(channel->channel_wq,
atomic_read(&channel->transfer_finish) == 1,
timeout) > 0) {
atomic_set(&channel->transfer_finish, 0);
kfree(req_num);
calculet_dev_log(CALCULET_LOG_DEBUG, dev, "EP MSI request completed successfully");
return 0;
} else {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "EP MSI request timed out");
kfree(req_num);
return -ETIMEDOUT;
}
}
/**
* PCIe软复位主函数
*/
static int calculet_pcie_soft_reset(struct calculet_pcie_board *board)
{
struct calculet_resource *resource = &board->pcie_resources.rt_registers;
struct pci_dev *pdev = board->pDev;
struct pci_bus *bus = pdev->bus;
int ret;
calculet_board_log(CALCULET_LOG_DEBUG, board, "Starting PCIe soft reset");
if (!board || !pdev) {
calculet_log(CALCULET_LOG_ERROR, "Invalid board or device pointer");
return -EINVAL;
}
// 准备全局软复位
ret = calculet_global_soft_reset_prepare(board);
if (ret) {
return ret;
}
/* 1. 触发设备端复位 */
calculet_resource_write32(resource, CALCULET_SOFT_RESET_REG, 1);
/* 2. 等待复位开始 */
msleep(100000); // 减少等待时间
pci_lock_rescan_remove();
pci_stop_and_remove_bus_device(pdev);
pci_rescan_bus(bus->parent);
pci_unlock_rescan_remove();
return 0;
}
static long calculet_get_process_list_ioctl(struct calculet_pcie_board *board, unsigned long arg)
{
int i = 0;
struct calculet_process_info *proc_info;
struct calculet_process_info_list_user *user_list = kmalloc(sizeof(*user_list), GFP_KERNEL);
if (!user_list) {
return -ENOMEM;
}
if (!board || !calculet_board_is_operational(board)) {
kfree(user_list);
return -ENODEV;
}
if (copy_from_user(user_list, (void __user*)arg, sizeof(*user_list))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_from_user fail");
kfree(user_list);
return -EFAULT;
}
// 清零并初始化
memset(user_list, 0, sizeof(*user_list));
user_list->board_index = board->board_index;
mutex_lock(&board->process_list_mutex);
list_for_each_entry(proc_info, &board->process_list, list) {
if (proc_info->is_active && i < MAX_PROCESS_ENTRIES) {
user_list->processes[i].pid = proc_info->pid;
user_list->processes[i].ppid = proc_info->ppid;
user_list->processes[i].uid = proc_info->uid;
strncpy(user_list->processes[i].comm, proc_info->comm, sizeof(user_list->processes[i].comm) - 1);
user_list->processes[i].comm[sizeof(user_list->processes[i].comm) - 1] = '\0';
user_list->processes[i].is_active = proc_info->is_active ? 1 : 0;
i++;
}
}
mutex_unlock(&board->process_list_mutex);
user_list->count = i; // 更新实际数量
if (copy_to_user((void __user*)arg, user_list, sizeof(*user_list))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_to_user fail");
kfree(user_list);
return -EFAULT;
}
kfree(user_list);
calculet_board_log(CALCULET_LOG_DEBUG, board, "Returned %d active processes", i);
return 0;
}
static long calculet_get_board_info_ioctl(struct calculet_pcie_board *board, unsigned long arg)
{
u16 lnksta;
u64 memory_band;
struct calculet_ioc_info *board_info = kmalloc(sizeof(struct calculet_ioc_info), GFP_KERNEL);
if (!board_info) {
return -ENOMEM;
}
if (!board || !calculet_board_is_operational(board)) {
kfree(board_info);
return -ENODEV;
}
if (copy_from_user((void *)board_info, (void __user*)arg, sizeof(struct calculet_ioc_info))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_from_user fail");
kfree(board_info);
return -EFAULT;
}
pcie_capability_read_word(board->pDev, PCI_EXP_LNKSTA, &lnksta);
// 解析速率
board_info->pci_info.link_speed = lnksta & PCI_EXP_LNKSTA_CLS;
// 解析通道宽度
board_info->pci_info.link_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT;
board_info->vendor_id = board->pDev->vendor;
board_info->device_id = board->pDev->device;
board_info->subsystem_vendor = board->pDev->subsystem_vendor;
board_info->subsystem_device = board->pDev->subsystem_device;
board_info->pci_info.domain = pci_domain_nr(board->pDev->bus);
board_info->pci_info.bus = board->pDev->bus->number;
board_info->pci_info.device = PCI_SLOT(board->pDev->devfn);
board_info->pci_info.function = PCI_FUNC(board->pDev->devfn);
// 从bar4读取硬件规格参数
calculet_read_strings(board_info->product_name, &board->pcie_resources.smi_registers, CALCULET_PRODUCT_NAME_REG, 32);
calculet_read_strings(board_info->serial_number, &board->pcie_resources.smi_registers, CALCULET_SERIAL_NUMBER_REG, 32);
calculet_read_strings(board_info->product_number, &board->pcie_resources.smi_registers, CALCULET_PRODUCT_NUMBER_REG, 32);
calculet_read_strings(board_info->firmware_version, &board->pcie_resources.smi_registers, CALCULET_FIRMWARE_VERSION_REG, 12);
calculet_generate_simple_uuid(board_info->serial_number, board_info->calculet_uuid);
// 总显存大小 (0x004C, 8字节)
board_info->total_memory = calculet_resource_read64(&board->pcie_resources.smi_registers, CALCULET_TOTAL_MEMORY_REG);
// 显存带宽 (0x0054, 8字节)
memory_band = calculet_resource_read64(&board->pcie_resources.smi_registers, CALCULET_MEMORY_BANDWIDTH_REG);
memcpy(&board_info->memory_bandwidth, &memory_band, sizeof(double));
// DDR频率 (0x005C, 4字节)
board_info->ddr_frequency = calculet_resource_read32(&board->pcie_resources.smi_registers, CALCULET_DDR_FREQUENCY_REG);
// 板卡 TDP上限 (0x0060, 4字节)
board_info->max_power_limit = calculet_resource_read32(&board->pcie_resources.smi_registers, CALCULET_MAX_POWER_LIMIT_REG);
// KS1温度上限 (0x0064, 4字节)
board_info->temp_limit_ks1 = calculet_resource_read32(&board->pcie_resources.smi_registers, CALCULET_TEMP_LIMIT_REG);
board_info->driver_version.major = CALCULET_DRV_VER_MAJOR;
board_info->driver_version.minor = CALCULET_DRV_VER_MINOR;
snprintf(board_info->driver_version.string, KS1_MAX_STRING_LEN,
"%s", CALCULET_DRV_VER);
if (copy_to_user((void __user*)arg, (void *)board_info, sizeof(struct calculet_ioc_info))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_to_user fail");
kfree(board_info);
return -EFAULT;
}
kfree(board_info);
return 0;
}
static long calculet_bar64_transfer_ioctl(struct calculet_pcie_board *board, unsigned long arg)
{
long err = 0;
struct calculet_bar64_transfer_params transfer;
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
if (copy_from_user(&transfer, (void __user*)arg, sizeof(transfer))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_from_user fail");
return -EFAULT;
}
err = calculet_pcie_bar64_transfer(&board->pcie_resources, &transfer);
if (err < 0) {
calculet_board_log(CALCULET_LOG_ERROR, board, "bar transfer failed %ld", err);
calculet_board_stats_update_error(board);
}
if (copy_to_user((void __user*)arg, &transfer, sizeof(transfer))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_to_user fail");
return -EFAULT;
}
return err;
}
static long calculet_bar32_transfer_ioctl(struct calculet_pcie_board *board, unsigned long arg)
{
long err = 0;
struct calculet_bar32_transfer_params transfer;
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
if (copy_from_user(&transfer, (void __user*)arg, sizeof(transfer))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_from_user fail");
return -EFAULT;
}
err = calculet_pcie_bar32_transfer(&board->pcie_resources, &transfer);
if (err < 0) {
calculet_board_log(CALCULET_LOG_ERROR, board, "bar32 transfer failed %ld", err);
calculet_board_stats_update_error(board);
}
if (copy_to_user((void __user*)arg, &transfer, sizeof(transfer))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_to_user fail");
return -EFAULT;
}
return err;
}
static long calculet_get_sn_ioctl(struct calculet_pcie_board *board, unsigned long arg)
{
struct calculet_sn_info sn_info;
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
if (copy_from_user(&sn_info, (void __user*)arg, sizeof(sn_info))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_from_user fail");
return -EFAULT;
}
calculet_read_strings(sn_info.sn, &board->pcie_resources.smi_registers, CALCULET_SERIAL_NUMBER_REG + sn_info.chip_id * 1024, 32);
if (copy_to_user((void __user*)arg, &sn_info, sizeof(sn_info))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_to_user fail");
return -EFAULT;
}
return 0;
}
static long calculet_general_ioctl(struct calculet_pcie_board *board, unsigned int cmd, unsigned long arg)
{
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
switch (cmd) {
case CALCULET_BOARD_INFO:
return calculet_get_board_info_ioctl(board, arg);
case CALCULET_BAR64_TRANSFER:
return calculet_bar64_transfer_ioctl(board, arg);
case CALCULET_BAR32_TRANSFER:
return calculet_bar32_transfer_ioctl(board, arg);
case CALCULET_GET_SN:
return calculet_get_sn_ioctl(board, arg);
default:
calculet_board_log(CALCULET_LOG_ERROR, board, "Invalid general ioctl code 0x%x (nr: %d)",
cmd, _IOC_NR(cmd));
return -ENOTTY;
}
}
static long calculet_ep_ioctl(struct calculet_pcie_board *board, unsigned int cmd, unsigned long arg)
{
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
switch (cmd) {
case CALCULET_EP_MSI_REQ:
return calculet_msi_ioctl(&board->pDev->dev, &board->cdma, arg);
case CALCULET_SOFT_RESET:
return calculet_pcie_soft_reset(board);
default:
calculet_board_log(CALCULET_LOG_ERROR, board, "Invalid ep ioctl code 0x%x (nr: %d)",
cmd, _IOC_NR(cmd));
return -ENOTTY;
}
}
static long calculet_proc_ioctl(struct calculet_pcie_board *board, unsigned int cmd, unsigned long arg)
{
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
switch (cmd) {
case CALCULET_GET_PROCESS_LIST:
return calculet_get_process_list_ioctl(board, arg);
default:
calculet_board_log(CALCULET_LOG_ERROR, board, "Invalid proc ioctl code 0x%x (nr: %d)",
cmd, _IOC_NR(cmd));
return -ENOTTY;
}
}
long calculet_pcie_fops_unlockedioctl(struct file* filp, unsigned int cmd, unsigned long arg)
{
long err = 0;
struct calculet_file_private *priv = (struct calculet_file_private*)filp->private_data;
struct calculet_pcie_board *board;
if (!priv || !priv->is_valid) {
calculet_log(CALCULET_LOG_ERROR, "Invalid file private data");
return -ENODEV;
}
board = priv->board;
if (!board || !board->pDev) {
calculet_log(CALCULET_LOG_ERROR, "Board is NULL or invalid");
return -ENODEV;
}
// 获取文件私有数据引用
calculet_file_private_get(priv);
// 再次检查板卡状态
if (!calculet_board_is_operational(board)) {
err = -ENODEV;
goto out;
}
// 更新进程访问记录
calculet_proc_update_process_access(board, current->pid);
switch (_IOC_TYPE(cmd)) {
case CALCULET_GENERAL_IOCTL_MAGIC:
err = calculet_general_ioctl(board, cmd, arg);
break;
case CALCULET_DMA_IOCTL_MAGIC:
err = calculet_dma_ioctl(&board->pcie_resources.dma_registers, &board->cdma, cmd, arg);
break;
case CALCULET_PCI_EP_IOCTL_MAGIC:
err = calculet_ep_ioctl(board, cmd, arg);
break;
case CALCULET_PROC_IOCTL_MAGIC:
err = calculet_proc_ioctl(board, cmd, arg);
break;
default:
calculet_board_log(CALCULET_LOG_ERROR, board, "Invalid ioctl type %d", _IOC_TYPE(cmd));
err = -ENOTTY;
}
out:
// 释放文件私有数据引用
calculet_file_private_put(priv);
return err;
}
int calculet_pcie_fops_mmap(struct file* filp, struct vm_area_struct *vma)
{
struct calculet_file_private *priv = (struct calculet_file_private*)filp->private_data;
struct calculet_pcie_board *board;
struct calculet_resource *resource;
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
unsigned long size = vma->vm_end - vma->vm_start;
unsigned long pfn;
int bar_num = -1;
if (!priv || !priv->is_valid) {
return -ENODEV;
}
board = priv->board;
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
calculet_board_log(CALCULET_LOG_DEBUG, board, "mmap called by PID %d", current->pid);
// 根据offset判断映射哪个空间
if (offset == MMAP_BAR2_OFFSET) {
resource = &board->pcie_resources.rt_registers;
bar_num = 2;
size = resource->size;
pfn = resource->phy_address >> PAGE_SHIFT;
// BAR空间:不可缓存
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
if (io_remap_pfn_range(vma, vma->vm_start, pfn,
size, vma->vm_page_prot))
return -EAGAIN;
} else if (offset == MMAP_BAR4_OFFSET) {
bar_num = 4;
size = resource->size;
pfn = resource->phy_address >> PAGE_SHIFT;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
if (io_remap_pfn_range(vma, vma->vm_start, pfn,
size, vma->vm_page_prot))
return -EAGAIN;
} else if (offset == MMAP_DMA_OFFSET) {
// // DMA buffer映射(这是系统内存,不是IO空间)
// if (size > pdev->dma_size)
// return -EINVAL;
// // DMA内存可以使用write-combine优化
// vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
// // 使用dma_mmap_coherent映射DMA内存
// if (dma_mmap_coherent(&pdev->pci_dev->dev, vma,
// pdev->dma_virt, pdev->dma_handle,
// pdev->dma_size))
// return -EAGAIN;
} else {
printk(KERN_ERR "Invalid mmap offset: 0x%lx\n", offset);
return -EINVAL;
}
printk(KERN_INFO "mmap: offset=0x%lx, size=%lu, bar=%d\n",
offset, size, bar_num);
return 0;
}
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#ifndef _CALCULET_FOPS_H_
#define _CALCULET_FOPS_H_
#include <linux/fs.h>
#include <linux/interrupt.h>
// 定义offset映射规则
#define MMAP_BAR2_OFFSET 0x10000000
#define MMAP_BAR4_OFFSET 0x20000000
#define MMAP_DMA_OFFSET 0x30000000
/* 为了兼容性,保留原有的一些常量定义 */
#define RESET_COMPLETION_TIMEOUT_MS 5000 // 减少到5秒
#define MAX_RESET_RETRY 100 // 增加重试次数但减少单次延时
#define RETRY_DELAY_US 1000 // 减少到1ms
// calculet ioc info bar4
#define CALCULET_PRODUCT_NAME_REG 0x0000
#define CALCULET_SERIAL_NUMBER_REG 0x4800
#define CALCULET_PRODUCT_NUMBER_REG 0x002C
#define CALCULET_FIRMWARE_VERSION_REG 0x0020
#define CALCULET_TOTAL_MEMORY_REG 0x004C
#define CALCULET_MEMORY_BANDWIDTH_REG 0x0054
#define CALCULET_DDR_FREQUENCY_REG 0x005C
#define CALCULET_MAX_POWER_LIMIT_REG 0x0060
#define CALCULET_TEMP_LIMIT_REG 0x0064
int calculet_pcie_fops_open(struct inode* inode, struct file* filp);
int calculet_pcie_fops_release(struct inode* inode, struct file* filp);
long calculet_pcie_fops_unlockedioctl(struct file* filp, unsigned int cmd, unsigned long arg);
int calculet_pcie_fops_mmap(struct file* filp, struct vm_area_struct *vma);
#endif /* _CALCULET_FOPS_H_ */
@@ -0,0 +1,207 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#ifndef _CALCULET_IOCTL_H_
#define _CALCULET_IOCTL_H_
#define CALCULET_DRV_VER_MAJOR 1
#define CALCULET_DRV_VER_MINOR 0
#define CALCULET_DRV_VER_REVISION 0
#define _STRINGIFY_EXPANDED( x ) #x
#define _STRINGIFY_NUMBER( x ) _STRINGIFY_EXPANDED(x)
#define CALCULET_DRV_VER _STRINGIFY_NUMBER(CALCULET_DRV_VER_MAJOR) "." _STRINGIFY_NUMBER(CALCULET_DRV_VER_MINOR) "." _STRINGIFY_NUMBER(CALCULET_DRV_VER_REVISION)
#define CALCULET_READ_CHANNELS 8
#define CALCULET_WRITE_CHANNELS 8
#define CALCULET_TOTAL_CHANNELS (CALCULET_READ_CHANNELS + CALCULET_WRITE_CHANNELS)
#define CALCULET_MAX_CHANNELS 32
#define KS1_MAX_DEVICES 64 // 最大支持的KS1设备数量
#define KS1_MAX_PATH_LEN 512 // 设备路径最大长度
#define KS1_MAX_STRING_LEN 256 // 一般字符串最大长度
#define KS1_MAX_UUID_LEN 64 // UUID字符串最大长度
#define KS1_MAX_CHIPS 16 // 最大Chip数量
#define MAX_PROCESS_ENTRIES 100 // 最大进程数
#ifdef __linux__
#ifndef __KERNEL__
#include <stdint.h>
#include <sys/types.h>
#else
#include <linux/types.h>
#include <linux/limits.h>
#include <linux/kernel.h>
#endif
#include <linux/ioctl.h>
#define _IOW_ _IOW
#define _IOR_ _IOR
#define _IOWR_ _IOWR
#define _IO_ _IO
#define CALCULET_GENERAL_IOCTL_MAGIC 'g'
#define CALCULET_DMA_IOCTL_MAGIC 'd'
#define CALCULET_SOC_IOCTL_MAGIC 's'
#define CALCULET_NNC_IOCTL_MAGIC 'n'
#define CALCULET_PCI_EP_IOCTL_MAGIC 'p'
#define CALCULET_PROC_IOCTL_MAGIC 'r'
#else
#error "unsupported platform!"
#endif
#pragma pack(push, 1)
// 传输方向枚举
enum calculet_transfer_direction {
TRANSFER_C2H_DIRECTION = 0, //device->host
TRANSFER_H2C_DIRECTION, //host->device
TRANSFER_MAX_ENUM = INT_MAX,
};
// BAR类型枚举
enum calculet_bar_type {
CALCULET_PCIE_BAR2 = 2,
CALCULET_PCIE_BAR4 = 4,
};
// BAR64传输参数
struct calculet_bar64_transfer_params {
enum calculet_transfer_direction transfer_direction; //传输方向
enum calculet_bar_type bar_type; //访问bar类型(2或4
uint64_t bar_offset; //基于bar的地址偏移
uint64_t bar_data; //读或写bar的值
};
// BAR32传输参数
struct calculet_bar32_transfer_params {
enum calculet_transfer_direction transfer_direction; //传输方向
enum calculet_bar_type bar_type; //访问bar类型(2或4
uint64_t bar_offset; //基于bar的地址偏移
uint32_t bar_data; //读或写bar的值
};
// DMA传输通道参数
struct calculet_dma_transfer_channels_params {
enum calculet_transfer_direction dma_transfer_direction; //传输方向
int dma_channel_id; //dma通道index0~7
size_t dma_channel_size; //dma传输一包数据大小(字节)
uint8_t *host_address; //应用层读或写buffer虚拟地址
uint64_t device_address; //要访问device的绝对地址
};
// 进程信息结构体
struct calculet_process_info_user {
int32_t pid; // 进程ID
int32_t ppid; // 父进程ID
uint32_t uid; // 用户ID
char comm[16]; // 进程名称(TASK_COMM_LEN
uint8_t is_active; // 是否活跃
uint8_t reserved[3]; // 对齐保留字段
};
// 进程信息列表结构体
struct calculet_process_info_list_user {
int32_t count; // 进程数量
int32_t board_index; // 板卡索引
struct calculet_process_info_user processes[MAX_PROCESS_ENTRIES];
};
struct calculet_ioc_info {
// 设备基本识别信息
char product_name[KS1_MAX_STRING_LEN]; // 产品名称
char serial_number[KS1_MAX_STRING_LEN]; // 序列号
char product_number[KS1_MAX_STRING_LEN]; // PN号
char calculet_uuid[KS1_MAX_UUID_LEN]; // calculet唯一标识符
// 设备硬件信息
uint16_t device_id; // 设备ID
uint16_t vendor_id; // 厂商ID
uint16_t subsystem_device; // 子系统ID
uint16_t subsystem_vendor; // 子系统厂商ID
// PCI信息
struct {
uint8_t domain; // PCI域
uint8_t bus; // PCI总线
uint8_t device; // PCI设备
uint8_t function; // PCI功能
uint8_t link_speed; // PCI速率
uint8_t link_width; // PCI位宽
} pci_info;
// 版本信息
struct {
uint32_t major; // 驱动主版本号
uint32_t minor; // 驱动次版本号
char string[KS1_MAX_STRING_LEN]; // 版本字符串
} driver_version;
char firmware_version[12]; // 固件版本
// 硬件规格(固定参数)
uint64_t total_memory; // 总显存大小 (0x004C, 8字节)
double memory_bandwidth; // 显存带宽 (0x0054)
uint32_t ddr_frequency; // DDR频率 (0x005C)
uint32_t max_power_limit; // KS1 TDP上限 (0x0060)
uint32_t temp_limit_ks1; // KS1温度上限 (0x0064)
};
struct calculet_msi_req {
uint32_t msi_req; // 自定义msi 请求号(0~15
};
struct calculet_sn_info {
uint32_t chip_id;
char sn[KS1_MAX_STRING_LEN];
};
#pragma pack(pop)
// 通用ioctl代码
enum calculet_general_ioctl_code {
CALCULET_BOARD_INFO_CODE,
CALCULET_BAR64_TRANSFER_CODE,
CALCULET_BAR32_TRANSFER_CODE,
CALCULET_GET_SN_CODE,
CALCULET_GENERAL_IOCTL_MAX_NR,
};
// DMA ioctl代码
enum calculet_dma_ioctl_code {
CALCULET_BLOCK_DMA_TRANSFER_CODE,
CALCULET_DMA_INTERRUPT_POLL_CODE,
CALCULET_EP_MSI_REQ_CODE,
CALCULET_SOFT_RESET_CODE,
CALCULET_DMA_IOCTL_MAX_NR,
};
// 进程信息ioctl代码
enum calculet_proc_ioctl_code {
CALCULET_GET_PROCESS_LIST_CODE,
CALCULET_PROC_IOCTL_MAX_NR,
};
// ioctl定义
// 获取calculet_ioc_info结构体所有参数
#define CALCULET_BOARD_INFO _IOWR_(CALCULET_GENERAL_IOCTL_MAGIC, CALCULET_BOARD_INFO_CODE, struct calculet_ioc_info)
// 64bit访问bar
#define CALCULET_BAR64_TRANSFER _IOWR_(CALCULET_GENERAL_IOCTL_MAGIC, CALCULET_BAR64_TRANSFER_CODE, struct calculet_bar64_transfer_params)
// 32bit访问bar
#define CALCULET_BAR32_TRANSFER _IOWR_(CALCULET_GENERAL_IOCTL_MAGIC, CALCULET_BAR32_TRANSFER_CODE, struct calculet_bar32_transfer_params)
// 获取SN信息
#define CALCULET_GET_SN _IOWR_(CALCULET_GENERAL_IOCTL_MAGIC, CALCULET_GET_SN_CODE, struct calculet_sn_info)
// dma传输
#define CALCULET_BLOCK_DMA_TRANSFER _IOWR_(CALCULET_DMA_IOCTL_MAGIC, CALCULET_BLOCK_DMA_TRANSFER_CODE, struct calculet_dma_transfer_channels_params)
// 轮询中断
#define CALCULET_INTERRUPT_POLL _IO_(CALCULET_DMA_IOCTL_MAGIC, CALCULET_DMA_INTERRUPT_POLL_CODE)
// device主动发起MSI中断(可用于jobq回馈)
#define CALCULET_EP_MSI_REQ _IOW_(CALCULET_PCI_EP_IOCTL_MAGIC, CALCULET_EP_MSI_REQ_CODE, struct calculet_msi_req)
// softreset
#define CALCULET_SOFT_RESET _IO_(CALCULET_PCI_EP_IOCTL_MAGIC, CALCULET_SOFT_RESET_CODE)
// 进程信息
#define CALCULET_GET_PROCESS_LIST _IOWR_(CALCULET_PROC_IOCTL_MAGIC, CALCULET_GET_PROCESS_LIST_CODE, struct calculet_process_info_list_user)
#endif /* _CALCULET_IOCTL_H_ */
@@ -0,0 +1,512 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#include "proc.h"
#include "board_mgr.h"
#include "utils.h"
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/pid.h>
#include <linux/sched.h>
#include <linux/cred.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/signal.h>
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
#define GET_PDE_DATA(inode) pde_data(inode)
#elif defined(RHEL_MAJOR) && RHEL_MAJOR >= 9
#define GET_PDE_DATA(inode) pde_data(inode)
#else
#define GET_PDE_DATA(inode) PDE_DATA(inode)
#endif
static struct calculet_proc_mgr g_proc_mgr = {
.root_dir = NULL,
.summary_entry = NULL,
.processes_entry = NULL,
.initialized = false,
};
// Proc文件操作结构体
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
static const struct proc_ops calculet_proc_summary_ops = {
.proc_open = calculet_proc_summary_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
static const struct proc_ops calculet_proc_device_ops = {
.proc_open = calculet_proc_device_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
static const struct proc_ops calculet_proc_processes_ops = {
.proc_open = calculet_proc_processes_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
#else
static const struct file_operations calculet_proc_summary_ops = {
.open = calculet_proc_summary_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations calculet_proc_device_ops = {
.open = calculet_proc_device_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations calculet_proc_processes_ops = {
.open = calculet_proc_processes_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#endif
int calculet_proc_init(void)
{
if (g_proc_mgr.initialized) {
return 0;
}
// 创建根目录
g_proc_mgr.root_dir = proc_mkdir(CALCULET_PROC_DIR_NAME, NULL);
if (!g_proc_mgr.root_dir) {
pr_err("calculet: Failed to create proc directory %s\n", CALCULET_PROC_DIR_NAME);
return -ENOMEM;
}
// 创建摘要文件
g_proc_mgr.summary_entry = proc_create("summary", 0444, g_proc_mgr.root_dir,
&calculet_proc_summary_ops);
if (!g_proc_mgr.summary_entry) {
pr_err("calculet: Failed to create proc summary file\n");
goto cleanup_root;
}
// 创建全局进程监测文件
g_proc_mgr.processes_entry = proc_create("processes", 0444, g_proc_mgr.root_dir,
&calculet_proc_processes_ops);
if (!g_proc_mgr.processes_entry) {
pr_err("calculet: Failed to create proc processes file\n");
goto cleanup_summary;
}
g_proc_mgr.initialized = true;
pr_info("calculet: Proc filesystem initialized\n");
return 0;
cleanup_summary:
proc_remove(g_proc_mgr.summary_entry);
g_proc_mgr.summary_entry = NULL;
cleanup_root:
proc_remove(g_proc_mgr.root_dir);
g_proc_mgr.root_dir = NULL;
return -ENOMEM;
}
void calculet_proc_cleanup(void)
{
if (!g_proc_mgr.initialized) {
return;
}
if (g_proc_mgr.processes_entry) {
proc_remove(g_proc_mgr.processes_entry);
g_proc_mgr.processes_entry = NULL;
}
if (g_proc_mgr.summary_entry) {
proc_remove(g_proc_mgr.summary_entry);
g_proc_mgr.summary_entry = NULL;
}
if (g_proc_mgr.root_dir) {
proc_remove(g_proc_mgr.root_dir);
g_proc_mgr.root_dir = NULL;
}
g_proc_mgr.initialized = false;
pr_info("calculet: Proc filesystem cleaned up\n");
}
int calculet_proc_add_device(struct calculet_pcie_board *board)
{
if (!g_proc_mgr.initialized || !board) {
return -EINVAL;
}
board->proc_entry = proc_create_data(board->proc_name, 0444, g_proc_mgr.root_dir,
&calculet_proc_device_ops, board);
if (!board->proc_entry) {
pr_err("calculet: Failed to create proc entry for device %d\n", board->board_index);
return -ENOMEM;
}
return 0;
}
void calculet_proc_remove_device(struct calculet_pcie_board *board)
{
if (board && board->proc_entry) {
proc_remove(board->proc_entry);
board->proc_entry = NULL;
}
}
// // 获取进程完整执行路径的函数
// static char *get_process_exe_path(struct task_struct *task, char *buffer, int buflen)
// {
// struct file *exe_file;
// char *path = NULL;
// if (!task || !task->mm) {
// return NULL;
// }
// // 获取可执行文件
// exe_file = get_task_exe_file(task);
// if (!exe_file) {
// return NULL;
// }
// // 获取文件路径
// path = d_path(&exe_file->f_path, buffer, buflen);
// fput(exe_file);
// return IS_ERR(path) ? NULL : path;
// }
// 进程监测相关函数
int calculet_proc_add_process(struct calculet_pcie_board *board, struct task_struct *task)
{
struct calculet_process_info *proc_info;
struct calculet_process_info *existing;
bool found = false;
char path_buffer[256];
char *exe_path;
if (!board || !task) {
return -EINVAL;
}
mutex_lock(&board->process_list_mutex);
// 检查是否已经存在该进程
list_for_each_entry(existing, &board->process_list, list) {
if (existing->pid == task->pid) {
if (!existing->is_active) {
// 重新激活已关闭的进程
existing->is_active = true;
found = true;
break;
} else {
found = true;
break;
}
}
}
if (!found) {
proc_info = kzalloc(sizeof(*proc_info), GFP_KERNEL);
if (!proc_info) {
mutex_unlock(&board->process_list_mutex);
return -ENOMEM;
}
proc_info->pid = task->pid;
proc_info->ppid = task->real_parent ? task->real_parent->pid : 0;
proc_info->uid = from_kuid(&init_user_ns, task_uid(task));
strncpy(proc_info->comm, task->comm, TASK_COMM_LEN - 1);
proc_info->comm[TASK_COMM_LEN - 1] = '\0';
proc_info->is_active = true;
list_add_tail(&proc_info->list, &board->process_list);
atomic_inc(&board->process_count);
// // 获取完整路径(如果需要的话)
// exe_path = get_process_exe_path(task, path_buffer, sizeof(path_buffer));
// if (exe_path) {
// // 可以添加一个新字段存储完整路径
// // strncpy(proc_info->exe_path, exe_path, MAX_PATH_LEN - 1);
// calculet_board_log(CALCULET_LOG_DEBUG, board,
// "Process full path: %s", exe_path);
// }
calculet_board_log(CALCULET_LOG_DEBUG, board,
"Process %s (PID: %d) started using device %d",
proc_info->comm, proc_info->pid, board->board_index);
}
mutex_unlock(&board->process_list_mutex);
return 0;
}
void calculet_proc_remove_process(struct calculet_pcie_board *board, pid_t pid)
{
struct calculet_process_info *proc_info;
if (!board) {
return;
}
mutex_lock(&board->process_list_mutex);
list_for_each_entry(proc_info, &board->process_list, list) {
if (proc_info->pid == pid && proc_info->is_active) {
proc_info->is_active = false;
calculet_board_log(CALCULET_LOG_DEBUG, board,
"Process %s (PID: %d) stopped using device %d",
proc_info->comm, proc_info->pid, board->board_index);
break;
}
}
mutex_unlock(&board->process_list_mutex);
}
void calculet_proc_update_process_access(struct calculet_pcie_board *board, pid_t pid)
{
struct calculet_process_info *proc_info;
if (!board) {
return;
}
mutex_lock(&board->process_list_mutex);
list_for_each_entry(proc_info, &board->process_list, list) {
if (proc_info->pid == pid && proc_info->is_active) {
break;
}
}
mutex_unlock(&board->process_list_mutex);
}
void calculet_proc_cleanup_processes(struct calculet_pcie_board *board)
{
struct calculet_process_info *proc_info, *tmp;
if (!board) {
return;
}
mutex_lock(&board->process_list_mutex);
list_for_each_entry_safe(proc_info, tmp, &board->process_list, list) {
list_del(&proc_info->list);
kfree(proc_info);
atomic_dec(&board->process_count);
}
mutex_unlock(&board->process_list_mutex);
}
// 简化版摘要显示函数 - 只显示核心统计信息
int calculet_proc_summary_show(struct seq_file *m, void *v)
{
struct calculet_pcie_board *boards[MAX_CALCULET_BOARDS];
int board_count, i;
int total_active_processes = 0;
board_count = calculet_board_mgr_get_all_boards(boards, MAX_CALCULET_BOARDS);
seq_printf(m, "CALCULET PCIe Driver Status\n");
seq_printf(m, "===========================\n");
seq_printf(m, "Driver Version: %s\n", CALCULET_DRV_VER);
seq_printf(m, "Active Boards: %d\n", board_count);
seq_printf(m, "\n");
if (board_count == 0) {
seq_printf(m, "No active boards found.\n");
return 0;
}
seq_printf(m, "Board Summary:\n");
seq_printf(m, "Board State Bus:Dev.Func Active Procs\n");
seq_printf(m, "----------------------------------------\n");
for (i = 0; i < board_count; i++) {
struct calculet_pcie_board *board = boards[i];
enum calculet_board_state state = calculet_board_get_state(board);
const char *state_str;
int active_procs = 0;
struct calculet_process_info *proc_info;
switch (state) {
case BOARD_STATE_ACTIVE: state_str = "ACTIVE"; break;
case BOARD_STATE_REMOVING: state_str = "REMOVE"; break;
default: state_str = "UNKNOWN"; break;
}
// 统计活跃进程数
mutex_lock(&board->process_list_mutex);
list_for_each_entry(proc_info, &board->process_list, list) {
if (proc_info->is_active) {
active_procs++;
}
}
mutex_unlock(&board->process_list_mutex);
seq_printf(m, "%-5d %-7s %02x:%02x.%x %-11d\n",
board->board_index, state_str,
board->pDev->bus->number, PCI_SLOT(board->pDev->devfn), PCI_FUNC(board->pDev->devfn),
active_procs);
total_active_processes += active_procs;
// 释放引用
calculet_board_mgr_put_board(board);
}
seq_printf(m, "----------------------------------------\n");
seq_printf(m, "Total Active Processes: %d\n", total_active_processes);
return 0;
}
int calculet_proc_summary_open(struct inode *inode, struct file *file)
{
return single_open(file, calculet_proc_summary_show, NULL);
}
// 简化版设备详细信息显示函数
int calculet_proc_device_show(struct seq_file *m, void *v)
{
struct calculet_pcie_board *board = (struct calculet_pcie_board *)m->private;
struct calculet_process_info *proc_info;
int active_processes = 0;
enum calculet_board_state state;
if (!board) {
seq_printf(m, "Error: Invalid board data\n");
return 0;
}
state = calculet_board_get_state(board);
seq_printf(m, "CALCULET Board %d Information\n", board->board_index);
seq_printf(m, "=============================\n\n");
// 板卡基本状态
seq_printf(m, "Board Status:\n");
seq_printf(m, " State: %s\n",
state == BOARD_STATE_ACTIVE ? "ACTIVE" :
state == BOARD_STATE_REMOVING ? "REMOVING" : "UNKNOWN");
seq_printf(m, " Operational: %s\n", calculet_board_is_operational(board) ? "YES" : "NO");
seq_printf(m, "\n");
// PCIe基本信息
seq_printf(m, "PCIe Information:\n");
seq_printf(m, " Device ID: %04x:%04x\n", board->pDev->vendor, board->pDev->device);
seq_printf(m, " Bus:Dev.Func: %02x:%02x.%x\n",
board->pDev->bus->number, PCI_SLOT(board->pDev->devfn), PCI_FUNC(board->pDev->devfn));
seq_printf(m, "\n");
// 进程信息 - 只显示活跃进程
mutex_lock(&board->process_list_mutex);
list_for_each_entry(proc_info, &board->process_list, list) {
if (proc_info->is_active) {
active_processes++;
}
}
seq_printf(m, "Active Process Information:\n");
seq_printf(m, " Active Processes: %d\n", active_processes);
seq_printf(m, "\n");
if (active_processes > 0) {
seq_printf(m, "Active Process Details:\n");
seq_printf(m, "PID PPID UID COMM\n");
seq_printf(m, "------------------------------------\n");
list_for_each_entry(proc_info, &board->process_list, list) {
if (proc_info->is_active) {
seq_printf(m, "%-7d %-7d %-7u %s\n",
proc_info->pid, proc_info->ppid, proc_info->uid,
proc_info->comm);
}
}
}
mutex_unlock(&board->process_list_mutex);
return 0;
}
int calculet_proc_device_open(struct inode *inode, struct file *file)
{
return single_open(file, calculet_proc_device_show, GET_PDE_DATA(inode));
}
// 简化版全局进程监测显示函数
int calculet_proc_processes_show(struct seq_file *m, void *v)
{
struct calculet_pcie_board *boards[MAX_CALCULET_BOARDS];
int board_count, i;
struct calculet_process_info *proc_info;
int total_active = 0;
board_count = calculet_board_mgr_get_all_boards(boards, MAX_CALCULET_BOARDS);
seq_printf(m, "CALCULET Active Process Monitor\n");
seq_printf(m, "===============================\n\n");
if (board_count == 0) {
seq_printf(m, "No active boards found.\n");
return 0;
}
seq_printf(m, "BOARD PID PPID UID COMM\n");
seq_printf(m, "-----------------------------------\n");
for (i = 0; i < board_count; i++) {
struct calculet_pcie_board *board = boards[i];
mutex_lock(&board->process_list_mutex);
list_for_each_entry(proc_info, &board->process_list, list) {
if (proc_info->is_active) { // 只显示活跃进程
seq_printf(m, "%-5d %-7d %-7d %-7u %s\n",
board->board_index,
proc_info->pid, proc_info->ppid, proc_info->uid,
proc_info->comm);
total_active++;
}
}
mutex_unlock(&board->process_list_mutex);
// 释放引用
calculet_board_mgr_put_board(board);
}
seq_printf(m, "---------------------------------------------------\n");
seq_printf(m, "Total active processes: %d\n", total_active);
return 0;
}
int calculet_proc_processes_open(struct inode *inode, struct file *file)
{
return single_open(file, calculet_proc_processes_show, NULL);
}
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#ifndef _CALCULET_PROC_H_
#define _CALCULET_PROC_H_
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/time.h>
#include <linux/atomic.h>
#define CALCULET_PROC_DIR_NAME "calculet_monitor"
#define MAX_PROCESS_ENTRIES 100
// 前向声明
struct calculet_pcie_board;
// 简化的内核进程信息结构体
struct calculet_process_info {
struct list_head list; // 链表节点
pid_t pid; // 进程ID
pid_t ppid; // 父进程ID
uid_t uid; // 用户ID
char comm[TASK_COMM_LEN]; // 进程名称
bool is_active; // 是否仍然活跃
};
// Proc文件系统管理结构体
struct calculet_proc_mgr {
struct proc_dir_entry *root_dir;
struct proc_dir_entry *summary_entry;
struct proc_dir_entry *processes_entry;
bool initialized;
};
// 函数声明
int calculet_proc_init(void);
void calculet_proc_cleanup(void);
int calculet_proc_add_device(struct calculet_pcie_board *board);
void calculet_proc_remove_device(struct calculet_pcie_board *board);
// 进程监测相关函数
int calculet_proc_add_process(struct calculet_pcie_board *board, struct task_struct *task);
void calculet_proc_remove_process(struct calculet_pcie_board *board, pid_t pid);
void calculet_proc_update_process_access(struct calculet_pcie_board *board, pid_t pid);
void calculet_proc_cleanup_processes(struct calculet_pcie_board *board);
// Proc文件显示函数
int calculet_proc_summary_show(struct seq_file *m, void *v);
int calculet_proc_summary_open(struct inode *inode, struct file *file);
int calculet_proc_device_show(struct seq_file *m, void *v);
int calculet_proc_device_open(struct inode *inode, struct file *file);
int calculet_proc_processes_show(struct seq_file *m, void *v);
int calculet_proc_processes_open(struct inode *inode, struct file *file);
#endif /* _CALCULET_PROC_H_ */
@@ -0,0 +1,420 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#include "utils.h"
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/random.h>
// 全局日志级别控制(可通过模块参数调整)
#ifndef CALCULET_LOG_LEVEL
#define CALCULET_LOG_LEVEL CALCULET_LOG_INFO
#endif
int calculet_log_level = CALCULET_LOG_LEVEL;
// 模块参数,允许运行时调整日志级别
module_param(calculet_log_level, int, 0644);
MODULE_PARM_DESC(calculet_log_level, "Debug level (0=EMERG, 1=ALERT, 2=CRIT, 3=ERR, 4=WARN, 5=NOTICE, 6=INFO, 7=DEBUG)");
// 全局统计信息
static struct calculet_stats_counter g_global_stats;
static bool g_stats_initialized = false;
static DEFINE_SPINLOCK(g_stats_lock);
/**
* 初始化全局统计
*/
void calculet_global_stats_init(void)
{
spin_lock(&g_stats_lock);
if (!g_stats_initialized) {
calculet_stats_init(&g_global_stats);
g_stats_initialized = true;
calculet_log(CALCULET_LOG_DEBUG, "Global statistics initialized");
}
spin_unlock(&g_stats_lock);
}
/**
* 更新全局统计
*/
void calculet_global_stats_update(s64 value)
{
if (g_stats_initialized) {
calculet_stats_update(&g_global_stats, value);
}
}
/**
* 获取全局统计信息
*/
void calculet_global_stats_get(s64 *count, s64 *total, s64 *min, s64 *max, s64 *avg)
{
if (!g_stats_initialized) {
if (count) *count = 0;
if (total) *total = 0;
if (min) *min = 0;
if (max) *max = 0;
if (avg) *avg = 0;
return;
}
if (count) *count = atomic64_read(&g_global_stats.count);
if (total) *total = atomic64_read(&g_global_stats.total);
if (min) *min = atomic64_read(&g_global_stats.min);
if (max) *max = atomic64_read(&g_global_stats.max);
if (avg) *avg = calculet_stats_avg(&g_global_stats);
}
/**
* 清理全局统计
*/
void calculet_global_stats_cleanup(void)
{
spin_lock(&g_stats_lock);
g_stats_initialized = false;
spin_unlock(&g_stats_lock);
calculet_log(CALCULET_LOG_DEBUG, "Global statistics cleaned up");
}
/**
* 内存分配跟踪器(仅在调试模式下)
*/
#ifdef CALCULET_DEBUG_MODE
struct calculet_mem_info {
void *ptr;
size_t size;
const char *file;
int line;
ktime_t alloc_time;
struct list_head list;
};
static LIST_HEAD(g_mem_list);
static DEFINE_SPINLOCK(g_mem_lock);
static atomic_t g_mem_count = ATOMIC_INIT(0);
static atomic64_t g_mem_total = ATOMIC64_INIT(0);
void *calculet_kmalloc_tracked(size_t size, gfp_t flags, const char *file, int line)
{
struct calculet_mem_info *info;
void *ptr;
ptr = kmalloc(size, flags);
if (!ptr) {
return NULL;
}
info = kmalloc(sizeof(*info), GFP_ATOMIC);
if (!info) {
kfree(ptr);
return NULL;
}
info->ptr = ptr;
info->size = size;
info->file = file;
info->line = line;
info->alloc_time = ktime_get();
spin_lock(&g_mem_lock);
list_add_tail(&info->list, &g_mem_list);
spin_unlock(&g_mem_lock);
atomic_inc(&g_mem_count);
atomic64_add(size, &g_mem_total);
calculet_log(CALCULET_LOG_DEBUG, "ALLOC %p (%zu bytes) at %s:%d, total=%d allocations, %lld bytes",
ptr, size, file, line, atomic_read(&g_mem_count), atomic64_read(&g_mem_total));
return ptr;
}
void calculet_kfree_tracked(void *ptr, const char *file, int line)
{
struct calculet_mem_info *info, *tmp;
bool found = false;
if (!ptr) {
return;
}
spin_lock(&g_mem_lock);
list_for_each_entry_safe(info, tmp, &g_mem_list, list) {
if (info->ptr == ptr) {
list_del(&info->list);
found = true;
break;
}
}
spin_unlock(&g_mem_lock);
if (found) {
atomic_dec(&g_mem_count);
atomic64_sub(info->size, &g_mem_total);
calculet_log(CALCULET_LOG_DEBUG, "FREE %p (%zu bytes) at %s:%d, allocated at %s:%d, lifetime=%lld us",
ptr, info->size, file, line, info->file, info->line,
calculet_ktime_to_us(ktime_sub(ktime_get(), info->alloc_time)));
kfree(info);
} else {
calculet_log(CALCULET_LOG_WARN, "FREE unknown pointer %p at %s:%d", ptr, file, line);
}
kfree(ptr);
}
void calculet_mem_dump_leaks(void)
{
struct calculet_mem_info *info;
int leak_count = 0;
calculet_log(CALCULET_LOG_DEBUG, "Memory leak check - %d allocations, %lld bytes total",
atomic_read(&g_mem_count), atomic64_read(&g_mem_total));
spin_lock(&g_mem_lock);
list_for_each_entry(info, &g_mem_list, list) {
calculet_log(CALCULET_LOG_WARN, "LEAK %p (%zu bytes) allocated at %s:%d, age=%lld us",
info->ptr, info->size, info->file, info->line,
calculet_ktime_to_us(ktime_sub(ktime_get(), info->alloc_time)));
leak_count++;
}
spin_unlock(&g_mem_lock);
if (leak_count == 0) {
calculet_log(CALCULET_LOG_DEBUG, "No memory leaks detected");
} else {
calculet_log(CALCULET_LOG_ERROR, "%d memory leaks detected!", leak_count);
}
}
void calculet_mem_cleanup_tracking(void)
{
struct calculet_mem_info *info, *tmp;
spin_lock(&g_mem_lock);
list_for_each_entry_safe(info, tmp, &g_mem_list, list) {
list_del(&info->list);
kfree(info);
}
spin_unlock(&g_mem_lock);
atomic_set(&g_mem_count, 0);
atomic64_set(&g_mem_total, 0);
}
#endif /* CALCULET_DEBUG_MODE */
/**
* 随机延迟函数(用于测试)
*/
void calculet_random_delay_us(unsigned int min_us, unsigned int max_us)
{
unsigned int delay_us;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
delay_us = min_us + (get_random_u32() % (max_us - min_us + 1));
#else
delay_us = min_us + (get_random_int() % (max_us - min_us + 1));
#endif
calculet_udelay_range(delay_us, delay_us + 10);
}
/**
* 字符串工具函数
*/
char *calculet_strdup(const char *s, gfp_t flags)
{
char *dup;
size_t len;
if (!s) {
return NULL;
}
len = strlen(s) + 1;
dup = kmalloc(len, flags);
if (dup) {
memcpy(dup, s, len);
}
return dup;
}
int calculet_snprintf_safe(char *buf, size_t size, const char *fmt, ...)
{
va_list args;
int ret;
if (!buf || size == 0) {
return -EINVAL;
}
va_start(args, fmt);
ret = vsnprintf(buf, size, fmt, args);
va_end(args);
// 确保字符串以null结尾
buf[size - 1] = '\0';
return ret;
}
/**
* 位操作工具函数
*/
unsigned int calculet_ffs(unsigned long word)
{
return __ffs(word);
}
unsigned int calculet_fls(unsigned long word)
{
return fls(word);
}
unsigned int calculet_popcount(unsigned long word)
{
return hweight_long(word);
}
/**
* 时间工具函数
*/
u64 calculet_get_cycles(void)
{
return get_cycles();
}
ktime_t calculet_get_monotonic_time(void)
{
return ktime_get();
}
ktime_t calculet_get_real_time(void)
{
return ktime_get_real();
}
/**
* CRC32计算(简单版本)
*/
static const u32 crc32_table[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
u32 calculet_crc32(u32 crc, const void *buf, size_t len)
{
const u8 *data = (const u8 *)buf;
crc = ~crc;
while (len--) {
crc = crc32_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
}
return ~crc;
}
/**
* 模块清理函数
*/
void calculet_utils_cleanup(void)
{
#ifdef CALCULET_DEBUG_MODE
calculet_mem_dump_leaks();
calculet_mem_cleanup_tracking();
#endif
calculet_global_stats_cleanup();
}
/**
* 模块初始化函数
*/
int calculet_utils_init(void)
{
calculet_global_stats_init();
calculet_log(CALCULET_LOG_DEBUG, "Utils module initialized (log_level=%d, debug_mode=%s)",
calculet_log_level,
#ifdef CALCULET_DEBUG_MODE
"enabled"
#else
"disabled"
#endif
);
return 0;
}
// 导出符号供其他模块使用
EXPORT_SYMBOL(calculet_log_level);
EXPORT_SYMBOL(calculet_global_stats_init);
EXPORT_SYMBOL(calculet_global_stats_update);
EXPORT_SYMBOL(calculet_global_stats_get);
EXPORT_SYMBOL(calculet_global_stats_cleanup);
#ifdef CALCULET_DEBUG_MODE
EXPORT_SYMBOL(calculet_kmalloc_tracked);
EXPORT_SYMBOL(calculet_kfree_tracked);
EXPORT_SYMBOL(calculet_mem_dump_leaks);
EXPORT_SYMBOL(calculet_mem_cleanup_tracking);
#endif
EXPORT_SYMBOL(calculet_random_delay_us);
EXPORT_SYMBOL(calculet_strdup);
EXPORT_SYMBOL(calculet_snprintf_safe);
EXPORT_SYMBOL(calculet_ffs);
EXPORT_SYMBOL(calculet_fls);
EXPORT_SYMBOL(calculet_popcount);
EXPORT_SYMBOL(calculet_get_cycles);
EXPORT_SYMBOL(calculet_get_monotonic_time);
EXPORT_SYMBOL(calculet_get_real_time);
EXPORT_SYMBOL(calculet_crc32);
EXPORT_SYMBOL(calculet_utils_init);
EXPORT_SYMBOL(calculet_utils_cleanup);
@@ -0,0 +1,335 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#ifndef _CALCULET_UTILS_H_
#define _CALCULET_UTILS_H_
#include <linux/version.h>
#include <linux/kern_levels.h>
#include <linux/scatterlist.h>
#include <linux/vmalloc.h>
#include <linux/time.h>
#include <linux/ktime.h>
#include <linux/delay.h>
#include <asm/delay.h>
#include <asm/div64.h>
// 日志级别定义
#define CALCULET_LOG_EMERG 0
#define CALCULET_LOG_ALERT 1
#define CALCULET_LOG_CRIT 2
#define CALCULET_LOG_ERROR 3
#define CALCULET_LOG_WARN 4
#define CALCULET_LOG_NOTICE 5
#define CALCULET_LOG_INFO 6
#define CALCULET_LOG_DEBUG 7
// 编译时日志级别控制(通过Makefile设置)
#ifndef CALCULET_LOG_LEVEL
#ifdef DEBUG
#define CALCULET_LOG_LEVEL CALCULET_LOG_DEBUG
#else
#define CALCULET_LOG_LEVEL CALCULET_LOG_INFO
#endif
#endif
// 统一的日志函数声明
extern int calculet_log_level;
// 主要的日志宏
#define calculet_log(level, fmt, ...) \
do { \
if ((level) <= CALCULET_LOG_LEVEL && (level) <= calculet_log_level) { \
const char *level_str; \
switch (level) { \
case CALCULET_LOG_EMERG: level_str = KERN_EMERG; break; \
case CALCULET_LOG_ALERT: level_str = KERN_ALERT; break; \
case CALCULET_LOG_CRIT: level_str = KERN_CRIT; break; \
case CALCULET_LOG_ERROR: level_str = KERN_ERR; break; \
case CALCULET_LOG_WARN: level_str = KERN_WARNING; break; \
case CALCULET_LOG_NOTICE: level_str = KERN_NOTICE; break; \
case CALCULET_LOG_INFO: level_str = KERN_INFO; break; \
case CALCULET_LOG_DEBUG: level_str = KERN_DEBUG; break; \
default: level_str = KERN_INFO; break; \
} \
printk("%s" "calculet: " fmt "\n", level_str, ##__VA_ARGS__); \
} \
} while (0)
// 带板卡信息的日志宏
#define calculet_board_log(level, board, fmt, ...) \
do { \
if ((level) <= CALCULET_LOG_LEVEL && (level) <= calculet_log_level && (board)) { \
const char *level_str; \
switch (level) { \
case CALCULET_LOG_EMERG: level_str = KERN_EMERG; break; \
case CALCULET_LOG_ALERT: level_str = KERN_ALERT; break; \
case CALCULET_LOG_CRIT: level_str = KERN_CRIT; break; \
case CALCULET_LOG_ERROR: level_str = KERN_ERR; break; \
case CALCULET_LOG_WARN: level_str = KERN_WARNING; break; \
case CALCULET_LOG_NOTICE: level_str = KERN_NOTICE; break; \
case CALCULET_LOG_INFO: level_str = KERN_INFO; break; \
case CALCULET_LOG_DEBUG: level_str = KERN_DEBUG; break; \
default: level_str = KERN_INFO; break; \
} \
if (board && (board)->pDev && &(board)->pDev->dev) { \
dev_printk(level_str, &(board)->pDev->dev, fmt, ##__VA_ARGS__); \
} else { \
printk("%s" "calculet: [board error] " fmt "\n", level_str, ##__VA_ARGS__); \
} \
} \
} while (0)
// 带设备信息的日志宏
#define calculet_dev_log(level, dev, fmt, ...) \
do { \
if ((level) <= CALCULET_LOG_LEVEL && (level) <= calculet_log_level && (dev)) { \
const char *level_str; \
switch (level) { \
case CALCULET_LOG_EMERG: level_str = KERN_EMERG; break; \
case CALCULET_LOG_ALERT: level_str = KERN_ALERT; break; \
case CALCULET_LOG_CRIT: level_str = KERN_CRIT; break; \
case CALCULET_LOG_ERROR: level_str = KERN_ERR; break; \
case CALCULET_LOG_WARN: level_str = KERN_WARNING; break; \
case CALCULET_LOG_NOTICE: level_str = KERN_NOTICE; break; \
case CALCULET_LOG_INFO: level_str = KERN_INFO; break; \
case CALCULET_LOG_DEBUG: level_str = KERN_DEBUG; break; \
default: level_str = KERN_INFO; break; \
} \
if (dev) { \
dev_printk(level_str, dev, fmt, ##__VA_ARGS__); \
} else { \
printk("%s" "calculet: [dev error] " fmt "\n", level_str, ##__VA_ARGS__); \
} \
} \
} while (0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
#define class_create_compat class_create
#elif defined(RHEL_MAJOR) && RHEL_MAJOR >= 9
#define class_create_compat class_create
#else
#define class_create_compat(name) class_create(THIS_MODULE, name)
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)
#define pci_printk(level, pdev, fmt, arg...) \
dev_printk(level, &(pdev)->dev, fmt, ##arg)
#define pci_emerg(pdev, fmt, arg...) dev_emerg(&(pdev)->dev, fmt, ##arg)
#define pci_alert(pdev, fmt, arg...) dev_alert(&(pdev)->dev, fmt, ##arg)
#define pci_crit(pdev, fmt, arg...) dev_crit(&(pdev)->dev, fmt, ##arg)
#define pci_err(pdev, fmt, arg...) dev_err(&(pdev)->dev, fmt, ##arg)
#define pci_warn(pdev, fmt, arg...) dev_warn(&(pdev)->dev, fmt, ##arg)
#define pci_notice(pdev, fmt, arg...) dev_notice(&(pdev)->dev, fmt, ##arg)
#define pci_info(pdev, fmt, arg...) dev_info(&(pdev)->dev, fmt, ##arg)
#define pci_dbg(pdev, fmt, arg...) dev_dbg(&(pdev)->dev, fmt, ##arg)
#endif
// 性能测量宏(简化)
#ifdef CALCULET_PERF_ANALYSIS
#define CALCULET_PERF_START(name) \
ktime_t __perf_start_##name = ktime_get()
#define CALCULET_PERF_END(name, fmt, ...) \
do { \
ktime_t __perf_end_##name = ktime_get(); \
s64 __perf_delta_##name = ktime_to_ns(ktime_sub(__perf_end_##name, __perf_start_##name)); \
calculet_log(CALCULET_LOG_DEBUG, "PERF %s: %lld ns - " fmt, #name, __perf_delta_##name, ##__VA_ARGS__); \
} while (0)
#else
#define CALCULET_PERF_START(name) do { } while (0)
#define CALCULET_PERF_END(name, fmt, ...) do { } while (0)
#endif
// 调试断言宏
#ifdef CALCULET_DEBUG_MODE
#define CALCULET_ASSERT(cond) \
do { \
if (unlikely(!(cond))) { \
calculet_log(CALCULET_LOG_CRIT, "ASSERTION FAILED: %s at %s:%d", #cond, __FILE__, __LINE__); \
dump_stack(); \
} \
} while (0)
#define CALCULET_ASSERT_MSG(cond, fmt, ...) \
do { \
if (unlikely(!(cond))) { \
calculet_log(CALCULET_LOG_CRIT, "ASSERTION FAILED: %s at %s:%d - " fmt, \
#cond, __FILE__, __LINE__, ##__VA_ARGS__); \
dump_stack(); \
} \
} while (0)
#else
#define CALCULET_ASSERT(cond) do { } while (0)
#define CALCULET_ASSERT_MSG(cond, fmt, ...) do { } while (0)
#endif
// 更多兼容性定义
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 5, 0, 0 )
#define compatible_access_ok(a,b,c) access_ok(b, c)
#else
#define compatible_access_ok(a,b,c) access_ok(a, b, c)
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
#define get_user_pages_compact get_user_pages
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
#define get_user_pages_compact(start, nr_pages, gup_flags, pages) \
get_user_pages(start, nr_pages, gup_flags, pages, NULL)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 168)) && (LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0))
#define get_user_pages_compact(start, nr_pages, gup_flags, pages) \
get_user_pages(current, current->mm, start, nr_pages, gup_flags, pages, NULL)
#else
static inline int get_user_pages_compact(struct task_struct *current,
struct mm_struct *mm,
unsigned long start,
int nr_pages,
int write,
int force,
struct page **pages,
struct vm_area_struct **vmas)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0)
return get_user_pages(start, nr_pages, write ? FOLL_WRITE : 0, pages, vmas);
#else
return get_user_pages(current, mm, start, nr_pages, write, force, pages, vmas);
#endif
}
#endif
#ifndef _LINUX_MMAP_LOCK_H
static inline void mmap_read_lock(struct mm_struct *mm)
{
down_read(&mm->mmap_sem);
}
static inline void mmap_read_unlock(struct mm_struct *mm)
{
up_read(&mm->mmap_sem);
}
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 4, 13, 0 )
#define wait_queue_t wait_queue_entry_t
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 4, 15, 0 )
#define ACCESS_ONCE READ_ONCE
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22))
#define CALCULET_IRQ_FLAGS (SA_SHIRQ | SA_INTERRUPT)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0))
#define CALCULET_IRQ_FLAGS (IRQF_SHARED | IRQF_DISABLED)
#else
#define CALCULET_IRQ_FLAGS (IRQF_SHARED)
#endif
// 实用工具宏
#define CALCULET_ALIGN_UP(value, align) \
(((value) + (align) - 1) & ~((align) - 1))
#define CALCULET_ALIGN_DOWN(value, align) \
((value) & ~((align) - 1))
#define CALCULET_DIV_ROUND_UP(n, d) \
(((n) + (d) - 1) / (d))
#define CALCULET_IS_POWER_OF_2(x) \
((x) != 0 && (((x) & ((x) - 1)) == 0))
// 错误处理工具
#define CALCULET_RETURN_IF_NULL(ptr, ret) \
do { if (unlikely(!(ptr))) return (ret); } while (0)
#define CALCULET_RETURN_IF_ERROR(expr) \
do { \
int __ret = (expr); \
if (unlikely(__ret < 0)) return __ret; \
} while (0)
// 时间转换工具
static inline u64 calculet_ktime_to_us(ktime_t kt)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)
return ktime_divns(kt, NSEC_PER_USEC);
#else
u64 ns = ktime_to_ns(kt);
do_div(ns, NSEC_PER_USEC);
return ns;
#endif
}
static inline u64 calculet_ktime_to_ms(ktime_t kt)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)
return ktime_divns(kt, NSEC_PER_MSEC);
#else
u64 ns = ktime_to_ns(kt);
do_div(ns, NSEC_PER_MSEC);
return ns;
#endif
}
// 延迟和等待工具
static inline void calculet_udelay_range(unsigned long min_us, unsigned long max_us)
{
if (min_us == max_us) {
udelay(min_us);
} else {
usleep_range(min_us, max_us);
}
}
// 统计工具(简化的原子版本)
struct calculet_stats_counter {
atomic64_t count;
atomic64_t total;
atomic64_t min;
atomic64_t max;
};
static inline void calculet_stats_init(struct calculet_stats_counter *stats)
{
atomic64_set(&stats->count, 0);
atomic64_set(&stats->total, 0);
atomic64_set(&stats->min, LLONG_MAX);
atomic64_set(&stats->max, 0);
}
static inline void calculet_stats_update(struct calculet_stats_counter *stats, s64 value)
{
s64 old_min, old_max;
atomic64_inc(&stats->count);
atomic64_add(value, &stats->total);
// 使用简单的CAS来更新min/max
do {
old_min = atomic64_read(&stats->min);
} while (value < old_min && atomic64_cmpxchg(&stats->min, old_min, value) != old_min);
do {
old_max = atomic64_read(&stats->max);
} while (value > old_max && atomic64_cmpxchg(&stats->max, old_max, value) != old_max);
}
static inline s64 calculet_stats_avg(struct calculet_stats_counter *stats)
{
s64 count = atomic64_read(&stats->count);
return count ? atomic64_read(&stats->total) / count : 0;
}
// 函数声明
int calculet_utils_init(void);
void calculet_utils_cleanup(void);
void calculet_global_stats_init(void);
void calculet_global_stats_update(s64 value);
void calculet_global_stats_get(s64 *count, s64 *total, s64 *min, s64 *max, s64 *avg);
void calculet_global_stats_cleanup(void);
void calculet_random_delay_us(unsigned int min_us, unsigned int max_us);
#endif /* _CALCULET_UTILS_H_ */
@@ -0,0 +1,22 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: cal-pcie
Source: http://192.168.20.33:1000/niuzhenyu/cal-pcie.git
Files: debian/*
Copyright: 2026 Junwei Qin <qinjunwei@calculet.tech>
License: GPL-2+
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
.
This package 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.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>
.
On Debian systems, the complete text of the GNU General
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
@@ -0,0 +1,25 @@
/.
/usr
/usr/share
/usr/share/doc
/usr/share/doc/cal-pcie
/usr/share/doc/cal-pcie/changelog.Debian.gz
/usr/share/doc/cal-pcie/copyright
/usr/src
/usr/src/cal-pcie-0.9.0
/usr/src/cal-pcie-0.9.0/Makefile
/usr/src/cal-pcie-0.9.0/board_mgr.c
/usr/src/cal-pcie-0.9.0/board_mgr.h
/usr/src/cal-pcie-0.9.0/calculet_pci.mod.c
/usr/src/cal-pcie-0.9.0/calculet_pci_core.c
/usr/src/cal-pcie-0.9.0/calculet_pci_core.h
/usr/src/cal-pcie-0.9.0/dkms.conf
/usr/src/cal-pcie-0.9.0/dma.c
/usr/src/cal-pcie-0.9.0/dma.h
/usr/src/cal-pcie-0.9.0/fops.c
/usr/src/cal-pcie-0.9.0/fops.h
/usr/src/cal-pcie-0.9.0/ioctl.h
/usr/src/cal-pcie-0.9.0/proc.c
/usr/src/cal-pcie-0.9.0/proc.h
/usr/src/cal-pcie-0.9.0/utils.c
/usr/src/cal-pcie-0.9.0/utils.h
@@ -0,0 +1,18 @@
c589326957579b94e55a9e11adc4098c usr/share/doc/cal-pcie/changelog.Debian.gz
a7f9bb8aebf66a03d992267809cff877 usr/share/doc/cal-pcie/copyright
788fdcca7726bfe0a2883e0f291b380c usr/src/cal-pcie-0.9.0/Makefile
4e755f9719aa171e82dd8d1909c29480 usr/src/cal-pcie-0.9.0/board_mgr.c
24b662df4710a9dc482b15e5d5a1f501 usr/src/cal-pcie-0.9.0/board_mgr.h
f720659b9f4260f2a226d1f39dcf5aac usr/src/cal-pcie-0.9.0/calculet_pci.mod.c
daf9a5d6a4faad743f65577363930f53 usr/src/cal-pcie-0.9.0/calculet_pci_core.c
e7185b5821ebbcc04d851f2763d17c21 usr/src/cal-pcie-0.9.0/calculet_pci_core.h
1c5ea12ea5ef46cdc127032587e8b315 usr/src/cal-pcie-0.9.0/dkms.conf
6692d02c13baed72ac24306c7428e288 usr/src/cal-pcie-0.9.0/dma.c
e68fc8b3641aad7096bec1240df2b67f usr/src/cal-pcie-0.9.0/dma.h
4cb1973a1b2da31db0816d6b9da6a53c usr/src/cal-pcie-0.9.0/fops.c
6798c3ca8909724756f565bd29a2ddcd usr/src/cal-pcie-0.9.0/fops.h
7bfe47920e7947dc2c4931a3ecad7b1b usr/src/cal-pcie-0.9.0/ioctl.h
3ed33ff44b0ee698ace348f2c746f603 usr/src/cal-pcie-0.9.0/proc.c
5c253d5cea8c5874dfbcae442b5d2799 usr/src/cal-pcie-0.9.0/proc.h
3b20c1cd73163609e6b96abbaf58e682 usr/src/cal-pcie-0.9.0/utils.c
2bc24094f0d939c8cbb4950650d7f8a7 usr/src/cal-pcie-0.9.0/utils.h
@@ -0,0 +1,107 @@
#!/bin/bash
# postinst - 安装后加载内核模块
set -e
# 当脚本由 dpkg 触发时,$DPKG_MAIN_TSN_PACKAGE 通常可用
# 如果为空,则回退到通过文件名解析
PACKAGE_NAME="${DPKG_MAIN_TSN_PACKAGE}"
if [ -z "$PACKAGE_NAME" ]; then
SCRIPT_NAME=$(basename "$0")
PACKAGE_NAME=$(echo "$SCRIPT_NAME" | sed 's/\.postinst$//' | sed 's/\.prerm$//')
fi
# 从 dpkg 数据库获取版本
if [ -n "$PACKAGE_NAME" ]; then
PACKAGE_VERSION=$(dpkg-query -W -f='${Version}' "$PACKAGE_NAME" 2>/dev/null)
# 去掉 Debian 修订号(-1, -2, -ubuntu1 等)
PACKAGE_VERSION=$(echo "$PACKAGE_VERSION" | sed 's/-.*$//')
fi
DKMS_SRC_DIR="/usr/src/${PACKAGE_NAME}-${PACKAGE_VERSION}"
# 从预期的源码释放路径去解析驱动名称
MAKEFILE_PATH="${DKMS_SRC_DIR}/Makefile"
MODULE_NAME=""
case "$1" in
configure)
echo "正在配置 ${PACKAGE_NAME} 软件包..."
# 创建 DKMS 源码目录
echo "创建 DKMS 源码目录: ${DKMS_SRC_DIR}"
mkdir -p "${DKMS_SRC_DIR}"
# 复制源码文件到 DKMS 目录
SOURCE_DIR="/usr/share/${PACKAGE_NAME}/source"
if [ -d "${SOURCE_DIR}" ]; then
echo "从 ${SOURCE_DIR} 复制源码到 DKMS 目录..."
cp -rf "${SOURCE_DIR}"/* "${DKMS_SRC_DIR}/"
fi
# 确保 dkms.conf 存在
if [ ! -f "${DKMS_SRC_DIR}/dkms.conf" ]; then
echo "错误: DKMS 配置文件不存在: ${DKMS_SRC_DIR}/dkms.conf" >&2
exit 1
fi
# 动态解析驱动模块名称
if [ -f "${MAKEFILE_PATH}" ]; then
MODULE_NAME=$(sed -n -E 's/^DRIVER_NAME[[:space:]]*:?=[[:space:]]*([^[:space:]]+).*/\1/p' "${MAKEFILE_PATH}" | head -1 | sed 's/\.ko$//')
fi
# 如果 Makefile 里没解析出来,降级使用包名作为模块名
if [ -z "$MODULE_NAME" ]; then
MODULE_NAME="${PACKAGE_NAME}"
fi
echo "解析到的驱动模块名称: ${MODULE_NAME}"
# 设置正确的权限
# chmod -R 644 "${DKMS_SRC_DIR}"
# 添加到 DKMS
echo "添加 ${PACKAGE_NAME} 到 DKMS..."
if ! dkms status -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" | grep -q "added"; then
dkms add -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}"
fi
# 编译模块
echo "编译内核模块..."
dkms build -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}"
# 安装模块
echo "安装内核模块..."
dkms install -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}"
# 更新模块依赖
echo "更新模块依赖..."
depmod -a
# 加载模块
echo "加载内核模块: ${MODULE_NAME}"
if modprobe "${MODULE_NAME}" 2>/dev/null; then
echo "模块 ${MODULE_NAME} 加载成功"
else
echo "错误: 模块 ${MODULE_NAME} 加载失败" >&2
exit 1
fi
# 设置模块开机自动加载
if [ ! -f "/etc/modules-load.d/${PACKAGE_NAME}.conf" ]; then
echo "配置模块开机自动加载..."
echo "${MODULE_NAME}" > "/etc/modules-load.d/${PACKAGE_NAME}.conf"
fi
echo "${PACKAGE_NAME} 安装完成"
;;
abort-upgrade|abort-remove|abort-deconfigure)
echo "安装过程被中断,不做配置"
;;
*)
echo "postinst 被调用时参数: $1" >&2
;;
esac
exit 0
@@ -0,0 +1,102 @@
#!/bin/bash
# prerm - 卸载前卸载内核模块
set -e
# 当脚本由 dpkg 触发时,$DPKG_MAIN_TSN_PACKAGE 通常可用
PACKAGE_NAME="${DPKG_MAIN_TSN_PACKAGE}"
if [ -z "$PACKAGE_NAME" ]; then
SCRIPT_NAME=$(basename "$0")
PACKAGE_NAME=$(echo "$SCRIPT_NAME" | sed 's/\.postinst$//' | sed 's/\.prerm$//')
fi
# 从 dpkg 数据库获取版本
if [ -n "$PACKAGE_NAME" ]; then
PACKAGE_VERSION=$(dpkg-query -W -f='${Version}' "$PACKAGE_NAME" 2>/dev/null)
# 去掉 Debian 修订号(-1, -2, -ubuntu1 等)
PACKAGE_VERSION=$(echo "$PACKAGE_VERSION" | sed 's/-.*$//')
fi
DKMS_SRC_DIR="/usr/src/${PACKAGE_NAME}-${PACKAGE_VERSION}"
MAKEFILE_ABS_PATH="${DKMS_SRC_DIR}/Makefile"
MODULE_NAME=""
# 【核心修复】:使用绝对路径去解析真正的 Makefile 提取驱动名
if [ -f "${MAKEFILE_ABS_PATH}" ]; then
MODULE_NAME=$(sed -n -E 's/^DRIVER_NAME[[:space:]]*:?=[[:space:]]*([^[:space:]]+).*/\1/p' "${MAKEFILE_ABS_PATH}" | head -1 | sed 's/\.ko$//')
fi
# 如果从 Makefile 没解析出来,降级使用包名作为模块名
if [ -z "$MODULE_NAME" ]; then
MODULE_NAME="${PACKAGE_NAME}"
fi
case "$1" in
remove)
echo "正在卸载 ${PACKAGE_NAME}..."
# 1. 卸载内核模块
echo "检查内核模块状态: ${MODULE_NAME}"
if lsmod | grep -q "^${MODULE_NAME}"; then
echo "正在从当前内核中卸载模块 ${MODULE_NAME}..."
if rmmod "${MODULE_NAME}" 2>/dev/null; then
echo "模块 ${MODULE_NAME} 卸载成功"
else
echo "警告: 模块 ${MODULE_NAME} 卸载失败,可能有其他进程正在使用它" >&2
fi
else
echo "模块 ${MODULE_NAME} 当前未加载"
fi
# 2. 从 DKMS 移除模块 (修复:扩大 grep 范围以兼容 added/installed 各种状态)
if dkms status | grep -q "${PACKAGE_NAME}/${PACKAGE_VERSION}"; then
echo "从 DKMS 移除 ${PACKAGE_NAME} ${PACKAGE_VERSION}..."
dkms remove -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" --all 2>/dev/null || true
fi
echo "正在强行清理 /lib/modules/ 下的所有 ${MODULE_NAME} 残留文件..."
find /lib/modules/ -name "${MODULE_NAME}.ko*" -delete 2>/dev/null || true
# 3. 更新模块依赖
echo "更新模块依赖..."
depmod -a
# 4. 删除开机自动加载配置
if [ -f "/etc/modules-load.d/${PACKAGE_NAME}.conf" ]; then
echo "删除开机自动加载配置..."
rm -f "/etc/modules-load.d/${PACKAGE_NAME}.conf"
fi
echo "${PACKAGE_NAME} 卸载完成"
;;
upgrade)
echo "软件包升级中,清理旧版本..."
# 1. 升级时卸载旧版本的内核运行模块
if lsmod | grep -q "^${MODULE_NAME}"; then
echo "正在卸载旧版本内核运行模块..."
rmmod "${MODULE_NAME}" 2>/dev/null || true
fi
# 2. 从 DKMS 移除旧版本
if dkms status | grep -q "${PACKAGE_NAME}/${PACKAGE_VERSION}"; then
echo "从 DKMS 移除旧版本模块记录..."
dkms remove -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" --all || true
fi
# 3. 清理旧的内核模块文件 (原逻辑保留,提供兜底支持)
rm -f /lib/modules/*/kernel/drivers/misc/${MODULE_NAME}.ko 2>/dev/null || true
depmod -a
;;
failed-upgrade)
echo "升级失败,保留原有配置"
;;
*)
echo "prerm 被调用时参数: $1" >&2
;;
esac
exit 0