Linux設備驅動模型

 

簡介

作者: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;

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章