一、设备驱动的分层思想
:以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,明显最起码都少写一倍的内容。