前两节介绍了class,device结构体的成员,在这一节主要介绍class和device的关键函数.
1.classes_init()
系统调用路径:
start_kernel()->>rest_init()->>kernel_init()->>do_basic_setup()->>driver_init()->>classes_init();
函数源码:
int __init classes_init(void)
{
// 创建/sys/class目录
class_kset = kset_create_and_add("class", NULL, NULL);
if (!class_kset)
return -ENOMEM;
return 0;
}
说明:
该函数动态创建一个kset(class_kset ),并把它注册进系统.会在sysfs文件系统创建sys/class目录
2.class_create()
调用路径:
am335x_gpio_init()->>class_create();
驱动模块初始化函数中调用.
函数源码:
/* This is a #define to keep the compiler from merging different
* instances of the __key variable */
#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
struct class *__class_create(struct module *owner, const char *name,
struct lock_class_key *key)
{
struct class *cls;
int retval;
// 动态创建class
cls = kzalloc(sizeof(*cls), GFP_KERNEL);
if (!cls) {
retval = -ENOMEM;
goto error;
}
cls->name = name;
cls->owner = owner;
cls->class_release = class_create_release;
// 注册该class
retval = __class_register(cls, key);
if (retval)
goto error;
return cls;
error:
kfree(cls);
return ERR_PTR(retval);
}
int __class_register(struct class *cls, struct lock_class_key *key)
{
struct subsys_private *cp;
int error;
pr_debug("device class '%s': registering\n", cls->name);
//动态创建subsys_private结构体
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
if (!cp)
return -ENOMEM;
//初始化subsys_private中的设备链表klist_devices
klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
//初始化subsys_private中的接口链表class_interfaces
INIT_LIST_HEAD(&cp->class_interfaces);
kset_init(&cp->glue_dirs);
__mutex_init(&cp->class_mutex, "struct class mutex", key);
//设置class名称为subsys_private.subsys.kobj的名字,用于创建/sys/class/name目录
error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
if (error) {
kfree(cp);
return error;
}
/* set the default /sys/dev directory for devices of this class */
//设置class.dev_kobj成员指向sysfs_dev_char_kobj,该kobject表示/sys/dev/char目录
if (!cls->dev_kobj)
cls->dev_kobj = sysfs_dev_char_kobj;// sys/dev/char
#if defined(CONFIG_BLOCK)
/* let the block class directory show up in the root of sysfs */
if (!sysfs_deprecated || cls != &block_class)
cp->subsys.kobj.kset = class_kset; //class_kset在classes_init()中创建,表示/sys/class目录
#else
cp->subsys.kobj.kset = class_kset;
#endif
cp->subsys.kobj.ktype = &class_ktype;
cp->class = cls;
cls->p = cp; // 设置p指针
// 创建/sys/class/name目录
error = kset_register(&cp->subsys);
if (error) {
kfree(cp);
return error;
}
// 创建class自己的属性文件class_attrs,这里好像没有,呵呵
error = add_class_attrs(class_get(cls));
class_put(cls);
return error;
}
说明:
1).动态创建class
2).注册该class
a.动态创建subsys_private结构体
b.初始化subsys_private中的设备链表klist_devices
c.初始化subsys_private中的接口链表class_interfaces
d.设置class名称为subsys_private.subsys.kobj的名字,用于创建/sys/class/name目录
e.设置class.dev_kobj成员指向sysfs_dev_char_kobj,该kobject表示/sys/dev/char目录
f.创建/sys/class/name目录
g.创建class自己的属性文件class_attrs,如果有
3.device_create
调用路径:
am335x_gpio_init()->>device_create();
class_dev = device_create(gpio_class,NULL,dev,0,“am335x_gpio”)
驱动模块初始化函数中调用.
函数源码:
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
struct device *dev;
va_start(vargs, fmt);
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
va_end(vargs);
return dev;
}
说明:
1).va_start(),va_end()函数的用法在后面会讲解
2).该函数功能解析参数,调用device_create_vargs().
struct device *device_create_vargs(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt,
va_list args)
{
struct device *dev = NULL;
int retval = -ENODEV;
if (class == NULL || IS_ERR(class))
goto error;
// 动态创建device
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
retval = -ENOMEM;
goto error;
}
// 初始化device
dev->devt = devt;
dev->class = class;
dev->parent = parent;// NULL
dev->release = device_create_release;
dev_set_drvdata(dev, drvdata); // drvdata = 0
// 设置dev->kobj.name = "am335x_gpio"
retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
if (retval)
goto error;
// 调用device_register()
retval = device_register(dev);
if (retval)
goto error;
return dev;
error:
put_device(dev);
return ERR_PTR(retval);
}
说明:
1). 动态创建device
2). 初始化device
3).设置dev->kobj.name = “am335x_gpio”
4).调用device_register()
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset; // devices_kset指向sys/devices目录的kset
kobject_init(&dev->kobj, &device_ktype);//A top-level, abstract class from which other classes are derived.
INIT_LIST_HEAD(&dev->dma_pools);/* dma pools (if dma'ble) */
mutex_init(&dev->mutex);/* mutex to synchronize calls to its driver.*/
lockdep_set_novalidate_class(&dev->mutex);
spin_lock_init(&dev->devres_lock);//Spinlock to protect the resource of the device.
INIT_LIST_HEAD(&dev->devres_head);//The resources list of the device.
device_pm_init(dev);//For device power management.
set_dev_node(dev, -1);// CONFIG_NUMA未定义,所以该函数是空函数
}
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error = -EINVAL;
//增加device的引用计数器
dev = get_device(dev);
if (!dev)
goto done;
//检测device->p私有数据成员,如果空则需要初始化
if (!dev->p) {
error = device_private_init(dev);
if (error)
goto done;
}
/*
* for statically allocated devices, which should all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
*/
// 设置dev.kobject.name = dev->init_name
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
// 回读验证name是否已经设置
if (!dev_name(dev)) {
error = -EINVAL;
goto name_error;
}
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
//增加device->parent的引用计数器,因为要增加设备节点
parent = get_device(dev->parent);
//设置dev->kobj.parent = parent,如果未设置parent,则创建到"sys/devices/virtual/class名"目录里
setup_parent(dev, parent);
/* use parent numa_node */
if (parent)
set_dev_node(dev, dev_to_node(parent));//CONFIG_NUMA未定义,空函数
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
//创建设备目录,如:/sys/devices/virtual/am335x_gpio/am335x_gpio
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error)
goto Error;
/* notify platform of device entry */
//调用平台相关的通知函数
if (platform_notify)
platform_notify(dev);
// 创建uevent属性文件
error = device_create_file(dev, &uevent_attr);
if (error)
goto attrError;
if (MAJOR(dev->devt)) {
// 创建dev属性文件
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
//在sys/dev/char目录创建文件连接到dev->kobj目录,如: 251:0 -> ../../devices/virtual/am335x_gpio/am335x_gpio
error = device_create_sys_dev_entry(dev);
if (error)
goto devtattrError;
//创建设备节点,原理还需要再研究???
devtmpfs_create_node(dev);
}
// 创建设备和class目录的符号连接,
//1,创建文件名subsystem到class的连接,如:subsystem -> ../../../../class/am335x_gpio
//2.创建class目录到当前目录的连接,如:am335x_gpio -> ../../devices/virtual/am335x_gpio/am335x_gpio
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
// 创建设备相关的属性文件
error = device_add_attrs(dev);
if (error)
goto AttrsError;
// 添加总线相关的属性文件,并加入总线的设备链表(这里没有添加到bus)
error = bus_add_device(dev);
if (error)
goto BusError;
// 添加电源管理属性文件
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
//Add a device to the PM core's list of active devices.
device_pm_add(dev);
/* Notify clients of device addition. This call must come
* after dpm_sysf_add() and before kobject_uevent().
*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
//通知用户空间kobject的KOBJ_ADD事件
kobject_uevent(&dev->kobj, KOBJ_ADD);
// probe drivers for a new device
bus_probe_device(dev);
// 添加到设备树
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
// 添加设备到class,并调用相应的interfaces
if (dev->class) {
mutex_lock(&dev->class->p->class_mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->class_interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->class_mutex);
}
done:
put_device(dev);
return error;
DPMError:
bus_remove_device(dev);
BusError:
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
if (MAJOR(dev->devt))
devtmpfs_delete_node(dev);
if (MAJOR(dev->devt))
device_remove_sys_dev_entry(dev);
devtattrError:
if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
ueventattrError:
device_remove_file(dev, &uevent_attr);
attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
kobject_del(&dev->kobj);
Error:
cleanup_device_parent(dev);
if (parent)
put_device(parent);
name_error:
kfree(dev->p);
dev->p = NULL;
goto done;
}
说明:
1).增加device的引用计数器
2).检测device->p私有数据成员,如果空则需要初始化
3).回读验证name是否已经设置
4).设置dev->kobj.parent = parent,如果未设置parent,则创建到"sys/devices/virtual/class名"目录里
5).创建设备目录,如:/sys/devices/virtual/am335x_gpio/am335x_gpio
6).调用平台相关的通知函数
7).创建uevent属性文件
8).创建dev属性文件
9).在sys/dev/char目录创建文件连接到dev->kobj目录,如: 251:0 -> …/…/devices/virtual/am335x_gpio/am335x_gpio
10).创建设备节点,原理还需要再研究???
11).创建设备和class目录的符号连接
a.创建文件名subsystem到class的连接,如:subsystem -> …/…/…/…/class/am335x_gpio
b.创建class目录到当前目录的连接,如:am335x_gpio -> …/…/devices/virtual/am335x_gpio/am335x_gpio
12).创建设备相关的属性文件
13).添加总线相关的属性文件,并加入总线的设备链表(这里没有添加到bus)
14).添加电源管理属性文件
15).添加device到pm(电源管理)链表
16).通知用户空间kobject的KOBJ_ADD事件
17). 添加到设备树
18).添加设备到class,并调用相应的interfaces