Ubuntu系统上安装PMC-5565PIORC驱动

PMC-5565PIORC 反射存储器节点卡提供高速、低延迟、确定性接口,允许最多 256 个独立系统(节点)以最高 170 Mbyte/s 的速度共享数据。每块反射存储板可配置 128 MB 或 256 MB 的板载 SDRAM。本地 SDRAM 提供存储数据的快速读取时间。写入存储在本地 SDRAM 中,并通过高速光纤数据路径广播给其他反射存储器节点。节点间数据传输通过软件透明,无需 I/O 开销。在峰值数据速率期间,发送和接收 FIFO 缓冲数据,以优化主机和总线性能,保持高数据吞吐量。PMC-5565PIORC 符合 RoHS 标准,并提升了 PIO 读取性能和可在现场升级的固件。

在NI PXle-1071机箱里,PMC-5565PIORC通过PCIe与控制器8840进行通讯,因此使用以下指令先验证 PMC-5565PIORC 硬件是否被系统识别。

lspci | grep -i "reflection" || lspci | grep -i "PMC" || lspci

安装驱动编译依赖

sudo apt update #更新系统软件源
sudo apt install -y build-essential gcc make #安装基础编译工具
sudo apt install -y linux-headers-$(uname -r) #安装对应实时内核的头文件

创建驱动源文件

# 创建专门的驱动目录,避免文件混乱
mkdir -p ~/pmc5565_driver && cd ~/pmc5565_driver
# 用nano编辑器创建pmc5565.c文件
nano pmc5565.c

驱动源代码

/*
 * PMC5565 通用驱动(适配Linux 5.15实时内核)
 * 核心功能:识别PMC5565硬件、启用PCI设备、映射反射内存
 * 使用方式:仅支持/dev/mem方式读写(基地址:0xC0000000)
 * 测试程序:test_pmc5565(需sudo运行)
 */
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/io.h>
#include <linux/printk.h>

// 1. 硬件基础配置(匹配PMC5565硬件ID和内存参数)
#define VENDOR_ID        0x114a
#define DEVICE_ID        0x5565
#define PMC5565_MEM_BASE 0xC0000000  // 硬件真实反射内存基地址
#define PMC5565_MEM_SIZE 0x10000000  // 256M(匹配lspci查询结果)
#define REG_BASE         0xd0200000  // 硬件寄存器基地址
#define REG_SIZE         0x1000      // 寄存器区域大小

// 2. 全局变量(仅保留核心内存映射变量)
static void __iomem *pmc5565_mem;    // 内核态映射的反射内存地址
static void __iomem *pmc5565_reg;    // 内核态映射的寄存器地址

// 3. PCI设备probe函数(仅保留核心初始化逻辑)
static int pmc5565_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
    int ret;

    // 打印硬件识别信息(便于排查)
    printk(KERN_INFO "PMC5565: 识别到硬件 PCI地址: %04x:%02x:%02x.%x\n",
           pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), PCI_FUNC(pdev->devfn));

    // 步骤1:启用PCI设备(适配5.15实时内核的启用方式)
    ret = pci_enable_device_mem(pdev);
    if (ret) {
        printk(KERN_ERR "PMC5565: 启用PCI设备失败,错误码: %d\n", ret);
        return ret;
    }

    // 步骤2:申请设备内存区域(防止内存冲突)
    ret = pci_request_mem_regions(pdev, "pmc5565");
    if (ret) {
        printk(KERN_ERR "PMC5565: 申请内存区域失败\n");
        pci_disable_device(pdev);
        return ret;
    }

    // 步骤3:映射反射内存到内核态(核心功能)
    pmc5565_mem = ioremap(PMC5565_MEM_BASE, PMC5565_MEM_SIZE);
    if (!pmc5565_mem) {
        printk(KERN_ERR "PMC5565: 映射反射内存失败\n");
        pci_release_mem_regions(pdev);
        pci_disable_device(pdev);
        return -ENOMEM;
    }

    // 步骤4:映射寄存器(硬件初始化备用,不影响核心读写)
    pmc5565_reg = ioremap(REG_BASE, REG_SIZE);
    if (!pmc5565_reg) {
        printk(KERN_WARNING "PMC5565: 映射寄存器失败(不影响核心读写)\n");
        // 仅警告,不终止驱动加载(核心内存映射已完成)
    }

    printk(KERN_INFO "PMC5565: 驱动加载成功!反射内存基地址: 0x%lx\n", (unsigned long)PMC5565_MEM_BASE);
    return 0;
}

// 4. PCI设备remove函数(仅保留核心清理逻辑)
static void pmc5565_remove(struct pci_dev *pdev)
{
    // 解除内存映射
    if (pmc5565_reg) iounmap(pmc5565_reg);
    if (pmc5565_mem) iounmap(pmc5565_mem);
    
    // 释放PCI资源
    pci_release_mem_regions(pdev);
    pci_disable_device(pdev);
    
    printk(KERN_INFO "PMC5565: 驱动卸载成功\n");
}

// 5. PCI设备ID表(仅匹配PMC5565硬件)
static const struct pci_device_id pmc5565_ids[] = {
    {PCI_DEVICE(VENDOR_ID, DEVICE_ID)},
    {0}
};
MODULE_DEVICE_TABLE(pci, pmc5565_ids);

// 6. PCI驱动结构体(仅保留核心配置)
static struct pci_driver pmc5565_driver = {
    .name     = "pmc5565",
    .id_table = pmc5565_ids,
    .probe    = pmc5565_probe,
    .remove   = pmc5565_remove,
};

// 7. 模块加载/卸载函数
static int __init pmc5565_init(void)
{
    return pci_register_driver(&pmc5565_driver);
}

static void __exit pmc5565_exit(void)
{
    pci_unregister_driver(&pmc5565_driver);
}

module_init(pmc5565_init);
module_exit(pmc5565_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PMC5565 通用驱动(适配5.15实时内核)");
MODULE_AUTHOR("Custom");

创建 Makefile 编译配置文件

# 创建并编辑Makefile文件(注意文件名是Makefile,首字母大写,无后缀)
nano Makefile

Makefile文件内容

# PMC5565驱动编译Makefile(适配Linux 5.15实时内核)
# 核心功能:编译/清理PMC5565内核驱动模块

# 指定要编译的驱动模块(pmc5565.c → pmc5565.ko)
obj-m += pmc5565.o

# 内核源码目录(自动匹配当前系统内核版本)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build

# 当前工作目录(驱动源码所在目录)
PWD := $(shell pwd)

# 编译规则:生成驱动模块(执行make时触发)
all:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

# 清理规则:删除编译生成的临时文件/驱动模块(执行make clean时触发)
clean:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) clean

编译驱动模块

make

加载驱动模块

sudo insmod ~/pmc5565_driver/pmc5565.ko

查看驱动是否加载成功

lsmod | grep pmc5565

测试

直接读写 PMC5565 反射内存,验证驱动的核心功能是否正常

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

// PMC5565反射内存基地址(根据硬件手册调整,若读写失败可改为0xC0000000)
#define PMC5565_MEM_BASE 0xC0000000
// 测试内存大小(1MB)
#define TEST_MEM_SIZE 1024*1024

int main() {
    int fd;
    void *mem_ptr;

    // 打开/dev/mem(需要root权限)
    fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (fd < 0) {
        perror("打开/dev/mem失败");
        printf("请用sudo运行此程序!\n");
        return -1;
    }

    // 映射PMC5565反射内存到用户空间
    mem_ptr = mmap(NULL, TEST_MEM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, PMC5565_MEM_BASE);
    if (mem_ptr == MAP_FAILED) {
        perror("内存映射失败");
        close(fd);
        return -1;
    }

    // 写入测试数据(32位,匹配硬件位宽)
    unsigned int test_data = 0x12345678;
    *((unsigned int *)mem_ptr) = test_data;
    printf("写入测试数据: 0x%08X\n", test_data);

    // 读取验证数据
    unsigned int read_data = *((unsigned int *)mem_ptr);
    printf("读取验证数据: 0x%08X\n", read_data);

    // 验证读写是否一致
    if (read_data == test_data) {
        printf("✅ PMC5565反射内存读写测试成功!无乱码\n");
    } else {
        printf("❌ 读写数据不一致,可能需要调整基地址(尝试0xC0000000)\n");
    }

    // 解除映射+关闭文件
    munmap(mem_ptr, TEST_MEM_SIZE);
    close(fd);
    return 0;
}

测试指令

sudo /home/j103/pmc5565_driver/test_pmc5565

测试结果:

alt text

配置驱动开机自启

# 创建并编辑配置文件(文件名自定义,以.conf结尾)
sudo nano /etc/modules-load.d/pmc5565.conf #在空白文件中输入pmc5565

# 创建驱动存放目录(若不存在)
sudo mkdir -p /lib/modules/$(uname -r)/kernel/drivers/misc/

# 复制编译好的pmc5565.ko到系统模块目录
sudo cp ~/pmc5565_driver/pmc5565.ko /lib/modules/$(uname -r)/kernel/drivers/misc/

# 更新模块依赖(让系统识别新驱动)
sudo depmod -a

附:相关文件内容

类别 路径 说明
驱动源文件 /home/j103/pmc5565_driver/pmc5565.c 通用版 PMC5565 驱动源码(仅保留核心硬件识别 / 内存映射)
驱动编译配置 /home/j103/pmc5565_driver/Makefile 驱动编译规则文件(适配 5.15 实时内核,用于make编译驱动)
核心测试程序 /home/j103/pmc5565_driver/test_pmc5565 编译后的可执行测试程序(验证 32 位反射内存读写,无乱码)
测试程序源码 /home/j103/pmc5565_driver/test_pmc5565.c 测试程序的 C 语言源码(固化了基地址0xC0000000,核心逻辑是读写内存)

本文章使用limfx的vscode插件快速发布