PCIe驱动开发(3)— 驱动设备文件的创建与操作

PCIe驱动开发(3)— 驱动设备文件的创建与操作

一、前言

在 Linux 中一切皆为文件,驱动加载成功以后会在“/dev”目录下生成一个相应的文件,应用程序通过对这个名为“/dev/xxx” (xxx 是具体的驱动文件名字)的文件进行相应的操作即可实现对硬件的操作。

二、创建设备文件

PCIe设备属于字符设备,我们按如下步骤创建一个字符设备:

	/* 1、Request device number */ret = alloc_chrdev_region(&hello_pci_info.dev_id, 0, 1, "hello_pcie");/* 2、Initial char_dev */hello_pci_info.cdev.owner = THIS_MODULE;cdev_init(&hello_pci_info.char_dev, &hello_pci_fops);/* 3、add char_dev */cdev_add(&hello_pci_info.char_dev, hello_pci_info.dev_id, 1);/* 4、create class */hello_pci_info.class = class_create(THIS_MODULE, "hello_pcie");if (IS_ERR(hello_pci_info.class)) {return PTR_ERR(hello_pci_info.class);}/* 5、create device */hello_pci_info.device = device_create(hello_pci_info.class, NULL, hello_pci_info.dev_id, NULL, "hello_pcie");if (IS_ERR(newchrled.device)) {return PTR_ERR(newchrled.device);}

其中需要定义一个设备文件操作函数结构体,可以暂时定义为如下所示:

/* device file operations function */
static struct file_operations hello_pcie_fops = {.owner = THIS_MODULE,
};

将上述创建一个字符设备的操作加在hello_pci_init函数里,同时hello_pci_exit添加对应的卸载操作:

static void __exit hello_pci_exit(void)
{if(hello_pci_info.dev != NULL) {cdev_del(&hello_pci_info.char_dev);						/* del cdev */unregister_chrdev_region(hello_pci_info.dev_id, 1); 	/* unregister device number */device_destroy(hello_pci_info.class, hello_pci_info.dev_id);class_destroy(hello_pci_info.class);}pci_unregister_driver(&hello_pci_driver);
}

然后编译加载驱动,便可以看到在/dev下有我们创建的hello_pcie设备了:
在这里插入图片描述

三、添加文件操作函数

如下所示,添加文件的open,close,write,read函数:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/device.h>#define HELLO_PCI_DEVICE_ID     0x11e8
#define HELLO_PCI_VENDOR_ID     0x1234
#define HELLO_PCI_REVISION_ID   0x10static struct pci_device_id ids[] = {{ PCI_DEVICE(HELLO_PCI_VENDOR_ID, HELLO_PCI_DEVICE_ID), },{ 0 , }
};static struct hello_pci_info_t {dev_t dev_id;struct cdev char_dev;struct class *class;struct device *device;struct pci_dev *dev;void __iomem *address_bar0;atomic_t compute_running;wait_queue_head_t r_wait;
} hello_pci_info;MODULE_DEVICE_TABLE(pci, ids);static irqreturn_t hello_pci_irq_handler(int irq, void *dev_info)
{struct hello_pci_info_t *_pci_info = dev_info;uint32_t irq_status;// get irq_stutasirq_status = *((uint32_t *)(_pci_info->address_bar0 + 0x24));printk("hello_pcie: get irq status: 0x%0x\n", irq_status);// clean irq*((uint32_t *)(_pci_info->address_bar0 + 0x64)) = irq_status;// get irq_stutasirq_status = *((uint32_t *)(_pci_info->address_bar0 + 0x24));if(irq_status == 0x00){printk("hello_pcie: receive irq and clean success. \n");}else{printk("hello_pcie: receive irq but clean failed !!! \n");return IRQ_NONE;}atomic_set(&(_pci_info->compute_running), 0);wake_up_interruptible(&(_pci_info->r_wait));return IRQ_HANDLED;
}/** @description     : 打开设备* @param - inode   : 传递给驱动的inode* @param - file    : 设备文件,file结构体有个叫做private_data的成员变量*                    一般在open的时候将private_data指向设备结构体。* @return          : 0 成功;其他 失败*/
static int hello_pcie_open(struct inode *inode, struct file *file)
{printk("hello_pcie: open dev file.\n");init_waitqueue_head(&hello_pci_info.r_wait);return 0;
}/** @description     : 关闭/释放设备* @param - file    : 要关闭的设备文件(文件描述符)* @return          : 0 成功;其他 失败*/
static int hello_pcie_close(struct inode *inode, struct file *file)
{printk("hello_pcie: close dev file.\n");return 0;
}/** @description     : 向设备写数据 * @param - filp    : 设备文件,表示打开的文件描述符* @param - buf     : 要写给设备写入的数据* @param - cnt     : 要写入的数据长度* @param - offt    : 相对于文件首地址的偏移* @return          : 写入的字节数,如果为负值,表示写入失败*/
static ssize_t hello_pcie_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue;unsigned char databuf[4] = {0, 0, 0, 0};uint32_t compute_value;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("hello_pcie: write failed!\n");return -EFAULT;}atomic_set(&hello_pci_info.compute_running, 1);compute_value = ((databuf[0]) | (databuf[1]<<8) | (databuf[2]<<16) | (databuf[3]<<24));*((uint32_t *)(hello_pci_info.address_bar0 + 0x08)) = compute_value;return 0;
}/** @description     : 从设备读取数据 * @param – filp    : 要打开的设备文件(文件描述符)* @param – buf     : 返回给用户空间的数据缓冲区* @param – cnt     : 要读取的数据长度* @param – offt    : 相对于文件首地址的偏移* @return          : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t hello_pcie_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int ret;uint32_t compute_result = 0;/* 加入等待队列,当有按键按下或松开动作发生时,才会被唤醒 */ret = wait_event_interruptible(hello_pci_info.r_wait, 0 == atomic_read(&hello_pci_info.compute_running));if(ret)return ret;compute_result = *((uint32_t *)(hello_pci_info.address_bar0 + 0x08));printk("hello_pcie: get compute_result: %0d\n", compute_result);/* 将按键状态信息发送给应用程序 */ret = copy_to_user(buf, &compute_result, sizeof(int));return ret;
}/* device file operations function */
static struct file_operations hello_pcie_fops = {.owner      = THIS_MODULE,.open       = hello_pcie_open,.release    = hello_pcie_close,.read       = hello_pcie_read,.write      = hello_pcie_write,
};static int hello_pcie_probe(struct pci_dev *dev, const struct pci_device_id *id)
{int bar = 0;int ret;resource_size_t len;ret = pci_enable_device(dev);if(ret) {return ret;}len = pci_resource_len(dev, bar);hello_pci_info.address_bar0 = pci_iomap(dev, bar, len);hello_pci_info.dev = dev;// register interruptret = request_irq(dev->irq, hello_pci_irq_handler, IRQF_SHARED, "hello_pci", &hello_pci_info);if(ret) {printk("request IRQ failed.\n");return ret;}// enable irq for finishing factorial computation*((uint32_t *)(hello_pci_info.address_bar0 + 0x20)) = 0x80;return 0;
}static void hello_pcie_remove(struct pci_dev *dev)
{// disable irq for finishing factorial computation*((uint32_t *)(hello_pci_info.address_bar0 + 0x20)) = 0x01;free_irq(dev->irq, &hello_pci_info);pci_iounmap(dev, hello_pci_info.address_bar0);pci_disable_device(dev);
}static struct pci_driver hello_pci_driver = {.name       = "hello_pcie",.id_table   = ids,.probe      = hello_pcie_probe,.remove     = hello_pcie_remove,
};static int __init hello_pci_init(void)
{int ret = pci_register_driver(&hello_pci_driver);if(hello_pci_info.dev == NULL){printk("hello_pci: probe pcie device failed!\n");return ret;}/* 1、Request device number */ret = alloc_chrdev_region(&hello_pci_info.dev_id, 0, 1, "hello_pcie");/* 2、Initial char_dev */hello_pci_info.char_dev.owner = THIS_MODULE;cdev_init(&hello_pci_info.char_dev, &hello_pcie_fops);/* 3、add char_dev */cdev_add(&hello_pci_info.char_dev, hello_pci_info.dev_id, 1);/* 4、create class */hello_pci_info.class = class_create(THIS_MODULE, "hello_pcie");if (IS_ERR(hello_pci_info.class)) {return PTR_ERR(hello_pci_info.class);}/* 5、create device */hello_pci_info.device = device_create(hello_pci_info.class, NULL, hello_pci_info.dev_id, NULL, "hello_pcie");if (IS_ERR(hello_pci_info.device)) {return PTR_ERR(hello_pci_info.device);}return ret;
}static void __exit hello_pci_exit(void)
{if(hello_pci_info.dev != NULL) {cdev_del(&hello_pci_info.char_dev);                     /* del cdev */unregister_chrdev_region(hello_pci_info.dev_id, 1);     /* unregister device number */device_destroy(hello_pci_info.class, hello_pci_info.dev_id);class_destroy(hello_pci_info.class);}pci_unregister_driver(&hello_pci_driver);
}module_init(hello_pci_init);
module_exit(hello_pci_exit);
MODULE_LICENSE("GPL");
MODULE_INFO(intree, "Y");

四、编写用户程序

编写用户测试程序testapp.c如下:

#include "stdio.h"
#include "stdint.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"int main(int argc, char *argv[])
{int fd, retvalue;char *filename = "/dev/hello_pcie";uint32_t data_val = 6;int read_val;/* 打开驱动设备文件 */fd = open(filename, O_RDWR);if(fd < 0){printf("file %s open failed!\n", filename);return -1;}/* 向/dev/hello_pcie文件写入数据 */retvalue = write(fd, &data_val, sizeof(int));if(retvalue < 0){printf("Open %s Failed!\n", filename);close(fd);return -1;}read(fd, &read_val, sizeof(int));printf("factorial computation result : %0d \n", read_val);retvalue = close(fd); /* 关闭文件 */if(retvalue < 0){printf("file %s close failed!\r\n", filename);return -1;}return 0;
}

五、运行测试

编译加载驱动,
在这里插入图片描述
使用如下命令编译测试程序:

gcc testapp.c 

然后运行测试程序,我们可以看到计算得到的阶乘结果为720,即6*5*4*3*2*1=720,符合预期结果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/378530.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

元宇宙深入解析

元宇宙&#xff08;Metaverse&#xff09;是一个新兴的概念&#xff0c;它激发了技术专家、艺术家和商业领袖的无限想象。它代表着数字互动的新前沿&#xff0c;提供了一个平行的数字宇宙&#xff0c;用户可以在其中实时互动&#xff0c;超越物理世界的限制。 元宇宙是什么&am…

安防视频监控/视频汇聚EasyCVR平台浏览器http可以播放,https不能播放,如何解决?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台基于云边端一体化架构&#xff0c;兼容性强、支持多协议接入&#xff0c;包括国标GB/T 28181协议、部标JT808、GA/T 1400协议、RTMP、RTSP/Onvif协议、海康Ehome、海康SDK、大华SDK、华为SDK、宇视SDK、乐橙SDK、萤石云SD…

链接追踪系列-00.es设置日志保存7天-番外篇

索引生命周期策略 ELK日志我们一般都是按天存储&#xff0c;例如索引名为"zipkin-span-2023-03-24"&#xff0c;因为日志量所占的存储是非常大的&#xff0c;我们不能一直保存&#xff0c;而是要定期清理旧的&#xff0c;这里就以保留7天日志为例。 自动清理7天以前…

Python基础语法篇(上)

Python基础语法&#xff08;上&#xff09; 一、基知二、基本数据类型&#xff08;一&#xff09;标准数据类型&#xff08;二&#xff09;数据类型转换 三、字符串基本操作&#xff08;一&#xff09;字符串的索引和切片&#xff08;二&#xff09;字符串的拼接 三、运算符四、…

【React笔记初学总结一】React新手的学习流程笔记总结,掰开了揉碎了,下载安装基础结构学习

REACT学习记录 一、React是什么&#xff1a;二、尝试安装下载&#xff1a;三、理解都有什么四、基础网页学习&#xff1a;1.几个比较重要的资源包例子2.第一个react示例&#xff1a;&#xff08;掰开了揉碎了&#xff0c;咱们先看懂它最简单的结构&#xff09;3.第二个react示例…

2.10、matlab中字符、数字、矩阵、字符串和元胞合并为字符串并将字符串以不同格式写入读出excel

1、前言 在 MATLAB 中&#xff0c;可以使用不同的数据类型&#xff08;字符、数字、矩阵、字符串和元胞&#xff09;合并为字符串&#xff0c;然后将字符串以不同格式写入 Excel 文件。 以下是一个示例代码&#xff0c;展示如何将不同数据类型合并为字符串&#xff0c;并以不…

一五六、Node+Vue 使用七牛上传图片,并配置个人域名

1. 七牛云ak/sk获取 点击注册&#x1f517;开通七牛开发者帐号如果已有账号&#xff0c;直接登录七牛开发者后台&#xff0c;点击这里&#x1f517;查看 Access Key 和 Secret Key 2. Node.js获取七牛token 安装qiniu npm install qiniu创建空间 Node获取token const qi…

Excel办公技巧:制作二级联动下拉菜单

分享制作二级联动下拉菜单的方法&#xff0c;即使数据有增删&#xff0c;菜单也能自动更新&#xff01; 可以通过先定义名称&#xff0c;再结合数据验证&#xff0c;来做二级联动下拉菜单。 1. 准备数据 首先&#xff0c;我们需要准备好要进行二级联动下拉菜单的数据&#xff…

PGCCC|【PostgreSQL】PCA+PCP+PCM等IT类认证申报个税退税指南

小编特将PostgreSQL证书申报个税退税流程&#xff0c;编辑成文&#xff0c;供大家申报参考哦~ 1.申报专项附加扣除 第一步&#xff1a;打开个人所得税APP&#xff0c;选择“专项附加扣除填报”&#xff1a; 第二步&#xff1a;“扣除年度”选择您要申报的年度&#xff0c;并…

韦东山嵌入式linux系列-具体单板的 LED 驱动程序

笔者使用的是STM32MP157的板子 1 怎么写 LED 驱动程序&#xff1f; 详细步骤如下&#xff1a; ① 看原理图确定引脚&#xff0c;确定引脚输出什么电平才能点亮/熄灭 LED ② 看主芯片手册&#xff0c;确定寄存器操作方法&#xff1a;哪些寄存器&#xff1f;哪些位&#xff1f;…

AI+折叠屏,荣耀的创新周期论

文&#xff5c;刘俊宏 编&#xff5c;王一粟 2024年&#xff0c;AI和折叠屏的演进路线&#xff0c;已经成为了手机行业的共识。 首先&#xff0c;手机市场的新增量已经被折叠屏所接管。据Counterpoint Research数据显示&#xff0c;中国2024年第一季度折叠屏手机销量同比增长…

怎样把视频压缩小一点 视频压缩工具

在数字媒体时代&#xff0c;视频文件的传输与分享已成为日常。然而&#xff0c;大视频文件往往给存储和分享带来诸多不便。如何将视频压缩到最小&#xff0c;同时保持画质和音质&#xff0c;成为了许多用户关注的焦点。本文将为你揭秘一系列高效的视频压缩方法&#xff0c;让你…

Linux离线安装Mysql5.7

Linux之Mysql安装配置 第一种&#xff1a;Linux离线安装Mysql&#xff08;提前手动下载好tar.gz包&#xff09; 第二种&#xff1a;通过yum安装配置Mysql&#xff08;服务器有网络&#xff09; 之前在阿里云上采用yum安装过一次&#xff08;请看这里&#xff09;&#xff0c;…

启智畅想火车类集装箱号码识别技术,软硬件解决方案

集装箱号码识别需求&#xff1a; 实时检测车皮号、火车底盘号码、集装箱号码&#xff0c;根据火车类型分为以下三种情况&#xff1a; 1、纯车皮&#xff0c;只检测车皮号&#xff1b; 2、火车拉货箱&#xff08;半车皮&#xff09;&#xff0c;检测车皮号集装箱号码&#xff1b…

算法力扣刷题记录 四十九【112. 路径总和】和【113. 路径总和ii】

前言 二叉树篇继续。 记录 四十九【112. 路径总和】和【113. 路径总和ii】 一、【112. 路径总和】题目阅读 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 target…

什么是边缘计算技术和边缘计算平台?

随着物联网、5G技术和人工智能的不断发展&#xff0c;数据的规模和种类也在快速增加。在这种背景下&#xff0c;传统的云计算模式面临着一些问题&#xff0c;例如延迟高、网络拥塞等&#xff0c;这些问题限制了数据的处理速度和效率&#xff0c;降低了用户的使用体验。为了解决…

Perl语言之数组

Perl数组可以存储多个标量&#xff0c;并且标量数据类型可以不同。   数组变量以开头。访问与定义格式如下&#xff1a; #! /usr/bin/perl arr("asdfasd",2,23.56,a); print "输出所有:arr\n"; print "arr[0]$arr[0]\n"; #输出指定下标 print…

【Quart 框架——来源于Flask的强大且灵活的异步Web框架】

目录 前言一、Quart简介1-1、简介1-2、与flask的区别 二、快速开始2-1、安装2-2、基本用法 三、核心功能3-1、异步路由3-2、WebSockets 支持3-3、中间件3-4、蓝图 (Blueprints) 四、部署4-1、使用uvicorn部署4-2、使用hypercorn部署 五、案例分析总结 前言 Quart 是一个基于 Py…

3.5、matlab打开显示保存点云文件(.ply/.pcd)以及经典点云模型数据

1、点云数据简介 点云数据是三维空间中由大量二维点坐标组成的数据集合。每个点代表空间中的一个坐标点&#xff0c;可以包含有关该点的颜色、法向量、强度值等额外信息。点云数据可以通过激光扫描、结构光扫描、摄像机捕捉等方式获取&#xff0c;广泛应用于计算机视觉、机器人…

排序——归并排序及排序章节总结

前面的文章中 我们详细介绍了排序的概念&#xff0c;插入排序&#xff0c;交换排序与选择排序&#xff0c;大家可以通过下面的链接再去学习&#xff1a; ​​​​​​排序的概念及插入排序 交换排序 选择排序 这篇文章就详细介绍一下另一种排序算法&#xff1a;归并排序以及…