分类: LINUX
2011-03-24 22:35:15
|
作者:hjlin
内核版本:2.6.29
设备驱动模型框架是linux驱动编程的基础。它通过kobject,kset,ktype等底层数据结构将bus_type, device, device_driver 等高层数据结构组织起来,形成一个层次、分类清晰的驱动模型。优点如下:
1. 代码重用。将对象抽象为总线、驱动、设备三种,各司其职。同一总线的多个驱动使用相同的总线对象。同一驱动可以关联驱动多个设备。
2. 通过sysfs文件系统,清晰了展示内核驱动模型中的层次关系。同时sysfs文件系统还提供了方便的同用户控件交互的接口。
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; }; |
通过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); }; |
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; }; |
代表一个总线。对应/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; }; |
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; }; |
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; }; |
static int kobject_add_internal(struct kobject *kobj)
将kobject添加到设备驱动模型中。主要设置父kobj以及将自己添加到所属的kset的list中,并且在sysfs中创建目录。
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; } |