linux驱动学习加强版-2(文件驱动的书写)

一、驱动的外设

我们的设备硬件都需要驱动才能工作,没有驱动的硬件可以称之为废铁,没有硬件的驱动也是没有实际意义的。
所以两者是要相辅相成的。
因为我们在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 <linux/module.h>
#include <linux/fs.h>
#include <linux/major.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/kmod.h>


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 <linux/module.h>
#include <linux/fs.h>
#include <linux/major.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/kmod.h>


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);

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

永不秃头的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值