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   0x10

static 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_stutas
    irq_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_stutas
    irq_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 interrupt
    ret = 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,符合预期结果
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言------指针讲解(2)

目录 一、数组名的理解 二、使用指针访问数组 三、一维数组传参的本质 四、冒泡排序 五、二级指针 六、指针数组 七、指针数组模拟二维数组 一、数组名的理解 通过学习&#xff0c;我们知道&#xff1a;数组名和数组首元素的地址打印出来的结果一模一样&#xff0c;数组…

信息安全CISSP认证重点学什么?学习后能掌握哪些安全技能?

引言&#xff1a; 想要在信息安全领域取得突破&#xff1f;那么CISSP认证是您必不可少的一步&#xff01;本文将为您介绍CISSP认证的重点学习内容以及学习后可以掌握的安全技能&#xff0c;助您更好地了解并准备这一全球认可的信息安全证书。 随着信息安全领域的不断发展&…

【教学类-67-02】20240716毛毛虫ABB排序

背景需求&#xff1a; 【教学类-67-01】20240715毛毛虫AB排序-CSDN博客文章浏览阅读584次&#xff0c;点赞16次&#xff0c;收藏6次。【教学类-67-01】20240715毛毛虫AB排序https://blog.csdn.net/reasonsummer/article/details/140443310 在AB排序基础上&#xff0c;继续制作…

2024年带你轻松掌握最火10款项目管理软件:解决企业项目管理难题的利器

本文向您推荐10款卓越非凡的项目管理软件&#xff01;它们能有效地助您梳理思绪&#xff0c;极大提高工作效率&#xff0c;使您即使身处繁忙之中仍能保持井然有序。这十款工具各具特色&#xff0c;从简洁明了的任务清单到复杂精密的项目追踪需求&#xff0c;满足各类用户群体的…

Python中的数据结构:五彩斑斓的糖果盒

在Python编程的世界里&#xff0c;数据结构就像是一个个五彩斑斓的糖果盒&#xff0c;每一种糖果都有其独特的味道和形状。这些多姿多彩&#xff0c;形状和味道各异的糖果盒子包括了&#xff1a;List&#xff08;列表&#xff09;、Tuple&#xff08;元组&#xff09;、Diction…

【Java开发实训】day05——数组常见算法

目录 一、数组翻转 1.1示例代码 1.2适用场景 二、冒泡排序 2.1示例代码 2.2适用场景 三、二分查找 3.1示例代码 3.2适用场景 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助。 &#x1f4a1;本文由Filotimo…

【软件建模与设计】-04-软件设计和体系结构概念

目录 1、类与对象 2、信息隐藏 2.1、示例 3、继承和泛化/特化 4、并发处理 4.1、并发对象间的协作 5、设计模式 6、软件体系结构和构件 7、软件质量属性 1、类与对象 一个对象是现实世界中物理的或概念的实体。 一个对象盖了数据(data)以及作用于数据之上的过程(pro…

Sentinel规则持久化Push模式两种实现方式

文章目录 sentinel持久化push推模式微服务端的实现具体实现源码分析读数据源写数据源的实现 微服务端解析读数据源流程 修改源码的实现官方demo修改源码实现配置类flowauthoritydegreadparamsystemgateway修改源码 测试补充 前置知识 pull模式 sentinel持久化push推模式 pull拉…

liunx面试题目

如何看当前Linux系统有几颗物理CPU和每颗CPU的核数&#xff1f; 查看物理cup&#xff1a; cat /proc/cpuinfo|grep -c ‘physical id’ 查看每颗cup核数 cat /proc/cpuinfo|grep -c ‘processor’ 若希望自动实现软件包的更新&#xff0c;可以使用yum-cron并启动该服务 yum -y …

解决一下git clone失败的问题

1&#xff09;.不开梯子&#xff0c;我们用https克隆 git clone https://github.com 报错&#xff1a; Failed to connect to github.com port 443 after 2091 ms: Couldnt connect to server 解决办法&#xff1a; 开梯子&#xff0c;然后# 注意修改成自己的IP和端口号 gi…

[HDCTF2019]MFC

[HDCTF2019]MFC-CSDN博客 不会写 完全画瓢 我还以为win32什么系统逆向 原来是小瘪三! VM保护 下载xspy(看雪上有) 打开32位的 再打开 这个窗口 把这个放大镜托到这个大窗口(里面有个小窗口,不要托错了) 下面这个 onmeg 就她不正常,是什么0464 #include <stdio.h&g…

简易ELK搭建

ELK搭建 1. elasticsearch1.1 下载1.2 ES配置1.3 启动ES1.4 开启权限认证1.5 IK分词器配置&#xff08;非必须&#xff09; 2. kibana2.1 下载2.2 配置2.3 启动kibana 3. logstash3.1 下载3.2 配置3.3 启动logstash 4. springboot推送数据 ELK包括elasticsearch、logstash、kib…

自然语言处理(NLP)——法国工程师IMT联盟 期末考试题

1. 问题1 &#xff08;法语&#xff09;En langue arabe lcrasante majorit des mots sont forms par des combinaisons de racines et de schmes. Dans ce mcanisme... &#xff08;英语&#xff09;In Arabic language the vast majority&#xff08;十之八九&#xff09; of…

《昇思25天学习打卡营第23天|onereal》

第23天学习内容简介&#xff1a; ----------------------------------------------------------------------------- 本案例基于MindNLP和ChatGLM-6B实现一个聊天应用。 1 环境配置 配置网络线路 2 代码开发 下载权重大约需要10分钟 ------------------------------- 运…

UI设计工具选择指南:Sketch、XD、Figma、即时设计

在数字产品设计产业链中&#xff0c;UI设计师往往起着连接前后的作用。产品经理从一个“需求”开始&#xff0c;制定一个抽象的产品概念原型。UI设计师通过视觉呈现将抽象概念具体化&#xff0c;完成线框图交互逻辑视觉用户体验&#xff0c;最终输出高保真原型&#xff0c;并将…

基于Java的在线考试系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;Java MySQL B/S架构 SpringBoot框架 工具&#xff1a;Eclipse、MySQL环境配置工具 系统展示 首…

ArkUI状态管理

State装饰器 在声明式UI中&#xff0c;是以状态驱动试图更新 状态 (State) 指驱动视图更新的数据(被装饰器标记的变量) 试图(View) 基于UI描述渲染得到用户界面 说明 1.State装饰器标记的变量必须初始化&#xff0c;不能为空 2.State支持Object、classstring、number、b…

【LeetCode:试题 16.06. 最小差 + 双指针 + 防止整型溢出】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Pythonselenium自动化测试实战项目详解

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 说明&#xff1a;本项目采用流程控制思想&#xff0c;未引用unittest&pytest等单元测试框架 …

SSE(Server Sent Event)实战(3)- Spring Web Flux 实现

上篇博客 SSE&#xff08;Server Sent Event&#xff09;实战&#xff08;2&#xff09;- Spring MVC 实现&#xff0c;我们用 Spring MVC 实现了简单的消息推送&#xff0c;并且留下了两个问题&#xff0c;这篇博客&#xff0c;我们用 Spring Web Flux 实现&#xff0c;并且看…