【迅为iTop4412学习笔记】7.以模块方式注册设备,以及驱动获取设备信息

声明

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

本节目标

  1. 以模块方式注册设备
  2. 驱动获取设备信息

正文

  注册设备,注册驱动,当linux匹配设备和驱动成功,则调用probe函数(调用的probe()函数是自己定义的,linux只是指定了匹配成功就执行某一个函数,就是一个流程)。

先回想一下编译进内核时,注册设备的流程:

  1. 我们先是在Kconfig里仿照LEDS_CTL编写了一个MRYANG_CTL(图形界面才会有选项)(笔记2的内容)。

  2. 因为定义了MRYANG_CTL,所以 .config文件才会出现宏定义CONFIG_MRYANG_CTL(笔记2的内容)。

  3. CONFIG_MRYANG_CTL定义了,我们还用他做开关,决定是否启动我们写的platform_device类型的结构体,同样作为开关,还决定了是否把s3c_device_mryang_ctl这个结构体传参给linux。

  4. 这样注册的设备就被直接编译进linux了,最后编译出来的zImage文件,就直接包含了我们的设备。

再回想一下注册驱动的流程:

  1. 我们选择以模块module的方式进行注册、卸载驱动(笔记1的内容)

  2. 模块加载的时候我们用API函数去注册驱动。

  3. 模块卸载的时候我们用API函数去卸载驱动。

  我们其实会发现,两者的区别其实在于,编译进内核了,则你一直在内核里。以模块的方式,可以想用就插入内核,不想用就卸载。后者的优势在于灵活。如果什么都编译进内核,那么内核的程序会非常大,对于内核而言很臃肿。除了必备的东西,其实都可以以模块的方式插入,既不影响内核,还很灵活。

1.以模块方式注册设备

  注册驱动可以以模块的方式注册,注册设备也可以。

  类似注册驱动的API函数,名字稍微一改就行了,注册驱动是platform_driver_register(),那么注册设备就是platform_device_register(),同理卸载也是,他们的函数都在 include/linux/platform_device.h 里。

打开后看到:

// 注册设备的结构体
struct platform_device {
        const char      *name;
        int             id;
        bool            id_auto;
        struct device   dev;
        u32             num_resources;
        struct resource *resource;

        const struct platform_device_id *id_entry;
        char *driver_override; /* Driver name to force a match */

        /* MFD cell pointer */
        struct mfd_cell *mfd_cell;

        /* arch specific additions */
        struct pdev_archdata    archdata;
};
// 注册设备的API函数
extern int platform_device_register(struct platform_device *);
// 卸载设备的API函数
extern void platform_device_unregister(struct platform_device *);

  这些东西其实在注册设备的时候就有看过,翻出来看一下其实是一样的。所以我们直接上代码

// 头文件
#include <linux/platform_device.h>

// 结构体内需要调用的release函数
static void mryang_module_release(struct device *dev)
{
	printk(KERN_EMERG "MrYang module release!\n");
}

// 结构体
struct platform_device mryang_device = {
        .name   = "mryang_module_ctl",
        .id             = -1,
        .dev = {
        	.release = mryang_module_release,		// 关闭调用的函数
        }
};

// 注册设备的API函数,放入加载模块的函数里
platform_device_register(&mryang_device);
// 卸载设备的API函数,放入卸载模块的函数里
platform_device_unregister(&mryang_device);

  相比编译进内核,结构体内为什么要有一个release参数?因为platform_device_unregister()函数在卸载时会调用release函数关闭设备(可以查看源代码,点击 讲解 可以看一下别人分析的源代码)。

完整的代码

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

#include <linux/platform_device.h>

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

static void mryang_module_release(struct device *dev)
{
	printk(KERN_EMERG "MrYang module release!\n");
}

struct platform_device mryang_device = {
        .name   = "mryang_module_ctl",
        .id             = -1,
        .dev = {
        	.release = mryang_module_release,
        }
};

static int mryang_init(void)
{
	printk(KERN_EMERG "HELLO MrYang\n");
	// platform_driver_register(&mryang_driver);
	platform_device_register(&mryang_device);
	return 0;
}

static void mryang_exit(void)
{
	printk(KERN_EMERG "Bye MrYang\n");
	// platform_driver_register(&mryang_driver);
	platform_device_unregister(&mryang_device);
}

module_init(mryang_init);
module_exit(mryang_exit);

make编译出来的文件我们在板子上试试

[root@iTOP-4412]# insmod mryang_module_device.ko                                
[  108.046209] HELLO MrYang
[root@iTOP-4412]# ls /sys/devices/platform/ | grep mr                           
mryang_ctl
mryang_module_ctl
[root@iTOP-4412]# rmmod mryang_module_device                                    
[  112.745444] Bye MrYang
[  112.746882] MrYang module release!

可以看到 输出确实符合我们的预期,卸载设备的时候调用release函数,通过使用命令

ls /sys/devices/platform/

也确实看到了我们的模块,mryang_ctl是当初编译进内核的,mryang_module_ctl是我们刚刚insmod的。

以上就完成了我们这次的以模块的形式来注册设备(我们之前是直接把设备编译进内核)

2.驱动获取设备信息

  我们无论是注册驱动(调用probe()函数)还是注册设备(调用release()函数),函数的参数都有一个结构体,但是我们在函数里从来都没有用过。

  当设备和驱动匹配之后,他们各自有各自的参数,而交流的渠道则是通过结构体传参。

  我们以模块加载的mryang_module_ctl这样一个设备为例。

我们的预期是打印 设备的 name,id,以及调用设备的release() 函数,前两个没啥问题,后者的话我们要注意到release()的函数原型

void (*release)(struct device *dev);

传入的参数是 结构体 device,是我们传入的结构体的子结构体,所以我们调用的话,也一定是传入结构体的结构体。

下面直接上全部代码,主要看probe()函数(注意驱动的名字,要对应哪个设备就改成哪个设备的名字)

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

#include <linux/platform_device.h>

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

int mryang_probe(struct platform_device *pdv)
{
	printk(KERN_EMERG "probe!\n");
	// 打印 name
	printk(KERN_EMERG "pdv->name = %s\n", pdv->name);
	// 打印 id
	printk(KERN_EMERG "pdv->id = %d\n", pdv->id);
	// 调用设备的release函数,传入的参数是一个指针,以及参数是platform结构体的子结构体device
	pdv->dev.release(&pdv->dev);
	return 0;
}

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 = "mryang_module_ctl",
		.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);

输出如下,注意我们刚刚设备的模块已经insmod了,所以我这里直接insmod驱动即可

[root@iTOP-4412]# insmod probe_linux_module.ko                                  
[   37.872810] HELLO MrYang
[   37.874240] probe!
[   37.876258] pdv->name = mryang_module_ctl
[   37.880008] pdv->id = -1
[   37.890154] MrYang module release!

输出符合预期

  1. 首先加载模块输出HELLO MrYang
  2. 调用probe打印 probe!
  3. 接着输出设备的name和id
  4. 最后调用设备的release()函数(我们之前写的)

以上就是全部内容

本节我们做了两个事

一个以模块的方式来注册设备(而非直接编译进内核)
一个是驱动通过结构体参数来读取设备的信息



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值