数据结构
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) |