Chinaunix首页 | 论坛 | 博客
  • 博客访问: 584575
  • 博文数量: 92
  • 博客积分: 5026
  • 博客等级: 大校
  • 技术积分: 1321
  • 用 户 组: 普通用户
  • 注册时间: 2008-02-28 11:04
文章分类

全部博文(92)

文章存档

2011年(9)

2010年(17)

2009年(12)

2008年(54)

我的朋友

分类: LINUX

2011-03-24 22:35:15

文件: 设备驱动模型.rar
大小: 520KB
下载: 下载
简介

作者:hjlin

内核版本:2.6.29

设备驱动模型框架是linux驱动编程的基础。它通过kobject,kset,ktype等底层数据结构将bus_type, device, device_driver 等高层数据结构组织起来,形成一个层次、分类清晰的驱动模型。优点如下:

1.       代码重用。将对象抽象为总线、驱动、设备三种,各司其职。同一总线的多个驱动使用相同的总线对象。同一驱动可以关联驱动多个设备。

2.       通过sysfs文件系统,清晰了展示内核驱动模型中的层次关系。同时sysfs文件系统还提供了方便的同用户控件交互的接口。

框架

数据结构

Kobject

Kobject是代表驱动模型中的一个对象。总线、驱动、设备都继承了它。(在结构体中包含kobject)。每个kobjectsysfs中表现为一个目录。

每个kobject都有一个parent kobject和所属的ksetKset就是kobject所属的kset,通过kset的链表可以找到所有属于它的kobject。这些kobject进行uevent操作时,都会调用所属的ksetuevent_ops方法。父kobj,用于表示kobject之间或者kobjectksetkset之间的在sysfs中的目录结构关系。如果父kobj不存在,并且所属的kset存在的话,则父kobj就是设置为所属的kset的内嵌kobj。因此,注册设备、驱动或者总线的时候如果不指定parent kobj的话,父kobj则会设置为所属的ksetkobj。(todo:最好画图表示关系)

 

struct kobject {

       const char              * k_name;

       struct kref              kref;

       struct list_head       entry;   //连入到所属的ksetlist

       struct kobject         * parent; //kobj,如果没有父kobj,则使用所属的kset内嵌kobj

       struct kset              * kset;  //所属的kset(如果有的话)

       struct kobj_type     * ktype; //所属的ktype,如果所属的kset也有ktype,则用ksetktype

       struct sysfs_dirent   * sd;

};

 

Kest

通过kset可以将kobject组织成一颗层次树。

struct kset {

       struct list_head       list; //属于该ksetkobj链表

       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; //相应的kobjectkset)的默认属性。对应于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_driverprobe方法

       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以及将自己添加到所属的ksetlist中,并且在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.cplatform_bus_init方法中使用了device_register(&platform_bus),注册了一个设备,该设备的sys目录为/sys/devices/platform/。但是一般情况下,内核对于不同的总线会提供不同的封装方法在内部调用device_register来注册设备。比如说platform_register_device,并且在platform_register_device方法中设置了device的父kobjplatform_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;

       //创建所属classkobject_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;

}

 

阅读(1589) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~