Chinaunix首页 | 论坛 | 博客
  • 博客访问: 770963
  • 博文数量: 370
  • 博客积分: 2334
  • 博客等级: 大尉
  • 技术积分: 3222
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-06 16:56
文章分类

全部博文(370)

文章存档

2013年(2)

2012年(368)

分类: LINUX

2012-06-15 21:11:33

数据结构

struct kobject {
    const char    * k_name;                /*kobject 的名字数组(sysfs 入口使用的名字)指针;
                                                                      如果名字数组大小小于KOBJ_NAME_LEN,它指向本数组的name,否则指向另外分配的一个名字数组空间 */
    char              name[KOBJ_NAME_LEN]; /*kobject 的名字数组,若名字数组大小不小于KOBJ_NAME_LEN,只储存前KOBJ_NAME_LEN个字符*/
    struct kref        kref;              /*kobject 的引用计数*/
    struct list_head    entry;           /*kobject 之间的双向链表,与所属的kset形成环形链表*/
    struct kobject        * parent;     /*在sysfs分层结构中定位对象,指向上一级kset中的struct kobject kobj*/
    struct kset        * kset;           *指向所属的kset*/
    struct kobj_type    * ktype;        /*负责对该kobject类型进行跟踪的struct kobj_type的指针*/
    struct dentry        * dentry;      /*sysfs文件系统中与该对象对应的文件节点路径指针*/
    wait_queue_head_t    poll;          /*等待队列头*/
};

struct kobj_type 关注的是对象的类型
struct kobj_type {
       void (*release)(struct kobject *);
     struct sysfs_ops  *sysfs_ops;
     struct sttribute  **default_attrs;
};


而struct kset 关心的是对象的聚合和集合,其主要功能是包容,可认为是kobjects 的顶层容器类。
struct kset {
    struct kobj_type    * ktype; /*指向该kset对象类型的指针*/
    struct list_head    list;/*用于连接该kset中所有kobject以形成环形链表的链表头*/
    spinlock_t        list_lock;/*用于避免竞态的自旋锁*/
    struct kobject        kobj; /*嵌入的kobject*/
    struct kset_uevent_ops    * uevent_ops;

/*原有的struct kset_hotplug_ops * hotplug_ops;已经不存在,被kset_uevent_ops 结构体替换,在热插拔操作中会介绍*/
};

在新的内核里, kset 不再包含一个子系统指针struct subsystem * subsys, 而且subsystem已经被kset取代。


子 系统是对整个内核中一些高级部分的表述。子系统通常(但不一定)出现在 sysfs分层结构中的顶层,内核子系统包括 block_subsys(/sys/block 块设备)、 devices_subsys(/sys/devices 核心设备层)以及内核已知的用于各种总线的特定子系统。

struct subsystem {
struct kset kset;       内嵌的kset对象
struct rw_semaphore rwsem; 互斥访问信号量
};


struct kobj_type {
 void (*release)(struct kobject *);
 struct sysfs_ops *sysfs_ops;/*提供实现以下属性的方法*/
 struct attribute **default_attrs; /*用于保存类型属性列表(指针的指针) */
};

struct attribute {
 char *name;/*属性的名字( 在 kobject 的 sysfs 目录中显示)*/
 struct module *owner;/*指向模块的指针(如果有), 此模块负责实现这个属性*/
 mode_t mode; /*属性的保护位,modes 的宏定义在 :例如S_IRUGO 为只读属性等等*/
}; /*default_attrs 列表中的最后一个元素必须用 0 填充*/

sysfs 读写这些属性是由 kobj_type->sysfs_ops 成员中的函数完成的:

struct sysfs_ops {
 ssize_t (*show)(struct kobject *kobj, struct attribute *attr, char *buffer);
 ssize_t (*store)(struct kobject *kobj, struct attribute *attr, const char *buffer, size_t size);
};





struct bus_type {
    const char        * name;/*总线类型名称*/
    struct module        * owner;/*指向模块的指针(如果有), 此模块负责操作这个总线*/

    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;

    struct bus_attribute    * bus_attrs; /*总线属性*/
    struct device_attribute * dev_attrs; /*设备属性,指向为每个加入总线的设备建立的默认属性链表*/
    struct driver_attribute * drv_attrs; /*驱动程序属性*/
    struct bus_attribute drivers_autoprobe_attr;/*驱动自动探测属性*/
    struct bus_attribute drivers_probe_attr;/*驱动探测属性*/

    int        (*match)(struct device * dev, struct device_driver * drv);
    int        (*uevent)(struct device *dev, char **envp,
                 int num_envp, char *buffer, int buffer_size);
    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 (*suspend_late)(struct device * dev, pm_message_t state);
    int (*resume_early)(struct device * dev);
    nt (*resume)(struct device * dev);
/*处理热插拔、电源管理、探测和移除等事件的方法*/
    unsigned int drivers_autoprobe:1;
};


设备

在最底层, Linux 系统中的每个设备由一个 struct device 代表:

struct device {
    struct klist        klist_children;
    struct klist_node    knode_parent;   /* node in sibling list */
    struct klist_node    knode_driver;
    struct klist_node    knode_bus;
    struct device        *parent;/* 设备的 "父" 设备,该设备所属的设备,通常一个父设备是某种总线或者主控制器. 如果 parent 是 NULL, 则该设备是顶层设备,较少见 */

    struct kobject kobj;/*代表该设备并将其连接到结构体系中的 kobject; 注意:作为通用的规则, device->kobj->parent 应等于 device->parent->kobj*/
    char    bus_id[BUS_ID_SIZE];/*在总线上唯一标识该设备的字符串;例如: PCI 设备使用标准的 PCI ID 格式, 包含:域, 总线, 设备, 和功能号.*/
    struct device_type    *type;
    unsigned        is_registered:1;
    unsigned        uevent_suppress:1;
    struct device_attribute uevent_attr;
    struct device_attribute *devt_attr;

    struct semaphore    sem;  /* semaphore to synchronize calls to its driver. */
    struct bus_type    * bus;     /*标识该设备连接在何种类型的总线上*/
    struct device_driver *driver;    /*管理该设备的驱动程序*/
    void        *driver_data;    /*该设备驱动使用的私有数据成员*/
    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 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;

    spinlock_t        devres_lock;
    struct list_head    devres_head;

    /* class_device migration path */
    struct list_head    node;
    struct class        *class;
    dev_t          devt;       /* dev_t, creates the sysfs "dev" */
    struct attribute_group    **groups;    /* optional groups */

    void    (*release)(struct device * dev);/*当这个设备的最后引用被删除时,内核调用该方法; 它从被嵌入的 kobject 的 release 方法中调用。所有注册到核心的设备结构必须有一个 release 方法, 否则内核将打印错误信息*/
};
/*在注册 struct device 前,最少要设置parent, bus_id, bus, 和 release 成员*/



设备驱动程序

设备模型跟踪所有系统已知的驱动,主要目的是使驱动程序核心能协调驱动和新设备之间的关系。一旦驱动在系统中是已知的对象就可能完成大量的工作。驱动程序的结构体 device_driver 定义如下:

/*定义在*/
struct device_driver {
    const char        * name;/*驱动程序的名字( 在 sysfs 中出现 )*/
    struct bus_type        * bus;/*驱动程序所操作的总线类型*/

    struct kobject        kobj;/*内嵌的kobject对象*/
    struct klist        klist_devices;/*当前驱动程序能操作的设备链表*/
    struct klist_node    knode_bus;

    struct module        * owner;
    const char         * mod_name;    /* used for built-in modules */
    struct module_kobject    * mkobj;

    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);
};

/*注册device_driver 结构的函数是:*/
int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);

/*driver的属性结构在:*/
struct driver_attribute {
 struct attribute attr;
 ssize_t (*show)(struct device_driver *drv, char *buf);
 ssize_t (*store)(struct device_driver *drv, const char *buf, size_t count);
};
DRIVER_ATTR(_name,_mode,_show,_store)

/*属性文件创建的方法:*/
int driver_create_file(struct device_driver * drv, struct driver_attribute * attr);
void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr);

/*bus_type 结构含有一个成员( drv_attrs ) 指向一组为属于该总线的所有设备创建的默认属性*/





管理类的接口

类由 struct class 的结构体来定义:

/*
 * device classes
 */
struct class {
    const char        * name;/*每个类需要一个唯一的名字, 它将显示在 /sys/class 中*/
    struct module        * owner;

    struct kset        subsys;
    struct list_head    children;
    struct list_head    devices;
    struct list_head    interfaces;
    struct kset        class_dirs;
    struct semaphore    sem;    /* locks both the children and interfaces lists */

    struct class_attribute        * class_attrs;/* 指向类属性的指针(以NULL结尾) */
    struct class_device_attribute    * class_dev_attrs;/* 指向类中每个设备的一组默认属性的指针 */
    struct device_attribute        * dev_attrs;

    int    (*uevent)(struct class_device *dev, char **envp,
             int num_envp, char *buffer, int buffer_size);/* 类热插拔产生时添加环境变量的函数 */
    int    (*dev_uevent)(struct device *dev, char **envp, int num_envp,
                char *buffer, int buffer_size);/* 类中的设备热插拔时添加环境变量的函数 */

    void    (*release)(struct class_device *dev);/* 把设备从类中删除的函数 */
    void    (*class_release)(struct class *class);/* 删除类本身的函数 */
    void    (*dev_release)(struct device *dev);

    int    (*suspend)(struct device *, pm_message_t state);
    int    (*resume)(struct device *);
};


/*类注册函数:*/
int class_register(struct class *cls);
void class_unregister(struct class *cls);

/*类属性的接口:*/
struct class_attribute {
 struct attribute attr;
 ssize_t (*show)(struct class *cls, char *buf);
 ssize_t (*store)(struct class *cls, const char *buf, size_t count);
};
CLASS_ATTR(_name,_mode,_show,_store);
int class_create_file(struct class *cls, const struct class_attribute *attr);
void class_remove_file(struct class *cls, const struct class_attribute *attr);




类设备

类存在的真正目的是给作为类成员的各个设备提供一个容器,成员由 struct class_device 来表示:

struct class_device {
    struct list_head    node;/*for internal use by the driver core only*/
    struct kobject        kobj;/*for internal use by the driver core only*/
    struct class        * class;    /* 指向该设备所属的类,必须*/
    dev_t            devt;        /* dev_t, creates the sysfs "dev" ,for internal use by the driver core only*/
    struct class_device_attribute *devt_attr;/*for internal use by the driver core only*/
    struct class_device_attribute uevent_attr;
    struct device        * dev;        /* 指向/*挂接在该总线的设备链表*/此设备相关的 device 结构体,可选。若不为NULL,应是一个从类入口到/sys/devices 下相应入口的符号连接,以便用户空间查找设备入口*/
    void            * class_data;    /* 私有数据指针 */
    struct class_device    *parent;    /* parent of this child device, if there is one */
    struct attribute_group ** groups;    /* optional groups */

    void    (*release)(struct class_device *dev);
    int    (*uevent)(struct class_device *dev, char **envp,
             int num_envp, char *buffer, int buffer_size);
    char    class_id[BUS_ID_SIZE];    /* 此类中的唯一的名字 */
};

/*类设备注册函数:*/
int class_device_register(struct class_device *cd);
void class_device_unregister(struct class_device *cd);

/*重命名一个已经注册的类设备入口:*/
int class_device_rename(struct class_device *cd, char *new_name);

/*类设备入口属性:*/
struct class_device_attribute {
 struct attribute attr;
 ssize_t (*show)(struct class_device *cls, char *buf);
 ssize_t (*store)(struct class_device *cls, const char *buf,
 size_t count);
};

CLASS_DEVICE_ATTR(_name, _mode, _show, _store);

/*创建和删除除struct class中设备默认属性外的属性*/
int class_device_create_file(struct class_device *cls, const struct class_device_attribute *attr);
void class_device_remove_file(struct class_device *cls, const struct class_device_attribute *attr);

类接口
类子系统有一个 Linux 设备模型的其他部分找不到的附加概念,称为“接口”, 可将它理解为一种设备加入或离开类时获得信息的触发机制,结构体如下:

struct class_interface {
    struct list_head    node;
    struct class        *class;/* 指向该接口所属的类*/

    int (*add) (struct class_device *, struct class_interface *);

/*当一个类设备被加入到在 class_interface 结构中指定的类时, 将调用接口的 add 函数,进行一些设备需要的额外设置,通常是添加更多属性或其他的一些工作*/
    void (*remove)    (struct class_device *, struct class_interface *);/*一个接口的功能是简单明了的. 当设备从类中删除, 将调用remove 方法来进行必要的清理*/
    int (*add_dev)     (struct device *, struct class_interface *);
    void (*remove_dev) (struct device *, struct class_interface *);
};

/*注册或注销接口的函数:*/
int class_interface_register(struct class_interface *class_intf);
void class_interface_unregister(struct class_interface *class_intf);
/*一个类可注册多个接口*/













11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111

static LIST_HEAD(adapters);
static LIST_HEAD(drivers);

linux-2.6/drivers/i2c/i2c-core.c

struct bus_type 2c_bus_type = {
    .name       = "i2c",
    .dev_attrs  = i2c_dev_attrs,
    .match      = i2c_device_match,
    .uevent     = i2c_device_uevent,
    .probe      = i2c_device_probe,
    .remove     = i2c_device_remove,
    .shutdown   = i2c_device_shutdown,
    .suspend    = i2c_device_suspend,
    .resume     = i2c_device_resume,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);

struct class i2c_adapter_class = {
    .owner          = THIS_MODULE,
    .name           = "i2c-adapter",
    .dev_attrs      = i2c_adapter_attrs,
};
EXPORT_SYMBOL_GPL(i2c_adapter_class);       /* exported to i2c-isa */




static int __init i2c_init(void)
{

    retval = bus_register(&i2c_bus_type);

    return class_register(&i2c_adapter_class);
}

subsys_initcall(i2c_init);
module_exit(i2c_exit);


int bus_register(struct bus_type * bus)
{

    retval = kobject_set_name(&bus->subsys.kobj, "%s", bus->name);  
    subsys_set_kset(bus, bus_subsys);                bus_subsys也是一个子系统。                                        设置bus的父亲   #define subsys_set_kset(obj,_subsys)  (obj)->subsys.kobj.kset = &(_subsys)
    retval = subsystem_register(&bus->subsys);



    kobject_set_name(&bus->devices.kobj, "devices");    在I2C目录下有devices目录
    bus->devices.kobj.parent = &bus->subsys.kobj;    在I2C目录下有devices目录
    retval = kset_register(&bus->devices);

 
    kobject_set_name(&bus->drivers.kobj, "drivers");   在I2C目录下有drivers 目录
    bus->drivers.kobj.parent = &bus->subsys.kobj;
    bus->drivers.ktype = &ktype_driver;
    retval = kset_register(&bus->drivers);

 
    klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
    klist_init(&bus->klist_drivers, NULL, NULL);
 
    retval = bus_add_attrs(bus);

}

总线注册总结:
    在注册总线时,基本分为三部分。
    第一,于sysfs文件系统对应的是调用subsystem_register(&bus->subsys),在/sys/bus目录下创建总线目录;
    第二,在刚刚创建的总线目录下分别创建devices和drivers目录。(调用kset_register()函数)
    第三,在刚刚创建的总线目录下增加总线属性。



struct class i2c_adapter_class = {
    .owner          = THIS_MODULE,
    .name           = "i2c-adapter",
    .dev_attrs      = i2c_adapter_attrs,
}; 
EXPORT_SYMBOL_GPL(i2c_adapter_class);       /* exported to i2c-isa */



int class_register(struct class * cls)
{
    INIT_LIST_HEAD(&cls->children);
    INIT_LIST_HEAD(&cls->devices);
    INIT_LIST_HEAD(&cls->interfaces);

    kset_init(&cls->class_dirs);      初始化class目录kset
    error = kobject_set_name(&cls->subsys.kobj, "%s", cls->name);
                        设置在sys/class/目录下显示的目录名称。
    subsys_set_kset(cls, class_subsys);    设置父目录为sys/class/目录
    error = subsystem_register(&cls->subsys);
  
    error = add_class_attrs(class_get(cls));
    class_put(cls);

}










22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222


linux-2.6/drivers/i2c/i2c-dev.c

static struct i2c_driver i2cdev_driver = {
    .driver = {
        .name   = "dev_driver",
    },
    .id     = I2C_DRIVERID_I2CDEV,
    .attach_adapter = i2cdev_attach_adapter,
    .detach_adapter = i2cdev_detach_adapter,
    .detach_client  = i2cdev_detach_client,
};

static int __init i2c_dev_init(void)
{

    res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);

    i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
 

    res = i2c_add_driver(&i2cdev_driver);

    return 0;

}

static inline int i2c_add_driver(struct i2c_driver *driver)
{  
    return i2c_register_driver(THIS_MODULE, driver);
}





int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{


    /* add the driver to the list of i2c drivers in the driver core */
    driver->driver.owner = owner;
    driver->driver.bus = &i2c_bus_type;


    res = driver_register(&driver->driver);


    mutex_lock(&core_lists);
    list_add_tail(&driver->list,&drivers);

   list_for_each_entry(adapter, &adapters, list)
         {
       driver->attach_adapter(adapter);        针对每个适配器调用 driver->attach_adapter(adapter);
    }

    mutex_unlock(&core_lists);
    return 0;
}


调用 driver->attach_adapter(adapter),即是这个函数。

static int i2cdev_attach_adapter(struct i2c_adapter *adap)

     i2c_dev = get_free_i2c_dev(adap);

    /* register this i2c device with the driver core */
    i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
                     MKDEV(I2C_MAJOR, adap->nr),
                     "i2c-%d", adap->nr);

    res = device_create_file(i2c_dev->dev, &dev_attr_name);



struct device *device_create(struct class *class, struct device *parent,
                 dev_t devt, const char *fmt, ...)
{
    struct device *dev = NULL;

    dev = kzalloc(sizeof(*dev), GFP_KERNEL);

    dev->devt = devt;
    dev->class = class;
    dev->parent = parent;
    dev->release = device_create_release;

    vsnprintf(dev->bus_id, BUS_ID_SIZE, fmt, args);

    retval = device_register(dev);

    return dev;

}









333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333

驱动注册:


int driver_register(struct device_driver * drv)
{
    return bus_add_driver(drv);
}


int bus_add_driver(struct device_driver *drv)
{

    struct bus_type * bus = get_bus(drv->bus);

    if (!bus)
        return -EINVAL;

    error = kobject_set_name(&drv->kobj, "%s", drv->name);


    drv->kobj.kset = &bus->drivers;               设置Kobject的kset指针为bus下的drivers成员(为kset类型)
                                                                     即设置驱动在sysfs中的父目录为/sys/bus_type/drivers。
    error = kobject_register(&drv->kobj)          在sysfs文件系统中注册以drv->name为名称的目录。


    error = driver_attach(drv);          遍历这个bus上的所有的设备,看有没有和driver相匹配的

    klist_add_tail(&drv->knode_bus, &bus->klist_drivers);     把驱动加入这个总线的一个驱动链表中。
    module_add_driver(drv->owner, drv);

    error = driver_add_attrs(bus, drv);

    error = add_bind_files(drv);    当编译配置了CONFIG_HOTPLUG(热插拔)时,为drv建立两个属性,bind and unbind。否则,什么也不做。


    return error;

}

driver_attach(drv);        
这个函数最终调用drv->probe或者总线子设备链表的dev->bus->probe——即drv->bus->bus->probe函数。



void module_add_driver(struct module *mod, struct device_driver *drv)
{
    struct module_kobject *mk = NULL;
    struct kobject *mkobj;


    if (mod)
        mk = &mod->mkobj;
    else if (drv->mod_name) {
      
        mkobj = kset_find_obj(&module_subsys, drv->mod_name);
        mk = container_of(mkobj, struct module_kobject, kobj);     
        drv->mkobj = mk;   
        drv->mkobj = mk;
          
        kobject_put(mkobj);
          }

    sysfs_create_link(&drv->kobj, &mk->kobj, "module");    在/sys/bus_type/driver/drv->name/下建立一个链接文件"module"
                                                                                指向/sys/module/module_name

    driver_name = make_driver_name(drv);
    if (driver_name) {
        module_create_drivers_dir(mk);               在/sys/module以模块名建立目录
        no_warn = sysfs_create_link(mk->drivers_dir, &drv->kobj,
                        driver_name);                   在/sys/module/module_name/drivers目录下建立指向
                                                                          /sys/bus_type/driver/drv->name/的链接。
        kfree(driver_name);
    }
}

驱动注册总结:
    在驱动注册时,首先要做得是在总线下的driver目录下以drv->name为名称的目录。
    在遍历总线上注册的所有的设备进行匹配(后面进行详细的讲述),再把要注册的驱动挂在总线的驱动链表上。
    通过调用module_add_driver()函数,在/sys/bus_type/driver/drv->name/下建立一个链接文件"module", 指向/sys/module/module_name。
    并且在/sys/module/module_name/drivers目录下建立指向/sys/bus_type/driver/drv->name/的链接。
    最后,再在/sys/bus_type/driver/drv->name/目录下增加几个属性文件。
   

4444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444

设备注册:


int device_register(struct device *dev)
{
    device_initialize(dev);             初始化 struct device *dev设备模型控制的一些变量
    return device_add(dev);
}



设备注册的大量工作由device_add(dev)函数来完成。
int (*platform_notify)(struct device * dev) = NULL;


int device_add(struct device *dev)
{
    struct device *parent = NULL;
    char *class_name = NULL;
    struct class_interface *class_intf;
    int error = -EINVAL;


    parent = get_device(dev->parent);      注册I2C从设备时,parent是apapter

    error = setup_parent(dev, parent);     设置dev->kobject.parent = parent->kobject;
                                                       


    kobject_set_name(&dev->kobj, "%s", dev->bus_id);
    error = kobject_add(&dev->kobj);              
    将kobj对象加入Linux设备层次。
挂接该kobject对象到kset的list链中,增加父目录各级kobject的引用计数,在其parent指向的目录下创建文件节点,并启动该类型内核对象的hotplug函数。

    if (platform_notify)           
        platform_notify(dev);

    if (dev->bus)                 
        blocking_notifier_call_chain(&dev->bus->bus_notifier,
                         BUS_NOTIFY_ADD_DEVICE, dev);



    dev->uevent_attr.attr.XXXXX = XXXXX;      设置设备的热插拔属性
    device_create_file(dev, &dev->uevent_attr);       在设备增加热插拔属性。在设备文件夹下创建文件“uevent”

   if (MAJOR(dev->devt)) {                       如果主设备号不为0, 则在设备目录下创建设备属性文件“dev”
        attr = kzalloc(sizeof(*attr), GFP_KERNEL);
        attr->attr.name = "dev";
        attr->attr.mode = S_IRUGO;
        attr->attr.owner = dev->driver->owner;
        attr->show = show_dev;
        error = device_create_file(dev, attr);    创建设备属性文件。
        dev->devt_attr = attr;        设置设备属性。

    }


    if (dev->class) {
        sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj,  "subsystem");   

        if (dev->kobj.parent != &dev->class->subsys.kobj)
            sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, dev->bus_id);
        if (parent) {
            sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");
#ifdef CONFIG_SYSFS_DEPRECATED
            class_name = make_class_name(dev->class->name, &dev->kobj);
            if (class_name)
                sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);
#endif
        }
    }

    error = device_add_attrs(dev)           

    error = device_pm_add(dev)               增加设备的powermanage属性,在设备目录下创建power目录,
                                                            并在其下创建两个文件,state和wakeup。

    error = bus_add_device(dev)              可以参考bus_add_deriver()

    kobject_uevent(&dev->kobj, KOBJ_ADD);
    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);
    }
    
    return error;
}


设备注册总结:
    在父设备目录目录下创建子设备目录。
    在子设备目录下创建uevent文件(设备热插拔属性),
    如果设备主设备号不为0,在子设备目录下创建dev文件(设备属性文件),
    如果设备的类型指针不为NULL,则创建链接文件“subsystem”,指向/sys/bus/bus_type,等等,
    增加设备的powermanage属性,在设备目录下创建power目录,并在其下创建两个文件,state和wakeup。
    在设备目录下创建总线属性文件(即在这条总线上的每个设备默认的属性),
    在/sys/bus/bus_type/device/目录下创建链接文件dev->bus_id,指向/sys/device/(dev->bus_id)。
    调用bus_attach_device(dev)函数,进行驱动探测,并把设备挂在BUS的设备链表上。
    把设备加入父设备的“子设备链表”上。
    如果设备的类型指针不为NULL,把设备挂在设备类型的设备链表上。
    最后,notify any interfaces that the device is here。
   


5555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555

IIC设备和驱动的注册

对于2.6内核来说,IIC适配器的注册分两个部分,设备的注册和驱动程序的注册。

paltform总线和设备的注册。

    由于IIC适配器是片上设备,集成在芯片内部,通过内部的设备总线直接通信,所以设备应该注册在系统的虚拟总线paltform上。

platform简介:platform是一个虚拟的总线。相比PCI、USB,它主要用于描述SOC上的一些资源,
比如说,cq8401上集成的控制器。platform所描述的资源有一个共同的特点,就是在CPU的总线上直接取址。
在内核源代码/drivers/base/core.c文件描述的platform总线和总线设备的注册和总线的一些算法。

platform总线类型
struct bus_type platform_bus_type = {
    .name       = "platform",
    .dev_attrs  = platform_dev_attrs,
    .match      = platform_match,
    .uevent     = platform_uevent,
    .suspend    = platform_suspend,
    .suspend_late   = platform_suspend_late,
    .resume_early   = platform_resume_early,
    .resume     = platform_resume,
};

struct device platform_bus = {
    .bus_id     = "platform",
};


在总线初始化中,调用device_register(&platform_bus)注册platform总线设备。
调用bus_register(&platform_bus_type)注册总线。


在头文件include/linux/platform_device.h中,导出了要注册到platform总线上的驱动和设备的类型。

struct platform_driver {
    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
    void (*shutdown)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*suspend_late)(struct platform_device *, pm_message_t state);
    int (*resume_early)(struct platform_device *);
    int (*resume)(struct platform_device *);
    struct device_driver driver;
};



struct platform_device {
    const char  * name;
    u32     id;
    struct device   dev;
    u32     num_resources;                 资源数目(资源数组的大小)
    struct resource * resource;            资源数组   
};











设备注册:






根据头文件导出的paltform设备类型来定义的platform总线上的IIC设备。
static struct platform_device cq8401_i2c_device = {
    .name       = "clx-i2c",
    .id     = 0,
};

定义在/arch/mips/cq8401/common/platform.c文件中,这个文件同样定义了USB,PCI适配器设备。

调用这个函数 platform_add_devices()把设备注册到platform总线上。

int platform_add_devices(struct platform_device **devs, int num)
{  
    int i, ret = 0;

    for (i = 0; i < num; i++) {
        ret = platform_device_register(devs[i]);
        }
    }

    return ret;
}
可以看出主要的注册工作有platform_device_register()来完成,这个函数主要循环注册一个设备。
接着调用platform_device_register()注册一个设备。


int platform_device_register(struct platform_device * pdev)
{  
    device_initialize(&pdev->dev);         把struct device 结构体中的一些设备模型核心自己使用的成员初始化。
    return platform_device_add(pdev);     
}  /drivers/base/core.c文件


int platform_device_add(struct platform_device *pdev)
{
    if (!pdev->dev.parent)
        pdev->dev.parent = &platform_bus;       设置父设备为platform_bus。

    pdev->dev.bus = &platform_bus_type;         把设备注册到那个总线上。

    if (pdev->id != -1)                         设置了struct device 结构体的bus_id成员。
        snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id);
    else
        strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);

    for (i = 0; i < pdev->num_resources; i++)
      {
              把资源进行一些初始化,主要是把他加入到资源链表中去。Inserts a resource in the resource tree!
      }

    ret = device_add(&pdev->dev);        把设备注册到sysfs 文件系统。
    return ret;
}







驱动注册:
   

根据头文件导出的paltform驱动类型来定义的platform总线上的IIC设备驱动。

static struct platform_driver i2c_pxa_driver = {
    .probe      = i2c_pxa_probe,
    .remove     = i2c_pxa_remove,
    .driver     = {
        .name   = "pxa2xx-i2c",
    },
}; 

在IIC适配器驱动模块初始化函数中进行注册。

platform_driver_register(&i2c_pxa_driver);
platform_driver_register()函数是/drivers/base/core.c文件导出的一些关于platform总线的算法。


int platform_driver_register(struct platform_driver *drv)
{
    drv->driver.bus = &platform_bus_type;
    if (drv->probe)
        drv->driver.probe = platform_drv_probe;
    if (drv->remove)
        drv->driver.remove = platform_drv_remove;
    if (drv->shutdown)
        drv->driver.shutdown = platform_drv_shutdown;
    if (drv->suspend)
        drv->driver.suspend = platform_drv_suspend;
    if (drv->resume)
        drv->driver.resume = platform_drv_resume;
    return driver_register(&drv->driver);
}
可以看出platform_driver_register()函数只不过把struct platform_driver
内嵌的struct driver结构体进行的一些初始化,直接调用的driver_register(&drv->driver);
把struct driver结构体注册到sysfs文件系统中。













666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666

番外篇:字符设备的注册过程。

struct cdev {
    struct kobject                         kobj;
    struct module                       *owner;
    const struct file_operations   *ops;
    struct list_head                     list;
    dev_t                                     dev;
    unsigned int                             count;
};

字符设备的初始化:
void cdev_init(struct cdev *, const struct file_operations *);   

字符设备的注册:
int cdev_add(struct cdev *, dev_t, unsigned);

int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
    p->dev = dev;
    p->count = count;
    return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}

p是cdev结构,dev是该设备对应的第一个设备编号,count是应该和该设备关联的设备编号的数量。
也就是说dev是该设备对应的第一个从设备号,而该设备对应的从设备号是从[dev, dev + count - 1];
大多数情况下,count的值是1。

在2.4内核中的字符设备的注册函数是
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
major是设备的主设备号,name是驱动程序的名称,fops是默认的file_operations结构。

这个函数和2.6内核的注册函数最大的区别是:
    register_chrdev()函数为给定的主设备号注册0 ~255作为次设备号,
主设备号标识设备对应的驱动程序,次设备号由内核用来确定设备文件所指向的设备。所以使用这个接口的驱动程序必须能够处理所有的
256个次设备号上的file_operation操作。并且不能够使用大于255的主设备号和次设备号。

    而2.6内核的cdev_add()函数,可以指定要注册的次设备号。

    当kobjects嵌入在其它数据结构内部的时候,数据结构接受了由kobject提供的标准函数。最为重要的是,数据结构的嵌入者kobject使此数 据结构成为对象层次结构的一部分。例如,通过父指针cdev->kobj->parent和列表 cdev->kobj->entry,就可以把cdev数据结构表示在对象层次结构之中。
阅读(1740) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~