645 lines
21 KiB
C
645 lines
21 KiB
C
// 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;
|
|
} |