# 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