自学内容网 自学内容网

[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


原文地址:https://blog.csdn.net/m0_72372635/article/details/137890294

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!