Linux设备驱动模型--分离分层思想

一、设备驱动的分层思想:以platform设备驱动、input设备驱动为例看看他们在分层思想上的体现

【1】在核心层:一类设备总线通用的功能特性,如果某一设备支持的总线比较特别可以不使用这些功能特性,在自己的设备总线结构体中

进行重载。

例(1)platform核心层:定义在drivers/base/platform.c文件

Linux内核对设备总线先进行了一次全局的抽象,即概括了所有设备驱动总线具有的功能特性:

struct bus_type {
	//具备总线名、总线属性、总线上设备属性、总线上驱动属性
	const char		*name;
	struct bus_attribute	*bus_attrs;
	struct device_attribute	*dev_attrs;
	struct driver_attribute	*drv_attrs;
	//具备匹配、热插拔、探测、移除设备方案
	int (*match)(struct device *dev, struct device_driver *drv);
	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
	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 (*suspend_late)(struct device *dev, pm_message_t state);
	int (*resume_early)(struct device *dev);
	int (*resume)(struct device *dev);

	struct dev_pm_ops *pm;
	struct bus_type_private *p; //设备具有不同特性--这本身就是一种共性
};

针对platform设备,内核并没有去实现上面这些通用的功能属性,毕竟在现实中大多数情况,设备的不同功能还是比较多,除非是要在一个cpu下控制批量相同的设备,这只是我个人的看法

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_attrs	= platform_dev_attrs,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= PLATFORM_PM_OPS_PTR,
};

也就是说内核认为platform设备在多数情况下具备的通用功能特性有上面5个方面并给他们赋予了默认方案,

仔细的我们发现platform总线中并没有probe成员,可以认为该总线中并没有或不需要这么一种功能。

经过内核的初始化调用:

int __init platform_bus_init(void);

bus_register(&platform_bus_type);

platform总线上就具有这类设备的一套默认的方案。

例(2)input核心层:对输入设备的管理进行总体抽象

struct input_handle {
	void *private;
	int open;
	const char *name;
	struct input_dev 	 *dev; //##关键成员
	struct input_handler *handler; //##关键成员
	struct list_head	d_node;
	struct list_head	h_node;
};

派生到:

struct evdev {
	int exist;
	int open;
	int minor;
	char name[16];
	struct input_handle handle;//继承input_handle基类
	wait_queue_head_t wait;
	struct evdev_client *grab;
	struct list_head client_list;
	spinlock_t client_lock; /* protects client_list */
	struct mutex mutex;
	struct device dev;
};

什么时候被实例化?

之前分析过input设备驱动框架,他是在input设备和驱动mach成功之后通过事件驱动层的connect函数

evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{
	......
	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
	evdev->minor = minor;
	evdev->handle.dev = input_get_device(dev); // 指向input_dev
	evdev->handle.name = evdev->name;
	evdev->handle.handler = handler; //指向input_handler
	evdev->handle.private = evdev;
	error = input_register_handle(&evdev->handle);//让input_dev和input_handler的h_list指向evdev->handle
	......
}

【2】在设备层和驱动层:同样,内核先抽象出设备驱动的基类,再派生出某种设备驱动,最后再实例化到具体的设备驱动中来

例(1)platform设备/驱动层

全局的抽象:

//设备基类:

struct device {
	struct device			*parent;
	struct device_private	*p;
	struct kobject 			kobj;
	const char				*init_name; 	/* initial name of the device 这个就是传统的bus_id,具体到每一个设备之后当做默认值 */
	struct device_type		*type;
	......
	struct bus_type			*bus;			/* type of bus device is on */
	struct device_driver 	*driver;		/* which driver has allocated this device */
	void					*driver_data;	/* data private to the driver */
	void					*platform_data;	/* Platform specific data, device core doesn't touch it */
	......
	void					(*release)(struct device *dev);
};

//驱动基类:

struct device_driver {
	const char		*name;
	struct bus_type		*bus;
	struct module		*owner;
	const char 		*mod_name;	/* used for built-in modules */
	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);
	struct attribute_group **groups;
	struct dev_pm_ops *pm;
	struct driver_private *p;
};

platform设备/驱动的派生:

struct platform_device {
	const char		*name;
	int				id;  // 硬件设备的象征/代表
	struct device	dev; // 由此继承基类
	u32				num_resources;
	struct resource	* resource;//这个驱动使用的资源
	struct platform_device_id	*id_entry;
};

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 (*suspend_late)(struct platform_device *, pm_message_t state);
	int (*resume_early)(struct platform_device *);
	int (*resume)(struct platform_device *);
	struct device_driver driver;  //继承基类
	struct platform_device_id *id_table;
};

实例化到具体的led设备:

static struct platform_device led_dev = {
    .name         = "myled",
    .id       = -1,
    .num_resources    = ARRAY_SIZE(led_resource),
    .resource     = led_resource,
    .dev = {
        .release = led_release,
    },
};  

struct platform_driver led_drv = {
    .probe      = led_probe,
    .remove     = led_remove,
    .driver     = {
        .name   = "myled",
    }
};  

例(2)input设备/事件驱动层

派生的输入设备基类:

struct input_dev {
	const char *name;
	const char *phys;
	const char *uniq;
	struct input_id id;
	unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
	unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
	unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
	unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
	......
	struct timer_list timer;
	int sync;
	int abs[ABS_MAX + 1];
	int rep[REP_MAX + 1];
	......
	unsigned long key[BITS_TO_LONGS(KEY_CNT)];
	unsigned long led[BITS_TO_LONGS(LED_CNT)];
	......
	int (*open)(struct input_dev *dev);
	void (*close)(struct input_dev *dev);
	int (*flush)(struct input_dev *dev, struct file *file);
	int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

	struct input_handle *grab;
	......
	struct device dev; //同样继承于内核抽象出来的设备基类
	struct list_head	h_list;
	struct list_head	node;
};

input设备实例化--按键:

struct input_dev buttons_dev = input_allocate_device();
set_bit(EV_KEY, buttons_dev->evbit);
set_bit(EV_REP, buttons_dev->evbit);
set_bit(KEY_L, buttons_dev->keybit);
set_bit(KEY_S, buttons_dev->keybit);
set_bit(KEY_ENTER, buttons_dev->keybit);
set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);

事件处理基类:

struct input_handler {
	void *private;
	void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
	int  (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
	void (*disconnect)(struct input_handle *handle);
	void (*start)(struct input_handle *handle);

	const struct file_operations *fops;
	int minor;
	const char *name;

	const struct input_device_id *id_table;
	const struct input_device_id *blacklist;

	struct list_head	h_list;
	struct list_head	node;
};

事件处理基类实例化:

static struct input_handler evdev_handler = {
	.event		= evdev_event,
	.connect	= evdev_connect,
	.disconnect	= evdev_disconnect,
	.fops		= &evdev_fops,
	.minor		= EVDEV_MINOR_BASE,
	.name		= "evdev",
	.id_table	= evdev_ids,
};

小结:

以上分析是从数据结构上局部分层的特点进行分析,驱动框架上的分层管理在上面两种特殊的驱动模型中同样有明显的体现:

①以总线-设备-驱动模型为框架的platform设备驱动:

核心层:bus--给下面一层提供注册接口和match匹配标准等函数

设备层/驱动层:dev/drv--使用核心层的接口进行注册

附图:借用韦东山老师的讲解图

②以非总线-设备-驱动为框架的input设备驱动:

核心层:给下面一层提供注册接口,同时给app提供file_operation中的open接口

设备层:使用核心层提供的注册接口

事件驱动层:使用核心层提供的注册接口,重新实现file_operation中的接口

附图:借用韦东山老师的讲解图

-------------------------------------------------------------------------------------------------------------------------------------------------------

二、主机驱动与外设驱动分离的思想

主机驱动:相对较稳定的纯软件的驱动部分

外设驱动:硬件平台相关的资源

比如上边提到的:platform设备层和驱动层、input的设备层和事件处理层

附图:

如此设计的好处主要体现在

(1)CPU不变,假设为2440,外设的数量在增加k个

①不进行分离的情况:

那么需要编写控制器对该外设的驱动k个,驱动每一个外设都要涉及到主机控制器相关的内容和外设相关的内容,

为了好比较我们这里把写一个主机控制器的内容当做一个驱动,一个外设相关的内容当做一个驱动,也就是说:

驱动一个外设需要2个驱动,那么需要增加2k个驱动内容

②进行分离的情况:

只需要增加k个外设相关的驱动内容

(2)更换j个不同的主机CPU时,外设数量n不变

①不进行分离的情况:

需要重新编写n个主机控制器操作外设的驱动,编写的内容同样涉及到主机控制器相关的操作和外设相关的操作

试想:如果我更换了j个CPU,外设还是不变,那么我就需要编写j*2n个驱动内容

②进行分离的情况:

总共只需要编写j*x个驱动内容。

我们知道,主机控制器的数量通常是比外设的数量要少得多的,如果你的主机控制器的数目比外设的数量还多只能

说明你的一些控制器是没有实际应用的。这样一来x小于等于n,明显最起码都少写一倍的内容。

时间: 2024-10-29 19:06:45

Linux设备驱动模型--分离分层思想的相关文章

Linux 设备驱动模型

Linux系统将设备和驱动归一到设备驱动模型中了来管理 设备驱动程序功能: 1,对硬件设备初始化和释放 2,对设备进行管理,包括实参设置,以及提供对设备的统一操作接口 3,读取应用程序传递给设备文件的数据或回送应用程序请求的数据 4,检测或处理设备出现的错误 设备驱动模型提供了硬件的抽象包括: 1,电源管理 其实,电源管理就是一些设备不工作的时候,让它歇一会,休眠一会(最低消耗),达到省电的目的 它的一个重要的功能是: 省电模式下,使系统中的设备以一定的先后顺序挂起 在全速工作模式下,使系统的设

Linux设备驱动模型【转】

本文转载自:http://blog.csdn.net/xiahouzuoxin/article/details/8943863 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 尽管LDD3中说对多数程序员掌握设备驱动模型不是必要的,但对于嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要. linux设备模型的目的:为内核建立一个统一的设备模型,从而又一个对系统结构的一般性抽象描述.换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同

linux设备驱动模型

1.总线概念 总线是内核与一个或者多个设备之间的通道.在设备模型中,所有的设备都是通过总线连接 2.sysfs 作用: 一个用于内核对象表述的文件系统 sysfs是一个基于ramfs的内存文件系统. 描述内核数据结构,属性以及与它们用户空间的链接关系 sys文件夹内含的顶层文件夹(使用ls -l /sys可以查看) block/ bus/ :包含内核中所有总线类型的描述. 每一个总线文件夹下面都有下面两类文件夹 devices/ :内核中所有注册的设备,内含的设备都是指向sys目录下面devic

linux设备驱动模型-浅析-转

1.  typeof typeof并非ISO C的关键字,而是gcc对C的一个扩展.typeof是一个关键字(类似sizeof),用于获取一个表达式的类型. 举个简单的例子: char tt; typeof(tt) cc; 则typeof(tt)等价于char,即相当于声明了char cc; 2.  offsetof 位置: 用途:获取结构类型TYPE里的 成员MEMBER 在结构体内的偏移 分析: #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE

linux设备驱动模型之平台总线实践环节(一)

1.首先回顾下之前写的驱动和数据在一起的led驱动代码,代码如下: #include <linux/module.h> #include <linux/init.h> #include <linux/leds.h> #include <asm/io.h> //ioremap和iounmap的头文件 writel等 /**********************************静态映射虚拟地址的方法,用的是内核移植时的静态映射表************

linux设备驱动模型之平台总线实践环节(四)

//通过led_classdev类型的指针得到s5pv210_led_pladata类型的指针,这个s5pv210_led_pladata类型结构体是我们自己定义的设备数据部分. static inline struct s5pv210_gpio_led *to_gpio(struct led_classdev *led_cdev) { return container_of(led_cdev, struct s5pv210_gpio_led, cdev); } #define X210_LED

Linux内核部件分析 设备驱动模型之device

来源:Linux社区 -- http://www.linuxidc.com/Linux/2011-10/44627p6.htm 作者 : qb_2008 linux的设备驱动模型,是建立在sysfs和kobject之上的,由总线.设备.驱动.类所组成的关系结构.从本节开始,我们将对linux这一设备驱动模型进行深入分析. 头文件是include/linux/device.h,实现在drivers/base目录中.本节要分析的,是其中的设备,主要在core.c中. struct device {

《Linux设备驱动开发详解(第3版)》海量更新总结

本博实时更新<Linux设备驱动开发详解(第3版)>的最新进展. 2015.2.26 几乎完成初稿. [F]是修正或升级:[N]是新增知识点:[D]是删除的内容 第1章 <Linux设备驱动概述及开发环境构建>[D]删除关于LDD6410开发板的介绍[F]更新新的Ubuntu虚拟机[N]添加关于QEMU模拟vexpress板的描述 第2章 <驱动设计的硬件基础> [N]增加关于SoC的介绍:[N]增加关于eFuse的内容:[D]删除ISA总线的内容了:[N]增加关于SP

《Linux设备驱动开发具体解释(第3版)》进展同步更新

本博实时更新<Linux设备驱动开发具体解释(第3版)>的最新进展. 2015.2.26 差点儿完毕初稿. 本书已经rebase到开发中的Linux 4.0内核,案例多数基于多核CORTEX-A9平台. [F]是修正或升级:[N]是新增知识点:[D]是删除的内容 第1章 <Linux设备驱动概述及开发环境构建>[D]删除关于LDD6410开发板的介绍[F]更新新的Ubuntu虚拟机[N]加入关于QEMU模拟vexpress板的描写叙述 第2章 <驱动设计的硬件基础> [