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;

}

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 魅族m6相册闪退怎么办 美图m6工厂模式怎么办 美图m6手机死机怎么办 美图m6s开不了机怎么办 美图手机m6进水怎么办 美图t8摔黑屏怎么办 美图屏幕点不动怎么办 朗动钥匙丢了怎么办 深圳小汽车摇号中签了怎么办 深圳摇号审核通过后怎么办 京东过保修期了怎么办 买的商业预付卡怎么办 壹钱包预付卡金额不足怎么办 美发店换老板原来预付卡怎么办 超市预付卡现超市停业怎么办? 利群购物卡丢了怎么办 利群金卡丢了怎么办 坐飞机洗漱用品超过规定怎么办 请律师团了解后怎么办 三星s9开不了机怎么办 三星手机开不了机了怎么办 安卓手机音响进水了怎么办 音响不读u盘怎么办 虎牌水壶显示f2怎么办 海尔冰箱门关不严没吸力怎么办 忘记京东金融账号怎么办 京东账号手机号已停用怎么办 京东账户忘了怎么办 京东登录名忘了怎么办? 京东已经发货了怎么办 苹果7p黑亮掉漆怎么办 淘宝卖家不肯退运费怎么办 健身付款收据丢了怎么办 收据丢了怎么办能退款 苹果售后不承认基带问题怎么办 电话卡欠费了不用了怎么办 软件移不到sd卡怎么办 手机显示sd卡受损怎么办 美的冰箱出现e6怎么办 美的冰箱显示e6怎么办 冰箱电脑板坏了怎么办