Linux设备驱动模型
来源:互联网 发布:身份证读取软件 编辑:程序博客网 时间:2024/05/07 14:30
简介
作者:hjlin
内核版本:2.6.29
设备驱动模型框架是linux驱动编程的基础。它通过kobject,kset,ktype等底层数据结构将bus_type, device, device_driver 等高层数据结构组织起来,形成一个层次、分类清晰的驱动模型。优点如下:
1. 代码重用。将对象抽象为总线、驱动、设备三种,各司其职。同一总线的多个驱动使用相同的总线对象。同一驱动可以关联驱动多个设备。
2. 通过sysfs文件系统,清晰了展示内核驱动模型中的层次关系。同时sysfs文件系统还提供了方便的同用户控件交互的接口。
框架
数据结构
Kobject
Kobject是代表驱动模型中的一个对象。总线、驱动、设备都继承了它。(在结构体中包含kobject)。每个kobject在sysfs中表现为一个目录。
每个kobject都有一个parent kobject和所属的kset。Kset就是kobject所属的kset,通过kset的链表可以找到所有属于它的kobject。这些kobject进行uevent操作时,都会调用所属的kset的uevent_ops方法。父kobj,用于表示kobject之间或者kobject和kset,kset之间的在sysfs中的目录结构关系。如果父kobj不存在,并且所属的kset存在的话,则父kobj就是设置为所属的kset的内嵌kobj。因此,注册设备、驱动或者总线的时候如果不指定parent kobj的话,父kobj则会设置为所属的kset的kobj。(todo:最好画图表示关系)
struct kobject {
const char * k_name;
struct kref kref;
struct list_head entry; //连入到所属的kset的list
struct kobject * parent; //父kobj,如果没有父kobj,则使用所属的kset内嵌kobj
struct kset * kset; //所属的kset(如果有的话)
struct kobj_type * ktype; //所属的ktype,如果所属的kset也有ktype,则用kset的ktype。
struct sysfs_dirent * sd;
};
Kest
通过kset可以将kobject组织成一颗层次树。
struct kset {
struct list_head list; //属于该kset的kobj链表
spinlock_t list_lock;
struct kobject kobj; //内嵌的kobject
struct kset_uevent_ops *uevent_ops; //uevent方法,对所有该kset下的kobj进行uevent操作时的方法。
};
struct kset_uevent_ops {
int (*filter)(struct kset *kset, struct kobject *kobj);
const char *(*name)(struct kset *kset, struct kobject *kobj);
int (*uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};
kobj_type
struct kobj_type {
void (*release)(struct kobject *kobj); //kobject释放时调用
struct sysfs_ops *sysfs_ops; //操纵默认属性的读写方法
struct attribute **default_attrs; //相应的kobject(kset)的默认属性。对应于sysfs下的文件。
};
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *,char *);
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
};
struct attribute {
const char *name;
struct module *owner;
mode_t mode;
};
bus_type
代表一个总线。对应/sys/bus下的一个目录。管理相应总线下的所有驱动和设备。
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); //uevent方法
int (*probe)(struct device *dev); //match成功之后会调用。一般该probe方法内部会调用相应的device_driver的probe方法
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;
};
struct bus_type_private {
struct kset subsys; //总线本身目录
struct kset *drivers_kset; //驱动目录
struct kset *devices_kset; //设备目录
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
};
Device
struct device {
struct device *parent; //父设备
struct device_private *p;
struct kobject kobj;
char bus_id[BUS_ID_SIZE];
const char *init_name; /* initial name of the device */
struct device_type *type;
struct semaphore sem; /* semaphore to synchronize calls to
* its driver.
*/
struct bus_type *bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this
device */
void *platform_data; /* Platform specific data, device
core doesn't touch it */
struct dev_pm_info power;
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */
struct device_dma_parameters *dma_parms;
struct list_head dma_pools; /* dma pools (if dma'ble) */
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
/* arch specific additions */
struct dev_archdata archdata;
dev_t devt; /* dev_t, creates the sysfs "dev" */
spinlock_t devres_lock;
struct list_head devres_head;
struct klist_node knode_class;
struct class *class;
const struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
};
struct device_private {
struct klist klist_children; //子设备的链表
struct klist_node knode_parent; //作为父设备的字设备链表(klist_children)的一个节点
struct klist_node knode_driver; //作为所属驱动的设备链表(klist_devices)的一个节点
struct klist_node knode_bus; //作为所属总线的设备链表(klist_devices)的一个节点
void *driver_data;
struct device *device;
};
device_driver
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;
};
struct driver_private {
struct kobject kobj;
struct klist klist_devices; //所驱动的设备的链表
struct klist_node knode_bus; //作为所属总线的驱动链表(klist_drivers)的一个节点
struct module_kobject *mkobj;
struct device_driver *driver;
};
基础方法
kobject_add_internal
static int kobject_add_internal(struct kobject *kobj)
将kobject添加到设备驱动模型中。主要设置父kobj以及将自己添加到所属的kset的list中,并且在sysfs中创建目录。
kobject_uevent
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
send an uevent to userspace
核心方法
注册总线
bus_register(struct bus_type *bus)
注册一个总线到驱动模型中。在sysfs中的结构是:
/sys/bus/platform/
|-- devices
| |-- …
|-- drivers
| |-- …
|-- drivers_autoprobe
|-- drivers_probe
`-- uevent
int bus_register(struct bus_type *bus)
{
int retval;
struct bus_type_private *priv;
//初始化private数据结构
priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->bus = bus;
bus->p = priv;
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
//设置kobject名称。即/bus/sys/下的目录名称
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
priv->subsys.kobj.kset = bus_kset;
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
//注册内嵌kset
retval = kset_register(&priv->subsys);
if (retval)
goto out;
retval = bus_create_file(bus, &bus_attr_uevent);
if (retval)
goto bus_uevent_fail;
//创建设备kset并且注册
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
//创建驱动kset并且注册
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
retval = bus_add_attrs(bus);
if (retval)
goto bus_attrs_fail;
pr_debug("bus: '%s': registered\n", bus->name);
return 0;
。。。
out:
return retval;
}
注册设备
主要有两个工作,初始化设备,将设备挂接到设备驱动模型中并且处理uevent事件和自动匹配设备驱动。
在platform.c的platform_bus_init方法中使用了device_register(&platform_bus),注册了一个设备,该设备的sys目录为/sys/devices/platform/。但是一般情况下,内核对于不同的总线会提供不同的封装方法在内部调用device_register来注册设备。比如说platform_register_device,并且在platform_register_device方法中设置了device的父kobj为platform_bus,导致所有通过的platform_register_device注册的设备的目录都会在/sys/devices/platform/下。
在sys文件系统中的结构是:todo
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
void device_initialize(struct device *dev)
{
kobj_set_kset_s(dev, devices_subsys);
kobject_init(&dev->kobj);
klist_init(&dev->klist_children, klist_children_get,
klist_children_put);
INIT_LIST_HEAD(&dev->dma_pools);
INIT_LIST_HEAD(&dev->node);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_init_wakeup(dev, 0);
set_dev_node(dev, -1);
}
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error = -EINVAL;
dev = get_device(dev);
if (!dev || !strlen(dev->bus_id))
goto Error;
pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);
parent = get_device(dev->parent);
error = setup_parent(dev, parent);
if (error)
goto Error;
/* first, register with generic layer. */
kobject_set_name(&dev->kobj, "%s", dev->bus_id);
error = kobject_add(&dev->kobj);
if (error)
goto Error;
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
/* notify clients of device entry (new way) */
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
//创建uevent属性文件。可以通过该文件发送事件
error = device_create_file(dev, &uevent_attr);
if (error)
goto attrError;
//创建dev的属性文件。
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
}
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
//创建所属class,kobject_type, 以及device本身自带的属性文件
error = device_add_attrs(dev);
if (error)
goto AttrsError;
//创建电源管理子目录 power
error = dpm_sysfs_add(dev);
if (error)
goto PMError;
//将该设备挂接到电源链表下
device_pm_add(dev);
//将该dev添加到bus
error = bus_add_device(dev);
if (error)
goto BusError;
kobject_uevent(&dev->kobj, KOBJ_ADD); //发送一个uevent事件到用户空间,实现热插拔
bus_attach_device(dev); //自动匹配设备和驱动
if (parent)
klist_add_tail(&dev->knode_parent, &parent->klist_children);
if (dev->class) {
down(&dev->class->sem);
/* tie the class to the device */
list_add_tail(&dev->node, &dev->class->devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf, &dev->class->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
up(&dev->class->sem);
}
Done:
put_device(dev);
return error;
BusError:
device_pm_remove(dev);
dpm_sysfs_remove(dev);
PMError:
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->bus_notifier,
BUS_NOTIFY_DEL_DEVICE, dev);
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
if (dev->class) {
sysfs_remove_link(&dev->kobj, "subsystem");
/* If this is not a "fake" compatible device, remove the
* symlink from the class to the device. */
if (dev->kobj.parent != &dev->class->subsys.kobj)
sysfs_remove_link(&dev->class->subsys.kobj,
dev->bus_id);
if (parent) {
#ifdef CONFIG_SYSFS_DEPRECATED
char *class_name = make_class_name(dev->class->name,
&dev->kobj);
if (class_name)
sysfs_remove_link(&dev->parent->kobj,
class_name);
kfree(class_name);
#endif
sysfs_remove_link(&dev->kobj, "device");
}
}
ueventattrError:
device_remove_file(dev, &uevent_attr);
attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
kobject_del(&dev->kobj);
Error:
if (parent)
put_device(parent);
goto Done;
}
void bus_attach_device(struct device * dev)
{
struct bus_type *bus = dev->bus;
int ret = 0;
if (bus) {
dev->is_registered = 1;
//如果总线自动探测设备,则进行设备驱动匹配。(可以通过bus下的autoprobe属性文件查看和修改是否支持自动探测)
if (bus->drivers_autoprobe)
ret = device_attach(dev);
WARN_ON(ret < 0);
if (ret >= 0)
klist_add_tail(&dev->knode_bus, &bus->klist_devices);
else
dev->is_registered = 0;
}
}
int device_attach(struct device * dev)
{
int ret = 0;
down(&dev->sem);
if (dev->driver) {
ret = device_bind_driver(dev);
if (ret == 0)
ret = 1;
else {
dev->driver = NULL;
ret = 0;
}
} else {
//将设备和该总线上的所有驱动进行匹配
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
}
up(&dev->sem);
return ret;
}
static int __device_attach(struct device_driver * drv, void * data)
{
struct device * dev = data;
return driver_probe_device(drv, dev);
}
int driver_probe_device(struct device_driver * drv, struct device * dev)
{
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
//首先调用总线的match方法进行匹配
if (drv->bus->match && !drv->bus->match(dev, drv))
goto done;
pr_debug("%s: Matched Device %s with Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
//调用总线或者驱动的probe方法继续进一步的匹配。
ret = really_probe(dev, drv);
done:
return ret;
}
注册驱动
int driver_register(struct device_driver *drv)
注册驱动到设备驱动模型,在sysfs创建相应的目录结构,以及自动匹配设备驱动。一般在内核中会包装在其他的方法中在内部调用driver_register方法。在sysfs中的结构是:
/sys/bus/platform/drivers/serial8250/
|-- bind
|-- serial8250 -> ../../../../devices/platform/serial8250
|-- uevent
`-- unbind
/sys/devices/platform/serial8250
|-- driver -> ../../../bus/platform/drivers/serial8250
int driver_register(struct device_driver * drv)
{
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown)) {
printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
}
klist_init(&drv->klist_devices, NULL, NULL);
return bus_add_driver(drv);
}
int bus_add_driver(struct device_driver *drv)
{
struct bus_type * bus = bus_get(drv->bus);
int error = 0;
if (!bus)
return -EINVAL;
pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
error = kobject_set_name(&drv->kobj, "%s", drv->name);
if (error)
goto out_put_bus;
drv->kobj.kset = &bus->drivers;
error = kobject_register(&drv->kobj);
if (error)
goto out_put_bus;
//自动匹配设备和驱动
if (drv->bus->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__FUNCTION__, drv->name);
}
error = driver_add_attrs(bus, drv);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
__FUNCTION__, drv->name);
}
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__FUNCTION__, drv->name);
}
return error;
out_unregister:
kobject_unregister(&drv->kobj);
out_put_bus:
bus_put(bus);
return error;
}
- Linux设备驱动模型
- Linux设备驱动模型
- Linux设备驱动模型
- linux设备驱动模型
- Linux设备驱动模型
- Linux设备驱动模型
- linux设备驱动模型
- linux设备驱动模型
- Linux设备驱动模型
- Linux 设备驱动模型
- Linux设备驱动模型 .
- Linux设备驱动模型
- linux驱动模型 - 设备
- Linux设备驱动模型
- Linux设备驱动模型
- Linux设备驱动模型
- Linux设备驱动模型
- Linux设备驱动模型
- MYSQL my.cnf 配置 163.com
- Totem协议(SRP/RRP)讲解PPT
- asn,sku含义
- Ext 4 下拉菜单 树形
- [故事]一位经济学大师的尴尬
- Linux设备驱动模型
- Android最佳流畅设计
- java计算两数百分比方法
- Android 对话框(Dialog)大全 建立你自己的对话框
- PB 调用存储过程全攻略[Oracle篇]
- BIOS、MBR、PBR等基础知识
- Android 下移植WIFI 驱动
- MFC中"没有找到MFC90ud.dll的解决办法
- flex4 学习资料内容介绍