[Linux_IMX6ULL驱动开发]-总线设备驱动模型

目录

框架分层

总线驱动模型实现

上层驱动代码(leddrv.c)的实现以及解析

交叉依赖的避免

下层驱动的设备文件(board_A_led.c)的实现

下层驱动的驱动文件(chip_demo_gpio.c)的实现


框架分层

在之前,我们对于驱动的框架有过两种不同的框架。第一种框架,驱动文件只有应用层和驱动层的两个文件,在驱动层的文件中,实现对应用层的系统调用的实现、设备的初始化、控制、引脚初始化等,这样的驱动框架虽然很简单,但是如果更换了开发板或者引脚发生了变更,就需要深入到底层进行寄存器地址的修改等,十分的繁琐

第二种框架,应用层是不变的,但是对于底层驱动,分为了两层,分为上层驱动和下层驱动。上层驱动主要实现file_operation结构体以及内部的成员函数指针、calss、device的创建和销毁等,下层驱动则实现物理地址到虚拟地址的映射,并且构造一个结构体,封装初始化函数和控制函数,提供给上层的驱动。这样做似乎可以做到很好的移植性,但是底层驱动和上层驱动要编译为一个ko文件,如果更换引脚等信息,那么我们还得对驱动重新编译。

在这里我们介绍第三种驱动框架,总线驱动框架,依赖与第二种模型,我们把下层驱动再次进行分层,分为资源文件和芯片操控文件,资源文件主要用来定义不同板子的外设资源、引脚等等,而芯片操控文件主要用来实现外设的初始化等等。

 


总线驱动模型实现

设备和驱动分别有两个对应的结构体,设备对应结构体为struct platform_device,驱动对应的结构体为struct platform_driver

设备和驱动的匹配规则如下,只有设备和驱动成功匹配了,那么该驱动才可以获取到设备资源文件的各种引脚信息 ,在配对成功后,在进行一些操作,在资源文件(设备文件)中定义组、引脚等,在驱动文件通过各种引脚实现物理地址映射、初始化、操控等。


上层驱动代码(leddrv.c)的实现以及解析

上层驱动代码的主要任务就是完成file_operation结构体,然后填充好里面的函数指针成员、class类的创建。需要注意的是,device设备的创建和销毁现在不在上层驱动进行,而是在下层驱动中的驱动文件中进行。

如下,由于创建设备需要class结构体,所以我们在上层驱动中封装该函数,然后通过如下 EXPORT_SYMBOL(),修饰函数,让该函数可以在其他文件中被使用,这样,下层驱动中的驱动文件就可以创建设备了。

交叉依赖的避免

在上层驱动中,我们需要获取下层驱动的驱动文件的操作控制函数,但是现在存在如下情况,下层驱动的驱动文件chip_demo_gpio.c需要使用上层驱动传入的device创建函数,上层驱动需要下层驱动的驱动文件chip_demo_gpio.c提供的具体操控的结构体,这就导致了交叉依赖,所以我们在这里需要让下层驱动来初始化上层驱动,也就是具体的操控结构体交给下层来初始化

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include "led_opr.h"

#define LED_NUM 2

/* 1. 确定主设备号                                                                 */
static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;


#define MIN(a, b) (a < b ? a : b)

/* 3. 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int err;
	char status;
	struct inode *inode = file_inode(file);
	int minor = iminor(inode);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_from_user(&status, buf, 1);

	/* 根据次设备号和status控制LED */
	p_led_opr->ctl(minor, status);
	
	return 1;
}

static int led_drv_open (struct inode *node, struct file *file)
{
	int minor = iminor(node);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 根据次设备号初始化LED */
	p_led_opr->init(minor);
	
	return 0;
}

static int led_drv_close (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/* 2. 定义自己的file_operations结构体                                              */
static struct file_operations led_drv = {
	.owner	 = THIS_MODULE,
	.open    = led_drv_open,
	.read    = led_drv_read,
	.write   = led_drv_write,
	.release = led_drv_close,
};

/* 4. 把file_operations结构体告诉内核:注册驱动程序                                */
/* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
static int __init led_init(void)
{
	int err;
	
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "100ask_led", &led_drv);  /* /dev/led */


	led_class = class_create(THIS_MODULE, "100ask_led_class");
	err = PTR_ERR(led_class);
	if (IS_ERR(led_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "led");
		return -1;
	}

	/* 会造成交叉依赖 */
	//p_led_opr = get_board_led_opr();
	
	return 0;
}




void led_device_create(int minor)
{
	device_create(led_class, NULL, MKDEV(major, minor), NULL, "100ask_led%d", minor);
}
void led_device_destroy(int minor)
{
	device_destroy(led_class,  MKDEV(major, minor));
}
void led_operation_register(struct led_operations *opr)
{
	/* 传递交给下层驱动,下层驱动来初始化上层驱动
		这样就上层驱动就不用需要先加载下层驱动*/
	p_led_opr = opr;
}
EXPORT_SYMBOL(led_device_create);
EXPORT_SYMBOL(led_device_destroy);
EXPORT_SYMBOL(led_operation_register);



/* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数           */
static void __exit led_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);


	class_destroy(led_class);
	unregister_chrdev(major, "100ask_led");
}


/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");



下层驱动的设备文件(board_A_led.c)的实现

board_A_led.c的主要思路如下:

 一、创建一个struct platform_device结构体,由于是三个ko文件,所以出口函数,入口函数以及对应的创建、卸载不可缺少

二、对于platform_device的各个成员进行初始化赋值 ,包括对应驱动文件的名字、资源为什么以及资源的个数

        name代表和驱动文件匹配的名称信息

        resource表示资源信息

        num_resources表示资源信息的个数

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include "led_resource.h"
#include <linux/ioport.h> 
#include<linux/platform_device.h>


static struct resource board_A_led_resource[] = {
	/* 设备1 */
	{
		.start = GROUP_PIN(3,1),
		.flags = IORESOURCE_IRQ,
	},
		
	/* 设备2 */
	{
		.start = GROUP_PIN(5,8),
		.flags = IORESOURCE_IRQ,
	},

};

static struct platform_device board_A_led_devices = {
	.name = "100ask_led",
	.resource = board_A_led_resource,
	.num_resources = ARRAY_SIZE(board_A_led_resource),

};
/* 创建了platform_device,因为装载了三个ko文件,所以
	单独的都需要出口函数和入口函数,资源文件中
	需要把platform_device注册*/

static __init int board_A_led_init(void)
{
	int err;
	/* device注册 */
	err = platform_device_register(&board_A_led_devices);
	return 1;
}

static __exit void board_A_led_exit(void)
{
	/* device卸载 */
	platform_device_unregister(&board_A_led_devices);
}


module_init(board_A_led_init);
module_exit(board_A_led_exit);

MODULE_LICENSE("GPL");


#ifndef _LED_RESOURCE_H
#define _LED_RESOURCE_H

/* GPIO3_0 */
/* bit[31:16] = group */
/* bit[15:0]  = which pin */
#define GROUP(x) (x>>16)
#define PIN(x)   (x&0xFFFF)
#define GROUP_PIN(g,p) ((g<<16) | (p))

struct led_resource {
	int pin;
};

struct led_resource *get_led_resouce(void);

#endif


下层驱动的驱动文件(chip_demo_gpio.c)的实现

chip_demo_gpio.c的主要思路如下:

该文件的主要思路就是首先对 platform_driver 结构体的定义,以及在出口入口函数对该结构体的注册以及销毁。                                                                                                               同时进行匹配,如果驱动和某个设备文件能够成功匹配上,则执行 platform_driver 结构体中的 probe函数进行引脚信息的获取。获取到引脚信息后,完成对应物理地址到虚拟地址的映射以及具体初始化、操控函数的封装。传递到具体封装这些函数的结构体中 ,传递给上层驱动函数

具体的实现步骤如下:

一、platform_driver结构体的定义,以及注册、初始化,同时需要实现驱动文件的出口入口,对其进行修饰

二、完成结构体中的probe函数以及remove函数,同时填入需要匹配的设备名称到 .driver.name当中

                                                                                                                                            三、通过probe函数获取的引脚,进行对应的寄存器操作,包括地址映射、对应外设初始化以及操控等等

 (该代码并未实现步骤三,也就是物理地址映射到虚拟地址以及具体控制、初始化并未实现)

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>


#include<linux/platform_device.h>

#include "led_opr.h"
#include "led_resource.h"

static struct led_resource *led_rsc;

static int led_pins[100] = {0};
static int led_count = 0;

static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */	   
{	
	//printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
	if (!led_rsc)
	{
		led_rsc = get_led_resouce();
	}
	
	printk("init gpio: group %d, pin %d\n", GROUP(led_pins[which]), PIN(led_pins[which]));
	switch(GROUP(led_pins[which]))
	{
		case 0:
		{
			printk("init pin of group 0 ...\n");
			break;
		}
		case 1:
		{
			printk("init pin of group 1 ...\n");
			break;
		}
		case 2:
		{
			printk("init pin of group 2 ...\n");
			break;
		}
		case 3:
		{
			printk("init pin of group 3 ...\n");
			break;
		}
	}
	
	return 0;
}

static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{
	//printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
	printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(led_pins[which]), PIN(led_pins[which]));

	switch(GROUP(led_pins[which]))
	{
		case 0:
		{
			printk("set pin of group 0 ...\n");
			break;
		}
		case 1:
		{
			printk("set pin of group 1 ...\n");
			break;
		}
		case 2:
		{
			printk("set pin of group 2 ...\n");
			break;
		}
		case 3:
		{
			printk("set pin of group 3 ...\n");
			break;
		}
	}

	return 0;
}

static struct led_operations board_demo_led_opr = {
	.init = board_demo_led_init,
	.ctl  = board_demo_led_ctl,
};


static int chip_demo_gpio_led_probe(struct platform_device* dev)
{

	int i = 0;
	struct resource *res;

	/* 发现名字对上后,记录引脚 */
	while(1)
	{
		res = platform_get_resource(dev, IORESOURCE_IRQ, i);
		if( NULL == res )
			break;
		else
		{
			/* 记录引脚 */
			led_pins[led_count] = res->start;
			/* device create */
			/* 由于device_create需要class结构体,通过上层驱动封装函数,此处调用 */
			led_device_create(led_count);
			led_count++;
		}
	}

	return 0;
}
static int chip_demo_gpio_led_remove(struct platform_device* dev)
{
	int i;
	/* device destory */
	for( i = 0; i < led_count; i++ )
	{
		led_device_destroy(i);
	}
	led_count = 0;
	return 0;
}


/* 由于我们使用了platform_device,我们在这里实现platform_driver */
static struct platform_driver chip_demo_gpio_driver = {
	.probe = chip_demo_gpio_led_probe,
	.remove = chip_demo_gpio_led_remove,
	.driver = {
		.name = "100ask_led",
	},
};


/* 驱动的入口出口 */
static __init int chip_demo_gpio_init(void)
{
	int err;
	/* 下层驱动初始化上层驱动的操作结构体 */
	led_operation_register(&board_demo_led_opr);
	/* driver注册 */
	err = platform_driver_register(&chip_demo_gpio_driver);
	return 1;
}

static __exit void chip_demo_gpio_exit(void)
{
	/* driver卸载 */
	platform_driver_unregister(&chip_demo_gpio_driver);
}


module_init(chip_demo_gpio_init);
module_exit(chip_demo_gpio_exit);

MODULE_LICENSE("GPL");




#ifndef _LED_OPR_H
#define _LED_OPR_H

struct led_operations {
	int (*init) (int which); /* 初始化LED, which-哪个LED */       
	int (*ctl) (int which, char status); /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
};

struct led_operations *get_board_led_opr(void);


/* 上层驱动传入的操作函数 */
void led_device_create(int minor);
void led_device_destroy(int minor);
void led_operation_register(struct led_operations *opr);


#endif

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-21 13:58:04       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-21 13:58:04       100 阅读
  3. 在Django里面运行非项目文件

    2024-04-21 13:58:04       82 阅读
  4. Python语言-面向对象

    2024-04-21 13:58:04       91 阅读

热门阅读

  1. 【QT进阶】Qt http编程之http与https简单介绍

    2024-04-21 13:58:04       42 阅读
  2. Gitea:轻量级全功能DevSecOps平台的深度解析

    2024-04-21 13:58:04       32 阅读
  3. IDM的实用功能及其在现代下载管理中的重要地位

    2024-04-21 13:58:04       40 阅读
  4. Postgresql float8类型精度丢失问题

    2024-04-21 13:58:04       37 阅读
  5. 通过docker在容器中通过Gunicorn运行flask

    2024-04-21 13:58:04       30 阅读
  6. Fastadmin解决异步高并发大并发阻塞超时问题

    2024-04-21 13:58:04       39 阅读
  7. XiaodiSec day034 Learn Note 小迪渗透学习笔记

    2024-04-21 13:58:04       32 阅读
  8. Android 应用更新提醒自动跳转安装

    2024-04-21 13:58:04       38 阅读
  9. Rust为什么这么难学?

    2024-04-21 13:58:04       73 阅读
  10. 【Vue3】ref基本类型的响应式数据

    2024-04-21 13:58:04       40 阅读