linux驱动学习加强版-2(文件驱动的书写)
创始人
2025-05-31 18:22:22
0

文章目录

  • 一、驱动的外设
  • 二、驱动操作文件原理
  • 三、编写一个驱动程序
    • 3.1 编写驱动程序的步骤
    • 3.1.2 确定主设备号以及注册驱动
    • 3.1.3 实现对应的函数
  • 四、一些错误现象

一、驱动的外设

我们的设备硬件都需要驱动才能工作,没有驱动的硬件可以称之为废铁,没有硬件的驱动也是没有实际意义的。
所以两者是要相辅相成的。
因为我们在ubuntu上进行的一些验证,我们可以外接一个SD卡当作硬件,实际上我们也可以放一个文件当作硬件,这样大家就可以不需要其余的东西了。

二、驱动操作文件原理

我们都知道,在Linux系统中,有种说法叫做一切皆文件,那么我们硬件也可以当作文件,我们用户想要直接操作文件是不可以的,就比如我们直接去open、read、write好像是只有open是可以的,但是read,write这些都是不可以的。
open可以的原因我已经忘了,大家可以去看韦东山的视频复习以下,而read、write这写都是没有权限的,为啥呢,因为内核机制问题,内核为了保证安全,就允许用户空间直接对底层硬件进行操作,只有通过驱动提供接口,然后对文件进行读写操作。
linux通过设备号对相应的文件进行操作

在这里插入图片描述

主设备号和次设备号统称为设备号,主设备号代表是的设备的类型,次设备号代表是的具体的设备,随着内核的更新,可以支持的主设备和次设备的数量也越来越多。

在这里插入图片描述

三、编写一个驱动程序

3.1 编写驱动程序的步骤

  1. 确定主设备号
  2. 定义自己的 file_operations 结构体
  3. 实现对应的 open/read/write函数,填写入结构体
  4. 把file_operations 结构体告诉内核,注册驱动程序
  5. 谁来注册驱动程序?需要一个入口函数;安装驱动程序时,就会调用这个入口函数
  6. 卸载驱动程序,调用出口函数
  7. 其他,提供设备信息,创建设备节点

3.1.2 确定主设备号以及注册驱动

为了方便,我这边就和注册一起写了。
source code:

#include 
#include 
#include 
#include 
#include 
#include static int major = 0; //确定主设备号,0默认系统分配static struct file_operations filectl_ops = {.owner   = THIS_MODULE,
};static int __init filectl_init(void)
{// 在初始化的时候进行驱动注册,设备号major = register_chrdev(0,"filectl",&filectl_ops);if(major < 0) {printk("[%s %d] filectl error\n", __FUNCTION__, __LINE__); // 注册失败return -1;}filectl_class = class_create(THIS_MODULE, "filectl"); // class_create 动态创建dev的类// IS_ERR 查看指针是否有错误if(IS_ERR(filectl_class)) {printk("[%s %d] class_create error\n", __FUNCTION__, __LINE__);unregister_chrdev(major,"filectl");return -1;}// 创建字符设备device_create(filectl_class, NULL, MKDEV(major, 0),NULL, "filectl");printk("[%s %d] filectl driver create success\n", __FUNCTION__, __LINE__);return 0;
}static void __exit filectl_exit(void) {device_destroy(filectl_class, MKDEV(major, 0));class_destroy(filectl_class);// 注销字符设备unregister_chrdev(major,"filectl");printk("[%s %d]goodbye filectl driver\n",  __FUNCTION__, __LINE__);
}module_init(filectl_init);
module_exit(filectl_exit);
MODULE_LICENSE      ("GPL");
MODULE_AUTHOR       ("cong.luo");
MODULE_DESCRIPTION  ("First file contrl module");

然后编译,加载模块:

在这里插入图片描述

然后我们就可以在dev下面看到我们字符设备了 。

3.1.3 实现对应的函数

实现汉函数就是在对应的ops操作集合里。

// 定义自己的驱动的 file_operations
// file_operations是一个函数指针的集合,用于存放我们定义的用于操作设备的函数的指针,如果我们不定义,它默认保留为NULL
static struct file_operations filectl_ops = {.owner   = THIS_MODULE,.read    = filectl_read,.write   = filectl_write,.open    = filectl_open,.release = filectl_close, // 好像没有close这个函数
};

这样我们就定义好了,定义好了过后我们就是完善这些函数了,不然会编译不过,

如下

ssize_t filectl_read(struct file *file, char __user *buf, size_t size, loff_t *pops)
{printk("[%s %d]\n", __FUNCTION__, __LINE__);return 1;
}ssize_t filectl_write(struct file *file,const char __user *buf, size_t size, loff_t *pops)
{printk("[%s %d]\n", __FUNCTION__, __LINE__);return 1;
}int filectl_open(struct inode *inode, struct file *file)
{printk("[%s %d]\n", __FUNCTION__, __LINE__);return 0;
}int filectl_close(struct inode *inode, struct file *file)
{printk("[%s %d]\n", __FUNCTION__, __LINE__);return 0;
}

这里对于初学者来说可能比较难的参数的传递,这些参数是怎么来的。
其实根本原因就是,虽然这些filectl_open, filectl_read, filectl_write函数是我们定的,但是实际上我们调用的函数还是底层的 open write这些,所以我们需要满足底层open write这些函数的参数结构。

struct inode *inode   

索引, 直白一点就是 文件的存放的地址

struct file *file

操作文件的结构体指针, 描述进程中打开的文件,进程中只要调用了open就有一个该对象。具体描述了打开文件的路径,权限,标志,内部偏移。file结构体是用来维护打开的文件的.

其实比较起来和我们正常的open close这些函数传递的参数是一样的,只不过在内核里面,可能有一些变换, 上面的read和write也都是一样的原理。

完整的code:

#include 
#include 
#include 
#include 
#include 
#include static int major = 0; //确定主设备号,0默认系统分配
static struct class *filectl_class;ssize_t filectl_read(struct file *file, char __user *buf, size_t size, loff_t *pops)
{printk("[%s %d]\n", __FUNCTION__, __LINE__);return 1;
}ssize_t filectl_write(struct file *file,const char __user *buf, size_t size, loff_t *pops)
{printk("[%s %d]\n", __FUNCTION__, __LINE__);return 1;
}int filectl_open(struct inode *inode, struct file *file)
{printk("[%s %d]\n", __FUNCTION__, __LINE__);return 0;
}int filectl_close(struct inode *inode, struct file *file)
{printk("[%s %d]\n", __FUNCTION__, __LINE__);return 0;
}// 定义自己的驱动的 file_operations
static struct file_operations filectl_ops = {.owner	 = THIS_MODULE,.read    = filectl_read,.write   = filectl_write,.open    = filectl_open,.release = filectl_close, // 好像没有close这个函数
};static int __init filectl_init(void)
{// 在初始化的时候进行驱动注册,设备号major = register_chrdev(0,"filectl",&filectl_ops);if(major < 0) {printk("[%s %d] filectl error\n", __FUNCTION__, __LINE__); // 注册失败return -1;}filectl_class = class_create(THIS_MODULE, "filectl"); // class_create 动态创建dev的类// IS_ERR 查看指针是否有错误if(IS_ERR(filectl_class)) {printk("[%s %d] class_create error\n", __FUNCTION__, __LINE__);unregister_chrdev(major,"filectl");return -1;}// 创建字符设备device_create(filectl_class, NULL, MKDEV(major, 0),NULL, "filectl");printk("[%s %d] filectl driver create success\n", __FUNCTION__, __LINE__);return 0;
}static void __exit filectl_exit(void) {device_destroy(filectl_class, MKDEV(major, 0));class_destroy(filectl_class);// 注销字符设备unregister_chrdev(major,"filectl");printk("[%s %d]goodbye filectl driver\n",  __FUNCTION__, __LINE__);
}module_init(filectl_init);
module_exit(filectl_exit);
MODULE_LICENSE		("GPL");
MODULE_AUTHOR		("cong.luo");
MODULE_DESCRIPTION	("First file contrl module");

这样我们的接口就好了。下一步就是去验证这几个read wrte函数了。

四、一些错误现象

错误现象:

/home/thundersoft/work_test/driver/filectl.c: In function ‘filectl_init’:
/home/thundersoft/work_test/driver/filectl.c:74:20: error: invalid storage class for function ‘filectl_exit’static void __exit filectl_exit(void) {^~~~~~~~~~~~
/home/thundersoft/work_test/driver/filectl.c:74:1: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]static void __exit filectl_exit(void) {^~~~~~
In file included from /home/thundersoft/work_test/driver/filectl.c:1:0:
./include/linux/module.h:128:42: error: invalid storage class for function ‘__inittest’static inline initcall_t __maybe_unused __inittest(void)  \^
/home/thundersoft/work_test/driver/filectl.c:81:1: note: in expansion of macro ‘module_init’module_init(filectl_init);

一般有这种的情况都是 多写了一个 } 或者 少写了一个 { 这个需要自己检查下就好

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...