【迅为iTop4412学习笔记】6. 编写以模块方式注册的驱动

声明

以下都是我刚开始看驱动视频的个人强行解读,如果有误请指出,共同进步。

本节目标

  1. 注册驱动

正文

上一篇我们注册了设备并编译进了内核,接下来我们再注册一个驱动试试

设备有一个结构体,同样的,驱动也有个结构体

首先我们打开头文件,看一下驱动注册的结构体长啥样

vim include/linux/platform_device.h

打开看到的就是上一节用到的注册设备的结构体,然而本节是注册驱动,所以我们搜索platform_driver并查看

struct platform_driver {
        int (*probe)(struct platform_device *);
        int (*remove)(struct platform_device *);
        void (*shutdown)(struct platform_device *);
        int (*suspend)(struct platform_device *, pm_message_t state);
        int (*resume)(struct platform_device *);
        struct device_driver driver;
        const struct platform_device_id *id_table;
};

extern int platform_driver_register(struct platform_driver *);
extern void platform_driver_unregister(struct platform_driver *);

对于驱动,有几种常见状态:初始化、移除、休眠、复位,所以结构体内定义了很多的函数(英文翻译很直白了…)
我们注册了设备,也注册了驱动,那么linux会调用platform_match进行匹配,匹配完成之后会调用probe函数进行初始化,这些下面配置结构体再说。

除了结构体,还发现下面有两行,看名字就能看出来是注册和卸载驱动的函数,也很直白了…

和注册设备不一样的地方在于,注册设备的结构体里就是关于设备的一些信息的参数。

但是注册驱动的结构体里包含的是关于驱动的函数,而参数则又封装了一个结构体device_driver,这个结构体内包含了驱动里面的参数。

这些参数也有name这样的参数,目的是根据name让设备和驱动匹配。

我们打开路径include/linux/device.h查看一下这个结构体是怎么定义的:

struct device_driver {
        const char              *name;
        struct bus_type         *bus;

        struct module           *owner;
        const char              *mod_name;      /* used for built-in modules */

        bool suppress_bind_attrs;       /* disables bind/unbind via sysfs */
        enum probe_type probe_type;

        const struct of_device_id       *of_match_table;
        const struct acpi_device_id     *acpi_match_table;

        int (*probe) (struct device *dev);
        int (*remove) (struct device *dev);
        void (*shutdown) (struct device *dev);
        int (*suspend) (struct device *dev, pm_message_t state);
        int (*resume) (struct device *dev);
        const struct attribute_group **groups;

        const struct dev_pm_ops *pm;
        void (*coredump) (struct device *dev);

        struct driver_private *p;
};

这个结构体内我们主要关注的是name和owner,name用于匹配设备,owner一般填THIS_MODULE,表示应用于此模块。

看到device设备里也有类似platform_device的函数,比如关闭,休眠。因为我们注册的是platform的平台字符设备,所以直接再platform里定义就行了。

因为设备只有一个,所以id上一节是-1,那么驱动通过name就可以直接找到设备,从而匹配。

对于注册驱动的步骤

  1. 编写注册驱动用到的结构体
  2. 把结构体传入注册驱动的函数进行注册

下面开始注册驱动

1. 先掏出模块的模板

本节是以模块的方式去注册,所以程序框架先写一个模块。

#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("MrYang");

static int mryang_init(void)
{
	printk(KERN_EMERG "HELLO MrYang\n");
	return 0;
}

static void mryang_exit(void)
{
	printk(KERN_EMERG "Bye MrYang\n");	
}

module_init(mryang_init);
module_exit(mryang_exit);

因为插入模块会调用mryang_init(),所以我们直接在此函数里去注册驱动就行了。

2.编写注册驱动用到的结构体

我们先要包含platform平台驱动的头文件

#include <linux/platform_device.h>

接下来就是定义驱动的结构体:
上面我们查看了linux是如何定义驱动的结构体,里面有probe,remove函数等等,我们只是学习+体验,所以只需要先编写加载和卸载两个结构体成员函数就可以了。

// 下面是结构体内的原型
// int (*probe)(struct platform_device *);
// int (*remove)(struct platform_device *);

// 这是我们编写的函数
int mryang_probe(struct platform_device *pdv)
{
	return 0;
}

int mryang_remove(struct platform_device *pdv)
{
	return 0;
}

结构体里的参数probe,remove函数已经定义完了,然后我们定义结构体时,把参数指向我们定义的probe和remove函数即可。

struct platform_driver mryang_driver = {
	.probe = mryang_probe,
	.remove = mryang_remove,
	.driver = {
		.name = "mryang_ctl",	// 像这种变量最好定义一个宏定义在开头,别问我为啥
		.owner = THIS_MODULE,
	}
};

3. 把结构体传入注册驱动的函数进行注册

现在我们驱动结构体也写好了,那么就可以注册了,咋注册,肯定是加载模块的时候注册,卸载模块的时候卸载啊。

所以我们在mryang_init和mryang_exit里分别添加注册和卸载驱动的函数(要是不知道函数叫什么,可以上翻查看结构体的时候有说),参数自然就是我们定义的结构体mryang_driver。

// 写在mryang_init里
platform_driver_register(&mryang_driver);
// 写在mryang_exit里
platform_driver_unregister(&mryang_driver);

完整的代码

#include <linux/init.h>
#include <linux/module.h>
// platform相关头文件(设备、驱动的结构体和函数)
#include <linux/platform_device.h>
// 驱动名(与设备名一致)
#define DRIVER_NAME "mryang_ctl"

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("MrYang");

/* 加载驱动,设备、驱动匹配成功,则调用probe()函数 */
int mryang_probe(struct platform_device *pdv)
{
	printk(KERN_EMERG "probe!\n");
	return 0;
}

/* 卸载驱动调用remove()函数 */
int mryang_remove(struct platform_device *pdv)
{
	printk(KERN_EMERG "remove!\n");
	return 0;
}

/* 驱动结构体 */
struct platform_driver mryang_driver = {
	.probe = mryang_probe,
	.remove = mryang_remove,
	.driver = {
		.name = DRIVER_NAME,
		.owner = THIS_MODULE,
	}
};

/* 加载模块 */
static int mryang_init(void)
{
	printk(KERN_EMERG "HELLO MrYang\n");
	platform_driver_register(&mryang_driver);
	return 0;
}

/* 卸载模块 */
static void mryang_exit(void)
{
	printk(KERN_EMERG "Bye MrYang\n");
	platform_driver_unregister(&mryang_driver);
}

module_init(mryang_init);
module_exit(mryang_exit);

Makefile文件

#!/bin/bash
obj-m += probe_linux_module.o 

KDIR := /root/iTop4412_Kernel_3.0

PWD ?= $(shell pwd)

all:
	make -C $(KDIR) M=$(PWD) modules
		
clean:
	rm -rf *.o

其实看到Makefile基本上没变,只是名字改成了probe_linux_module。以后就不再叙述Makefile了。

编译驱动!

make

并非编译linux内核,只是编译驱动,不知道咋整详见第一节…

加载、卸载驱动

[root@iTOP-4412]# insmod probe_linux_module.ko                                  
[  295.708430] HELLO MrYang
[  295.709815] probe!
[root@iTOP-4412]# rmmod probe_linux_module                                      
[  297.924409] Bye MrYang
[  297.925330] remove!

总结

其实也是融合了之前的所有内容

  1. 加载模块调用了module_init(mryang_init);

其中的mryang_init是我们自定义的函数,里面输出了HELLO MrYang

  1. 紧接着还调用了注册驱动的函数platform_driver_register(&mryang_driver);

mryang_driver就是我们定义的驱动的结构体,里面包含了
probe和remove函数。

  1. 当我们注册驱动的时候,linux去匹配设备,发现设备也注册了,就调用probe函数

结构体内的probe指向了我们定义的mryang_probe(struct platform_device *pdv)函数
函数的内容就是输出probe。

这就是完整的流程,你可能还会问,我们啥时候注册设备了?傻呀,看上一节注册设备的笔记,我们在编译linux内核的时候已经勾选把注册设备也编译进内核了。

至此,其实已经可以好好思考整个流程了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值