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
测试结果:

# 创建并编辑配置文件(文件名自定义,以.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插件快速发布